diff --git a/AKDashboard/templates/AKDashboard/dashboard.html b/AKDashboard/templates/AKDashboard/dashboard.html
index 5f4df6f2cc65d1af383ad78658cc2e2fe826ae3d..1e54236d4e7070e97ad54038ba367b39ce3071f2 100644
--- a/AKDashboard/templates/AKDashboard/dashboard.html
+++ b/AKDashboard/templates/AKDashboard/dashboard.html
@@ -25,14 +25,14 @@
                 {% if 'AKPlan'|check_app_installed %}
 
                     <a class="dashboard-box btn btn-primary"
-                       href="{% url 'plan:ak_plan_current_next' event_slug=event.slug %}">
+                       href="{% url 'plan:plan_overview' event_slug=event.slug %}">
                         <div class="col-sm-12 col-md-3 col-lg-2 dashboard-button">
                             <span class="fa fa-list-ul"></span>
                             <span class='text'>{% trans 'Current AKs' %}</span>
                         </div>
                     </a>
                     <a class="dashboard-box btn btn-primary"
-                       href="{% url 'plan:ak_plan_timeline' event_slug=event.slug %}">
+                       href="{% url 'plan:plan_wall' event_slug=event.slug %}">
                         <div class="col-sm-12 col-md-3 col-lg-2 dashboard-button">
                             <span class="fa fa-calendar"></span>
                             <span class='text'>{% trans "AK Wall" %}</span>
diff --git a/AKModel/admin.py b/AKModel/admin.py
index 51db59e78c3badc6fca0fe9d83ad1ac1724ccb2a..8446259d31b0a3fad0a8b1c981d7151e4a578b2e 100644
--- a/AKModel/admin.py
+++ b/AKModel/admin.py
@@ -2,12 +2,23 @@ from django.contrib import admin
 from django.contrib.admin import SimpleListFilter
 from django.db.models import Count, F
 from django.shortcuts import render
+from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 
 from AKModel.availability import Availability
 from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, Room, AKSlot
 
-admin.site.register(Event)
+
+@admin.register(Event)
+class EventAdmin(admin.ModelAdmin):
+    def get_form(self, request, obj=None, change=False, **kwargs):
+        # Use timezone of event
+        if obj is not None and obj.timezone:
+            timezone.activate(obj.timezone)
+        # No timezone available? Use UTC
+        else:
+            timezone.activate("UTC")
+        return super().get_form(request, obj, change, **kwargs)
 
 admin.site.register(AKOwner)
 
@@ -60,6 +71,28 @@ admin.site.register(AK, AKAdmin)
 
 admin.site.register(Room)
 
-admin.site.register(AKSlot)
 
-admin.site.register(Availability)
+@admin.register(AKSlot)
+class AKSlotAdmin(admin.ModelAdmin):
+    readonly_fields = ['updated']
+
+    def get_form(self, request, obj=None, change=False, **kwargs):
+        # Use timezone of associated event
+        if obj is not None and obj.event.timezone:
+            timezone.activate(obj.event.timezone)
+        # No timezone available? Use UTC
+        else:
+            timezone.activate("UTC")
+        return super().get_form(request, obj, change, **kwargs)
+
+
+@admin.register(Availability)
+class AvailabilityAdmin(admin.ModelAdmin):
+    def get_form(self, request, obj=None, change=False, **kwargs):
+        # Use timezone of associated event
+        if obj is not None and obj.event.timezone:
+            timezone.activate(obj.event.timezone)
+        # No timezone available? Use UTC
+        else:
+            timezone.activate("UTC")
+        return super().get_form(request, obj, change, **kwargs)
diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po
index 0424d2432fd669bbe5dffb5c159f676a0bc3e9bb..cb70a53ab00eb7efa16c89de131334f716f11e2a 100644
--- a/AKModel/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-30 13:18+0000\n"
+"POT-Creation-Date: 2020-03-01 14:21+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -23,17 +23,17 @@ msgstr "Ist ein Wunsch"
 msgid "Is not a wish"
 msgstr "Ist kein Wunsch"
 
-#: admin.py:53
+#: admin.py:54
 msgid "Export to wiki syntax"
 msgstr "In Wiki-Syntax exportieren"
 
-#: availability.py:38 models.py:30 models.py:51 models.py:103 models.py:121
-#: models.py:152 models.py:201 models.py:237 models.py:263
+#: availability.py:38 models.py:37 models.py:58 models.py:110 models.py:128
+#: models.py:159 models.py:208 models.py:244 models.py:274
 msgid "Event"
 msgstr "Event"
 
-#: availability.py:39 models.py:52 models.py:104 models.py:122 models.py:153
-#: models.py:202 models.py:238 models.py:264
+#: availability.py:39 models.py:59 models.py:111 models.py:129 models.py:160
+#: models.py:209 models.py:245 models.py:275
 msgid "Associated event"
 msgstr "Zugehöriges Event"
 
@@ -45,7 +45,7 @@ msgstr "Person"
 msgid "Person whose availability this is"
 msgstr "Person deren Verfügbarkeit hier abgebildet wird"
 
-#: availability.py:56 models.py:241 models.py:256
+#: availability.py:56 models.py:248 models.py:267
 msgid "Room"
 msgstr "Raum"
 
@@ -53,17 +53,15 @@ msgstr "Raum"
 msgid "Room whose availability this is"
 msgstr "Raum dessen Verfügbarkeit hier abgebildet wird"
 
-#: availability.py:65 models.py:205 models.py:255
+#: availability.py:65 models.py:212 models.py:266
 msgid "AK"
 msgstr "AK"
 
 #: availability.py:66
-#, fuzzy
-#| msgid "Availabilities"
 msgid "AK whose availability this is"
 msgstr "Verfügbarkeiten"
 
-#: availability.py:74 models.py:107
+#: availability.py:74 models.py:114
 msgid "AK Category"
 msgstr "AK Kategorie"
 
@@ -79,70 +77,86 @@ msgstr "Verfügbarkeit"
 msgid "Availabilities"
 msgstr "Verfügbarkeiten"
 
-#: models.py:12 models.py:98 models.py:118 models.py:136 models.py:150
-#: models.py:167 models.py:230
+#: models.py:14 models.py:105 models.py:125 models.py:143 models.py:157
+#: models.py:174 models.py:237
 msgid "Name"
 msgstr "Name"
 
-#: models.py:13
+#: models.py:15
 msgid "Name or iteration of the event"
 msgstr "Name oder Iteration des Events"
 
-#: models.py:14
-#, fuzzy
-#| msgid "Short Name"
+#: models.py:16
 msgid "Short Form"
 msgstr "Kurzer Name"
 
-#: models.py:15
+#: models.py:17
 msgid "Short name of letters/numbers/dots/dashes/underscores used in URLs."
 msgstr ""
+"Kurzname bestehend aus Buchstaben, Nummern, Punkten und Unterstrichen zur "
+"Nutzung in URLs"
 
-#: models.py:16
+#: models.py:19
+msgid "Place"
+msgstr "Ort"
+
+#: models.py:20
+msgid "City etc. the event takes place in"
+msgstr "Stadt o.ä. in der das Event stattfindet"
+
+#: models.py:22
+msgid "Time Zone"
+msgstr "Zeitzone"
+
+#: models.py:22
+msgid "Time Zone where this event takes place in"
+msgstr "Zeitzone in der das Event stattfindet"
+
+#: models.py:23
 msgid "Start"
 msgstr "Start"
 
-#: models.py:16
+#: models.py:23
 msgid "Time the event begins"
 msgstr "Zeit zu der das Event beginnt"
 
-#: models.py:17
+#: models.py:24
 msgid "End"
 msgstr "Ende"
 
-#: models.py:17
+#: models.py:24
 msgid "Time the event ends"
 msgstr "Zeit zu der das Event endet"
 
-#: models.py:18
-msgid "Place"
-msgstr "Ort"
-
-#: models.py:19
-msgid "City etc. the event takes place in"
-msgstr "Stadt o.ä. in der das Event stattfindet"
-
-#: models.py:20
+#: models.py:26
 msgid "Active State"
 msgstr "Aktiver Status"
 
-#: models.py:20
+#: models.py:26
 msgid "Marks currently active events"
 msgstr "Markiert aktuell aktive Events"
 
-#: models.py:22
+#: models.py:28
 msgid "Base URL"
 msgstr "URL-Prefix"
 
-#: models.py:22
+#: models.py:28
 msgid "Prefix for wiki link construction"
 msgstr "Prefix für die automatische Generierung von Wiki-Links"
 
-#: models.py:26
+#: models.py:29
+msgid "Default Slot Length"
+msgstr "Standardslotlänge"
+
+#: models.py:30
+msgid "Default length in hours that is assumed for AKs in this event."
+msgstr "Standardlänge von Slots (in Stunden) für dieses Event"
+
+#: models.py:32
 msgid "Contact email address"
 msgstr "E-Mail Kontaktadresse"
 
-#: models.py:27
+#: models.py:34
 msgid ""
 "An email address that is displayed on every page and can be used for all "
 "kinds of questions"
@@ -150,288 +164,292 @@ msgstr ""
 "Eine Mailadresse die auf jeder Seite angezeigt wird und für alle Arten von "
 "Fragen genutzt werden kann"
 
-#: models.py:31
+#: models.py:38
 msgid "Events"
 msgstr "Events"
 
-#: models.py:45
+#: models.py:52
 msgid "Nickname"
 msgstr "Spitzname"
 
-#: models.py:45
+#: models.py:52
 msgid "Name to identify an AK owner by"
 msgstr "Name durch den eine AK Leitung identifiziert wird"
 
-#: models.py:46
+#: models.py:53
 msgid "Slug"
 msgstr "Slug"
 
-#: models.py:47
+#: models.py:54
 msgid "Slug for URL generation"
 msgstr "Slug für URL-Generierung"
 
-#: models.py:48
+#: models.py:55
 msgid "Institution"
 msgstr "Instutution"
 
-#: models.py:48
+#: models.py:55
 msgid "Uni etc."
 msgstr "Universität o.ä."
 
-#: models.py:49 models.py:176
+#: models.py:56 models.py:183
 msgid "Web Link"
 msgstr "Internet Link"
 
-#: models.py:49
+#: models.py:56
 msgid "Link to Homepage"
 msgstr "Link zu Homepage oder Webseite"
 
-#: models.py:55
+#: models.py:62
 msgid "AK Owner"
 msgstr "AK Leitung"
 
-#: models.py:56
+#: models.py:63
 msgid "AK Owners"
 msgstr "AK Leitungen"
 
-#: models.py:98
+#: models.py:105
 msgid "Name of the AK Category"
 msgstr "Name des AK Kategorie"
 
-#: models.py:99 models.py:119
+#: models.py:106 models.py:126
 msgid "Color"
 msgstr "Farbe"
 
-#: models.py:99 models.py:119
+#: models.py:106 models.py:126
 msgid "Color for displaying"
 msgstr "Farbe für die Anzeige"
 
-#: models.py:100 models.py:170
+#: models.py:107 models.py:177
 msgid "Description"
 msgstr "Beschreibung"
 
-#: models.py:101
+#: models.py:108
 msgid "Short description of this AK Category"
 msgstr "Beschreibung der AK-Kategorie"
 
-#: models.py:108
+#: models.py:115
 msgid "AK Categories"
 msgstr "AK Kategorien"
 
-#: models.py:118
+#: models.py:125
 msgid "Name of the AK Track"
 msgstr "Name des AK Tracks"
 
-#: models.py:125
+#: models.py:132
 msgid "AK Track"
 msgstr "AK Track"
 
-#: models.py:126
+#: models.py:133
 msgid "AK Tracks"
 msgstr "AK Tracks"
 
-#: models.py:136
+#: models.py:143
 msgid "Name of the AK Tag"
 msgstr "Name das AK Tags"
 
-#: models.py:139
+#: models.py:146
 msgid "AK Tag"
 msgstr "AK Tag"
 
-#: models.py:140
+#: models.py:147
 msgid "AK Tags"
 msgstr "AK Tags"
 
-#: models.py:150
+#: models.py:157
 msgid "Name of the Requirement"
 msgstr "Name der Anforderung"
 
-#: models.py:156
+#: models.py:163
 msgid "AK Requirement"
 msgstr "AK Anforderung"
 
-#: models.py:157
+#: models.py:164
 msgid "AK Requirements"
 msgstr "AK Anforderungen"
 
-#: models.py:167
+#: models.py:174
 msgid "Name of the AK"
 msgstr "Name des AKs"
 
-#: models.py:168
+#: models.py:175
 msgid "Short Name"
 msgstr "Kurzer Name"
 
-#: models.py:169
+#: models.py:176
 msgid "Name displayed in the schedule"
 msgstr "Name zur Anzeige im AK Plan"
 
-#: models.py:170
+#: models.py:177
 msgid "Description of the AK"
 msgstr "Beschreibung des AKs"
 
-#: models.py:172
+#: models.py:179
 msgid "Owners"
 msgstr "Leitungen"
 
-#: models.py:173
+#: models.py:180
 msgid "Those organizing the AK"
 msgstr "Menschen, die den AK organisieren und halten"
 
-#: models.py:176
+#: models.py:183
 msgid "Link to wiki page"
 msgstr "Link zur Wiki Seite"
 
-#: models.py:178
+#: models.py:185
 msgid "Category"
 msgstr "Kategorie"
 
-#: models.py:179
+#: models.py:186
 msgid "Category of the AK"
 msgstr "Kategorie des AKs"
 
-#: models.py:180
+#: models.py:187
 msgid "Tags"
 msgstr "Tags"
 
-#: models.py:180
+#: models.py:187
 msgid "Tags provided by owners"
 msgstr "Tags, die durch die AK Leitung vergeben wurden"
 
-#: models.py:181
+#: models.py:188
 msgid "Track"
 msgstr "Track"
 
-#: models.py:182
+#: models.py:189
 msgid "Track the AK belongs to"
 msgstr "Track zu dem der AK gehört"
 
-#: models.py:184
+#: models.py:191
 msgid "Resolution Intention"
 msgstr "Resolutionsabsicht"
 
-#: models.py:185
+#: models.py:192
 msgid "Intends to submit a resolution"
 msgstr "Beabsichtigt eine Resolution einzureichen"
 
-#: models.py:186
+#: models.py:193
 msgid "Present this AK"
 msgstr "AK Präsentieren"
 
-#: models.py:187
+#: models.py:194
 msgid "Present results of this AK"
 msgstr "Die Ergebnisse dieses AKs vorstellen"
 
-#: models.py:189
+#: models.py:196
 msgid "Requirements"
 msgstr "Anforderungen"
 
-#: models.py:190
+#: models.py:197
 msgid "AK's Requirements"
 msgstr "Anforderungen des AKs"
 
-#: models.py:192
+#: models.py:199
 msgid "Conflicting AKs"
 msgstr "AK Konflikte"
 
-#: models.py:193
+#: models.py:200
 msgid "AKs that conflict and thus must not take place at the same time"
 msgstr ""
 "AKs, die Konflikte haben und deshalb nicht gleichzeitig stattfinden dürfen"
 
-#: models.py:194
+#: models.py:201
 msgid "Prerequisite AKs"
 msgstr "Vorausgesetzte AKs"
 
-#: models.py:195
+#: models.py:202
 msgid "AKs that should precede this AK in the schedule"
 msgstr "AKS die im AK Plan vor diesem AK stattfinden müssen"
 
-#: models.py:197
+#: models.py:204
 msgid "Internal Notes"
 msgstr "Interne Notizen"
 
-#: models.py:197
+#: models.py:204
 msgid "Notes to organizers"
 msgstr "Notizen an die Organisator*innen"
 
-#: models.py:199
+#: models.py:206
 msgid "Interest"
 msgstr "Interesse"
 
-#: models.py:199
+#: models.py:206
 msgid "Expected number of people"
 msgstr "Erwartete Personenzahl"
 
-#: models.py:206
+#: models.py:213
 msgid "AKs"
 msgstr "AKs"
 
-#: models.py:230
+#: models.py:237
 msgid "Name or number of the room"
 msgstr "Name oder Nummer des Raums"
 
-#: models.py:231
+#: models.py:238
 msgid "Building"
 msgstr "Gebäude"
 
-#: models.py:232
+#: models.py:239
 msgid "Name or number of the building"
 msgstr "Name oder Nummer des Gebäudes"
 
-#: models.py:233
+#: models.py:240
 msgid "Capacity"
 msgstr "Kapazität"
 
-#: models.py:233
+#: models.py:240
 msgid "Maximum number of people"
 msgstr "Maximale Personenzahl"
 
-#: models.py:234
+#: models.py:241
 msgid "Properties"
 msgstr "Eigenschaften"
 
-#: models.py:235
+#: models.py:242
 msgid "AK requirements fulfilled by the room"
 msgstr "AK Anforderungen, die dieser Raum erfüllt"
 
-#: models.py:242
+#: models.py:249
 msgid "Rooms"
 msgstr "Räume"
 
-#: models.py:255
+#: models.py:266
 msgid "AK being mapped"
 msgstr "AK, der zugeordnet wird"
 
-#: models.py:257
+#: models.py:268
 msgid "Room the AK will take place in"
 msgstr "Raum in dem der AK stattfindet"
 
-#: models.py:258
+#: models.py:269
 msgid "Slot Begin"
 msgstr "Beginn des Slots"
 
-#: models.py:258
+#: models.py:269
 msgid "Time and date the slot begins"
 msgstr "Zeit und Datum zu der der AK beginnt"
 
-#: models.py:260
+#: models.py:271
 msgid "Duration"
 msgstr "Dauer"
 
-#: models.py:261
+#: models.py:272
 msgid "Length in hours"
 msgstr "Länge in Stunden"
 
-#: models.py:267
+#: models.py:277
+msgid "Last update"
+msgstr "Letzte Aktualisierung"
+
+#: models.py:280
 msgid "AK Slot"
 msgstr "AK Slot"
 
-#: models.py:268
+#: models.py:281
 msgid "AK Slots"
 msgstr "AK Slot"
 
-#: models.py:282
+#: models.py:295
 msgid "Not scheduled yet"
 msgstr "Noch nicht geplant"
 
diff --git a/AKModel/migrations/0027_event_timezone.py b/AKModel/migrations/0027_event_timezone.py
new file mode 100644
index 0000000000000000000000000000000000000000..ab526080bb128f5d9c8f6a51341ce923e0b5de72
--- /dev/null
+++ b/AKModel/migrations/0027_event_timezone.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.2.6 on 2020-03-01 14:13
+
+from django.db import migrations
+import timezone_field.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('AKModel', '0026_akslot_updated'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='event',
+            name='timezone',
+            field=timezone_field.fields.TimeZoneField(choices=[('Pacific/Apia', 'GMT-11:00 Pacific/Apia'), ('Pacific/Fakaofo', 'GMT-11:00 Pacific/Fakaofo'), ('Pacific/Midway', 'GMT-11:00 Pacific/Midway'), ('Pacific/Niue', 'GMT-11:00 Pacific/Niue'), ('Pacific/Pago_Pago', 'GMT-11:00 Pacific/Pago Pago'), ('America/Adak', 'GMT-10:00 America/Adak'), ('Pacific/Honolulu', 'GMT-10:00 Pacific/Honolulu'), ('Pacific/Rarotonga', 'GMT-10:00 Pacific/Rarotonga'), ('Pacific/Tahiti', 'GMT-10:00 Pacific/Tahiti'), ('US/Hawaii', 'GMT-10:00 US/Hawaii'), ('Pacific/Marquesas', 'GMT-09:00 Pacific/Marquesas'), ('America/Anchorage', 'GMT-09:00 America/Anchorage'), ('America/Juneau', 'GMT-09:00 America/Juneau'), ('America/Nome', 'GMT-09:00 America/Nome'), ('America/Sitka', 'GMT-09:00 America/Sitka'), ('America/Yakutat', 'GMT-09:00 America/Yakutat'), ('Pacific/Gambier', 'GMT-09:00 Pacific/Gambier'), ('US/Alaska', 'GMT-09:00 US/Alaska'), ('America/Dawson', 'GMT-08:00 America/Dawson'), ('America/Fort_Nelson', 'GMT-08:00 America/Fort Nelson'), ('America/Los_Angeles', 'GMT-08:00 America/Los Angeles'), ('America/Metlakatla', 'GMT-08:00 America/Metlakatla'), ('America/Tijuana', 'GMT-08:00 America/Tijuana'), ('America/Vancouver', 'GMT-08:00 America/Vancouver'), ('America/Whitehorse', 'GMT-08:00 America/Whitehorse'), ('Canada/Pacific', 'GMT-08:00 Canada/Pacific'), ('Pacific/Pitcairn', 'GMT-08:00 Pacific/Pitcairn'), ('US/Pacific', 'GMT-08:00 US/Pacific'), ('America/Bahia_Banderas', 'GMT-07:00 America/Bahia Banderas'), ('America/Boise', 'GMT-07:00 America/Boise'), ('America/Chihuahua', 'GMT-07:00 America/Chihuahua'), ('America/Creston', 'GMT-07:00 America/Creston'), ('America/Dawson_Creek', 'GMT-07:00 America/Dawson Creek'), ('America/Denver', 'GMT-07:00 America/Denver'), ('America/Edmonton', 'GMT-07:00 America/Edmonton'), ('America/Hermosillo', 'GMT-07:00 America/Hermosillo'), ('America/Inuvik', 'GMT-07:00 America/Inuvik'), ('America/Mazatlan', 'GMT-07:00 America/Mazatlan'), ('America/North_Dakota/Beulah', 'GMT-07:00 America/North Dakota/Beulah'), ('America/North_Dakota/New_Salem', 'GMT-07:00 America/North Dakota/New Salem'), ('America/Ojinaga', 'GMT-07:00 America/Ojinaga'), ('America/Phoenix', 'GMT-07:00 America/Phoenix'), ('America/Yellowknife', 'GMT-07:00 America/Yellowknife'), ('Canada/Mountain', 'GMT-07:00 Canada/Mountain'), ('US/Arizona', 'GMT-07:00 US/Arizona'), ('US/Mountain', 'GMT-07:00 US/Mountain'), ('America/Belize', 'GMT-06:00 America/Belize'), ('America/Cambridge_Bay', 'GMT-06:00 America/Cambridge Bay'), ('America/Cancun', 'GMT-06:00 America/Cancun'), ('America/Chicago', 'GMT-06:00 America/Chicago'), ('America/Costa_Rica', 'GMT-06:00 America/Costa Rica'), ('America/El_Salvador', 'GMT-06:00 America/El Salvador'), ('America/Guatemala', 'GMT-06:00 America/Guatemala'), ('America/Iqaluit', 'GMT-06:00 America/Iqaluit'), ('America/Kentucky/Monticello', 'GMT-06:00 America/Kentucky/Monticello'), ('America/Managua', 'GMT-06:00 America/Managua'), ('America/Matamoros', 'GMT-06:00 America/Matamoros'), ('America/Menominee', 'GMT-06:00 America/Menominee'), ('America/Merida', 'GMT-06:00 America/Merida'), ('America/Mexico_City', 'GMT-06:00 America/Mexico City'), ('America/Monterrey', 'GMT-06:00 America/Monterrey'), ('America/North_Dakota/Center', 'GMT-06:00 America/North Dakota/Center'), ('America/Pangnirtung', 'GMT-06:00 America/Pangnirtung'), ('America/Rainy_River', 'GMT-06:00 America/Rainy River'), ('America/Rankin_Inlet', 'GMT-06:00 America/Rankin Inlet'), ('America/Regina', 'GMT-06:00 America/Regina'), ('America/Resolute', 'GMT-06:00 America/Resolute'), ('America/Swift_Current', 'GMT-06:00 America/Swift Current'), ('America/Tegucigalpa', 'GMT-06:00 America/Tegucigalpa'), ('America/Winnipeg', 'GMT-06:00 America/Winnipeg'), ('Canada/Central', 'GMT-06:00 Canada/Central'), ('Pacific/Galapagos', 'GMT-06:00 Pacific/Galapagos'), ('US/Central', 'GMT-06:00 US/Central'), ('America/Atikokan', 'GMT-05:00 America/Atikokan'), ('America/Bogota', 'GMT-05:00 America/Bogota'), ('America/Cayman', 'GMT-05:00 America/Cayman'), ('America/Detroit', 'GMT-05:00 America/Detroit'), ('America/Eirunepe', 'GMT-05:00 America/Eirunepe'), ('America/Grand_Turk', 'GMT-05:00 America/Grand Turk'), ('America/Guayaquil', 'GMT-05:00 America/Guayaquil'), ('America/Havana', 'GMT-05:00 America/Havana'), ('America/Indiana/Indianapolis', 'GMT-05:00 America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'GMT-05:00 America/Indiana/Knox'), ('America/Indiana/Marengo', 'GMT-05:00 America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'GMT-05:00 America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'GMT-05:00 America/Indiana/Tell City'), ('America/Indiana/Vevay', 'GMT-05:00 America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'GMT-05:00 America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'GMT-05:00 America/Indiana/Winamac'), ('America/Jamaica', 'GMT-05:00 America/Jamaica'), ('America/Kentucky/Louisville', 'GMT-05:00 America/Kentucky/Louisville'), ('America/Lima', 'GMT-05:00 America/Lima'), ('America/Nassau', 'GMT-05:00 America/Nassau'), ('America/New_York', 'GMT-05:00 America/New York'), ('America/Nipigon', 'GMT-05:00 America/Nipigon'), ('America/Panama', 'GMT-05:00 America/Panama'), ('America/Port-au-Prince', 'GMT-05:00 America/Port-au-Prince'), ('America/Rio_Branco', 'GMT-05:00 America/Rio Branco'), ('America/Thunder_Bay', 'GMT-05:00 America/Thunder Bay'), ('America/Toronto', 'GMT-05:00 America/Toronto'), ('Canada/Eastern', 'GMT-05:00 Canada/Eastern'), ('Pacific/Easter', 'GMT-05:00 Pacific/Easter'), ('US/Eastern', 'GMT-05:00 US/Eastern'), ('America/Anguilla', 'GMT-04:00 America/Anguilla'), ('America/Antigua', 'GMT-04:00 America/Antigua'), ('America/Aruba', 'GMT-04:00 America/Aruba'), ('America/Barbados', 'GMT-04:00 America/Barbados'), ('America/Blanc-Sablon', 'GMT-04:00 America/Blanc-Sablon'), ('America/Caracas', 'GMT-04:00 America/Caracas'), ('America/Curacao', 'GMT-04:00 America/Curacao'), ('America/Dominica', 'GMT-04:00 America/Dominica'), ('America/Glace_Bay', 'GMT-04:00 America/Glace Bay'), ('America/Goose_Bay', 'GMT-04:00 America/Goose Bay'), ('America/Grenada', 'GMT-04:00 America/Grenada'), ('America/Guadeloupe', 'GMT-04:00 America/Guadeloupe'), ('America/Guyana', 'GMT-04:00 America/Guyana'), ('America/Halifax', 'GMT-04:00 America/Halifax'), ('America/Kralendijk', 'GMT-04:00 America/Kralendijk'), ('America/La_Paz', 'GMT-04:00 America/La Paz'), ('America/Lower_Princes', 'GMT-04:00 America/Lower Princes'), ('America/Manaus', 'GMT-04:00 America/Manaus'), ('America/Marigot', 'GMT-04:00 America/Marigot'), ('America/Martinique', 'GMT-04:00 America/Martinique'), ('America/Moncton', 'GMT-04:00 America/Moncton'), ('America/Montserrat', 'GMT-04:00 America/Montserrat'), ('America/Port_of_Spain', 'GMT-04:00 America/Port of Spain'), ('America/Porto_Velho', 'GMT-04:00 America/Porto Velho'), ('America/Puerto_Rico', 'GMT-04:00 America/Puerto Rico'), ('America/Santarem', 'GMT-04:00 America/Santarem'), ('America/Santo_Domingo', 'GMT-04:00 America/Santo Domingo'), ('America/St_Barthelemy', 'GMT-04:00 America/St Barthelemy'), ('America/St_Kitts', 'GMT-04:00 America/St Kitts'), ('America/St_Lucia', 'GMT-04:00 America/St Lucia'), ('America/St_Thomas', 'GMT-04:00 America/St Thomas'), ('America/St_Vincent', 'GMT-04:00 America/St Vincent'), ('America/Thule', 'GMT-04:00 America/Thule'), ('America/Tortola', 'GMT-04:00 America/Tortola'), ('Atlantic/Bermuda', 'GMT-04:00 Atlantic/Bermuda'), ('Canada/Atlantic', 'GMT-04:00 Canada/Atlantic'), ('America/St_Johns', 'GMT-03:00 America/St Johns'), ('Canada/Newfoundland', 'GMT-03:00 Canada/Newfoundland'), ('America/Argentina/Buenos_Aires', 'GMT-03:00 America/Argentina/Buenos Aires'), ('America/Argentina/Catamarca', 'GMT-03:00 America/Argentina/Catamarca'), ('America/Argentina/Cordoba', 'GMT-03:00 America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'GMT-03:00 America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'GMT-03:00 America/Argentina/La Rioja'), ('America/Argentina/Mendoza', 'GMT-03:00 America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'GMT-03:00 America/Argentina/Rio Gallegos'), ('America/Argentina/Salta', 'GMT-03:00 America/Argentina/Salta'), ('America/Argentina/San_Juan', 'GMT-03:00 America/Argentina/San Juan'), ('America/Argentina/San_Luis', 'GMT-03:00 America/Argentina/San Luis'), ('America/Argentina/Tucuman', 'GMT-03:00 America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'GMT-03:00 America/Argentina/Ushuaia'), ('America/Asuncion', 'GMT-03:00 America/Asuncion'), ('America/Belem', 'GMT-03:00 America/Belem'), ('America/Boa_Vista', 'GMT-03:00 America/Boa Vista'), ('America/Campo_Grande', 'GMT-03:00 America/Campo Grande'), ('America/Cayenne', 'GMT-03:00 America/Cayenne'), ('America/Cuiaba', 'GMT-03:00 America/Cuiaba'), ('America/Godthab', 'GMT-03:00 America/Godthab'), ('America/Miquelon', 'GMT-03:00 America/Miquelon'), ('America/Montevideo', 'GMT-03:00 America/Montevideo'), ('America/Paramaribo', 'GMT-03:00 America/Paramaribo'), ('America/Punta_Arenas', 'GMT-03:00 America/Punta Arenas'), ('America/Santiago', 'GMT-03:00 America/Santiago'), ('Antarctica/Palmer', 'GMT-03:00 Antarctica/Palmer'), ('Antarctica/Rothera', 'GMT-03:00 Antarctica/Rothera'), ('Atlantic/Stanley', 'GMT-03:00 Atlantic/Stanley'), ('America/Araguaina', 'GMT-02:00 America/Araguaina'), ('America/Bahia', 'GMT-02:00 America/Bahia'), ('America/Fortaleza', 'GMT-02:00 America/Fortaleza'), ('America/Maceio', 'GMT-02:00 America/Maceio'), ('America/Recife', 'GMT-02:00 America/Recife'), ('America/Sao_Paulo', 'GMT-02:00 America/Sao Paulo'), ('Atlantic/South_Georgia', 'GMT-02:00 Atlantic/South Georgia'), ('America/Noronha', 'GMT-01:00 America/Noronha'), ('America/Scoresbysund', 'GMT-01:00 America/Scoresbysund'), ('Atlantic/Azores', 'GMT-01:00 Atlantic/Azores'), ('Atlantic/Cape_Verde', 'GMT-01:00 Atlantic/Cape Verde'), ('Africa/Abidjan', 'GMT+00:00 Africa/Abidjan'), ('Africa/Accra', 'GMT+00:00 Africa/Accra'), ('Africa/Bamako', 'GMT+00:00 Africa/Bamako'), ('Africa/Banjul', 'GMT+00:00 Africa/Banjul'), ('Africa/Bissau', 'GMT+00:00 Africa/Bissau'), ('Africa/Casablanca', 'GMT+00:00 Africa/Casablanca'), ('Africa/Conakry', 'GMT+00:00 Africa/Conakry'), ('Africa/Dakar', 'GMT+00:00 Africa/Dakar'), ('Africa/El_Aaiun', 'GMT+00:00 Africa/El Aaiun'), ('Africa/Freetown', 'GMT+00:00 Africa/Freetown'), ('Africa/Lome', 'GMT+00:00 Africa/Lome'), ('Africa/Monrovia', 'GMT+00:00 Africa/Monrovia'), ('Africa/Nouakchott', 'GMT+00:00 Africa/Nouakchott'), ('Africa/Ouagadougou', 'GMT+00:00 Africa/Ouagadougou'), ('Africa/Sao_Tome', 'GMT+00:00 Africa/Sao Tome'), ('America/Danmarkshavn', 'GMT+00:00 America/Danmarkshavn'), ('Antarctica/Troll', 'GMT+00:00 Antarctica/Troll'), ('Atlantic/Canary', 'GMT+00:00 Atlantic/Canary'), ('Atlantic/Faroe', 'GMT+00:00 Atlantic/Faroe'), ('Atlantic/Madeira', 'GMT+00:00 Atlantic/Madeira'), ('Atlantic/Reykjavik', 'GMT+00:00 Atlantic/Reykjavik'), ('Atlantic/St_Helena', 'GMT+00:00 Atlantic/St Helena'), ('Europe/Dublin', 'GMT+00:00 Europe/Dublin'), ('Europe/Guernsey', 'GMT+00:00 Europe/Guernsey'), ('Europe/Isle_of_Man', 'GMT+00:00 Europe/Isle of Man'), ('Europe/Jersey', 'GMT+00:00 Europe/Jersey'), ('Europe/Lisbon', 'GMT+00:00 Europe/Lisbon'), ('Europe/London', 'GMT+00:00 Europe/London'), ('GMT', 'GMT+00:00 GMT'), ('UTC', 'GMT+00:00 UTC'), ('Africa/Algiers', 'GMT+01:00 Africa/Algiers'), ('Africa/Bangui', 'GMT+01:00 Africa/Bangui'), ('Africa/Brazzaville', 'GMT+01:00 Africa/Brazzaville'), ('Africa/Ceuta', 'GMT+01:00 Africa/Ceuta'), ('Africa/Douala', 'GMT+01:00 Africa/Douala'), ('Africa/Kinshasa', 'GMT+01:00 Africa/Kinshasa'), ('Africa/Lagos', 'GMT+01:00 Africa/Lagos'), ('Africa/Libreville', 'GMT+01:00 Africa/Libreville'), ('Africa/Luanda', 'GMT+01:00 Africa/Luanda'), ('Africa/Malabo', 'GMT+01:00 Africa/Malabo'), ('Africa/Ndjamena', 'GMT+01:00 Africa/Ndjamena'), ('Africa/Niamey', 'GMT+01:00 Africa/Niamey'), ('Africa/Porto-Novo', 'GMT+01:00 Africa/Porto-Novo'), ('Africa/Tunis', 'GMT+01:00 Africa/Tunis'), ('Arctic/Longyearbyen', 'GMT+01:00 Arctic/Longyearbyen'), ('Europe/Amsterdam', 'GMT+01:00 Europe/Amsterdam'), ('Europe/Andorra', 'GMT+01:00 Europe/Andorra'), ('Europe/Belgrade', 'GMT+01:00 Europe/Belgrade'), ('Europe/Berlin', 'GMT+01:00 Europe/Berlin'), ('Europe/Bratislava', 'GMT+01:00 Europe/Bratislava'), ('Europe/Brussels', 'GMT+01:00 Europe/Brussels'), ('Europe/Budapest', 'GMT+01:00 Europe/Budapest'), ('Europe/Busingen', 'GMT+01:00 Europe/Busingen'), ('Europe/Copenhagen', 'GMT+01:00 Europe/Copenhagen'), ('Europe/Gibraltar', 'GMT+01:00 Europe/Gibraltar'), ('Europe/Ljubljana', 'GMT+01:00 Europe/Ljubljana'), ('Europe/Luxembourg', 'GMT+01:00 Europe/Luxembourg'), ('Europe/Madrid', 'GMT+01:00 Europe/Madrid'), ('Europe/Malta', 'GMT+01:00 Europe/Malta'), ('Europe/Monaco', 'GMT+01:00 Europe/Monaco'), ('Europe/Oslo', 'GMT+01:00 Europe/Oslo'), ('Europe/Paris', 'GMT+01:00 Europe/Paris'), ('Europe/Podgorica', 'GMT+01:00 Europe/Podgorica'), ('Europe/Prague', 'GMT+01:00 Europe/Prague'), ('Europe/Rome', 'GMT+01:00 Europe/Rome'), ('Europe/San_Marino', 'GMT+01:00 Europe/San Marino'), ('Europe/Sarajevo', 'GMT+01:00 Europe/Sarajevo'), ('Europe/Skopje', 'GMT+01:00 Europe/Skopje'), ('Europe/Stockholm', 'GMT+01:00 Europe/Stockholm'), ('Europe/Tirane', 'GMT+01:00 Europe/Tirane'), ('Europe/Vaduz', 'GMT+01:00 Europe/Vaduz'), ('Europe/Vatican', 'GMT+01:00 Europe/Vatican'), ('Europe/Vienna', 'GMT+01:00 Europe/Vienna'), ('Europe/Warsaw', 'GMT+01:00 Europe/Warsaw'), ('Europe/Zagreb', 'GMT+01:00 Europe/Zagreb'), ('Europe/Zurich', 'GMT+01:00 Europe/Zurich'), ('Africa/Blantyre', 'GMT+02:00 Africa/Blantyre'), ('Africa/Bujumbura', 'GMT+02:00 Africa/Bujumbura'), ('Africa/Cairo', 'GMT+02:00 Africa/Cairo'), ('Africa/Gaborone', 'GMT+02:00 Africa/Gaborone'), ('Africa/Harare', 'GMT+02:00 Africa/Harare'), ('Africa/Johannesburg', 'GMT+02:00 Africa/Johannesburg'), ('Africa/Juba', 'GMT+02:00 Africa/Juba'), ('Africa/Khartoum', 'GMT+02:00 Africa/Khartoum'), ('Africa/Kigali', 'GMT+02:00 Africa/Kigali'), ('Africa/Lubumbashi', 'GMT+02:00 Africa/Lubumbashi'), ('Africa/Lusaka', 'GMT+02:00 Africa/Lusaka'), ('Africa/Maputo', 'GMT+02:00 Africa/Maputo'), ('Africa/Maseru', 'GMT+02:00 Africa/Maseru'), ('Africa/Mbabane', 'GMT+02:00 Africa/Mbabane'), ('Africa/Tripoli', 'GMT+02:00 Africa/Tripoli'), ('Africa/Windhoek', 'GMT+02:00 Africa/Windhoek'), ('Asia/Amman', 'GMT+02:00 Asia/Amman'), ('Asia/Beirut', 'GMT+02:00 Asia/Beirut'), ('Asia/Damascus', 'GMT+02:00 Asia/Damascus'), ('Asia/Famagusta', 'GMT+02:00 Asia/Famagusta'), ('Asia/Gaza', 'GMT+02:00 Asia/Gaza'), ('Asia/Hebron', 'GMT+02:00 Asia/Hebron'), ('Asia/Jerusalem', 'GMT+02:00 Asia/Jerusalem'), ('Asia/Nicosia', 'GMT+02:00 Asia/Nicosia'), ('Europe/Athens', 'GMT+02:00 Europe/Athens'), ('Europe/Bucharest', 'GMT+02:00 Europe/Bucharest'), ('Europe/Chisinau', 'GMT+02:00 Europe/Chisinau'), ('Europe/Helsinki', 'GMT+02:00 Europe/Helsinki'), ('Europe/Istanbul', 'GMT+02:00 Europe/Istanbul'), ('Europe/Kaliningrad', 'GMT+02:00 Europe/Kaliningrad'), ('Europe/Kiev', 'GMT+02:00 Europe/Kiev'), ('Europe/Mariehamn', 'GMT+02:00 Europe/Mariehamn'), ('Europe/Minsk', 'GMT+02:00 Europe/Minsk'), ('Europe/Riga', 'GMT+02:00 Europe/Riga'), ('Europe/Simferopol', 'GMT+02:00 Europe/Simferopol'), ('Europe/Sofia', 'GMT+02:00 Europe/Sofia'), ('Europe/Tallinn', 'GMT+02:00 Europe/Tallinn'), ('Europe/Uzhgorod', 'GMT+02:00 Europe/Uzhgorod'), ('Europe/Vilnius', 'GMT+02:00 Europe/Vilnius'), ('Europe/Zaporozhye', 'GMT+02:00 Europe/Zaporozhye'), ('Africa/Addis_Ababa', 'GMT+03:00 Africa/Addis Ababa'), ('Africa/Asmara', 'GMT+03:00 Africa/Asmara'), ('Africa/Dar_es_Salaam', 'GMT+03:00 Africa/Dar es Salaam'), ('Africa/Djibouti', 'GMT+03:00 Africa/Djibouti'), ('Africa/Kampala', 'GMT+03:00 Africa/Kampala'), ('Africa/Mogadishu', 'GMT+03:00 Africa/Mogadishu'), ('Africa/Nairobi', 'GMT+03:00 Africa/Nairobi'), ('Antarctica/Syowa', 'GMT+03:00 Antarctica/Syowa'), ('Asia/Aden', 'GMT+03:00 Asia/Aden'), ('Asia/Baghdad', 'GMT+03:00 Asia/Baghdad'), ('Asia/Bahrain', 'GMT+03:00 Asia/Bahrain'), ('Asia/Kuwait', 'GMT+03:00 Asia/Kuwait'), ('Asia/Qatar', 'GMT+03:00 Asia/Qatar'), ('Asia/Riyadh', 'GMT+03:00 Asia/Riyadh'), ('Europe/Astrakhan', 'GMT+03:00 Europe/Astrakhan'), ('Europe/Kirov', 'GMT+03:00 Europe/Kirov'), ('Europe/Moscow', 'GMT+03:00 Europe/Moscow'), ('Europe/Saratov', 'GMT+03:00 Europe/Saratov'), ('Europe/Ulyanovsk', 'GMT+03:00 Europe/Ulyanovsk'), ('Europe/Volgograd', 'GMT+03:00 Europe/Volgograd'), ('Indian/Antananarivo', 'GMT+03:00 Indian/Antananarivo'), ('Indian/Comoro', 'GMT+03:00 Indian/Comoro'), ('Indian/Mayotte', 'GMT+03:00 Indian/Mayotte'), ('Asia/Tehran', 'GMT+03:00 Asia/Tehran'), ('Asia/Aqtau', 'GMT+04:00 Asia/Aqtau'), ('Asia/Atyrau', 'GMT+04:00 Asia/Atyrau'), ('Asia/Baku', 'GMT+04:00 Asia/Baku'), ('Asia/Dubai', 'GMT+04:00 Asia/Dubai'), ('Asia/Muscat', 'GMT+04:00 Asia/Muscat'), ('Asia/Oral', 'GMT+04:00 Asia/Oral'), ('Asia/Tbilisi', 'GMT+04:00 Asia/Tbilisi'), ('Asia/Yerevan', 'GMT+04:00 Asia/Yerevan'), ('Europe/Samara', 'GMT+04:00 Europe/Samara'), ('Indian/Mahe', 'GMT+04:00 Indian/Mahe'), ('Indian/Mauritius', 'GMT+04:00 Indian/Mauritius'), ('Indian/Reunion', 'GMT+04:00 Indian/Reunion'), ('Asia/Kabul', 'GMT+04:00 Asia/Kabul'), ('Asia/Aqtobe', 'GMT+05:00 Asia/Aqtobe'), ('Asia/Ashgabat', 'GMT+05:00 Asia/Ashgabat'), ('Asia/Bishkek', 'GMT+05:00 Asia/Bishkek'), ('Asia/Dushanbe', 'GMT+05:00 Asia/Dushanbe'), ('Asia/Karachi', 'GMT+05:00 Asia/Karachi'), ('Asia/Qostanay', 'GMT+05:00 Asia/Qostanay'), ('Asia/Qyzylorda', 'GMT+05:00 Asia/Qyzylorda'), ('Asia/Samarkand', 'GMT+05:00 Asia/Samarkand'), ('Asia/Tashkent', 'GMT+05:00 Asia/Tashkent'), ('Asia/Yekaterinburg', 'GMT+05:00 Asia/Yekaterinburg'), ('Indian/Kerguelen', 'GMT+05:00 Indian/Kerguelen'), ('Indian/Maldives', 'GMT+05:00 Indian/Maldives'), ('Asia/Kolkata', 'GMT+05:00 Asia/Kolkata'), ('Asia/Kathmandu', 'GMT+05:00 Asia/Kathmandu'), ('Antarctica/Mawson', 'GMT+06:00 Antarctica/Mawson'), ('Antarctica/Vostok', 'GMT+06:00 Antarctica/Vostok'), ('Asia/Almaty', 'GMT+06:00 Asia/Almaty'), ('Asia/Barnaul', 'GMT+06:00 Asia/Barnaul'), ('Asia/Colombo', 'GMT+06:00 Asia/Colombo'), ('Asia/Dhaka', 'GMT+06:00 Asia/Dhaka'), ('Asia/Novosibirsk', 'GMT+06:00 Asia/Novosibirsk'), ('Asia/Omsk', 'GMT+06:00 Asia/Omsk'), ('Asia/Thimphu', 'GMT+06:00 Asia/Thimphu'), ('Asia/Urumqi', 'GMT+06:00 Asia/Urumqi'), ('Indian/Chagos', 'GMT+06:00 Indian/Chagos'), ('Asia/Yangon', 'GMT+06:00 Asia/Yangon'), ('Indian/Cocos', 'GMT+06:00 Indian/Cocos'), ('Antarctica/Davis', 'GMT+07:00 Antarctica/Davis'), ('Asia/Bangkok', 'GMT+07:00 Asia/Bangkok'), ('Asia/Ho_Chi_Minh', 'GMT+07:00 Asia/Ho Chi Minh'), ('Asia/Hovd', 'GMT+07:00 Asia/Hovd'), ('Asia/Jakarta', 'GMT+07:00 Asia/Jakarta'), ('Asia/Krasnoyarsk', 'GMT+07:00 Asia/Krasnoyarsk'), ('Asia/Novokuznetsk', 'GMT+07:00 Asia/Novokuznetsk'), ('Asia/Phnom_Penh', 'GMT+07:00 Asia/Phnom Penh'), ('Asia/Pontianak', 'GMT+07:00 Asia/Pontianak'), ('Asia/Tomsk', 'GMT+07:00 Asia/Tomsk'), ('Asia/Vientiane', 'GMT+07:00 Asia/Vientiane'), ('Indian/Christmas', 'GMT+07:00 Indian/Christmas'), ('Antarctica/Casey', 'GMT+08:00 Antarctica/Casey'), ('Asia/Brunei', 'GMT+08:00 Asia/Brunei'), ('Asia/Dili', 'GMT+08:00 Asia/Dili'), ('Asia/Hong_Kong', 'GMT+08:00 Asia/Hong Kong'), ('Asia/Irkutsk', 'GMT+08:00 Asia/Irkutsk'), ('Asia/Kuala_Lumpur', 'GMT+08:00 Asia/Kuala Lumpur'), ('Asia/Kuching', 'GMT+08:00 Asia/Kuching'), ('Asia/Macau', 'GMT+08:00 Asia/Macau'), ('Asia/Makassar', 'GMT+08:00 Asia/Makassar'), ('Asia/Manila', 'GMT+08:00 Asia/Manila'), ('Asia/Shanghai', 'GMT+08:00 Asia/Shanghai'), ('Asia/Singapore', 'GMT+08:00 Asia/Singapore'), ('Asia/Taipei', 'GMT+08:00 Asia/Taipei'), ('Asia/Ulaanbaatar', 'GMT+08:00 Asia/Ulaanbaatar'), ('Australia/Perth', 'GMT+08:00 Australia/Perth'), ('Australia/Eucla', 'GMT+08:00 Australia/Eucla'), ('Asia/Chita', 'GMT+09:00 Asia/Chita'), ('Asia/Choibalsan', 'GMT+09:00 Asia/Choibalsan'), ('Asia/Jayapura', 'GMT+09:00 Asia/Jayapura'), ('Asia/Khandyga', 'GMT+09:00 Asia/Khandyga'), ('Asia/Pyongyang', 'GMT+09:00 Asia/Pyongyang'), ('Asia/Seoul', 'GMT+09:00 Asia/Seoul'), ('Asia/Tokyo', 'GMT+09:00 Asia/Tokyo'), ('Asia/Yakutsk', 'GMT+09:00 Asia/Yakutsk'), ('Pacific/Palau', 'GMT+09:00 Pacific/Palau'), ('Australia/Darwin', 'GMT+09:00 Australia/Darwin'), ('Antarctica/DumontDUrville', 'GMT+10:00 Antarctica/DumontDUrville'), ('Asia/Sakhalin', 'GMT+10:00 Asia/Sakhalin'), ('Asia/Vladivostok', 'GMT+10:00 Asia/Vladivostok'), ('Australia/Brisbane', 'GMT+10:00 Australia/Brisbane'), ('Australia/Lindeman', 'GMT+10:00 Australia/Lindeman'), ('Pacific/Bougainville', 'GMT+10:00 Pacific/Bougainville'), ('Pacific/Chuuk', 'GMT+10:00 Pacific/Chuuk'), ('Pacific/Guam', 'GMT+10:00 Pacific/Guam'), ('Pacific/Port_Moresby', 'GMT+10:00 Pacific/Port Moresby'), ('Pacific/Saipan', 'GMT+10:00 Pacific/Saipan'), ('Australia/Adelaide', 'GMT+10:00 Australia/Adelaide'), ('Australia/Broken_Hill', 'GMT+10:00 Australia/Broken Hill'), ('Antarctica/Macquarie', 'GMT+11:00 Antarctica/Macquarie'), ('Asia/Magadan', 'GMT+11:00 Asia/Magadan'), ('Asia/Srednekolymsk', 'GMT+11:00 Asia/Srednekolymsk'), ('Asia/Ust-Nera', 'GMT+11:00 Asia/Ust-Nera'), ('Australia/Currie', 'GMT+11:00 Australia/Currie'), ('Australia/Hobart', 'GMT+11:00 Australia/Hobart'), ('Australia/Lord_Howe', 'GMT+11:00 Australia/Lord Howe'), ('Australia/Melbourne', 'GMT+11:00 Australia/Melbourne'), ('Australia/Sydney', 'GMT+11:00 Australia/Sydney'), ('Pacific/Efate', 'GMT+11:00 Pacific/Efate'), ('Pacific/Guadalcanal', 'GMT+11:00 Pacific/Guadalcanal'), ('Pacific/Kosrae', 'GMT+11:00 Pacific/Kosrae'), ('Pacific/Noumea', 'GMT+11:00 Pacific/Noumea'), ('Pacific/Pohnpei', 'GMT+11:00 Pacific/Pohnpei'), ('Pacific/Norfolk', 'GMT+11:00 Pacific/Norfolk'), ('Asia/Anadyr', 'GMT+12:00 Asia/Anadyr'), ('Asia/Kamchatka', 'GMT+12:00 Asia/Kamchatka'), ('Pacific/Funafuti', 'GMT+12:00 Pacific/Funafuti'), ('Pacific/Kwajalein', 'GMT+12:00 Pacific/Kwajalein'), ('Pacific/Majuro', 'GMT+12:00 Pacific/Majuro'), ('Pacific/Nauru', 'GMT+12:00 Pacific/Nauru'), ('Pacific/Tarawa', 'GMT+12:00 Pacific/Tarawa'), ('Pacific/Wake', 'GMT+12:00 Pacific/Wake'), ('Pacific/Wallis', 'GMT+12:00 Pacific/Wallis'), ('Antarctica/McMurdo', 'GMT+13:00 Antarctica/McMurdo'), ('Pacific/Auckland', 'GMT+13:00 Pacific/Auckland'), ('Pacific/Enderbury', 'GMT+13:00 Pacific/Enderbury'), ('Pacific/Fiji', 'GMT+13:00 Pacific/Fiji'), ('Pacific/Chatham', 'GMT+13:00 Pacific/Chatham'), ('Pacific/Kiritimati', 'GMT+14:00 Pacific/Kiritimati'), ('Pacific/Tongatapu', 'GMT+14:00 Pacific/Tongatapu')], default='Europe/Berlin', help_text='Time Zone where this event takes place in', verbose_name='Time Zone'),
+        ),
+    ]
diff --git a/AKModel/models.py b/AKModel/models.py
index c6c2ffef71ea586cf6bf49895c960e1eeabb60de..24ceb4d7e7a1771a34b293b549f41f57dd7d2562 100644
--- a/AKModel/models.py
+++ b/AKModel/models.py
@@ -1,10 +1,11 @@
-# Create your models here.
 import datetime
 import itertools
 
 from django.db import models
+from django.utils import timezone
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
+from timezone_field import TimeZoneField
 
 
 class Event(models.Model):
@@ -14,15 +15,19 @@ class Event(models.Model):
                             help_text=_('Name or iteration of the event'))
     slug = models.SlugField(max_length=32, unique=True, verbose_name=_('Short Form'),
                             help_text=_('Short name of letters/numbers/dots/dashes/underscores used in URLs.'))
-    start = models.DateTimeField(verbose_name=_('Start'), help_text=_('Time the event begins'))
-    end = models.DateTimeField(verbose_name=_('End'), help_text=_('Time the event ends'))
+
     place = models.CharField(max_length=128, blank=True, verbose_name=_('Place'),
                              help_text=_('City etc. the event takes place in'))
+    timezone = TimeZoneField(default='Europe/Berlin', display_GMT_offset=True, blank=False,
+                             verbose_name=_('Time Zone'), help_text=_('Time Zone where this event takes place in'))
+    start = models.DateTimeField(verbose_name=_('Start'), help_text=_('Time the event begins'))
+    end = models.DateTimeField(verbose_name=_('End'), help_text=_('Time the event ends'))
+
     active = models.BooleanField(verbose_name=_('Active State'), help_text=_('Marks currently active events'))
 
     base_url = models.URLField(verbose_name=_("Base URL"), help_text=_("Prefix for wiki link construction"), blank=True)
-    default_slot = models.DecimalField(max_digits=4, decimal_places=2, default=2, verbose_name='Default Slot Length',
-                                       help_text='Default length in hours that is assumed for AKs in this event.')
+    default_slot = models.DecimalField(max_digits=4, decimal_places=2, default=2, verbose_name=_('Default Slot Length'),
+                                       help_text=_('Default length in hours that is assumed for AKs in this event.'))
 
     contact_email = models.EmailField(verbose_name=_("Contact email address"), blank=True,
                                       help_text=_(
@@ -245,11 +250,15 @@ class Room(models.Model):
         ordering = ['building', 'name']
         unique_together = [['name', 'building']]
 
-    def __str__(self):
+    @property
+    def title(self):
         if self.building:
             return f"{self.building} {self.name}"
         return self.name
 
+    def __str__(self):
+        return self.title
+
 
 class AKSlot(models.Model):
     """ An AK Mapping matches an AK to a room during a certain time.
@@ -284,7 +293,7 @@ class AKSlot(models.Model):
         """
         if self.start is None:
             return _("Not scheduled yet")
-        return self.start.strftime('%a %H:%M')
+        return self.start.astimezone(self.event.timezone).strftime('%a %H:%M')
 
     @property
     def end(self):
@@ -292,3 +301,12 @@ class AKSlot(models.Model):
         Retrieve end time of the AK slot
         """
         return self.start + datetime.timedelta(hours=float(self.duration))
+
+    @property
+    def seconds_since_last_update(self):
+        """
+        Return minutes since last update
+        :return: minutes since last update
+        :rtype: float
+        """
+        return (timezone.now() - self.updated).total_seconds()
diff --git a/AKPlan/locale/de_DE/LC_MESSAGES/django.po b/AKPlan/locale/de_DE/LC_MESSAGES/django.po
new file mode 100644
index 0000000000000000000000000000000000000000..234e750064ade3d95517d900c8fc6352d554a610
--- /dev/null
+++ b/AKPlan/locale/de_DE/LC_MESSAGES/django.po
@@ -0,0 +1,67 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-03-04 00:53+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: templates/AKPlan/plan_base.html:24
+msgid "Write to organizers of this event for questions and comments"
+msgstr "Fragen oder Kommentare? Schreib den Orgas dieses Events eine Mail"
+
+#: templates/AKPlan/plan_breadcrumbs.html:13
+msgid "AK Plan"
+msgstr "AK-Plan"
+
+#: templates/AKPlan/plan_index.html:40
+msgid "Day"
+msgstr "Tag"
+
+#: templates/AKPlan/plan_index.html:50
+msgid "Event"
+msgstr "Veranstaltung"
+
+#: templates/AKPlan/plan_index.html:59 templates/AKPlan/plan_room.html:8
+#: templates/AKPlan/plan_room.html:44 templates/AKPlan/plan_wall.html:64
+msgid "Room"
+msgstr "Raum"
+
+#: templates/AKPlan/plan_index.html:80 templates/AKPlan/plan_room.html:34
+msgid "Rooms"
+msgstr "Räume"
+
+#: templates/AKPlan/plan_index.html:88 templates/AKPlan/plan_track.html:34
+msgid "Tracks"
+msgstr "Tracks"
+
+#: templates/AKPlan/plan_index.html:96
+msgid "AK Wall"
+msgstr "AK-Wall"
+
+#: templates/AKPlan/plan_index.html:106 templates/AKPlan/plan_wall.html:88
+msgid "Current AKs"
+msgstr "Aktuelle AKs"
+
+#: templates/AKPlan/plan_index.html:113 templates/AKPlan/plan_wall.html:93
+msgid "Next AKs"
+msgstr "Nächste AKs"
+
+#: templates/AKPlan/plan_track.html:8 templates/AKPlan/plan_track.html:44
+msgid "Track"
+msgstr "Track"
+
+#: templates/AKPlan/slots_table.html:12
+msgid "No AKs"
+msgstr "Keine AKs"
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/bootstrap/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/README.md b/AKPlan/static/AKPlan/fullcalendar/bootstrap/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1da7990c19c607faebe472c57f8a4f0f051a71cd
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Bootstrap Plugin
+
+Bootstrap 4 theming for your calendar
+
+[View the docs &raquo;](https://fullcalendar.io/docs/bootstrap-theme)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.css b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..3dc71f583170af3673c27e6006dab2e0cec55963
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.css
@@ -0,0 +1,36 @@
+.fc.fc-bootstrap a {
+  text-decoration: none;
+}
+
+.fc.fc-bootstrap a[data-goto]:hover {
+  text-decoration: underline;
+}
+
+.fc-bootstrap hr.fc-divider {
+  border-color: inherit;
+}
+
+.fc-bootstrap .fc-today.alert {
+  border-radius: 0;
+}
+
+.fc-bootstrap a.fc-event:not([href]):not([tabindex]) {
+  color: #fff;
+}
+
+.fc-bootstrap .fc-popover.card {
+  position: absolute;
+}
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-bootstrap .fc-popover .card-body {
+  padding: 0;
+}
+
+/* TimeGrid Slats (lines that run horizontally)
+--------------------------------------------------------------------------------------------------*/
+.fc-bootstrap .fc-time-grid .fc-slats table {
+  /* some themes have background color. see through to slats */
+  background: none;
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4aa91f33d3f20835f131156c462bb3d50b76de90
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.d.ts
@@ -0,0 +1,12 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/bootstrap' {
+    import { Theme } from '@fullcalendar/core';
+    export class BootstrapTheme extends Theme {
+    }
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a02e1b2d1573ffe64245691c2d939959db3534e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.esm.js
@@ -0,0 +1,83 @@
+/*!
+FullCalendar Bootstrap Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { createPlugin, Theme } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var BootstrapTheme = /** @class */ (function (_super) {
+    __extends(BootstrapTheme, _super);
+    function BootstrapTheme() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    return BootstrapTheme;
+}(Theme));
+BootstrapTheme.prototype.classes = {
+    widget: 'fc-bootstrap',
+    tableGrid: 'table-bordered',
+    tableList: 'table',
+    tableListHeading: 'table-active',
+    buttonGroup: 'btn-group',
+    button: 'btn btn-primary',
+    buttonActive: 'active',
+    today: 'alert alert-info',
+    popover: 'card card-primary',
+    popoverHeader: 'card-header',
+    popoverContent: 'card-body',
+    // day grid
+    // for left/right border color when border is inset from edges (all-day in timeGrid view)
+    // avoid `table` class b/c don't want margins/padding/structure. only border color.
+    headerRow: 'table-bordered',
+    dayRow: 'table-bordered',
+    // list view
+    listView: 'card card-primary'
+};
+BootstrapTheme.prototype.baseIconClass = 'fa';
+BootstrapTheme.prototype.iconClasses = {
+    close: 'fa-times',
+    prev: 'fa-chevron-left',
+    next: 'fa-chevron-right',
+    prevYear: 'fa-angle-double-left',
+    nextYear: 'fa-angle-double-right'
+};
+BootstrapTheme.prototype.iconOverrideOption = 'bootstrapFontAwesome';
+BootstrapTheme.prototype.iconOverrideCustomButtonOption = 'bootstrapFontAwesome';
+BootstrapTheme.prototype.iconOverridePrefix = 'fa-';
+var main = createPlugin({
+    themeClasses: {
+        bootstrap: BootstrapTheme
+    }
+});
+
+export default main;
+export { BootstrapTheme };
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.js b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..b063deb8a683348e66f69528e57f19dfce439976
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.js
@@ -0,0 +1,91 @@
+/*!
+FullCalendar Bootstrap Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarBootstrap = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var BootstrapTheme = /** @class */ (function (_super) {
+        __extends(BootstrapTheme, _super);
+        function BootstrapTheme() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        return BootstrapTheme;
+    }(core.Theme));
+    BootstrapTheme.prototype.classes = {
+        widget: 'fc-bootstrap',
+        tableGrid: 'table-bordered',
+        tableList: 'table',
+        tableListHeading: 'table-active',
+        buttonGroup: 'btn-group',
+        button: 'btn btn-primary',
+        buttonActive: 'active',
+        today: 'alert alert-info',
+        popover: 'card card-primary',
+        popoverHeader: 'card-header',
+        popoverContent: 'card-body',
+        // day grid
+        // for left/right border color when border is inset from edges (all-day in timeGrid view)
+        // avoid `table` class b/c don't want margins/padding/structure. only border color.
+        headerRow: 'table-bordered',
+        dayRow: 'table-bordered',
+        // list view
+        listView: 'card card-primary'
+    };
+    BootstrapTheme.prototype.baseIconClass = 'fa';
+    BootstrapTheme.prototype.iconClasses = {
+        close: 'fa-times',
+        prev: 'fa-chevron-left',
+        next: 'fa-chevron-right',
+        prevYear: 'fa-angle-double-left',
+        nextYear: 'fa-angle-double-right'
+    };
+    BootstrapTheme.prototype.iconOverrideOption = 'bootstrapFontAwesome';
+    BootstrapTheme.prototype.iconOverrideCustomButtonOption = 'bootstrapFontAwesome';
+    BootstrapTheme.prototype.iconOverridePrefix = 'fa-';
+    var main = core.createPlugin({
+        themeClasses: {
+            bootstrap: BootstrapTheme
+        }
+    });
+
+    exports.BootstrapTheme = BootstrapTheme;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.min.css b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..e9249b56801a3b8961b3ef78e557a31625177dc6
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.min.css
@@ -0,0 +1 @@
+.fc.fc-bootstrap a{text-decoration:none}.fc.fc-bootstrap a[data-goto]:hover{text-decoration:underline}.fc-bootstrap hr.fc-divider{border-color:inherit}.fc-bootstrap .fc-today.alert{border-radius:0}.fc-bootstrap a.fc-event:not([href]):not([tabindex]){color:#fff}.fc-bootstrap .fc-popover.card{position:absolute}.fc-bootstrap .fc-popover .card-body{padding:0}.fc-bootstrap .fc-time-grid .fc-slats table{background:0 0}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.min.js b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..107ed48b1d66ed6fbbeef697548eaac8466a0289
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Bootstrap Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarBootstrap={},e.FullCalendar)}(this,function(e,t){"use strict";var o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var o in t)t.hasOwnProperty(o)&&(e[o]=t[o])})(e,t)};var r=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return function(e,t){function r(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}(t,e),t}(t.Theme);r.prototype.classes={widget:"fc-bootstrap",tableGrid:"table-bordered",tableList:"table",tableListHeading:"table-active",buttonGroup:"btn-group",button:"btn btn-primary",buttonActive:"active",today:"alert alert-info",popover:"card card-primary",popoverHeader:"card-header",popoverContent:"card-body",headerRow:"table-bordered",dayRow:"table-bordered",listView:"card card-primary"},r.prototype.baseIconClass="fa",r.prototype.iconClasses={close:"fa-times",prev:"fa-chevron-left",next:"fa-chevron-right",prevYear:"fa-angle-double-left",nextYear:"fa-angle-double-right"},r.prototype.iconOverrideOption="bootstrapFontAwesome",r.prototype.iconOverrideCustomButtonOption="bootstrapFontAwesome",r.prototype.iconOverridePrefix="fa-";var a=t.createPlugin({themeClasses:{bootstrap:r}});e.BootstrapTheme=r,e.default=a,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/bootstrap/package.json b/AKPlan/static/AKPlan/fullcalendar/bootstrap/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..3f1845b573b2ed2996cdf2588676f7eb7bc733a7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/bootstrap/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/bootstrap",
+  "version": "4.3.0",
+  "title": "FullCalendar Bootstrap Plugin",
+  "description": "Bootstrap 4 theming for your calendar",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/bootstrap-theme",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/core/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/README.md b/AKPlan/static/AKPlan/fullcalendar/core/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7ed36f442bafc3d4c46eef9e4d21a0e9e86f82c5
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Core Package
+
+Provides core functionality, including the Calendar class
+
+[View the docs &raquo;](https://fullcalendar.io/docs/initialize-es6)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales-all.js b/AKPlan/static/AKPlan/fullcalendar/core/locales-all.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c7c9fa9380aaf3a29f87cb455bd46f8e81e6a7c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales-all.js
@@ -0,0 +1,1348 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, global.FullCalendarLocalesAll = factory());
+}(this, function () { 'use strict';
+
+    var _m0 = {
+        code: "af",
+        week: {
+            dow: 1,
+            doy: 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar.
+        },
+        buttonText: {
+            prev: "Vorige",
+            next: "Volgende",
+            today: "Vandag",
+            year: "Jaar",
+            month: "Maand",
+            week: "Week",
+            day: "Dag",
+            list: "Agenda"
+        },
+        allDayHtml: "Heeldag",
+        eventLimitText: "Addisionele",
+        noEventsMessage: "Daar is geen gebeurtenisse nie"
+    };
+
+    var _m1 = {
+        code: "ar-dz",
+        week: {
+            dow: 0,
+            doy: 4 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m2 = {
+        code: "ar-kw",
+        week: {
+            dow: 0,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m3 = {
+        code: "ar-ly",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m4 = {
+        code: "ar-ma",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m5 = {
+        code: "ar-sa",
+        week: {
+            dow: 0,
+            doy: 6 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m6 = {
+        code: "ar-tn",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m7 = {
+        code: "ar",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    var _m8 = {
+        code: "bg",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "назад",
+            next: "напред",
+            today: "днес",
+            month: "Месец",
+            week: "Седмица",
+            day: "Ден",
+            list: "График"
+        },
+        allDayText: "Цял ден",
+        eventLimitText: function (n) {
+            return "+още " + n;
+        },
+        noEventsMessage: "Няма събития за показване"
+    };
+
+    var _m9 = {
+        code: "bs",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prošli",
+            next: "Sljedeći",
+            today: "Danas",
+            month: "Mjesec",
+            week: "Sedmica",
+            day: "Dan",
+            list: "Raspored"
+        },
+        weekLabel: "Sed",
+        allDayText: "Cijeli dan",
+        eventLimitText: function (n) {
+            return "+ još " + n;
+        },
+        noEventsMessage: "Nema događaja za prikazivanje"
+    };
+
+    var _m10 = {
+        code: "ca",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Anterior",
+            next: "Següent",
+            today: "Avui",
+            month: "Mes",
+            week: "Setmana",
+            day: "Dia",
+            list: "Agenda"
+        },
+        weekLabel: "Set",
+        allDayText: "Tot el dia",
+        eventLimitText: "més",
+        noEventsMessage: "No hi ha esdeveniments per mostrar"
+    };
+
+    var _m11 = {
+        code: "cs",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Dříve",
+            next: "Později",
+            today: "Nyní",
+            month: "Měsíc",
+            week: "Týden",
+            day: "Den",
+            list: "Agenda"
+        },
+        weekLabel: "Týd",
+        allDayText: "Celý den",
+        eventLimitText: function (n) {
+            return "+další: " + n;
+        },
+        noEventsMessage: "Žádné akce k zobrazení"
+    };
+
+    var _m12 = {
+        code: "da",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Forrige",
+            next: "Næste",
+            today: "I dag",
+            month: "MÃ¥ned",
+            week: "Uge",
+            day: "Dag",
+            list: "Agenda"
+        },
+        weekLabel: "Uge",
+        allDayText: "Hele dagen",
+        eventLimitText: "flere",
+        noEventsMessage: "Ingen arrangementer at vise"
+    };
+
+    var _m13 = {
+        code: "de",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Zurück",
+            next: "Vor",
+            today: "Heute",
+            year: "Jahr",
+            month: "Monat",
+            week: "Woche",
+            day: "Tag",
+            list: "Terminübersicht"
+        },
+        weekLabel: "KW",
+        allDayText: "Ganztägig",
+        eventLimitText: function (n) {
+            return "+ weitere " + n;
+        },
+        noEventsMessage: "Keine Ereignisse anzuzeigen"
+    };
+
+    var _m14 = {
+        code: "el",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Προηγούμενος",
+            next: "Επόμενος",
+            today: "Σήμερα",
+            month: "Μήνας",
+            week: "Εβδομάδα",
+            day: "Ημέρα",
+            list: "Ατζέντα"
+        },
+        weekLabel: "Εβδ",
+        allDayText: "Ολοήμερο",
+        eventLimitText: "περισσότερα",
+        noEventsMessage: "Δεν υπάρχουν γεγονότα για να εμφανιστεί"
+    };
+
+    var _m15 = {
+        code: "en-au",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        }
+    };
+
+    var _m16 = {
+        code: "en-gb",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        }
+    };
+
+    var _m17 = {
+        code: "en-nz",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        }
+    };
+
+    var _m18 = {
+        code: "es",
+        week: {
+            dow: 0,
+            doy: 6 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Ant",
+            next: "Sig",
+            today: "Hoy",
+            month: "Mes",
+            week: "Semana",
+            day: "Día",
+            list: "Agenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Todo<br/>el día",
+        eventLimitText: "más",
+        noEventsMessage: "No hay eventos para mostrar"
+    };
+
+    var _m19 = {
+        code: "es",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Ant",
+            next: "Sig",
+            today: "Hoy",
+            month: "Mes",
+            week: "Semana",
+            day: "Día",
+            list: "Agenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Todo<br/>el día",
+        eventLimitText: "más",
+        noEventsMessage: "No hay eventos para mostrar"
+    };
+
+    var _m20 = {
+        code: "et",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Eelnev",
+            next: "Järgnev",
+            today: "Täna",
+            month: "Kuu",
+            week: "Nädal",
+            day: "Päev",
+            list: "Päevakord"
+        },
+        weekLabel: "näd",
+        allDayText: "Kogu päev",
+        eventLimitText: function (n) {
+            return "+ veel " + n;
+        },
+        noEventsMessage: "Kuvamiseks puuduvad sündmused"
+    };
+
+    var _m21 = {
+        code: "eu",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Aur",
+            next: "Hur",
+            today: "Gaur",
+            month: "Hilabetea",
+            week: "Astea",
+            day: "Eguna",
+            list: "Agenda"
+        },
+        weekLabel: "As",
+        allDayHtml: "Egun<br/>osoa",
+        eventLimitText: "gehiago",
+        noEventsMessage: "Ez dago ekitaldirik erakusteko"
+    };
+
+    var _m22 = {
+        code: "fa",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "قبلی",
+            next: "بعدی",
+            today: "امروز",
+            month: "ماه",
+            week: "هفته",
+            day: "روز",
+            list: "برنامه"
+        },
+        weekLabel: "هف",
+        allDayText: "تمام روز",
+        eventLimitText: function (n) {
+            return "بیش از " + n;
+        },
+        noEventsMessage: "هیچ رویدادی به نمایش"
+    };
+
+    var _m23 = {
+        code: "fi",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Edellinen",
+            next: "Seuraava",
+            today: "Tänään",
+            month: "Kuukausi",
+            week: "Viikko",
+            day: "Päivä",
+            list: "Tapahtumat"
+        },
+        weekLabel: "Vk",
+        allDayText: "Koko päivä",
+        eventLimitText: "lisää",
+        noEventsMessage: "Ei näytettäviä tapahtumia"
+    };
+
+    var _m24 = {
+        code: "fr",
+        buttonText: {
+            prev: "Précédent",
+            next: "Suivant",
+            today: "Aujourd'hui",
+            year: "Année",
+            month: "Mois",
+            week: "Semaine",
+            day: "Jour",
+            list: "Mon planning"
+        },
+        weekLabel: "Sem.",
+        allDayHtml: "Toute la<br/>journée",
+        eventLimitText: "en plus",
+        noEventsMessage: "Aucun événement à afficher"
+    };
+
+    var _m25 = {
+        code: "fr-ch",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Précédent",
+            next: "Suivant",
+            today: "Courant",
+            year: "Année",
+            month: "Mois",
+            week: "Semaine",
+            day: "Jour",
+            list: "Mon planning"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Toute la<br/>journée",
+        eventLimitText: "en plus",
+        noEventsMessage: "Aucun événement à afficher"
+    };
+
+    var _m26 = {
+        code: "fr",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Précédent",
+            next: "Suivant",
+            today: "Aujourd'hui",
+            year: "Année",
+            month: "Mois",
+            week: "Semaine",
+            day: "Jour",
+            list: "Mon planning"
+        },
+        weekLabel: "Sem.",
+        allDayHtml: "Toute la<br/>journée",
+        eventLimitText: "en plus",
+        noEventsMessage: "Aucun événement à afficher"
+    };
+
+    var _m27 = {
+        code: "gl",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Ant",
+            next: "Seg",
+            today: "Hoxe",
+            month: "Mes",
+            week: "Semana",
+            day: "Día",
+            list: "Axenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Todo<br/>o día",
+        eventLimitText: "máis",
+        noEventsMessage: "Non hai eventos para amosar"
+    };
+
+    var _m28 = {
+        code: "he",
+        dir: 'rtl',
+        buttonText: {
+            prev: "הקודם",
+            next: "הבא",
+            today: "היום",
+            month: "חודש",
+            week: "שבוע",
+            day: "יום",
+            list: "סדר יום"
+        },
+        allDayText: "כל היום",
+        eventLimitText: "אחר",
+        noEventsMessage: "אין אירועים להצגה",
+        weekLabel: "שבוע"
+    };
+
+    var _m29 = {
+        code: "hi",
+        week: {
+            dow: 0,
+            doy: 6 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "पिछला",
+            next: "अगला",
+            today: "आज",
+            month: "महीना",
+            week: "सप्ताह",
+            day: "दिन",
+            list: "कार्यसूची"
+        },
+        weekLabel: "हफ्ता",
+        allDayText: "सभी दिन",
+        eventLimitText: function (n) {
+            return "+अधिक " + n;
+        },
+        noEventsMessage: "कोई घटनाओं को प्रदर्शित करने के लिए"
+    };
+
+    var _m30 = {
+        code: "hr",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prijašnji",
+            next: "Sljedeći",
+            today: "Danas",
+            month: "Mjesec",
+            week: "Tjedan",
+            day: "Dan",
+            list: "Raspored"
+        },
+        weekLabel: "Tje",
+        allDayText: "Cijeli dan",
+        eventLimitText: function (n) {
+            return "+ još " + n;
+        },
+        noEventsMessage: "Nema događaja za prikaz"
+    };
+
+    var _m31 = {
+        code: "hu",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "vissza",
+            next: "előre",
+            today: "ma",
+            month: "Hónap",
+            week: "Hét",
+            day: "Nap",
+            list: "Napló"
+        },
+        weekLabel: "Hét",
+        allDayText: "Egész nap",
+        eventLimitText: "további",
+        noEventsMessage: "Nincs megjeleníthető esemény"
+    };
+
+    var _m32 = {
+        code: "id",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "mundur",
+            next: "maju",
+            today: "hari ini",
+            month: "Bulan",
+            week: "Minggu",
+            day: "Hari",
+            list: "Agenda"
+        },
+        weekLabel: "Mg",
+        allDayHtml: "Sehari<br/>penuh",
+        eventLimitText: "lebih",
+        noEventsMessage: "Tidak ada acara untuk ditampilkan"
+    };
+
+    var _m33 = {
+        code: "is",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Fyrri",
+            next: "Næsti",
+            today: "Í dag",
+            month: "Mánuður",
+            week: "Vika",
+            day: "Dagur",
+            list: "Dagskrá"
+        },
+        weekLabel: "Vika",
+        allDayHtml: "Allan<br/>daginn",
+        eventLimitText: "meira",
+        noEventsMessage: "Engir viðburðir til að sýna"
+    };
+
+    var _m34 = {
+        code: "it",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prec",
+            next: "Succ",
+            today: "Oggi",
+            month: "Mese",
+            week: "Settimana",
+            day: "Giorno",
+            list: "Agenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Tutto il<br/>giorno",
+        eventLimitText: function (n) {
+            return "+altri " + n;
+        },
+        noEventsMessage: "Non ci sono eventi da visualizzare"
+    };
+
+    var _m35 = {
+        code: "ja",
+        buttonText: {
+            prev: "前",
+            next: "次",
+            today: "今日",
+            month: "月",
+            week: "週",
+            day: "æ—¥",
+            list: "予定リスト"
+        },
+        weekLabel: "週",
+        allDayText: "終日",
+        eventLimitText: function (n) {
+            return "他 " + n + " 件";
+        },
+        noEventsMessage: "表示する予定はありません"
+    };
+
+    var _m36 = {
+        code: "ka",
+        week: {
+            dow: 1,
+            doy: 7
+        },
+        buttonText: {
+            prev: "წინა",
+            next: "შემდეგი",
+            today: "დღეს",
+            month: "თვე",
+            week: "კვირა",
+            day: "დღე",
+            list: "დღის წესრიგი"
+        },
+        weekLabel: "კვ",
+        allDayText: "მთელი დღე",
+        eventLimitText: function (n) {
+            return "+ კიდევ " + n;
+        },
+        noEventsMessage: "ღონისძიებები არ არის"
+    };
+
+    var _m37 = {
+        code: "kk",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Алдыңғы",
+            next: "Келесі",
+            today: "Бүгін",
+            month: "Ай",
+            week: "Апта",
+            day: "Күн",
+            list: "Күн тәртібі"
+        },
+        weekLabel: "Не",
+        allDayText: "Күні бойы",
+        eventLimitText: function (n) {
+            return "+ тағы " + n;
+        },
+        noEventsMessage: "Көрсету үшін оқиғалар жоқ"
+    };
+
+    var _m38 = {
+        code: "ko",
+        buttonText: {
+            prev: "이전달",
+            next: "다음달",
+            today: "오늘",
+            month: "ì›”",
+            week: "주",
+            day: "일",
+            list: "일정목록"
+        },
+        weekLabel: "주",
+        allDayText: "종일",
+        eventLimitText: "개",
+        noEventsMessage: "일정이 없습니다"
+    };
+
+    var _m39 = {
+        code: "lb",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Zréck",
+            next: "Weider",
+            today: "Haut",
+            month: "Mount",
+            week: "Woch",
+            day: "Dag",
+            list: "Terminiwwersiicht"
+        },
+        weekLabel: "W",
+        allDayText: "Ganzen Dag",
+        eventLimitText: "méi",
+        noEventsMessage: "Nee Evenementer ze affichéieren"
+    };
+
+    var _m40 = {
+        code: "lt",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Atgal",
+            next: "Pirmyn",
+            today: "Å iandien",
+            month: "MÄ—nuo",
+            week: "SavaitÄ—",
+            day: "Diena",
+            list: "DarbotvarkÄ—"
+        },
+        weekLabel: "SAV",
+        allDayText: "VisÄ… dienÄ…",
+        eventLimitText: "daugiau",
+        noEventsMessage: "Nėra įvykių rodyti"
+    };
+
+    var _m41 = {
+        code: "lv",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Iepr.",
+            next: "Nāk.",
+            today: "Å odien",
+            month: "MÄ“nesis",
+            week: "Nedēļa",
+            day: "Diena",
+            list: "Dienas kārtība"
+        },
+        weekLabel: "Ned.",
+        allDayText: "Visu dienu",
+        eventLimitText: function (n) {
+            return "+vēl " + n;
+        },
+        noEventsMessage: "Nav notikumu"
+    };
+
+    var _m42 = {
+        code: "mk",
+        buttonText: {
+            prev: "претходно",
+            next: "следно",
+            today: "Денес",
+            month: "Месец",
+            week: "Недела",
+            day: "Ден",
+            list: "График"
+        },
+        weekLabel: "Сед",
+        allDayText: "Цел ден",
+        eventLimitText: function (n) {
+            return "+повеќе " + n;
+        },
+        noEventsMessage: "Нема настани за прикажување"
+    };
+
+    var _m43 = {
+        code: "ms",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Sebelum",
+            next: "Selepas",
+            today: "hari ini",
+            month: "Bulan",
+            week: "Minggu",
+            day: "Hari",
+            list: "Agenda"
+        },
+        weekLabel: "Mg",
+        allDayText: "Sepanjang hari",
+        eventLimitText: function (n) {
+            return "masih ada " + n + " acara";
+        },
+        noEventsMessage: "Tiada peristiwa untuk dipaparkan"
+    };
+
+    var _m44 = {
+        code: "nb",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Forrige",
+            next: "Neste",
+            today: "I dag",
+            month: "MÃ¥ned",
+            week: "Uke",
+            day: "Dag",
+            list: "Agenda"
+        },
+        weekLabel: "Uke",
+        allDayText: "Hele dagen",
+        eventLimitText: "til",
+        noEventsMessage: "Ingen hendelser å vise"
+    };
+
+    var _m45 = {
+        code: "nl",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Voorgaand",
+            next: "Volgende",
+            today: "Vandaag",
+            year: "Jaar",
+            month: "Maand",
+            week: "Week",
+            day: "Dag",
+            list: "Agenda"
+        },
+        allDayText: "Hele dag",
+        eventLimitText: "extra",
+        noEventsMessage: "Geen evenementen om te laten zien"
+    };
+
+    var _m46 = {
+        code: "nn",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Førre",
+            next: "Neste",
+            today: "I dag",
+            month: "MÃ¥nad",
+            week: "Veke",
+            day: "Dag",
+            list: "Agenda"
+        },
+        weekLabel: "Veke",
+        allDayText: "Heile dagen",
+        eventLimitText: "til",
+        noEventsMessage: "Ingen hendelser å vise"
+    };
+
+    var _m47 = {
+        code: "pl",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Poprzedni",
+            next: "Następny",
+            today: "DziÅ›",
+            month: "MiesiÄ…c",
+            week: "Tydzień",
+            day: "Dzień",
+            list: "Plan dnia"
+        },
+        weekLabel: "Tydz",
+        allDayText: "Cały dzień",
+        eventLimitText: "więcej",
+        noEventsMessage: "Brak wydarzeń do wyświetlenia"
+    };
+
+    var _m48 = {
+        code: "pt-br",
+        buttonText: {
+            prev: "Anterior",
+            next: "Próximo",
+            today: "Hoje",
+            month: "Mês",
+            week: "Semana",
+            day: "Dia",
+            list: "Compromissos"
+        },
+        weekLabel: "Sm",
+        allDayText: "dia inteiro",
+        eventLimitText: function (n) {
+            return "mais +" + n;
+        },
+        noEventsMessage: "Não há eventos para mostrar"
+    };
+
+    var _m49 = {
+        code: "pt",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Anterior",
+            next: "Seguinte",
+            today: "Hoje",
+            month: "Mês",
+            week: "Semana",
+            day: "Dia",
+            list: "Agenda"
+        },
+        weekLabel: "Sem",
+        allDayText: "Todo o dia",
+        eventLimitText: "mais",
+        noEventsMessage: "Não há eventos para mostrar"
+    };
+
+    var _m50 = {
+        code: "ro",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "precedentă",
+            next: "următoare",
+            today: "Azi",
+            month: "Lună",
+            week: "Săptămână",
+            day: "Zi",
+            list: "Agendă"
+        },
+        weekLabel: "Săpt",
+        allDayText: "Toată ziua",
+        eventLimitText: function (n) {
+            return "+alte " + n;
+        },
+        noEventsMessage: "Nu există evenimente de afișat"
+    };
+
+    var _m51 = {
+        code: "ru",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Пред",
+            next: "След",
+            today: "Сегодня",
+            month: "Месяц",
+            week: "Неделя",
+            day: "День",
+            list: "Повестка дня"
+        },
+        weekLabel: "Нед",
+        allDayText: "Весь день",
+        eventLimitText: function (n) {
+            return "+ ещё " + n;
+        },
+        noEventsMessage: "Нет событий для отображения"
+    };
+
+    var _m52 = {
+        code: "sk",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Predchádzajúci",
+            next: "Nasledujúci",
+            today: "Dnes",
+            month: "Mesiac",
+            week: "Týždeň",
+            day: "Deň",
+            list: "Rozvrh"
+        },
+        weekLabel: "Ty",
+        allDayText: "Celý deň",
+        eventLimitText: function (n) {
+            return "+ďalšie: " + n;
+        },
+        noEventsMessage: "Žiadne akcie na zobrazenie"
+    };
+
+    var _m53 = {
+        code: "sl",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prejšnji",
+            next: "Naslednji",
+            today: "Trenutni",
+            month: "Mesec",
+            week: "Teden",
+            day: "Dan",
+            list: "Dnevni red"
+        },
+        weekLabel: "Teden",
+        allDayText: "Ves dan",
+        eventLimitText: "več",
+        noEventsMessage: "Ni dogodkov za prikaz"
+    };
+
+    var _m54 = {
+        code: "sq",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "mbrapa",
+            next: "Përpara",
+            today: "sot",
+            month: "Muaj",
+            week: "Javë",
+            day: "Ditë",
+            list: "Listë"
+        },
+        weekLabel: "Ja",
+        allDayHtml: "Gjithë<br/>ditën",
+        eventLimitText: function (n) {
+            return "+më tepër " + n;
+        },
+        noEventsMessage: "Nuk ka evente për të shfaqur"
+    };
+
+    var _m55 = {
+        code: "sr-cyrl",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Претходна",
+            next: "следећи",
+            today: "Данас",
+            month: "Месец",
+            week: "Недеља",
+            day: "Дан",
+            list: "Планер"
+        },
+        weekLabel: "Сед",
+        allDayText: "Цео дан",
+        eventLimitText: function (n) {
+            return "+ још " + n;
+        },
+        noEventsMessage: "Нема догађаја за приказ"
+    };
+
+    var _m56 = {
+        code: "sr",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prethodna",
+            next: "Sledeći",
+            today: "Danas",
+            month: "Mеsеc",
+            week: "Nеdеlja",
+            day: "Dan",
+            list: "Planеr"
+        },
+        weekLabel: "Sed",
+        allDayText: "Cеo dan",
+        eventLimitText: function (n) {
+            return "+ još " + n;
+        },
+        noEventsMessage: "Nеma događaja za prikaz"
+    };
+
+    var _m57 = {
+        code: "sv",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Förra",
+            next: "Nästa",
+            today: "Idag",
+            month: "MÃ¥nad",
+            week: "Vecka",
+            day: "Dag",
+            list: "Program"
+        },
+        weekLabel: "v.",
+        allDayText: "Heldag",
+        eventLimitText: "till",
+        noEventsMessage: "Inga händelser att visa"
+    };
+
+    var _m58 = {
+        code: "th",
+        buttonText: {
+            prev: "ย้อน",
+            next: "ถัดไป",
+            today: "วันนี้",
+            month: "เดือน",
+            week: "สัปดาห์",
+            day: "วัน",
+            list: "แผนงาน"
+        },
+        allDayText: "ตลอดวัน",
+        eventLimitText: "เพิ่มเติม",
+        noEventsMessage: "ไม่มีกิจกรรมที่จะแสดง"
+    };
+
+    var _m59 = {
+        code: "tr",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "geri",
+            next: "ileri",
+            today: "bugün",
+            month: "Ay",
+            week: "Hafta",
+            day: "Gün",
+            list: "Ajanda"
+        },
+        weekLabel: "Hf",
+        allDayText: "Tüm gün",
+        eventLimitText: "daha fazla",
+        noEventsMessage: "Gösterilecek etkinlik yok"
+    };
+
+    var _m60 = {
+        code: "uk",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Попередній",
+            next: "далі",
+            today: "Сьогодні",
+            month: "Місяць",
+            week: "Тиждень",
+            day: "День",
+            list: "Порядок денний"
+        },
+        weekLabel: "Тиж",
+        allDayText: "Увесь день",
+        eventLimitText: function (n) {
+            return "+ще " + n + "...";
+        },
+        noEventsMessage: "Немає подій для відображення"
+    };
+
+    var _m61 = {
+        code: "vi",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "TrÆ°á»›c",
+            next: "Tiếp",
+            today: "Hôm nay",
+            month: "Tháng",
+            week: "Tuần",
+            day: "Ngày",
+            list: "Lịch biểu"
+        },
+        weekLabel: "Tu",
+        allDayText: "Cả ngày",
+        eventLimitText: function (n) {
+            return "+ thêm " + n;
+        },
+        noEventsMessage: "Không có sự kiện để hiển thị"
+    };
+
+    var _m62 = {
+        code: "zh-cn",
+        week: {
+            // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "上月",
+            next: "下月",
+            today: "今天",
+            month: "月",
+            week: "周",
+            day: "æ—¥",
+            list: "日程"
+        },
+        weekLabel: "周",
+        allDayText: "全天",
+        eventLimitText: function (n) {
+            return "另外 " + n + " 个";
+        },
+        noEventsMessage: "没有事件显示"
+    };
+
+    var _m63 = {
+        code: "zh-tw",
+        buttonText: {
+            prev: "上月",
+            next: "下月",
+            today: "今天",
+            month: "月",
+            week: "週",
+            day: "天",
+            list: "活動列表"
+        },
+        weekLabel: "周",
+        allDayText: "整天",
+        eventLimitText: '顯示更多',
+        noEventsMessage: "没有任何活動"
+    };
+
+    var _rollupPluginMultiEntry_entryPoint = [
+    _m0, _m1, _m2, _m3, _m4, _m5, _m6, _m7, _m8, _m9, _m10, _m11, _m12, _m13, _m14, _m15, _m16, _m17, _m18, _m19, _m20, _m21, _m22, _m23, _m24, _m25, _m26, _m27, _m28, _m29, _m30, _m31, _m32, _m33, _m34, _m35, _m36, _m37, _m38, _m39, _m40, _m41, _m42, _m43, _m44, _m45, _m46, _m47, _m48, _m49, _m50, _m51, _m52, _m53, _m54, _m55, _m56, _m57, _m58, _m59, _m60, _m61, _m62, _m63
+    ];
+
+    return _rollupPluginMultiEntry_entryPoint;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales-all.min.js b/AKPlan/static/AKPlan/fullcalendar/core/locales-all.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..810d6deaf97c70c0f721ee254c67407b43c0aaf8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales-all.min.js
@@ -0,0 +1 @@
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).FullCalendarLocalesAll=t()}(this,function(){"use strict";return[{code:"af",week:{dow:1,doy:4},buttonText:{prev:"Vorige",next:"Volgende",today:"Vandag",year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayHtml:"Heeldag",eventLimitText:"Addisionele",noEventsMessage:"Daar is geen gebeurtenisse nie"},{code:"ar-dz",week:{dow:0,doy:4},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-kw",week:{dow:0,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-ly",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-ma",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-sa",week:{dow:0,doy:6},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar-tn",week:{dow:1,doy:4},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"ar",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"السابق",next:"التالي",today:"اليوم",month:"شهر",week:"أسبوع",day:"يوم",list:"أجندة"},weekLabel:"أسبوع",allDayText:"اليوم كله",eventLimitText:"أخرى",noEventsMessage:"أي أحداث لعرض"},{code:"bg",week:{dow:1,doy:7},buttonText:{prev:"назад",next:"напред",today:"днес",month:"Месец",week:"Седмица",day:"Ден",list:"График"},allDayText:"Цял ден",eventLimitText:function(e){return"+още "+e},noEventsMessage:"Няма събития за показване"},{code:"bs",week:{dow:1,doy:7},buttonText:{prev:"Prošli",next:"Sljedeći",today:"Danas",month:"Mjesec",week:"Sedmica",day:"Dan",list:"Raspored"},weekLabel:"Sed",allDayText:"Cijeli dan",eventLimitText:function(e){return"+ još "+e},noEventsMessage:"Nema događaja za prikazivanje"},{code:"ca",week:{dow:1,doy:4},buttonText:{prev:"Anterior",next:"Següent",today:"Avui",month:"Mes",week:"Setmana",day:"Dia",list:"Agenda"},weekLabel:"Set",allDayText:"Tot el dia",eventLimitText:"més",noEventsMessage:"No hi ha esdeveniments per mostrar"},{code:"cs",week:{dow:1,doy:4},buttonText:{prev:"Dříve",next:"Později",today:"Nyní",month:"Měsíc",week:"Týden",day:"Den",list:"Agenda"},weekLabel:"Týd",allDayText:"Celý den",eventLimitText:function(e){return"+další: "+e},noEventsMessage:"Žádné akce k zobrazení"},{code:"da",week:{dow:1,doy:4},buttonText:{prev:"Forrige",next:"Næste",today:"I dag",month:"Måned",week:"Uge",day:"Dag",list:"Agenda"},weekLabel:"Uge",allDayText:"Hele dagen",eventLimitText:"flere",noEventsMessage:"Ingen arrangementer at vise"},{code:"de",week:{dow:1,doy:4},buttonText:{prev:"Zurück",next:"Vor",today:"Heute",year:"Jahr",month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},weekLabel:"KW",allDayText:"Ganztägig",eventLimitText:function(e){return"+ weitere "+e},noEventsMessage:"Keine Ereignisse anzuzeigen"},{code:"el",week:{dow:1,doy:4},buttonText:{prev:"Προηγούμενος",next:"Επόμενος",today:"Σήμερα",month:"Μήνας",week:"Εβδομάδα",day:"Ημέρα",list:"Ατζέντα"},weekLabel:"Εβδ",allDayText:"Ολοήμερο",eventLimitText:"περισσότερα",noEventsMessage:"Δεν υπάρχουν γεγονότα για να εμφανιστεί"},{code:"en-au",week:{dow:1,doy:4}},{code:"en-gb",week:{dow:1,doy:4}},{code:"en-nz",week:{dow:1,doy:4}},{code:"es",week:{dow:0,doy:6},buttonText:{prev:"Ant",next:"Sig",today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Agenda"},weekLabel:"Sm",allDayHtml:"Todo<br/>el día",eventLimitText:"más",noEventsMessage:"No hay eventos para mostrar"},{code:"es",week:{dow:1,doy:4},buttonText:{prev:"Ant",next:"Sig",today:"Hoy",month:"Mes",week:"Semana",day:"Día",list:"Agenda"},weekLabel:"Sm",allDayHtml:"Todo<br/>el día",eventLimitText:"más",noEventsMessage:"No hay eventos para mostrar"},{code:"et",week:{dow:1,doy:4},buttonText:{prev:"Eelnev",next:"Järgnev",today:"Täna",month:"Kuu",week:"Nädal",day:"Päev",list:"Päevakord"},weekLabel:"näd",allDayText:"Kogu päev",eventLimitText:function(e){return"+ veel "+e},noEventsMessage:"Kuvamiseks puuduvad sündmused"},{code:"eu",week:{dow:1,doy:7},buttonText:{prev:"Aur",next:"Hur",today:"Gaur",month:"Hilabetea",week:"Astea",day:"Eguna",list:"Agenda"},weekLabel:"As",allDayHtml:"Egun<br/>osoa",eventLimitText:"gehiago",noEventsMessage:"Ez dago ekitaldirik erakusteko"},{code:"fa",week:{dow:6,doy:12},dir:"rtl",buttonText:{prev:"قبلی",next:"بعدی",today:"امروز",month:"ماه",week:"هفته",day:"روز",list:"برنامه"},weekLabel:"هف",allDayText:"تمام روز",eventLimitText:function(e){return"بیش از "+e},noEventsMessage:"هیچ رویدادی به نمایش"},{code:"fi",week:{dow:1,doy:4},buttonText:{prev:"Edellinen",next:"Seuraava",today:"Tänään",month:"Kuukausi",week:"Viikko",day:"Päivä",list:"Tapahtumat"},weekLabel:"Vk",allDayText:"Koko päivä",eventLimitText:"lisää",noEventsMessage:"Ei näytettäviä tapahtumia"},{code:"fr",buttonText:{prev:"Précédent",next:"Suivant",today:"Aujourd'hui",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekLabel:"Sem.",allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"},{code:"fr-ch",week:{dow:1,doy:4},buttonText:{prev:"Précédent",next:"Suivant",today:"Courant",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekLabel:"Sm",allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"},{code:"fr",week:{dow:1,doy:4},buttonText:{prev:"Précédent",next:"Suivant",today:"Aujourd'hui",year:"Année",month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},weekLabel:"Sem.",allDayHtml:"Toute la<br/>journée",eventLimitText:"en plus",noEventsMessage:"Aucun événement à afficher"},{code:"gl",week:{dow:1,doy:4},buttonText:{prev:"Ant",next:"Seg",today:"Hoxe",month:"Mes",week:"Semana",day:"Día",list:"Axenda"},weekLabel:"Sm",allDayHtml:"Todo<br/>o día",eventLimitText:"máis",noEventsMessage:"Non hai eventos para amosar"},{code:"he",dir:"rtl",buttonText:{prev:"הקודם",next:"הבא",today:"היום",month:"חודש",week:"שבוע",day:"יום",list:"סדר יום"},allDayText:"כל היום",eventLimitText:"אחר",noEventsMessage:"אין אירועים להצגה",weekLabel:"שבוע"},{code:"hi",week:{dow:0,doy:6},buttonText:{prev:"पिछला",next:"अगला",today:"आज",month:"महीना",week:"सप्ताह",day:"दिन",list:"कार्यसूची"},weekLabel:"हफ्ता",allDayText:"सभी दिन",eventLimitText:function(e){return"+अधिक "+e},noEventsMessage:"कोई घटनाओं को प्रदर्शित करने के लिए"},{code:"hr",week:{dow:1,doy:7},buttonText:{prev:"Prijašnji",next:"Sljedeći",today:"Danas",month:"Mjesec",week:"Tjedan",day:"Dan",list:"Raspored"},weekLabel:"Tje",allDayText:"Cijeli dan",eventLimitText:function(e){return"+ još "+e},noEventsMessage:"Nema događaja za prikaz"},{code:"hu",week:{dow:1,doy:4},buttonText:{prev:"vissza",next:"előre",today:"ma",month:"Hónap",week:"Hét",day:"Nap",list:"Napló"},weekLabel:"Hét",allDayText:"Egész nap",eventLimitText:"további",noEventsMessage:"Nincs megjeleníthető esemény"},{code:"id",week:{dow:1,doy:7},buttonText:{prev:"mundur",next:"maju",today:"hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},weekLabel:"Mg",allDayHtml:"Sehari<br/>penuh",eventLimitText:"lebih",noEventsMessage:"Tidak ada acara untuk ditampilkan"},{code:"is",week:{dow:1,doy:4},buttonText:{prev:"Fyrri",next:"Næsti",today:"Í dag",month:"Mánuður",week:"Vika",day:"Dagur",list:"Dagskrá"},weekLabel:"Vika",allDayHtml:"Allan<br/>daginn",eventLimitText:"meira",noEventsMessage:"Engir viðburðir til að sýna"},{code:"it",week:{dow:1,doy:4},buttonText:{prev:"Prec",next:"Succ",today:"Oggi",month:"Mese",week:"Settimana",day:"Giorno",list:"Agenda"},weekLabel:"Sm",allDayHtml:"Tutto il<br/>giorno",eventLimitText:function(e){return"+altri "+e},noEventsMessage:"Non ci sono eventi da visualizzare"},{code:"ja",buttonText:{prev:"前",next:"次",today:"今日",month:"月",week:"週",day:"日",list:"予定リスト"},weekLabel:"週",allDayText:"終日",eventLimitText:function(e){return"他 "+e+" 件"},noEventsMessage:"表示する予定はありません"},{code:"ka",week:{dow:1,doy:7},buttonText:{prev:"წინა",next:"შემდეგი",today:"დღეს",month:"თვე",week:"კვირა",day:"დღე",list:"დღის წესრიგი"},weekLabel:"კვ",allDayText:"მთელი დღე",eventLimitText:function(e){return"+ კიდევ "+e},noEventsMessage:"ღონისძიებები არ არის"},{code:"kk",week:{dow:1,doy:7},buttonText:{prev:"Алдыңғы",next:"Келесі",today:"Бүгін",month:"Ай",week:"Апта",day:"Күн",list:"Күн тәртібі"},weekLabel:"Не",allDayText:"Күні бойы",eventLimitText:function(e){return"+ тағы "+e},noEventsMessage:"Көрсету үшін оқиғалар жоқ"},{code:"ko",buttonText:{prev:"이전달",next:"다음달",today:"오늘",month:"월",week:"주",day:"일",list:"일정목록"},weekLabel:"주",allDayText:"종일",eventLimitText:"개",noEventsMessage:"일정이 없습니다"},{code:"lb",week:{dow:1,doy:4},buttonText:{prev:"Zréck",next:"Weider",today:"Haut",month:"Mount",week:"Woch",day:"Dag",list:"Terminiwwersiicht"},weekLabel:"W",allDayText:"Ganzen Dag",eventLimitText:"méi",noEventsMessage:"Nee Evenementer ze affichéieren"},{code:"lt",week:{dow:1,doy:4},buttonText:{prev:"Atgal",next:"Pirmyn",today:"Šiandien",month:"Mėnuo",week:"Savaitė",day:"Diena",list:"Darbotvarkė"},weekLabel:"SAV",allDayText:"Visą dieną",eventLimitText:"daugiau",noEventsMessage:"Nėra įvykių rodyti"},{code:"lv",week:{dow:1,doy:4},buttonText:{prev:"Iepr.",next:"Nāk.",today:"Šodien",month:"Mēnesis",week:"Nedēļa",day:"Diena",list:"Dienas kārtība"},weekLabel:"Ned.",allDayText:"Visu dienu",eventLimitText:function(e){return"+vēl "+e},noEventsMessage:"Nav notikumu"},{code:"mk",buttonText:{prev:"претходно",next:"следно",today:"Денес",month:"Месец",week:"Недела",day:"Ден",list:"График"},weekLabel:"Сед",allDayText:"Цел ден",eventLimitText:function(e){return"+повеќе "+e},noEventsMessage:"Нема настани за прикажување"},{code:"ms",week:{dow:1,doy:7},buttonText:{prev:"Sebelum",next:"Selepas",today:"hari ini",month:"Bulan",week:"Minggu",day:"Hari",list:"Agenda"},weekLabel:"Mg",allDayText:"Sepanjang hari",eventLimitText:function(e){return"masih ada "+e+" acara"},noEventsMessage:"Tiada peristiwa untuk dipaparkan"},{code:"nb",week:{dow:1,doy:4},buttonText:{prev:"Forrige",next:"Neste",today:"I dag",month:"Måned",week:"Uke",day:"Dag",list:"Agenda"},weekLabel:"Uke",allDayText:"Hele dagen",eventLimitText:"til",noEventsMessage:"Ingen hendelser å vise"},{code:"nl",week:{dow:1,doy:4},buttonText:{prev:"Voorgaand",next:"Volgende",today:"Vandaag",year:"Jaar",month:"Maand",week:"Week",day:"Dag",list:"Agenda"},allDayText:"Hele dag",eventLimitText:"extra",noEventsMessage:"Geen evenementen om te laten zien"},{code:"nn",week:{dow:1,doy:4},buttonText:{prev:"Førre",next:"Neste",today:"I dag",month:"Månad",week:"Veke",day:"Dag",list:"Agenda"},weekLabel:"Veke",allDayText:"Heile dagen",eventLimitText:"til",noEventsMessage:"Ingen hendelser å vise"},{code:"pl",week:{dow:1,doy:4},buttonText:{prev:"Poprzedni",next:"Następny",today:"Dziś",month:"Miesiąc",week:"Tydzień",day:"Dzień",list:"Plan dnia"},weekLabel:"Tydz",allDayText:"Cały dzień",eventLimitText:"więcej",noEventsMessage:"Brak wydarzeń do wyświetlenia"},{code:"pt-br",buttonText:{prev:"Anterior",next:"Próximo",today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Compromissos"},weekLabel:"Sm",allDayText:"dia inteiro",eventLimitText:function(e){return"mais +"+e},noEventsMessage:"Não há eventos para mostrar"},{code:"pt",week:{dow:1,doy:4},buttonText:{prev:"Anterior",next:"Seguinte",today:"Hoje",month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},weekLabel:"Sem",allDayText:"Todo o dia",eventLimitText:"mais",noEventsMessage:"Não há eventos para mostrar"},{code:"ro",week:{dow:1,doy:7},buttonText:{prev:"precedentă",next:"următoare",today:"Azi",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},weekLabel:"Săpt",allDayText:"Toată ziua",eventLimitText:function(e){return"+alte "+e},noEventsMessage:"Nu există evenimente de afișat"},{code:"ru",week:{dow:1,doy:4},buttonText:{prev:"Пред",next:"След",today:"Сегодня",month:"Месяц",week:"Неделя",day:"День",list:"Повестка дня"},weekLabel:"Нед",allDayText:"Весь день",eventLimitText:function(e){return"+ ещё "+e},noEventsMessage:"Нет событий для отображения"},{code:"sk",week:{dow:1,doy:4},buttonText:{prev:"Predchádzajúci",next:"Nasledujúci",today:"Dnes",month:"Mesiac",week:"Týždeň",day:"Deň",list:"Rozvrh"},weekLabel:"Ty",allDayText:"Celý deň",eventLimitText:function(e){return"+ďalšie: "+e},noEventsMessage:"Žiadne akcie na zobrazenie"},{code:"sl",week:{dow:1,doy:7},buttonText:{prev:"Prejšnji",next:"Naslednji",today:"Trenutni",month:"Mesec",week:"Teden",day:"Dan",list:"Dnevni red"},weekLabel:"Teden",allDayText:"Ves dan",eventLimitText:"več",noEventsMessage:"Ni dogodkov za prikaz"},{code:"sq",week:{dow:1,doy:4},buttonText:{prev:"mbrapa",next:"Përpara",today:"sot",month:"Muaj",week:"Javë",day:"Ditë",list:"Listë"},weekLabel:"Ja",allDayHtml:"Gjithë<br/>ditën",eventLimitText:function(e){return"+më tepër "+e},noEventsMessage:"Nuk ka evente për të shfaqur"},{code:"sr-cyrl",week:{dow:1,doy:7},buttonText:{prev:"Претходна",next:"следећи",today:"Данас",month:"Месец",week:"Недеља",day:"Дан",list:"Планер"},weekLabel:"Сед",allDayText:"Цео дан",eventLimitText:function(e){return"+ још "+e},noEventsMessage:"Нема догађаја за приказ"},{code:"sr",week:{dow:1,doy:7},buttonText:{prev:"Prethodna",next:"Sledeći",today:"Danas",month:"Mеsеc",week:"Nеdеlja",day:"Dan",list:"Planеr"},weekLabel:"Sed",allDayText:"Cеo dan",eventLimitText:function(e){return"+ još "+e},noEventsMessage:"Nеma događaja za prikaz"},{code:"sv",week:{dow:1,doy:4},buttonText:{prev:"Förra",next:"Nästa",today:"Idag",month:"Månad",week:"Vecka",day:"Dag",list:"Program"},weekLabel:"v.",allDayText:"Heldag",eventLimitText:"till",noEventsMessage:"Inga händelser att visa"},{code:"th",buttonText:{prev:"ย้อน",next:"ถัดไป",today:"วันนี้",month:"เดือน",week:"สัปดาห์",day:"วัน",list:"แผนงาน"},allDayText:"ตลอดวัน",eventLimitText:"เพิ่มเติม",noEventsMessage:"ไม่มีกิจกรรมที่จะแสดง"},{code:"tr",week:{dow:1,doy:7},buttonText:{prev:"geri",next:"ileri",today:"bugün",month:"Ay",week:"Hafta",day:"Gün",list:"Ajanda"},weekLabel:"Hf",allDayText:"Tüm gün",eventLimitText:"daha fazla",noEventsMessage:"Gösterilecek etkinlik yok"},{code:"uk",week:{dow:1,doy:7},buttonText:{prev:"Попередній",next:"далі",today:"Сьогодні",month:"Місяць",week:"Тиждень",day:"День",list:"Порядок денний"},weekLabel:"Тиж",allDayText:"Увесь день",eventLimitText:function(e){return"+ще "+e+"..."},noEventsMessage:"Немає подій для відображення"},{code:"vi",week:{dow:1,doy:4},buttonText:{prev:"Trước",next:"Tiếp",today:"Hôm nay",month:"Tháng",week:"Tuần",day:"Ngày",list:"Lịch biểu"},weekLabel:"Tu",allDayText:"Cả ngày",eventLimitText:function(e){return"+ thêm "+e},noEventsMessage:"Không có sự kiện để hiển thị"},{code:"zh-cn",week:{dow:1,doy:4},buttonText:{prev:"上月",next:"下月",today:"今天",month:"月",week:"周",day:"日",list:"日程"},weekLabel:"周",allDayText:"全天",eventLimitText:function(e){return"另外 "+e+" 个"},noEventsMessage:"没有事件显示"},{code:"zh-tw",buttonText:{prev:"上月",next:"下月",today:"今天",month:"月",week:"週",day:"天",list:"活動列表"},weekLabel:"周",allDayText:"整天",eventLimitText:"顯示更多",noEventsMessage:"没有任何活動"}]});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/af.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/af.js
new file mode 100644
index 0000000000000000000000000000000000000000..ee9f9f747565c255303f52e9dccfc9289ef3ca28
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/af.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.af = factory()));
+}(this, function () { 'use strict';
+
+    var af = {
+        code: "af",
+        week: {
+            dow: 1,
+            doy: 4 // Die week wat die 4de Januarie bevat is die eerste week van die jaar.
+        },
+        buttonText: {
+            prev: "Vorige",
+            next: "Volgende",
+            today: "Vandag",
+            year: "Jaar",
+            month: "Maand",
+            week: "Week",
+            day: "Dag",
+            list: "Agenda"
+        },
+        allDayHtml: "Heeldag",
+        eventLimitText: "Addisionele",
+        noEventsMessage: "Daar is geen gebeurtenisse nie"
+    };
+
+    return af;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-dz.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-dz.js
new file mode 100644
index 0000000000000000000000000000000000000000..201eb171ae6e5389fe8b7346b27772d6aa8a206d
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-dz.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-dz'] = factory()));
+}(this, function () { 'use strict';
+
+    var arDz = {
+        code: "ar-dz",
+        week: {
+            dow: 0,
+            doy: 4 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return arDz;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-kw.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-kw.js
new file mode 100644
index 0000000000000000000000000000000000000000..94c69001466ed1ab9015465bb6e321081e469c1b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-kw.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-kw'] = factory()));
+}(this, function () { 'use strict';
+
+    var arKw = {
+        code: "ar-kw",
+        week: {
+            dow: 0,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return arKw;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-ly.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-ly.js
new file mode 100644
index 0000000000000000000000000000000000000000..e1c8aeb07bc9f33e677b5c13b1635cdc4171e310
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-ly.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-ly'] = factory()));
+}(this, function () { 'use strict';
+
+    var arLy = {
+        code: "ar-ly",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return arLy;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-ma.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-ma.js
new file mode 100644
index 0000000000000000000000000000000000000000..00cc7c6790623c99ae60112d6916b3139fa81833
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-ma.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-ma'] = factory()));
+}(this, function () { 'use strict';
+
+    var arMa = {
+        code: "ar-ma",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return arMa;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-sa.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-sa.js
new file mode 100644
index 0000000000000000000000000000000000000000..0361f6d87019a7e70570ff915c8195440ed3b904
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-sa.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-sa'] = factory()));
+}(this, function () { 'use strict';
+
+    var arSa = {
+        code: "ar-sa",
+        week: {
+            dow: 0,
+            doy: 6 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return arSa;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-tn.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-tn.js
new file mode 100644
index 0000000000000000000000000000000000000000..57a07f8f5141d4b52d1dcfb3a543b5f6b652706c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar-tn.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['ar-tn'] = factory()));
+}(this, function () { 'use strict';
+
+    var arTn = {
+        code: "ar-tn",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return arTn;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ar.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar.js
new file mode 100644
index 0000000000000000000000000000000000000000..f789afd151e9ef50c4ec1ca04237011a1b0cab56
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ar.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ar = factory()));
+}(this, function () { 'use strict';
+
+    var ar = {
+        code: "ar",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "السابق",
+            next: "التالي",
+            today: "اليوم",
+            month: "شهر",
+            week: "أسبوع",
+            day: "يوم",
+            list: "أجندة"
+        },
+        weekLabel: "أسبوع",
+        allDayText: "اليوم كله",
+        eventLimitText: "أخرى",
+        noEventsMessage: "أي أحداث لعرض"
+    };
+
+    return ar;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/bg.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/bg.js
new file mode 100644
index 0000000000000000000000000000000000000000..e7343a6c5ab768bf9b51e52e8244bb9a6b5c79b2
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/bg.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.bg = factory()));
+}(this, function () { 'use strict';
+
+    var bg = {
+        code: "bg",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "назад",
+            next: "напред",
+            today: "днес",
+            month: "Месец",
+            week: "Седмица",
+            day: "Ден",
+            list: "График"
+        },
+        allDayText: "Цял ден",
+        eventLimitText: function (n) {
+            return "+още " + n;
+        },
+        noEventsMessage: "Няма събития за показване"
+    };
+
+    return bg;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/bs.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/bs.js
new file mode 100644
index 0000000000000000000000000000000000000000..d96b8adb3d968a1d96ebf5f57a5c6feac3d066f1
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/bs.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.bs = factory()));
+}(this, function () { 'use strict';
+
+    var bs = {
+        code: "bs",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prošli",
+            next: "Sljedeći",
+            today: "Danas",
+            month: "Mjesec",
+            week: "Sedmica",
+            day: "Dan",
+            list: "Raspored"
+        },
+        weekLabel: "Sed",
+        allDayText: "Cijeli dan",
+        eventLimitText: function (n) {
+            return "+ još " + n;
+        },
+        noEventsMessage: "Nema događaja za prikazivanje"
+    };
+
+    return bs;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ca.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ca.js
new file mode 100644
index 0000000000000000000000000000000000000000..d2d3e2aa770888d3cb4cff6720c406459f24430f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ca.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ca = factory()));
+}(this, function () { 'use strict';
+
+    var ca = {
+        code: "ca",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Anterior",
+            next: "Següent",
+            today: "Avui",
+            month: "Mes",
+            week: "Setmana",
+            day: "Dia",
+            list: "Agenda"
+        },
+        weekLabel: "Set",
+        allDayText: "Tot el dia",
+        eventLimitText: "més",
+        noEventsMessage: "No hi ha esdeveniments per mostrar"
+    };
+
+    return ca;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/cs.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/cs.js
new file mode 100644
index 0000000000000000000000000000000000000000..2624e3607df8084c022c63728427d984af24efa6
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/cs.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.cs = factory()));
+}(this, function () { 'use strict';
+
+    var cs = {
+        code: "cs",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Dříve",
+            next: "Později",
+            today: "Nyní",
+            month: "Měsíc",
+            week: "Týden",
+            day: "Den",
+            list: "Agenda"
+        },
+        weekLabel: "Týd",
+        allDayText: "Celý den",
+        eventLimitText: function (n) {
+            return "+další: " + n;
+        },
+        noEventsMessage: "Žádné akce k zobrazení"
+    };
+
+    return cs;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/da.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/da.js
new file mode 100644
index 0000000000000000000000000000000000000000..73d15592990f86bb6ad876930269878a7b9d1ac4
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/da.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.da = factory()));
+}(this, function () { 'use strict';
+
+    var da = {
+        code: "da",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Forrige",
+            next: "Næste",
+            today: "I dag",
+            month: "MÃ¥ned",
+            week: "Uge",
+            day: "Dag",
+            list: "Agenda"
+        },
+        weekLabel: "Uge",
+        allDayText: "Hele dagen",
+        eventLimitText: "flere",
+        noEventsMessage: "Ingen arrangementer at vise"
+    };
+
+    return da;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/de.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/de.js
new file mode 100644
index 0000000000000000000000000000000000000000..ab5a815a0cc563a14aa42f8418226e747afb391c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/de.js
@@ -0,0 +1,33 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.de = factory()));
+}(this, function () { 'use strict';
+
+    var de = {
+        code: "de",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Zurück",
+            next: "Vor",
+            today: "Heute",
+            year: "Jahr",
+            month: "Monat",
+            week: "Woche",
+            day: "Tag",
+            list: "Terminübersicht"
+        },
+        weekLabel: "KW",
+        allDayText: "Ganztägig",
+        eventLimitText: function (n) {
+            return "+ weitere " + n;
+        },
+        noEventsMessage: "Keine Ereignisse anzuzeigen"
+    };
+
+    return de;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/el.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/el.js
new file mode 100644
index 0000000000000000000000000000000000000000..9f59e365710e411cf2504b91bbd207fecfe2037f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/el.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.el = factory()));
+}(this, function () { 'use strict';
+
+    var el = {
+        code: "el",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Προηγούμενος",
+            next: "Επόμενος",
+            today: "Σήμερα",
+            month: "Μήνας",
+            week: "Εβδομάδα",
+            day: "Ημέρα",
+            list: "Ατζέντα"
+        },
+        weekLabel: "Εβδ",
+        allDayText: "Ολοήμερο",
+        eventLimitText: "περισσότερα",
+        noEventsMessage: "Δεν υπάρχουν γεγονότα για να εμφανιστεί"
+    };
+
+    return el;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/en-au.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/en-au.js
new file mode 100644
index 0000000000000000000000000000000000000000..be10bfb66eaa30b3cbcf29cda459e5208615e87d
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/en-au.js
@@ -0,0 +1,17 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['en-au'] = factory()));
+}(this, function () { 'use strict';
+
+    var enAu = {
+        code: "en-au",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        }
+    };
+
+    return enAu;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/en-gb.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/en-gb.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a4a84e6b427a53aad4bd6e1561c8532df49aa4e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/en-gb.js
@@ -0,0 +1,17 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['en-gb'] = factory()));
+}(this, function () { 'use strict';
+
+    var enGb = {
+        code: "en-gb",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        }
+    };
+
+    return enGb;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/en-nz.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/en-nz.js
new file mode 100644
index 0000000000000000000000000000000000000000..df56c14550693aae4c3c71cae266741fd0196ce1
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/en-nz.js
@@ -0,0 +1,17 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['en-nz'] = factory()));
+}(this, function () { 'use strict';
+
+    var enNz = {
+        code: "en-nz",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        }
+    };
+
+    return enNz;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/es-us.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/es-us.js
new file mode 100644
index 0000000000000000000000000000000000000000..1efa89a4d1869a71c71f58cdadc746ed9dc6c8d2
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/es-us.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['es-us'] = factory()));
+}(this, function () { 'use strict';
+
+    var esUs = {
+        code: "es",
+        week: {
+            dow: 0,
+            doy: 6 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Ant",
+            next: "Sig",
+            today: "Hoy",
+            month: "Mes",
+            week: "Semana",
+            day: "Día",
+            list: "Agenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Todo<br/>el día",
+        eventLimitText: "más",
+        noEventsMessage: "No hay eventos para mostrar"
+    };
+
+    return esUs;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/es.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/es.js
new file mode 100644
index 0000000000000000000000000000000000000000..bfd9af4c626fc8f7ef5a9bd1305c36da42ac6e4e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/es.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.es = factory()));
+}(this, function () { 'use strict';
+
+    var es = {
+        code: "es",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Ant",
+            next: "Sig",
+            today: "Hoy",
+            month: "Mes",
+            week: "Semana",
+            day: "Día",
+            list: "Agenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Todo<br/>el día",
+        eventLimitText: "más",
+        noEventsMessage: "No hay eventos para mostrar"
+    };
+
+    return es;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/et.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/et.js
new file mode 100644
index 0000000000000000000000000000000000000000..c44fcaec9e5f84d26b92279eca746b67898fa420
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/et.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.et = factory()));
+}(this, function () { 'use strict';
+
+    var et = {
+        code: "et",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Eelnev",
+            next: "Järgnev",
+            today: "Täna",
+            month: "Kuu",
+            week: "Nädal",
+            day: "Päev",
+            list: "Päevakord"
+        },
+        weekLabel: "näd",
+        allDayText: "Kogu päev",
+        eventLimitText: function (n) {
+            return "+ veel " + n;
+        },
+        noEventsMessage: "Kuvamiseks puuduvad sündmused"
+    };
+
+    return et;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/eu.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/eu.js
new file mode 100644
index 0000000000000000000000000000000000000000..91903aaaf172765aeea87daca1d9b78c49173613
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/eu.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.eu = factory()));
+}(this, function () { 'use strict';
+
+    var eu = {
+        code: "eu",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Aur",
+            next: "Hur",
+            today: "Gaur",
+            month: "Hilabetea",
+            week: "Astea",
+            day: "Eguna",
+            list: "Agenda"
+        },
+        weekLabel: "As",
+        allDayHtml: "Egun<br/>osoa",
+        eventLimitText: "gehiago",
+        noEventsMessage: "Ez dago ekitaldirik erakusteko"
+    };
+
+    return eu;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/fa.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/fa.js
new file mode 100644
index 0000000000000000000000000000000000000000..031fc7b304f8aaf6809fe3111a56af55d7d92cad
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/fa.js
@@ -0,0 +1,33 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.fa = factory()));
+}(this, function () { 'use strict';
+
+    var fa = {
+        code: "fa",
+        week: {
+            dow: 6,
+            doy: 12 // The week that contains Jan 1st is the first week of the year.
+        },
+        dir: 'rtl',
+        buttonText: {
+            prev: "قبلی",
+            next: "بعدی",
+            today: "امروز",
+            month: "ماه",
+            week: "هفته",
+            day: "روز",
+            list: "برنامه"
+        },
+        weekLabel: "هف",
+        allDayText: "تمام روز",
+        eventLimitText: function (n) {
+            return "بیش از " + n;
+        },
+        noEventsMessage: "هیچ رویدادی به نمایش"
+    };
+
+    return fa;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/fi.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/fi.js
new file mode 100644
index 0000000000000000000000000000000000000000..3912845cf75a152bbe7e8f55b8fc474ef9c60207
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/fi.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.fi = factory()));
+}(this, function () { 'use strict';
+
+    var fi = {
+        code: "fi",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Edellinen",
+            next: "Seuraava",
+            today: "Tänään",
+            month: "Kuukausi",
+            week: "Viikko",
+            day: "Päivä",
+            list: "Tapahtumat"
+        },
+        weekLabel: "Vk",
+        allDayText: "Koko päivä",
+        eventLimitText: "lisää",
+        noEventsMessage: "Ei näytettäviä tapahtumia"
+    };
+
+    return fi;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/fr-ca.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/fr-ca.js
new file mode 100644
index 0000000000000000000000000000000000000000..d554c1408f831109daf5ed42cb5d9b1ba3eb3cbf
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/fr-ca.js
@@ -0,0 +1,27 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['fr-ca'] = factory()));
+}(this, function () { 'use strict';
+
+    var frCa = {
+        code: "fr",
+        buttonText: {
+            prev: "Précédent",
+            next: "Suivant",
+            today: "Aujourd'hui",
+            year: "Année",
+            month: "Mois",
+            week: "Semaine",
+            day: "Jour",
+            list: "Mon planning"
+        },
+        weekLabel: "Sem.",
+        allDayHtml: "Toute la<br/>journée",
+        eventLimitText: "en plus",
+        noEventsMessage: "Aucun événement à afficher"
+    };
+
+    return frCa;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/fr-ch.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/fr-ch.js
new file mode 100644
index 0000000000000000000000000000000000000000..358b8bf311a15e7909bc8e91e5a28d577a0c2970
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/fr-ch.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['fr-ch'] = factory()));
+}(this, function () { 'use strict';
+
+    var frCh = {
+        code: "fr-ch",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Précédent",
+            next: "Suivant",
+            today: "Courant",
+            year: "Année",
+            month: "Mois",
+            week: "Semaine",
+            day: "Jour",
+            list: "Mon planning"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Toute la<br/>journée",
+        eventLimitText: "en plus",
+        noEventsMessage: "Aucun événement à afficher"
+    };
+
+    return frCh;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/fr.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/fr.js
new file mode 100644
index 0000000000000000000000000000000000000000..b679ceffde6d7762c44833a10a92e6cf3e8ca0c7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/fr.js
@@ -0,0 +1,31 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.fr = factory()));
+}(this, function () { 'use strict';
+
+    var fr = {
+        code: "fr",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Précédent",
+            next: "Suivant",
+            today: "Aujourd'hui",
+            year: "Année",
+            month: "Mois",
+            week: "Semaine",
+            day: "Jour",
+            list: "Mon planning"
+        },
+        weekLabel: "Sem.",
+        allDayHtml: "Toute la<br/>journée",
+        eventLimitText: "en plus",
+        noEventsMessage: "Aucun événement à afficher"
+    };
+
+    return fr;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/gl.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/gl.js
new file mode 100644
index 0000000000000000000000000000000000000000..721a6a89b255be548592ece3c3bf8575280fbe1a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/gl.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.gl = factory()));
+}(this, function () { 'use strict';
+
+    var gl = {
+        code: "gl",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Ant",
+            next: "Seg",
+            today: "Hoxe",
+            month: "Mes",
+            week: "Semana",
+            day: "Día",
+            list: "Axenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Todo<br/>o día",
+        eventLimitText: "máis",
+        noEventsMessage: "Non hai eventos para amosar"
+    };
+
+    return gl;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/he.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/he.js
new file mode 100644
index 0000000000000000000000000000000000000000..3521d9e335b054c12aa7e0e01818d9198df04385
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/he.js
@@ -0,0 +1,27 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.he = factory()));
+}(this, function () { 'use strict';
+
+    var he = {
+        code: "he",
+        dir: 'rtl',
+        buttonText: {
+            prev: "הקודם",
+            next: "הבא",
+            today: "היום",
+            month: "חודש",
+            week: "שבוע",
+            day: "יום",
+            list: "סדר יום"
+        },
+        allDayText: "כל היום",
+        eventLimitText: "אחר",
+        noEventsMessage: "אין אירועים להצגה",
+        weekLabel: "שבוע"
+    };
+
+    return he;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/hi.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/hi.js
new file mode 100644
index 0000000000000000000000000000000000000000..15348e697f75cb9a253b7f4c7c81dc965dcf12f2
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/hi.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.hi = factory()));
+}(this, function () { 'use strict';
+
+    var hi = {
+        code: "hi",
+        week: {
+            dow: 0,
+            doy: 6 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "पिछला",
+            next: "अगला",
+            today: "आज",
+            month: "महीना",
+            week: "सप्ताह",
+            day: "दिन",
+            list: "कार्यसूची"
+        },
+        weekLabel: "हफ्ता",
+        allDayText: "सभी दिन",
+        eventLimitText: function (n) {
+            return "+अधिक " + n;
+        },
+        noEventsMessage: "कोई घटनाओं को प्रदर्शित करने के लिए"
+    };
+
+    return hi;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/hr.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/hr.js
new file mode 100644
index 0000000000000000000000000000000000000000..295b4856634c26ec097b1c3035cedccc10ae9b2d
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/hr.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.hr = factory()));
+}(this, function () { 'use strict';
+
+    var hr = {
+        code: "hr",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prijašnji",
+            next: "Sljedeći",
+            today: "Danas",
+            month: "Mjesec",
+            week: "Tjedan",
+            day: "Dan",
+            list: "Raspored"
+        },
+        weekLabel: "Tje",
+        allDayText: "Cijeli dan",
+        eventLimitText: function (n) {
+            return "+ još " + n;
+        },
+        noEventsMessage: "Nema događaja za prikaz"
+    };
+
+    return hr;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/hu.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/hu.js
new file mode 100644
index 0000000000000000000000000000000000000000..2f0fe8acb6b9c700cac825790844ccb4898dd2ec
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/hu.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.hu = factory()));
+}(this, function () { 'use strict';
+
+    var hu = {
+        code: "hu",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "vissza",
+            next: "előre",
+            today: "ma",
+            month: "Hónap",
+            week: "Hét",
+            day: "Nap",
+            list: "Napló"
+        },
+        weekLabel: "Hét",
+        allDayText: "Egész nap",
+        eventLimitText: "további",
+        noEventsMessage: "Nincs megjeleníthető esemény"
+    };
+
+    return hu;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/id.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/id.js
new file mode 100644
index 0000000000000000000000000000000000000000..b742e80dda7d6c2b80b2e75de4806edb4beeb53f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/id.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.id = factory()));
+}(this, function () { 'use strict';
+
+    var id = {
+        code: "id",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "mundur",
+            next: "maju",
+            today: "hari ini",
+            month: "Bulan",
+            week: "Minggu",
+            day: "Hari",
+            list: "Agenda"
+        },
+        weekLabel: "Mg",
+        allDayHtml: "Sehari<br/>penuh",
+        eventLimitText: "lebih",
+        noEventsMessage: "Tidak ada acara untuk ditampilkan"
+    };
+
+    return id;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/is.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/is.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd569bce72f55bd97ee39860a758ef549206af6e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/is.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.is = factory()));
+}(this, function () { 'use strict';
+
+    var is = {
+        code: "is",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Fyrri",
+            next: "Næsti",
+            today: "Í dag",
+            month: "Mánuður",
+            week: "Vika",
+            day: "Dagur",
+            list: "Dagskrá"
+        },
+        weekLabel: "Vika",
+        allDayHtml: "Allan<br/>daginn",
+        eventLimitText: "meira",
+        noEventsMessage: "Engir viðburðir til að sýna"
+    };
+
+    return is;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/it.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/it.js
new file mode 100644
index 0000000000000000000000000000000000000000..39a2829e5221a84a912d2e617111f36348475d0b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/it.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.it = factory()));
+}(this, function () { 'use strict';
+
+    var it = {
+        code: "it",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prec",
+            next: "Succ",
+            today: "Oggi",
+            month: "Mese",
+            week: "Settimana",
+            day: "Giorno",
+            list: "Agenda"
+        },
+        weekLabel: "Sm",
+        allDayHtml: "Tutto il<br/>giorno",
+        eventLimitText: function (n) {
+            return "+altri " + n;
+        },
+        noEventsMessage: "Non ci sono eventi da visualizzare"
+    };
+
+    return it;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ja.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ja.js
new file mode 100644
index 0000000000000000000000000000000000000000..eb4245b2a076e378d2461c6c5d6496d254091f2a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ja.js
@@ -0,0 +1,28 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ja = factory()));
+}(this, function () { 'use strict';
+
+    var ja = {
+        code: "ja",
+        buttonText: {
+            prev: "前",
+            next: "次",
+            today: "今日",
+            month: "月",
+            week: "週",
+            day: "æ—¥",
+            list: "予定リスト"
+        },
+        weekLabel: "週",
+        allDayText: "終日",
+        eventLimitText: function (n) {
+            return "他 " + n + " 件";
+        },
+        noEventsMessage: "表示する予定はありません"
+    };
+
+    return ja;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ka.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ka.js
new file mode 100644
index 0000000000000000000000000000000000000000..b971c033f811feacd41ab29f4a4bafa704d0a50b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ka.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ka = factory()));
+}(this, function () { 'use strict';
+
+    var ka = {
+        code: "ka",
+        week: {
+            dow: 1,
+            doy: 7
+        },
+        buttonText: {
+            prev: "წინა",
+            next: "შემდეგი",
+            today: "დღეს",
+            month: "თვე",
+            week: "კვირა",
+            day: "დღე",
+            list: "დღის წესრიგი"
+        },
+        weekLabel: "კვ",
+        allDayText: "მთელი დღე",
+        eventLimitText: function (n) {
+            return "+ კიდევ " + n;
+        },
+        noEventsMessage: "ღონისძიებები არ არის"
+    };
+
+    return ka;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/kk.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/kk.js
new file mode 100644
index 0000000000000000000000000000000000000000..5b19b99d51054f26b05ac53733835b0bc8597bc4
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/kk.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.kk = factory()));
+}(this, function () { 'use strict';
+
+    var kk = {
+        code: "kk",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Алдыңғы",
+            next: "Келесі",
+            today: "Бүгін",
+            month: "Ай",
+            week: "Апта",
+            day: "Күн",
+            list: "Күн тәртібі"
+        },
+        weekLabel: "Не",
+        allDayText: "Күні бойы",
+        eventLimitText: function (n) {
+            return "+ тағы " + n;
+        },
+        noEventsMessage: "Көрсету үшін оқиғалар жоқ"
+    };
+
+    return kk;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ko.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ko.js
new file mode 100644
index 0000000000000000000000000000000000000000..ffe985d6c5d33c0a3708497f9c5fa969a6e6ca6e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ko.js
@@ -0,0 +1,26 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ko = factory()));
+}(this, function () { 'use strict';
+
+    var ko = {
+        code: "ko",
+        buttonText: {
+            prev: "이전달",
+            next: "다음달",
+            today: "오늘",
+            month: "ì›”",
+            week: "주",
+            day: "일",
+            list: "일정목록"
+        },
+        weekLabel: "주",
+        allDayText: "종일",
+        eventLimitText: "개",
+        noEventsMessage: "일정이 없습니다"
+    };
+
+    return ko;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/lb.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/lb.js
new file mode 100644
index 0000000000000000000000000000000000000000..b9b17e3ec1f7f6bfd5bed7bf409cd5109c0b5a8e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/lb.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.lb = factory()));
+}(this, function () { 'use strict';
+
+    var lb = {
+        code: "lb",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Zréck",
+            next: "Weider",
+            today: "Haut",
+            month: "Mount",
+            week: "Woch",
+            day: "Dag",
+            list: "Terminiwwersiicht"
+        },
+        weekLabel: "W",
+        allDayText: "Ganzen Dag",
+        eventLimitText: "méi",
+        noEventsMessage: "Nee Evenementer ze affichéieren"
+    };
+
+    return lb;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/lt.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/lt.js
new file mode 100644
index 0000000000000000000000000000000000000000..ec641b7500e5af7b2e46e4253b4ee9d8c2fa3c12
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/lt.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.lt = factory()));
+}(this, function () { 'use strict';
+
+    var lt = {
+        code: "lt",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Atgal",
+            next: "Pirmyn",
+            today: "Å iandien",
+            month: "MÄ—nuo",
+            week: "SavaitÄ—",
+            day: "Diena",
+            list: "DarbotvarkÄ—"
+        },
+        weekLabel: "SAV",
+        allDayText: "VisÄ… dienÄ…",
+        eventLimitText: "daugiau",
+        noEventsMessage: "Nėra įvykių rodyti"
+    };
+
+    return lt;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/lv.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/lv.js
new file mode 100644
index 0000000000000000000000000000000000000000..5453630df116f6e1edd47c1fdf2d2f311fe183cc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/lv.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.lv = factory()));
+}(this, function () { 'use strict';
+
+    var lv = {
+        code: "lv",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Iepr.",
+            next: "Nāk.",
+            today: "Å odien",
+            month: "MÄ“nesis",
+            week: "Nedēļa",
+            day: "Diena",
+            list: "Dienas kārtība"
+        },
+        weekLabel: "Ned.",
+        allDayText: "Visu dienu",
+        eventLimitText: function (n) {
+            return "+vēl " + n;
+        },
+        noEventsMessage: "Nav notikumu"
+    };
+
+    return lv;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/mk.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/mk.js
new file mode 100644
index 0000000000000000000000000000000000000000..6729fa63d39a23840f433054acd3c88bc97e1db5
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/mk.js
@@ -0,0 +1,28 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.mk = factory()));
+}(this, function () { 'use strict';
+
+    var mk = {
+        code: "mk",
+        buttonText: {
+            prev: "претходно",
+            next: "следно",
+            today: "Денес",
+            month: "Месец",
+            week: "Недела",
+            day: "Ден",
+            list: "График"
+        },
+        weekLabel: "Сед",
+        allDayText: "Цел ден",
+        eventLimitText: function (n) {
+            return "+повеќе " + n;
+        },
+        noEventsMessage: "Нема настани за прикажување"
+    };
+
+    return mk;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ms.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ms.js
new file mode 100644
index 0000000000000000000000000000000000000000..7205ecc72755fb6885a4f73f177c40817bff80c6
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ms.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ms = factory()));
+}(this, function () { 'use strict';
+
+    var ms = {
+        code: "ms",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Sebelum",
+            next: "Selepas",
+            today: "hari ini",
+            month: "Bulan",
+            week: "Minggu",
+            day: "Hari",
+            list: "Agenda"
+        },
+        weekLabel: "Mg",
+        allDayText: "Sepanjang hari",
+        eventLimitText: function (n) {
+            return "masih ada " + n + " acara";
+        },
+        noEventsMessage: "Tiada peristiwa untuk dipaparkan"
+    };
+
+    return ms;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/nb.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/nb.js
new file mode 100644
index 0000000000000000000000000000000000000000..6464461c648494308b9f9e5935e811d7bda67199
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/nb.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.nb = factory()));
+}(this, function () { 'use strict';
+
+    var nb = {
+        code: "nb",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Forrige",
+            next: "Neste",
+            today: "I dag",
+            month: "MÃ¥ned",
+            week: "Uke",
+            day: "Dag",
+            list: "Agenda"
+        },
+        weekLabel: "Uke",
+        allDayText: "Hele dagen",
+        eventLimitText: "til",
+        noEventsMessage: "Ingen hendelser å vise"
+    };
+
+    return nb;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/nl.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/nl.js
new file mode 100644
index 0000000000000000000000000000000000000000..c91b5e55fdd9c7df8fa98e1b66a06e4f1d10e299
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/nl.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.nl = factory()));
+}(this, function () { 'use strict';
+
+    var nl = {
+        code: "nl",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Voorgaand",
+            next: "Volgende",
+            today: "Vandaag",
+            year: "Jaar",
+            month: "Maand",
+            week: "Week",
+            day: "Dag",
+            list: "Agenda"
+        },
+        allDayText: "Hele dag",
+        eventLimitText: "extra",
+        noEventsMessage: "Geen evenementen om te laten zien"
+    };
+
+    return nl;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/nn.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/nn.js
new file mode 100644
index 0000000000000000000000000000000000000000..a5cdd16268ecd602156ba68725666233ee852655
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/nn.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.nn = factory()));
+}(this, function () { 'use strict';
+
+    var nn = {
+        code: "nn",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Førre",
+            next: "Neste",
+            today: "I dag",
+            month: "MÃ¥nad",
+            week: "Veke",
+            day: "Dag",
+            list: "Agenda"
+        },
+        weekLabel: "Veke",
+        allDayText: "Heile dagen",
+        eventLimitText: "til",
+        noEventsMessage: "Ingen hendelser å vise"
+    };
+
+    return nn;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/pl.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/pl.js
new file mode 100644
index 0000000000000000000000000000000000000000..0a22e69c436423ec990a1eef85623e2829666320
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/pl.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.pl = factory()));
+}(this, function () { 'use strict';
+
+    var pl = {
+        code: "pl",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Poprzedni",
+            next: "Następny",
+            today: "DziÅ›",
+            month: "MiesiÄ…c",
+            week: "Tydzień",
+            day: "Dzień",
+            list: "Plan dnia"
+        },
+        weekLabel: "Tydz",
+        allDayText: "Cały dzień",
+        eventLimitText: "więcej",
+        noEventsMessage: "Brak wydarzeń do wyświetlenia"
+    };
+
+    return pl;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/pt-br.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/pt-br.js
new file mode 100644
index 0000000000000000000000000000000000000000..0133cd6b176b730aae09f6fc0f7a99eb36e7ad87
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/pt-br.js
@@ -0,0 +1,28 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['pt-br'] = factory()));
+}(this, function () { 'use strict';
+
+    var ptBr = {
+        code: "pt-br",
+        buttonText: {
+            prev: "Anterior",
+            next: "Próximo",
+            today: "Hoje",
+            month: "Mês",
+            week: "Semana",
+            day: "Dia",
+            list: "Compromissos"
+        },
+        weekLabel: "Sm",
+        allDayText: "dia inteiro",
+        eventLimitText: function (n) {
+            return "mais +" + n;
+        },
+        noEventsMessage: "Não há eventos para mostrar"
+    };
+
+    return ptBr;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/pt.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/pt.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c54d8d401e315e701797aa5afd9d57f90172872
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/pt.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.pt = factory()));
+}(this, function () { 'use strict';
+
+    var pt = {
+        code: "pt",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Anterior",
+            next: "Seguinte",
+            today: "Hoje",
+            month: "Mês",
+            week: "Semana",
+            day: "Dia",
+            list: "Agenda"
+        },
+        weekLabel: "Sem",
+        allDayText: "Todo o dia",
+        eventLimitText: "mais",
+        noEventsMessage: "Não há eventos para mostrar"
+    };
+
+    return pt;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ro.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ro.js
new file mode 100644
index 0000000000000000000000000000000000000000..e8992f27628a6a408eba0ea65a32f6d3329d8930
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ro.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ro = factory()));
+}(this, function () { 'use strict';
+
+    var ro = {
+        code: "ro",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "precedentă",
+            next: "următoare",
+            today: "Azi",
+            month: "Lună",
+            week: "Săptămână",
+            day: "Zi",
+            list: "Agendă"
+        },
+        weekLabel: "Săpt",
+        allDayText: "Toată ziua",
+        eventLimitText: function (n) {
+            return "+alte " + n;
+        },
+        noEventsMessage: "Nu există evenimente de afișat"
+    };
+
+    return ro;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/ru.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/ru.js
new file mode 100644
index 0000000000000000000000000000000000000000..77e0308e2c4c1d13ba7345c9cb1001d297025ec4
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/ru.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.ru = factory()));
+}(this, function () { 'use strict';
+
+    var ru = {
+        code: "ru",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Пред",
+            next: "След",
+            today: "Сегодня",
+            month: "Месяц",
+            week: "Неделя",
+            day: "День",
+            list: "Повестка дня"
+        },
+        weekLabel: "Нед",
+        allDayText: "Весь день",
+        eventLimitText: function (n) {
+            return "+ ещё " + n;
+        },
+        noEventsMessage: "Нет событий для отображения"
+    };
+
+    return ru;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/sk.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/sk.js
new file mode 100644
index 0000000000000000000000000000000000000000..3513a64ad3b389f1423f5b2a31755663165420c5
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/sk.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sk = factory()));
+}(this, function () { 'use strict';
+
+    var sk = {
+        code: "sk",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Predchádzajúci",
+            next: "Nasledujúci",
+            today: "Dnes",
+            month: "Mesiac",
+            week: "Týždeň",
+            day: "Deň",
+            list: "Rozvrh"
+        },
+        weekLabel: "Ty",
+        allDayText: "Celý deň",
+        eventLimitText: function (n) {
+            return "+ďalšie: " + n;
+        },
+        noEventsMessage: "Žiadne akcie na zobrazenie"
+    };
+
+    return sk;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/sl.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/sl.js
new file mode 100644
index 0000000000000000000000000000000000000000..323355359ed3b87a96b60da70028299d2db3f9cf
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/sl.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sl = factory()));
+}(this, function () { 'use strict';
+
+    var sl = {
+        code: "sl",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prejšnji",
+            next: "Naslednji",
+            today: "Trenutni",
+            month: "Mesec",
+            week: "Teden",
+            day: "Dan",
+            list: "Dnevni red"
+        },
+        weekLabel: "Teden",
+        allDayText: "Ves dan",
+        eventLimitText: "več",
+        noEventsMessage: "Ni dogodkov za prikaz"
+    };
+
+    return sl;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/sq.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/sq.js
new file mode 100644
index 0000000000000000000000000000000000000000..0d43a522094ab6e166e159f8f3d2b84c8b9565c1
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/sq.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sq = factory()));
+}(this, function () { 'use strict';
+
+    var sq = {
+        code: "sq",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "mbrapa",
+            next: "Përpara",
+            today: "sot",
+            month: "Muaj",
+            week: "Javë",
+            day: "Ditë",
+            list: "Listë"
+        },
+        weekLabel: "Ja",
+        allDayHtml: "Gjithë<br/>ditën",
+        eventLimitText: function (n) {
+            return "+më tepër " + n;
+        },
+        noEventsMessage: "Nuk ka evente për të shfaqur"
+    };
+
+    return sq;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/sr-cyrl.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/sr-cyrl.js
new file mode 100644
index 0000000000000000000000000000000000000000..ba0d0dfa31ed6289a6a2e769ecc205afccf2106b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/sr-cyrl.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['sr-cyrl'] = factory()));
+}(this, function () { 'use strict';
+
+    var srCyrl = {
+        code: "sr-cyrl",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Претходна",
+            next: "следећи",
+            today: "Данас",
+            month: "Месец",
+            week: "Недеља",
+            day: "Дан",
+            list: "Планер"
+        },
+        weekLabel: "Сед",
+        allDayText: "Цео дан",
+        eventLimitText: function (n) {
+            return "+ још " + n;
+        },
+        noEventsMessage: "Нема догађаја за приказ"
+    };
+
+    return srCyrl;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/sr.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/sr.js
new file mode 100644
index 0000000000000000000000000000000000000000..23e5c9b23fe19dd188f0287c31cd0fe253f17757
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/sr.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sr = factory()));
+}(this, function () { 'use strict';
+
+    var sr = {
+        code: "sr",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Prethodna",
+            next: "Sledeći",
+            today: "Danas",
+            month: "Mеsеc",
+            week: "Nеdеlja",
+            day: "Dan",
+            list: "Planеr"
+        },
+        weekLabel: "Sed",
+        allDayText: "Cеo dan",
+        eventLimitText: function (n) {
+            return "+ još " + n;
+        },
+        noEventsMessage: "Nеma događaja za prikaz"
+    };
+
+    return sr;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/sv.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/sv.js
new file mode 100644
index 0000000000000000000000000000000000000000..a887060ba4e3f25a6f185339f6a8ffc38837e81a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/sv.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.sv = factory()));
+}(this, function () { 'use strict';
+
+    var sv = {
+        code: "sv",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "Förra",
+            next: "Nästa",
+            today: "Idag",
+            month: "MÃ¥nad",
+            week: "Vecka",
+            day: "Dag",
+            list: "Program"
+        },
+        weekLabel: "v.",
+        allDayText: "Heldag",
+        eventLimitText: "till",
+        noEventsMessage: "Inga händelser att visa"
+    };
+
+    return sv;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/th.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/th.js
new file mode 100644
index 0000000000000000000000000000000000000000..caa3fe9a67a6ea983bf19ae9ddc91a91520a53e8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/th.js
@@ -0,0 +1,25 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.th = factory()));
+}(this, function () { 'use strict';
+
+    var th = {
+        code: "th",
+        buttonText: {
+            prev: "ย้อน",
+            next: "ถัดไป",
+            today: "วันนี้",
+            month: "เดือน",
+            week: "สัปดาห์",
+            day: "วัน",
+            list: "แผนงาน"
+        },
+        allDayText: "ตลอดวัน",
+        eventLimitText: "เพิ่มเติม",
+        noEventsMessage: "ไม่มีกิจกรรมที่จะแสดง"
+    };
+
+    return th;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/tr.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/tr.js
new file mode 100644
index 0000000000000000000000000000000000000000..48458982fb666057a7f745f195e24def15fb98cb
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/tr.js
@@ -0,0 +1,30 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.tr = factory()));
+}(this, function () { 'use strict';
+
+    var tr = {
+        code: "tr",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "geri",
+            next: "ileri",
+            today: "bugün",
+            month: "Ay",
+            week: "Hafta",
+            day: "Gün",
+            list: "Ajanda"
+        },
+        weekLabel: "Hf",
+        allDayText: "Tüm gün",
+        eventLimitText: "daha fazla",
+        noEventsMessage: "Gösterilecek etkinlik yok"
+    };
+
+    return tr;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/uk.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/uk.js
new file mode 100644
index 0000000000000000000000000000000000000000..de33f250c3e3facbacd9deebb08603f931e1a086
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/uk.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.uk = factory()));
+}(this, function () { 'use strict';
+
+    var uk = {
+        code: "uk",
+        week: {
+            dow: 1,
+            doy: 7 // The week that contains Jan 1st is the first week of the year.
+        },
+        buttonText: {
+            prev: "Попередній",
+            next: "далі",
+            today: "Сьогодні",
+            month: "Місяць",
+            week: "Тиждень",
+            day: "День",
+            list: "Порядок денний"
+        },
+        weekLabel: "Тиж",
+        allDayText: "Увесь день",
+        eventLimitText: function (n) {
+            return "+ще " + n + "...";
+        },
+        noEventsMessage: "Немає подій для відображення"
+    };
+
+    return uk;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/vi.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/vi.js
new file mode 100644
index 0000000000000000000000000000000000000000..167ce11d74b63cf9ad4e58a258f79133609dafa8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/vi.js
@@ -0,0 +1,32 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales.vi = factory()));
+}(this, function () { 'use strict';
+
+    var vi = {
+        code: "vi",
+        week: {
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "TrÆ°á»›c",
+            next: "Tiếp",
+            today: "Hôm nay",
+            month: "Tháng",
+            week: "Tuần",
+            day: "Ngày",
+            list: "Lịch biểu"
+        },
+        weekLabel: "Tu",
+        allDayText: "Cả ngày",
+        eventLimitText: function (n) {
+            return "+ thêm " + n;
+        },
+        noEventsMessage: "Không có sự kiện để hiển thị"
+    };
+
+    return vi;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/zh-cn.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/zh-cn.js
new file mode 100644
index 0000000000000000000000000000000000000000..4debbb9e60ee21e27ec14606c300ebd50a0e9bea
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/zh-cn.js
@@ -0,0 +1,33 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['zh-cn'] = factory()));
+}(this, function () { 'use strict';
+
+    var zhCn = {
+        code: "zh-cn",
+        week: {
+            // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效
+            dow: 1,
+            doy: 4 // The week that contains Jan 4th is the first week of the year.
+        },
+        buttonText: {
+            prev: "上月",
+            next: "下月",
+            today: "今天",
+            month: "月",
+            week: "周",
+            day: "æ—¥",
+            list: "日程"
+        },
+        weekLabel: "周",
+        allDayText: "全天",
+        eventLimitText: function (n) {
+            return "另外 " + n + " 个";
+        },
+        noEventsMessage: "没有事件显示"
+    };
+
+    return zhCn;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/locales/zh-tw.js b/AKPlan/static/AKPlan/fullcalendar/core/locales/zh-tw.js
new file mode 100644
index 0000000000000000000000000000000000000000..bc14dcd4bcaba47772cb45938fdbdb139c21156a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/locales/zh-tw.js
@@ -0,0 +1,26 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+    typeof define === 'function' && define.amd ? define(factory) :
+    (global = global || self, (global.FullCalendarLocales = global.FullCalendarLocales || {}, global.FullCalendarLocales['zh-tw'] = factory()));
+}(this, function () { 'use strict';
+
+    var zhTw = {
+        code: "zh-tw",
+        buttonText: {
+            prev: "上月",
+            next: "下月",
+            today: "今天",
+            month: "月",
+            week: "週",
+            day: "天",
+            list: "活動列表"
+        },
+        weekLabel: "周",
+        allDayText: "整天",
+        eventLimitText: '顯示更多',
+        noEventsMessage: "没有任何活動"
+    };
+
+    return zhTw;
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/main.css b/AKPlan/static/AKPlan/fullcalendar/core/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..4412a18580caf6aadf35e042bf6eb53fe3184bc0
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/main.css
@@ -0,0 +1,1052 @@
+@charset "UTF-8";
+.fc {
+  direction: ltr;
+  text-align: left;
+}
+
+.fc-rtl {
+  text-align: right;
+}
+
+body .fc {
+  /* extra precedence to overcome jqui */
+  font-size: 1em;
+}
+
+/* Colors
+--------------------------------------------------------------------------------------------------*/
+.fc-highlight {
+  /* when user is selecting cells */
+  background: #bce8f1;
+  opacity: 0.3;
+}
+
+.fc-bgevent {
+  /* default look for background events */
+  background: #8fdf82;
+  opacity: 0.3;
+}
+
+.fc-nonbusiness {
+  /* default look for non-business-hours areas */
+  /* will inherit .fc-bgevent's styles */
+  background: #d7d7d7;
+}
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-popover {
+  position: absolute;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+}
+
+.fc-popover .fc-header {
+  /* TODO: be more consistent with fc-head/fc-body */
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  padding: 2px 4px;
+}
+
+.fc-rtl .fc-popover .fc-header {
+  flex-direction: row-reverse;
+}
+
+.fc-popover .fc-header .fc-title {
+  margin: 0 2px;
+}
+
+.fc-popover .fc-header .fc-close {
+  cursor: pointer;
+  opacity: 0.65;
+  font-size: 1.1em;
+}
+
+/* Misc Reusable Components
+--------------------------------------------------------------------------------------------------*/
+.fc-divider {
+  border-style: solid;
+  border-width: 1px;
+}
+
+hr.fc-divider {
+  height: 0;
+  margin: 0;
+  padding: 0 0 2px;
+  /* height is unreliable across browsers, so use padding */
+  border-width: 1px 0;
+}
+
+.fc-bg,
+.fc-bgevent-skeleton,
+.fc-highlight-skeleton,
+.fc-mirror-skeleton {
+  /* these element should always cling to top-left/right corners */
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+}
+
+.fc-bg {
+  bottom: 0;
+  /* strech bg to bottom edge */
+}
+
+.fc-bg table {
+  height: 100%;
+  /* strech bg to bottom edge */
+}
+
+/* Tables
+--------------------------------------------------------------------------------------------------*/
+.fc table {
+  width: 100%;
+  box-sizing: border-box;
+  /* fix scrollbar issue in firefox */
+  table-layout: fixed;
+  border-collapse: collapse;
+  border-spacing: 0;
+  font-size: 1em;
+  /* normalize cross-browser */
+}
+
+.fc th {
+  text-align: center;
+}
+
+.fc th,
+.fc td {
+  border-style: solid;
+  border-width: 1px;
+  padding: 0;
+  vertical-align: top;
+}
+
+.fc td.fc-today {
+  border-style: double;
+  /* overcome neighboring borders */
+}
+
+/* Internal Nav Links
+--------------------------------------------------------------------------------------------------*/
+a[data-goto] {
+  cursor: pointer;
+}
+
+a[data-goto]:hover {
+  text-decoration: underline;
+}
+
+/* Fake Table Rows
+--------------------------------------------------------------------------------------------------*/
+.fc .fc-row {
+  /* extra precedence to overcome themes forcing a 1px border */
+  /* no visible border by default. but make available if need be (scrollbar width compensation) */
+  border-style: solid;
+  border-width: 0;
+}
+
+.fc-row table {
+  /* don't put left/right border on anything within a fake row.
+     the outer tbody will worry about this */
+  border-left: 0 hidden transparent;
+  border-right: 0 hidden transparent;
+  /* no bottom borders on rows */
+  border-bottom: 0 hidden transparent;
+}
+
+.fc-row:first-child table {
+  border-top: 0 hidden transparent;
+  /* no top border on first row */
+}
+
+/* Day Row (used within the header and the DayGrid)
+--------------------------------------------------------------------------------------------------*/
+.fc-row {
+  position: relative;
+}
+
+.fc-row .fc-bg {
+  z-index: 1;
+}
+
+/* highlighting cells & background event skeleton */
+.fc-row .fc-bgevent-skeleton,
+.fc-row .fc-highlight-skeleton {
+  bottom: 0;
+  /* stretch skeleton to bottom of row */
+}
+
+.fc-row .fc-bgevent-skeleton table,
+.fc-row .fc-highlight-skeleton table {
+  height: 100%;
+  /* stretch skeleton to bottom of row */
+}
+
+.fc-row .fc-highlight-skeleton td,
+.fc-row .fc-bgevent-skeleton td {
+  border-color: transparent;
+}
+
+.fc-row .fc-bgevent-skeleton {
+  z-index: 2;
+}
+
+.fc-row .fc-highlight-skeleton {
+  z-index: 3;
+}
+
+/*
+row content (which contains day/week numbers and events) as well as "mirror" (which contains
+temporary rendered events).
+*/
+.fc-row .fc-content-skeleton {
+  position: relative;
+  z-index: 4;
+  padding-bottom: 2px;
+  /* matches the space above the events */
+}
+
+.fc-row .fc-mirror-skeleton {
+  z-index: 5;
+}
+
+.fc .fc-row .fc-content-skeleton table,
+.fc .fc-row .fc-content-skeleton td,
+.fc .fc-row .fc-mirror-skeleton td {
+  /* see-through to the background below */
+  /* extra precedence to prevent theme-provided backgrounds */
+  background: none;
+  /* in case <td>s are globally styled */
+  border-color: transparent;
+}
+
+.fc-row .fc-content-skeleton td,
+.fc-row .fc-mirror-skeleton td {
+  /* don't put a border between events and/or the day number */
+  border-bottom: 0;
+}
+
+.fc-row .fc-content-skeleton tbody td,
+.fc-row .fc-mirror-skeleton tbody td {
+  /* don't put a border between event cells */
+  border-top: 0;
+}
+
+/* Scrolling Container
+--------------------------------------------------------------------------------------------------*/
+.fc-scroller {
+  -webkit-overflow-scrolling: touch;
+}
+
+/* TODO: move to timegrid/daygrid */
+.fc-scroller > .fc-day-grid,
+.fc-scroller > .fc-time-grid {
+  position: relative;
+  /* re-scope all positions */
+  width: 100%;
+  /* hack to force re-sizing this inner element when scrollbars appear/disappear */
+}
+
+/* Global Event Styles
+--------------------------------------------------------------------------------------------------*/
+.fc-event {
+  position: relative;
+  /* for resize handle and other inner positioning */
+  display: block;
+  /* make the <a> tag block */
+  font-size: 0.85em;
+  line-height: 1.4;
+  border-radius: 3px;
+  border: 1px solid #3788d8;
+}
+
+.fc-event,
+.fc-event-dot {
+  background-color: #3788d8;
+  /* default BACKGROUND color */
+}
+
+.fc-event,
+.fc-event:hover {
+  color: #fff;
+  /* default TEXT color */
+  text-decoration: none;
+  /* if <a> has an href */
+}
+
+.fc-event[href],
+.fc-event.fc-draggable {
+  cursor: pointer;
+  /* give events with links and draggable events a hand mouse pointer */
+}
+
+.fc-not-allowed,
+.fc-not-allowed .fc-event {
+  /* to override an event's custom cursor */
+  cursor: not-allowed;
+}
+
+.fc-event .fc-content {
+  position: relative;
+  z-index: 2;
+}
+
+/* resizer (cursor AND touch devices) */
+.fc-event .fc-resizer {
+  position: absolute;
+  z-index: 4;
+}
+
+/* resizer (touch devices) */
+.fc-event .fc-resizer {
+  display: none;
+}
+
+.fc-event.fc-allow-mouse-resize .fc-resizer,
+.fc-event.fc-selected .fc-resizer {
+  /* only show when hovering or selected (with touch) */
+  display: block;
+}
+
+/* hit area */
+.fc-event.fc-selected .fc-resizer:before {
+  /* 40x40 touch area */
+  content: "";
+  position: absolute;
+  z-index: 9999;
+  /* user of this util can scope within a lower z-index */
+  top: 50%;
+  left: 50%;
+  width: 40px;
+  height: 40px;
+  margin-left: -20px;
+  margin-top: -20px;
+}
+
+/* Event Selection (only for touch devices)
+--------------------------------------------------------------------------------------------------*/
+.fc-event.fc-selected {
+  z-index: 9999 !important;
+  /* overcomes inline z-index */
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+}
+
+.fc-event.fc-selected:after {
+  content: "";
+  position: absolute;
+  z-index: 1;
+  /* same z-index as fc-bg, behind text */
+  /* overcome the borders */
+  top: -1px;
+  right: -1px;
+  bottom: -1px;
+  left: -1px;
+  /* darkening effect */
+  background: #000;
+  opacity: 0.25;
+}
+
+/* Event Dragging
+--------------------------------------------------------------------------------------------------*/
+.fc-event.fc-dragging.fc-selected {
+  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.3);
+}
+
+.fc-event.fc-dragging:not(.fc-selected) {
+  opacity: 0.75;
+}
+
+/* Horizontal Events
+--------------------------------------------------------------------------------------------------*/
+/* bigger touch area when selected */
+.fc-h-event.fc-selected:before {
+  content: "";
+  position: absolute;
+  z-index: 3;
+  /* below resizers */
+  top: -10px;
+  bottom: -10px;
+  left: 0;
+  right: 0;
+}
+
+/* events that are continuing to/from another week. kill rounded corners and butt up against edge */
+.fc-ltr .fc-h-event.fc-not-start,
+.fc-rtl .fc-h-event.fc-not-end {
+  margin-left: 0;
+  border-left-width: 0;
+  padding-left: 1px;
+  /* replace the border with padding */
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.fc-ltr .fc-h-event.fc-not-end,
+.fc-rtl .fc-h-event.fc-not-start {
+  margin-right: 0;
+  border-right-width: 0;
+  padding-right: 1px;
+  /* replace the border with padding */
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+/* resizer (cursor AND touch devices) */
+/* left resizer  */
+.fc-ltr .fc-h-event .fc-start-resizer,
+.fc-rtl .fc-h-event .fc-end-resizer {
+  cursor: w-resize;
+  left: -1px;
+  /* overcome border */
+}
+
+/* right resizer */
+.fc-ltr .fc-h-event .fc-end-resizer,
+.fc-rtl .fc-h-event .fc-start-resizer {
+  cursor: e-resize;
+  right: -1px;
+  /* overcome border */
+}
+
+/* resizer (mouse devices) */
+.fc-h-event.fc-allow-mouse-resize .fc-resizer {
+  width: 7px;
+  top: -1px;
+  /* overcome top border */
+  bottom: -1px;
+  /* overcome bottom border */
+}
+
+/* resizer (touch devices) */
+.fc-h-event.fc-selected .fc-resizer {
+  /* 8x8 little dot */
+  border-radius: 4px;
+  border-width: 1px;
+  width: 6px;
+  height: 6px;
+  border-style: solid;
+  border-color: inherit;
+  background: #fff;
+  /* vertically center */
+  top: 50%;
+  margin-top: -4px;
+}
+
+/* left resizer  */
+.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,
+.fc-rtl .fc-h-event.fc-selected .fc-end-resizer {
+  margin-left: -4px;
+  /* centers the 8x8 dot on the left edge */
+}
+
+/* right resizer */
+.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,
+.fc-rtl .fc-h-event.fc-selected .fc-start-resizer {
+  margin-right: -4px;
+  /* centers the 8x8 dot on the right edge */
+}
+
+/* DayGrid events
+----------------------------------------------------------------------------------------------------
+We use the full "fc-day-grid-event" class instead of using descendants because the event won't
+be a descendant of the grid when it is being dragged.
+*/
+.fc-day-grid-event {
+  margin: 1px 2px 0;
+  /* spacing between events and edges */
+  padding: 0 1px;
+}
+
+tr:first-child > td > .fc-day-grid-event {
+  margin-top: 2px;
+  /* a little bit more space before the first event */
+}
+
+.fc-mirror-skeleton tr:first-child > td > .fc-day-grid-event {
+  margin-top: 0;
+  /* except for mirror skeleton */
+}
+
+.fc-day-grid-event .fc-content {
+  /* force events to be one-line tall */
+  white-space: nowrap;
+  overflow: hidden;
+}
+
+.fc-day-grid-event .fc-time {
+  font-weight: bold;
+}
+
+/* resizer (cursor devices) */
+/* left resizer  */
+.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,
+.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer {
+  margin-left: -2px;
+  /* to the day cell's edge */
+}
+
+/* right resizer */
+.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,
+.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer {
+  margin-right: -2px;
+  /* to the day cell's edge */
+}
+
+/* Event Limiting
+--------------------------------------------------------------------------------------------------*/
+/* "more" link that represents hidden events */
+a.fc-more {
+  margin: 1px 3px;
+  font-size: 0.85em;
+  cursor: pointer;
+  text-decoration: none;
+}
+
+a.fc-more:hover {
+  text-decoration: underline;
+}
+
+.fc-limited {
+  /* rows and cells that are hidden because of a "more" link */
+  display: none;
+}
+
+/* popover that appears when "more" link is clicked */
+.fc-day-grid .fc-row {
+  z-index: 1;
+  /* make the "more" popover one higher than this */
+}
+
+.fc-more-popover {
+  z-index: 2;
+  width: 220px;
+}
+
+.fc-more-popover .fc-event-container {
+  padding: 10px;
+}
+
+/* Now Indicator
+--------------------------------------------------------------------------------------------------*/
+.fc-now-indicator {
+  position: absolute;
+  border: 0 solid red;
+}
+
+/* Utilities
+--------------------------------------------------------------------------------------------------*/
+.fc-unselectable {
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  -webkit-touch-callout: none;
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+/*
+TODO: more distinction between this file and common.css
+*/
+/* Colors
+--------------------------------------------------------------------------------------------------*/
+.fc-unthemed th,
+.fc-unthemed td,
+.fc-unthemed thead,
+.fc-unthemed tbody,
+.fc-unthemed .fc-divider,
+.fc-unthemed .fc-row,
+.fc-unthemed .fc-content,
+.fc-unthemed .fc-popover,
+.fc-unthemed .fc-list-view,
+.fc-unthemed .fc-list-heading td {
+  border-color: #ddd;
+}
+
+.fc-unthemed .fc-popover {
+  background-color: #fff;
+}
+
+.fc-unthemed .fc-divider,
+.fc-unthemed .fc-popover .fc-header,
+.fc-unthemed .fc-list-heading td {
+  background: #eee;
+}
+
+.fc-unthemed td.fc-today {
+  background: #fcf8e3;
+}
+
+.fc-unthemed .fc-disabled-day {
+  background: #d7d7d7;
+  opacity: 0.3;
+}
+
+/* Icons
+--------------------------------------------------------------------------------------------------
+from https://feathericons.com/ and built with IcoMoon
+*/
+@font-face {
+  font-family: "fcicons";
+  src: url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype");
+  font-weight: normal;
+  font-style: normal;
+}
+.fc-icon {
+  /* use !important to prevent issues with browser extensions that change fonts */
+  font-family: "fcicons" !important;
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.fc-icon-chevron-left:before {
+  content: "";
+}
+
+.fc-icon-chevron-right:before {
+  content: "";
+}
+
+.fc-icon-chevrons-left:before {
+  content: "";
+}
+
+.fc-icon-chevrons-right:before {
+  content: "";
+}
+
+.fc-icon-minus-square:before {
+  content: "";
+}
+
+.fc-icon-plus-square:before {
+  content: "";
+}
+
+.fc-icon-x:before {
+  content: "";
+}
+
+.fc-icon {
+  display: inline-block;
+  width: 1em;
+  height: 1em;
+  text-align: center;
+}
+
+/* Buttons
+--------------------------------------------------------------------------------------------------
+Lots taken from Flatly (MIT): https://bootswatch.com/4/flatly/bootstrap.css
+*/
+/* reset */
+.fc-button {
+  border-radius: 0;
+  overflow: visible;
+  text-transform: none;
+  margin: 0;
+  font-family: inherit;
+  font-size: inherit;
+  line-height: inherit;
+}
+
+.fc-button:focus {
+  outline: 1px dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+}
+
+.fc-button {
+  -webkit-appearance: button;
+}
+
+.fc-button:not(:disabled) {
+  cursor: pointer;
+}
+
+.fc-button::-moz-focus-inner {
+  padding: 0;
+  border-style: none;
+}
+
+/* theme */
+.fc-button {
+  display: inline-block;
+  font-weight: 400;
+  color: #212529;
+  text-align: center;
+  vertical-align: middle;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  background-color: transparent;
+  border: 1px solid transparent;
+  padding: 0.4em 0.65em;
+  font-size: 1em;
+  line-height: 1.5;
+  border-radius: 0.25em;
+}
+
+.fc-button:hover {
+  color: #212529;
+  text-decoration: none;
+}
+
+.fc-button:focus {
+  outline: 0;
+  -webkit-box-shadow: 0 0 0 0.2rem rgba(44, 62, 80, 0.25);
+  box-shadow: 0 0 0 0.2rem rgba(44, 62, 80, 0.25);
+}
+
+.fc-button:disabled {
+  opacity: 0.65;
+}
+
+/* "primary" coloring */
+.fc-button-primary {
+  color: #fff;
+  background-color: #2C3E50;
+  border-color: #2C3E50;
+}
+
+.fc-button-primary:hover {
+  color: #fff;
+  background-color: #1e2b37;
+  border-color: #1a252f;
+}
+
+.fc-button-primary:focus {
+  -webkit-box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5);
+  box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5);
+}
+
+.fc-button-primary:disabled {
+  color: #fff;
+  background-color: #2C3E50;
+  border-color: #2C3E50;
+}
+
+.fc-button-primary:not(:disabled):active,
+.fc-button-primary:not(:disabled).fc-button-active {
+  color: #fff;
+  background-color: #1a252f;
+  border-color: #151e27;
+}
+
+.fc-button-primary:not(:disabled):active:focus,
+.fc-button-primary:not(:disabled).fc-button-active:focus {
+  -webkit-box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5);
+  box-shadow: 0 0 0 0.2rem rgba(76, 91, 106, 0.5);
+}
+
+/* icons within buttons */
+.fc-button .fc-icon {
+  vertical-align: middle;
+  font-size: 1.5em;
+}
+
+/* Buttons Groups
+--------------------------------------------------------------------------------------------------*/
+.fc-button-group {
+  position: relative;
+  display: -webkit-inline-box;
+  display: -ms-inline-flexbox;
+  display: inline-flex;
+  vertical-align: middle;
+}
+
+.fc-button-group > .fc-button {
+  position: relative;
+  -webkit-box-flex: 1;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+}
+
+.fc-button-group > .fc-button:hover {
+  z-index: 1;
+}
+
+.fc-button-group > .fc-button:focus,
+.fc-button-group > .fc-button:active,
+.fc-button-group > .fc-button.fc-button-active {
+  z-index: 1;
+}
+
+.fc-button-group > .fc-button:not(:first-child) {
+  margin-left: -1px;
+}
+
+.fc-button-group > .fc-button:not(:last-child) {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+.fc-button-group > .fc-button:not(:first-child) {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+/* Popover
+--------------------------------------------------------------------------------------------------*/
+.fc-unthemed .fc-popover {
+  border-width: 1px;
+  border-style: solid;
+}
+
+/* List View
+--------------------------------------------------------------------------------------------------*/
+.fc-unthemed .fc-list-item:hover td {
+  background-color: #f5f5f5;
+}
+
+/* Toolbar
+--------------------------------------------------------------------------------------------------*/
+.fc-toolbar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.fc-toolbar.fc-header-toolbar {
+  margin-bottom: 1.5em;
+}
+
+.fc-toolbar.fc-footer-toolbar {
+  margin-top: 1.5em;
+}
+
+/* inner content */
+.fc-toolbar > * > :not(:first-child) {
+  margin-left: 0.75em;
+}
+
+.fc-toolbar h2 {
+  font-size: 1.75em;
+  margin: 0;
+}
+
+/* View Structure
+--------------------------------------------------------------------------------------------------*/
+.fc-view-container {
+  position: relative;
+}
+
+/* undo twitter bootstrap's box-sizing rules. normalizes positioning techniques */
+/* don't do this for the toolbar because we'll want bootstrap to style those buttons as some pt */
+.fc-view-container *,
+.fc-view-container *:before,
+.fc-view-container *:after {
+  -webkit-box-sizing: content-box;
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.fc-view,
+.fc-view > table {
+  /* so dragged elements can be above the view's main element */
+  position: relative;
+  z-index: 1;
+}
+
+@media print {
+  .fc {
+    max-width: 100% !important;
+  }
+
+  /* Global Event Restyling
+  --------------------------------------------------------------------------------------------------*/
+  .fc-event {
+    background: #fff !important;
+    color: #000 !important;
+    page-break-inside: avoid;
+  }
+
+  .fc-event .fc-resizer {
+    display: none;
+  }
+
+  /* Table & Day-Row Restyling
+  --------------------------------------------------------------------------------------------------*/
+  .fc th,
+.fc td,
+.fc hr,
+.fc thead,
+.fc tbody,
+.fc-row {
+    border-color: #ccc !important;
+    background: #fff !important;
+  }
+
+  /* kill the overlaid, absolutely-positioned components */
+  /* common... */
+  .fc-bg,
+.fc-bgevent-skeleton,
+.fc-highlight-skeleton,
+.fc-mirror-skeleton,
+.fc-bgevent-container,
+.fc-business-container,
+.fc-highlight-container,
+.fc-mirror-container {
+    display: none;
+  }
+
+  /* don't force a min-height on rows (for DayGrid) */
+  .fc tbody .fc-row {
+    height: auto !important;
+    /* undo height that JS set in distributeHeight */
+    min-height: 0 !important;
+    /* undo the min-height from each view's specific stylesheet */
+  }
+
+  .fc tbody .fc-row .fc-content-skeleton {
+    position: static;
+    /* undo .fc-rigid */
+    padding-bottom: 0 !important;
+    /* use a more border-friendly method for this... */
+  }
+
+  .fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td {
+    /* only works in newer browsers */
+    padding-bottom: 1em;
+    /* ...gives space within the skeleton. also ensures min height in a way */
+  }
+
+  .fc tbody .fc-row .fc-content-skeleton table {
+    /* provides a min-height for the row, but only effective for IE, which exaggerates this value,
+       making it look more like 3em. for other browers, it will already be this tall */
+    height: 1em;
+  }
+
+  /* Undo month-view event limiting. Display all events and hide the "more" links
+  --------------------------------------------------------------------------------------------------*/
+  .fc-more-cell,
+.fc-more {
+    display: none !important;
+  }
+
+  .fc tr.fc-limited {
+    display: table-row !important;
+  }
+
+  .fc td.fc-limited {
+    display: table-cell !important;
+  }
+
+  .fc-popover {
+    display: none;
+    /* never display the "more.." popover in print mode */
+  }
+
+  /* TimeGrid Restyling
+  --------------------------------------------------------------------------------------------------*/
+  /* undo the min-height 100% trick used to fill the container's height */
+  .fc-time-grid {
+    min-height: 0 !important;
+  }
+
+  /* don't display the side axis at all ("all-day" and time cells) */
+  .fc-timeGrid-view .fc-axis {
+    display: none;
+  }
+
+  /* don't display the horizontal lines */
+  .fc-slats,
+.fc-time-grid hr {
+    /* this hr is used when height is underused and needs to be filled */
+    display: none !important;
+    /* important overrides inline declaration */
+  }
+
+  /* let the container that holds the events be naturally positioned and create real height */
+  .fc-time-grid .fc-content-skeleton {
+    position: static;
+  }
+
+  /* in case there are no events, we still want some height */
+  .fc-time-grid .fc-content-skeleton table {
+    height: 4em;
+  }
+
+  /* kill the horizontal spacing made by the event container. event margins will be done below */
+  .fc-time-grid .fc-event-container {
+    margin: 0 !important;
+  }
+
+  /* TimeGrid *Event* Restyling
+  --------------------------------------------------------------------------------------------------*/
+  /* naturally position events, vertically stacking them */
+  .fc-time-grid .fc-event {
+    position: static !important;
+    margin: 3px 2px !important;
+  }
+
+  /* for events that continue to a future day, give the bottom border back */
+  .fc-time-grid .fc-event.fc-not-end {
+    border-bottom-width: 1px !important;
+  }
+
+  /* indicate the event continues via "..." text */
+  .fc-time-grid .fc-event.fc-not-end:after {
+    content: "...";
+  }
+
+  /* for events that are continuations from previous days, give the top border back */
+  .fc-time-grid .fc-event.fc-not-start {
+    border-top-width: 1px !important;
+  }
+
+  /* indicate the event is a continuation via "..." text */
+  .fc-time-grid .fc-event.fc-not-start:before {
+    content: "...";
+  }
+
+  /* time */
+  /* undo a previous declaration and let the time text span to a second line */
+  .fc-time-grid .fc-event .fc-time {
+    white-space: normal !important;
+  }
+
+  /* hide the the time that is normally displayed... */
+  .fc-time-grid .fc-event .fc-time span {
+    display: none;
+  }
+
+  /* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */
+  .fc-time-grid .fc-event .fc-time:after {
+    content: attr(data-full);
+  }
+
+  /* Vertical Scroller & Containers
+  --------------------------------------------------------------------------------------------------*/
+  /* kill the scrollbars and allow natural height */
+  .fc-scroller,
+.fc-day-grid-container,
+.fc-time-grid-container {
+    /* */
+    overflow: visible !important;
+    height: auto !important;
+  }
+
+  /* kill the horizontal border/padding used to compensate for scrollbars */
+  .fc-row {
+    border: 0 !important;
+    margin: 0 !important;
+  }
+
+  /* Button Controls
+  --------------------------------------------------------------------------------------------------*/
+  .fc-button-group,
+.fc button {
+    display: none;
+    /* don't display any button-related controls */
+  }
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/core/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cfd23435344c07ef999e72e218e1f217a7145350
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/main.d.ts
@@ -0,0 +1,2730 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/core' {
+    export const version = "<%= version %>";
+    export { OptionsInput } from '@fullcalendar/core/types/input-types';
+    export { EventInput, EventDef, EventDefHash, EventInstance, EventInstanceHash, parseEventDef, createEventInstance, EventTuple } from '@fullcalendar/core/structs/event';
+    export { BusinessHoursInput, parseBusinessHours } from '@fullcalendar/core/structs/business-hours';
+    export { applyAll, debounce, padStart, isInt, capitaliseFirstLetter, parseFieldSpecs, compareByFieldSpecs, compareByFieldSpec, flexibleCompare, computeVisibleDayRange, refineProps, matchCellWidths, uncompensateScroll, compensateScroll, subtractInnerElHeight, isMultiDayRange, distributeHeight, undistributeHeight, preventSelection, allowSelection, preventContextMenu, allowContextMenu, compareNumbers, enableCursor, disableCursor, diffDates } from '@fullcalendar/core/util/misc';
+    export { htmlEscape, cssToStr } from '@fullcalendar/core/util/html';
+    export { removeExact, isArraysEqual } from '@fullcalendar/core/util/array';
+    export { memoize, memoizeOutput } from '@fullcalendar/core/util/memoize';
+    export { memoizeRendering, MemoizedRendering } from '@fullcalendar/core/component/memoized-rendering';
+    export { intersectRects, Rect, pointInsideRect, constrainPoint, getRectCenter, diffPoints, Point, translateRect } from '@fullcalendar/core/util/geom';
+    export { mapHash, filterHash, isPropsEqual } from '@fullcalendar/core/util/object';
+    export { findElements, findChildren, htmlToElement, createElement, insertAfterElement, prependToElement, removeElement, appendToElement, applyStyle, applyStyleProp, elementMatches, elementClosest, forceClassName } from '@fullcalendar/core/util/dom-manip';
+    export { EventStore, filterEventStoreDefs, createEmptyEventStore, mergeEventStores, getRelevantEvents, eventTupleToStore } from '@fullcalendar/core/structs/event-store';
+    export { EventUiHash, EventUi, processScopedUiProps, combineEventUis } from '@fullcalendar/core/component/event-ui';
+    export { default as Splitter, SplittableProps } from '@fullcalendar/core/component/event-splitting';
+    export { buildGotoAnchorHtml, getAllDayHtml, getDayClasses } from '@fullcalendar/core/component/date-rendering';
+    export { preventDefault, listenBySelector, whenTransitionDone } from '@fullcalendar/core/util/dom-event';
+    export { computeInnerRect, computeEdges, computeHeightAndMargins, getClippingParents, computeClippingRect, computeRect } from '@fullcalendar/core/util/dom-geom';
+    export { unpromisify } from '@fullcalendar/core/util/promise';
+    export { default as EmitterMixin, EmitterInterface } from '@fullcalendar/core/common/EmitterMixin';
+    export { DateRange, rangeContainsMarker, intersectRanges, rangesEqual, rangesIntersect, rangeContainsRange } from '@fullcalendar/core/datelib/date-range';
+    export { default as Mixin } from '@fullcalendar/core/common/Mixin';
+    export { default as PositionCache } from '@fullcalendar/core/common/PositionCache';
+    export { default as ScrollComponent, ScrollbarWidths } from '@fullcalendar/core/common/ScrollComponent';
+    export { ScrollController, ElementScrollController, WindowScrollController } from '@fullcalendar/core/common/scroll-controller';
+    export { default as Theme } from '@fullcalendar/core/theme/Theme';
+    export { default as Component, ComponentContext } from '@fullcalendar/core/component/Component';
+    export { default as DateComponent, Seg, EventSegUiInteractionState } from '@fullcalendar/core/component/DateComponent';
+    export { default as Calendar, DatePointTransform, DateSpanTransform, DateSelectionApi } from '@fullcalendar/core/Calendar';
+    export { default as View, ViewProps } from '@fullcalendar/core/View';
+    export { default as FgEventRenderer, buildSegCompareObj } from '@fullcalendar/core/component/renderers/FgEventRenderer';
+    export { default as FillRenderer } from '@fullcalendar/core/component/renderers/FillRenderer';
+    export { default as DateProfileGenerator, DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    export { ViewDef } from '@fullcalendar/core/structs/view-def';
+    export { ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    export { DateSpan, DateSpanApi, DatePointApi, isDateSpansEqual } from '@fullcalendar/core/structs/date-span';
+    export { DateMarker, addDays, startOfDay, addMs, addWeeks, diffWeeks, diffWholeWeeks, diffWholeDays, diffDayAndTime, diffDays, isValidDate } from '@fullcalendar/core/datelib/marker';
+    export { Duration, createDuration, isSingleDay, multiplyDuration, addDurations, asRoughMinutes, asRoughSeconds, asRoughMs, wholeDivideDurations, greatestDurationDenominator } from '@fullcalendar/core/datelib/duration';
+    export { DateEnv, DateMarkerMeta } from '@fullcalendar/core/datelib/env';
+    export { DateFormatter, createFormatter, VerboseFormattingArg, formatIsoTimeString } from '@fullcalendar/core/datelib/formatting';
+    export { NamedTimeZoneImpl } from '@fullcalendar/core/datelib/timezone';
+    export { parse as parseMarker } from '@fullcalendar/core/datelib/parsing';
+    export { EventSourceDef, EventSource, EventSourceHash } from '@fullcalendar/core/structs/event-source';
+    export { Interaction, InteractionSettings, interactionSettingsToStore, interactionSettingsStore, InteractionSettingsStore } from '@fullcalendar/core/interactions/interaction';
+    export { PointerDragEvent } from '@fullcalendar/core/interactions/pointer';
+    export { Hit } from '@fullcalendar/core/interactions/hit';
+    export { dateSelectionJoinTransformer } from '@fullcalendar/core/interactions/date-selecting';
+    export { eventDragMutationMassager, EventDropTransformers } from '@fullcalendar/core/interactions/event-dragging';
+    export { EventResizeJoinTransforms } from '@fullcalendar/core/interactions/event-resizing';
+    export { default as ElementDragging } from '@fullcalendar/core/interactions/ElementDragging';
+    export { formatDate, formatRange } from '@fullcalendar/core/formatting-api';
+    export { globalDefaults, config } from '@fullcalendar/core/options';
+    export { RecurringType, ParsedRecurring } from '@fullcalendar/core/structs/recurring-event';
+    export { DragMetaInput, DragMeta, parseDragMeta } from '@fullcalendar/core/structs/drag-meta';
+    export { createPlugin, PluginDef, PluginDefInput, ViewPropsTransformer, ViewContainerModifier } from '@fullcalendar/core/plugin-system';
+    export { reducerFunc, Action, CalendarState } from '@fullcalendar/core/reducers/types';
+    export { CalendarComponentProps } from '@fullcalendar/core/CalendarComponent';
+    export { default as DayHeader } from '@fullcalendar/core/common/DayHeader';
+    export { computeFallbackHeaderFormat, renderDateCell } from '@fullcalendar/core/common/table-utils';
+    export { default as DaySeries } from '@fullcalendar/core/common/DaySeries';
+    export { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    export { EventRenderRange, sliceEventStore, hasBgRendering, getElSeg } from '@fullcalendar/core/component/event-rendering';
+    export { default as DayTable, DayTableSeg, DayTableCell } from '@fullcalendar/core/common/DayTable';
+    export { default as Slicer, SlicedProps } from '@fullcalendar/core/common/slicing-utils';
+    export { EventMutation, applyMutationToEventStore } from '@fullcalendar/core/structs/event-mutation';
+    export { Constraint, ConstraintInput, AllowFunc, isPropsValid, isInteractionValid } from '@fullcalendar/core/validation';
+    export { default as EventApi } from '@fullcalendar/core/api/EventApi';
+    export { default as requestJson } from '@fullcalendar/core/util/requestJson';
+}
+
+declare module '@fullcalendar/core/types/input-types' {
+    import View from '@fullcalendar/core/View';
+    import { EventSourceInput, EventInputTransformer } from '@fullcalendar/core/structs/event-source';
+    import { Duration, DurationInput } from '@fullcalendar/core/datelib/duration';
+    import { DateInput } from '@fullcalendar/core/datelib/env';
+    import { FormatterInput } from '@fullcalendar/core/datelib/formatting';
+    import { DateRangeInput } from '@fullcalendar/core/datelib/date-range';
+    import { BusinessHoursInput } from '@fullcalendar/core/structs/business-hours';
+    import EventApi from '@fullcalendar/core/api/EventApi';
+    import { AllowFunc, ConstraintInput, OverlapFunc } from '@fullcalendar/core/validation';
+    import { PluginDef } from '@fullcalendar/core/plugin-system';
+    import { LocaleSingularArg, RawLocale } from '@fullcalendar/core/datelib/locale';
+    export interface ToolbarInput {
+        left?: string;
+        center?: string;
+        right?: string;
+    }
+    export interface CustomButtonInput {
+        text: string;
+        icon?: string;
+        themeIcon?: string;
+        bootstrapFontAwesome?: string;
+        click(element: HTMLElement): void;
+    }
+    export interface ButtonIconsInput {
+        prev?: string;
+        next?: string;
+        prevYear?: string;
+        nextYear?: string;
+    }
+    export interface ButtonTextCompoundInput {
+        prev?: string;
+        next?: string;
+        prevYear?: string;
+        nextYear?: string;
+        today?: string;
+        month?: string;
+        week?: string;
+        day?: string;
+        [viewId: string]: string | undefined;
+    }
+    export interface EventSegment {
+        event: EventApi;
+        start: Date;
+        end: Date;
+        isStart: boolean;
+        isEnd: boolean;
+    }
+    export interface CellInfo {
+        date: Date;
+        dayEl: HTMLElement;
+        moreEl: HTMLElement;
+        segs: EventSegment[];
+        hiddenSegs: EventSegment[];
+    }
+    export interface DropInfo {
+        start: Date;
+        end: Date;
+    }
+    export type EventHandlerName = '_init' | 'selectAllow' | 'eventAllow' | 'eventDataTransform' | 'datesRender' | 'datesDestroy' | 'dayRender' | 'windowResize' | 'dateClick' | 'eventClick' | 'eventMouseEnter' | 'eventMouseLeave' | 'select' | 'unselect' | 'loading' | 'eventRender' | 'eventPositioned' | '_eventsPositioned' | 'eventDestroy' | 'eventDragStart' | 'eventDragStop' | 'eventDrop' | '_destroyed' | 'drop' | 'eventResizeStart' | 'eventResizeStop' | 'eventResize' | 'eventReceive' | 'eventLeave' | 'viewSkeletonRender' | 'viewSkeletonDestroy' | '_noEventDrop' | '_noEventResize' | 'eventLimitClick' | 'resourceRender';
+    export type EventHandlerArgs<T extends EventHandlerName> = Parameters<Extract<OptionsInput[T], (...args: any[]) => any>>;
+    export type EventHandlerArg<T extends EventHandlerName> = EventHandlerArgs<T>[0];
+    export interface OptionsInputBase {
+        header?: boolean | ToolbarInput;
+        footer?: boolean | ToolbarInput;
+        customButtons?: {
+            [name: string]: CustomButtonInput;
+        };
+        buttonIcons?: boolean | ButtonIconsInput;
+        themeSystem?: 'standard' | string;
+        bootstrapFontAwesome?: boolean | ButtonIconsInput;
+        firstDay?: number;
+        dir?: 'ltr' | 'rtl' | 'auto';
+        weekends?: boolean;
+        hiddenDays?: number[];
+        fixedWeekCount?: boolean;
+        weekNumbers?: boolean;
+        weekNumbersWithinDays?: boolean;
+        weekNumberCalculation?: 'local' | 'ISO' | ((m: Date) => number);
+        businessHours?: BusinessHoursInput;
+        showNonCurrentDates?: boolean;
+        height?: number | 'auto' | 'parent' | (() => number);
+        contentHeight?: number | 'auto' | (() => number);
+        aspectRatio?: number;
+        handleWindowResize?: boolean;
+        windowResizeDelay?: number;
+        eventLimit?: boolean | number;
+        eventLimitClick?: 'popover' | 'week' | 'day' | 'timeGridWeek' | 'timeGridDay' | string | ((arg: {
+            date: Date;
+            allDay: boolean;
+            dayEl: HTMLElement;
+            moreEl: HTMLElement;
+            segs: any[];
+            hiddenSegs: any[];
+            jsEvent: MouseEvent;
+            view: View;
+        }) => void);
+        timeZone?: string | boolean;
+        now?: DateInput | (() => DateInput);
+        defaultView?: string;
+        allDaySlot?: boolean;
+        allDayText?: string;
+        slotDuration?: DurationInput;
+        slotLabelFormat?: FormatterInput;
+        slotLabelInterval?: DurationInput;
+        snapDuration?: DurationInput;
+        scrollTime?: DurationInput;
+        minTime?: DurationInput;
+        maxTime?: DurationInput;
+        slotEventOverlap?: boolean;
+        listDayFormat?: FormatterInput | boolean;
+        listDayAltFormat?: FormatterInput | boolean;
+        noEventsMessage?: string;
+        defaultDate?: DateInput;
+        nowIndicator?: boolean;
+        visibleRange?: ((currentDate: Date) => DateRangeInput) | DateRangeInput;
+        validRange?: DateRangeInput;
+        dateIncrement?: DurationInput;
+        dateAlignment?: string;
+        duration?: DurationInput;
+        dayCount?: number;
+        locales?: RawLocale[];
+        locale?: LocaleSingularArg;
+        eventTimeFormat?: FormatterInput;
+        columnHeader?: boolean;
+        columnHeaderFormat?: FormatterInput;
+        columnHeaderText?: string | ((date: DateInput) => string);
+        columnHeaderHtml?: string | ((date: DateInput) => string);
+        titleFormat?: FormatterInput;
+        weekLabel?: string;
+        displayEventTime?: boolean;
+        displayEventEnd?: boolean;
+        eventLimitText?: string | ((eventCnt: number) => string);
+        dayPopoverFormat?: FormatterInput;
+        navLinks?: boolean;
+        navLinkDayClick?: string | ((date: Date, jsEvent: Event) => void);
+        navLinkWeekClick?: string | ((weekStart: any, jsEvent: Event) => void);
+        selectable?: boolean;
+        selectMirror?: boolean;
+        unselectAuto?: boolean;
+        unselectCancel?: string;
+        defaultAllDayEventDuration?: DurationInput;
+        defaultTimedEventDuration?: DurationInput;
+        cmdFormatter?: string;
+        defaultRangeSeparator?: string;
+        selectConstraint?: ConstraintInput;
+        selectOverlap?: boolean | OverlapFunc;
+        selectAllow?: AllowFunc;
+        editable?: boolean;
+        eventStartEditable?: boolean;
+        eventDurationEditable?: boolean;
+        eventConstraint?: ConstraintInput;
+        eventOverlap?: boolean | OverlapFunc;
+        eventAllow?: AllowFunc;
+        eventClassName?: string[] | string;
+        eventClassNames?: string[] | string;
+        eventBackgroundColor?: string;
+        eventBorderColor?: string;
+        eventTextColor?: string;
+        eventColor?: string;
+        events?: EventSourceInput;
+        eventSources?: EventSourceInput[];
+        allDayDefault?: boolean;
+        startParam?: string;
+        endParam?: string;
+        lazyFetching?: boolean;
+        nextDayThreshold?: DurationInput;
+        eventOrder?: string | Array<((a: EventApi, b: EventApi) => number) | (string | ((a: EventApi, b: EventApi) => number))>;
+        rerenderDelay?: number | null;
+        dragRevertDuration?: number;
+        dragScroll?: boolean;
+        longPressDelay?: number;
+        eventLongPressDelay?: number;
+        droppable?: boolean;
+        dropAccept?: string | ((draggable: any) => boolean);
+        eventDataTransform?: EventInputTransformer;
+        allDayMaintainDuration?: boolean;
+        eventResizableFromStart?: boolean;
+        timeGridEventMinHeight?: number;
+        allDayHtml?: string;
+        eventDragMinDistance?: number;
+        eventSourceFailure?: any;
+        eventSourceSuccess?: any;
+        forceEventDuration?: boolean;
+        progressiveEventRendering?: boolean;
+        selectLongPressDelay?: number;
+        selectMinDistance?: number;
+        timeZoneParam?: string;
+        titleRangeSeparator?: string;
+        datesRender?(arg: {
+            view: View;
+            el: HTMLElement;
+        }): void;
+        datesDestroy?(arg: {
+            view: View;
+            el: HTMLElement;
+        }): void;
+        dayRender?(arg: {
+            view: View;
+            date: Date;
+            allDay?: boolean;
+            el: HTMLElement;
+        }): void;
+        windowResize?(view: View): void;
+        dateClick?(arg: {
+            date: Date;
+            dateStr: string;
+            allDay: boolean;
+            resource?: any;
+            dayEl: HTMLElement;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventClick?(arg: {
+            el: HTMLElement;
+            event: EventApi;
+            jsEvent: MouseEvent;
+            view: View;
+        }): boolean | void;
+        eventMouseEnter?(arg: {
+            el: HTMLElement;
+            event: EventApi;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventMouseLeave?(arg: {
+            el: HTMLElement;
+            event: EventApi;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        select?(arg: {
+            start: Date;
+            end: Date;
+            startStr: string;
+            endStr: string;
+            allDay: boolean;
+            resource?: any;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        unselect?(arg: {
+            view: View;
+            jsEvent: Event;
+        }): void;
+        loading?(isLoading: boolean): void;
+        eventRender?(arg: {
+            isMirror: boolean;
+            isStart: boolean;
+            isEnd: boolean;
+            event: EventApi;
+            el: HTMLElement;
+            view: View;
+        }): void;
+        eventPositioned?(arg: {
+            isMirror: boolean;
+            isStart: boolean;
+            isEnd: boolean;
+            event: EventApi;
+            el: HTMLElement;
+            view: View;
+        }): void;
+        _eventsPositioned?(arg: {
+            view: View;
+        }): void;
+        eventDestroy?(arg: {
+            isMirror: boolean;
+            event: EventApi;
+            el: HTMLElement;
+            view: View;
+        }): void;
+        eventDragStart?(arg: {
+            event: EventApi;
+            el: HTMLElement;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventDragStop?(arg: {
+            event: EventApi;
+            el: HTMLElement;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventDrop?(arg: {
+            el: HTMLElement;
+            event: EventApi;
+            oldEvent: EventApi;
+            delta: Duration;
+            revert: () => void;
+            jsEvent: Event;
+            view: View;
+        }): void;
+        eventResizeStart?(arg: {
+            el: HTMLElement;
+            event: EventApi;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventResizeStop?(arg: {
+            el: HTMLElement;
+            event: EventApi;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventResize?(arg: {
+            el: HTMLElement;
+            startDelta: Duration;
+            endDelta: Duration;
+            prevEvent: EventApi;
+            event: EventApi;
+            revert: () => void;
+            jsEvent: Event;
+            view: View;
+        }): void;
+        drop?(arg: {
+            date: Date;
+            dateStr: string;
+            allDay: boolean;
+            draggedEl: HTMLElement;
+            jsEvent: MouseEvent;
+            view: View;
+        }): void;
+        eventReceive?(arg: {
+            event: EventApi;
+            draggedEl: HTMLElement;
+            view: View;
+        }): void;
+        eventLeave?(arg: {
+            draggedEl: HTMLElement;
+            event: EventApi;
+            view: View;
+        }): void;
+        viewSkeletonRender?(arg: {
+            el: HTMLElement;
+            view: View;
+        }): void;
+        viewSkeletonDestroy?(arg: {
+            el: HTMLElement;
+            view: View;
+        }): void;
+        _destroyed?(): void;
+        _init?(): void;
+        _noEventDrop?(): void;
+        _noEventResize?(): void;
+        resourceRender?(arg: {
+            resource: any;
+            el: HTMLElement;
+            view: View;
+        }): void;
+    }
+    export interface ViewOptionsInput extends OptionsInputBase {
+        type?: string;
+        buttonText?: string;
+    }
+    export interface OptionsInput extends OptionsInputBase {
+        buttonText?: ButtonTextCompoundInput;
+        views?: {
+            [viewId: string]: ViewOptionsInput;
+        };
+        plugins?: (PluginDef | string)[];
+    }
+}
+
+declare module '@fullcalendar/core/structs/event' {
+    import { DateInput } from '@fullcalendar/core/datelib/env';
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { UnscopedEventUiInput, EventUi } from '@fullcalendar/core/component/event-ui';
+    export type EventRenderingChoice = '' | 'background' | 'inverse-background' | 'none';
+    export interface EventNonDateInput extends UnscopedEventUiInput {
+        id?: string | number;
+        groupId?: string | number;
+        title?: string;
+        url?: string;
+        rendering?: EventRenderingChoice;
+        extendedProps?: object;
+        [extendedProp: string]: any;
+    }
+    export interface EventDateInput {
+        start?: DateInput;
+        end?: DateInput;
+        date?: DateInput;
+        allDay?: boolean;
+    }
+    export type EventInput = EventNonDateInput & EventDateInput;
+    export interface EventDef {
+        defId: string;
+        sourceId: string;
+        publicId: string;
+        groupId: string;
+        allDay: boolean;
+        hasEnd: boolean;
+        recurringDef: {
+            typeId: number;
+            typeData: any;
+            duration: Duration | null;
+        } | null;
+        title: string;
+        url: string;
+        rendering: EventRenderingChoice;
+        ui: EventUi;
+        extendedProps: any;
+    }
+    export interface EventInstance {
+        instanceId: string;
+        defId: string;
+        range: DateRange;
+        forcedStartTzo: number | null;
+        forcedEndTzo: number | null;
+    }
+    export interface EventTuple {
+        def: EventDef;
+        instance: EventInstance | null;
+    }
+    export type EventInstanceHash = {
+        [instanceId: string]: EventInstance;
+    };
+    export type EventDefHash = {
+        [defId: string]: EventDef;
+    };
+    export const NON_DATE_PROPS: {
+        id: StringConstructor;
+        groupId: StringConstructor;
+        title: StringConstructor;
+        url: StringConstructor;
+        rendering: StringConstructor;
+        extendedProps: any;
+    };
+    export const DATE_PROPS: {
+        start: any;
+        date: any;
+        end: any;
+        allDay: any;
+    };
+    export function parseEvent(raw: EventInput, sourceId: string, calendar: Calendar, allowOpenRange?: boolean): EventTuple | null;
+    export function parseEventDef(raw: EventNonDateInput, sourceId: string, allDay: boolean, hasEnd: boolean, calendar: Calendar): EventDef;
+    export type eventDefParserFunc = (def: EventDef, props: any, leftovers: any) => void;
+    export function createEventInstance(defId: string, range: DateRange, forcedStartTzo?: number, forcedEndTzo?: number): EventInstance;
+}
+
+declare module '@fullcalendar/core/structs/business-hours' {
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { EventInput } from '@fullcalendar/core/structs/event';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    export type BusinessHoursInput = boolean | EventInput | EventInput[];
+    export function parseBusinessHours(input: BusinessHoursInput, calendar: Calendar): EventStore;
+}
+
+declare module '@fullcalendar/core/util/misc' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { DateEnv } from '@fullcalendar/core/datelib/env';
+    import { DateRange, OpenDateRange } from '@fullcalendar/core/datelib/date-range';
+    export function compensateScroll(rowEl: HTMLElement, scrollbarWidths: any): void;
+    export function uncompensateScroll(rowEl: HTMLElement): void;
+    export function disableCursor(): void;
+    export function enableCursor(): void;
+    export function distributeHeight(els: HTMLElement[], availableHeight: any, shouldRedistribute: any): void;
+    export function undistributeHeight(els: HTMLElement[]): void;
+    export function matchCellWidths(els: HTMLElement[]): number;
+    export function subtractInnerElHeight(outerEl: HTMLElement, innerEl: HTMLElement): number;
+    export function preventSelection(el: HTMLElement): void;
+    export function allowSelection(el: HTMLElement): void;
+    export function preventContextMenu(el: HTMLElement): void;
+    export function allowContextMenu(el: HTMLElement): void;
+    export function parseFieldSpecs(input: any): any[];
+    export function compareByFieldSpecs(obj0: any, obj1: any, fieldSpecs: any): any;
+    export function compareByFieldSpec(obj0: any, obj1: any, fieldSpec: any): any;
+    export function flexibleCompare(a: any, b: any): number;
+    export function capitaliseFirstLetter(str: any): any;
+    export function padStart(val: any, len: any): string;
+    export function compareNumbers(a: any, b: any): number;
+    export function isInt(n: any): boolean;
+    export function applyAll(functions: any, thisObj: any, args: any): any;
+    export function firstDefined(...args: any[]): any;
+    export function debounce(func: any, wait: any): () => any;
+    export type GenericHash = {
+        [key: string]: any;
+    };
+    export function refineProps(rawProps: GenericHash, processors: GenericHash, defaults?: GenericHash, leftoverProps?: GenericHash): GenericHash;
+    export function computeAlignedDayRange(timedRange: DateRange): DateRange;
+    export function computeVisibleDayRange(timedRange: OpenDateRange, nextDayThreshold?: Duration): OpenDateRange;
+    export function isMultiDayRange(range: DateRange): boolean;
+    export function diffDates(date0: DateMarker, date1: DateMarker, dateEnv: DateEnv, largeUnit?: string): Duration;
+}
+
+declare module '@fullcalendar/core/util/html' {
+    export function htmlEscape(s: any): string;
+    export function cssToStr(cssProps: any): string;
+    export function attrsToStr(attrs: any): string;
+    export type ClassNameInput = string | string[];
+    export function parseClassName(raw: ClassNameInput): string[];
+}
+
+declare module '@fullcalendar/core/util/array' {
+    export function removeMatching(array: any, testFunc: any): number;
+    export function removeExact(array: any, exactVal: any): number;
+    export function isArraysEqual(a0: any, a1: any): boolean;
+}
+
+declare module '@fullcalendar/core/util/memoize' {
+    export function memoize<T>(workerFunc: T): T;
+    export function memoizeOutput<T>(workerFunc: T, equalityFunc: (output0: any, output1: any) => boolean): T;
+}
+
+declare module '@fullcalendar/core/component/memoized-rendering' {
+    export interface MemoizedRendering<ArgsType extends any[]> {
+        (...args: ArgsType): void;
+        unrender: () => void;
+        dependents: MemoizedRendering<any>[];
+    }
+    export function memoizeRendering<ArgsType extends any[]>(renderFunc: (...args: ArgsType) => void, unrenderFunc?: (...args: ArgsType) => void, dependencies?: MemoizedRendering<any>[]): MemoizedRendering<ArgsType>;
+}
+
+declare module '@fullcalendar/core/util/geom' {
+    export interface Point {
+        left: number;
+        top: number;
+    }
+    export interface Rect {
+        left: number;
+        right: number;
+        top: number;
+        bottom: number;
+    }
+    export function pointInsideRect(point: Point, rect: Rect): boolean;
+    export function intersectRects(rect1: Rect, rect2: Rect): Rect | false;
+    export function translateRect(rect: Rect, deltaX: number, deltaY: number): Rect;
+    export function constrainPoint(point: Point, rect: Rect): Point;
+    export function getRectCenter(rect: Rect): Point;
+    export function diffPoints(point1: Point, point2: Point): Point;
+}
+
+declare module '@fullcalendar/core/util/object' {
+    export function mergeProps(propObjs: any, complexProps?: any): any;
+    export function filterHash(hash: any, func: any): {};
+    export function mapHash<InputItem, OutputItem>(hash: {
+        [key: string]: InputItem;
+    }, func: (input: InputItem, key: string) => OutputItem): {
+        [key: string]: OutputItem;
+    };
+    export function arrayToHash(a: any): {
+        [key: string]: true;
+    };
+    export function hashValuesToArray(obj: any): any[];
+    export function isPropsEqual(obj0: any, obj1: any): boolean;
+}
+
+declare module '@fullcalendar/core/util/dom-manip' {
+    export function createElement(tagName: string, attrs: object | null, content?: ElementContent): HTMLElement;
+    export function htmlToElement(html: string): HTMLElement;
+    export function htmlToElements(html: string): HTMLElement[];
+    export type ElementContent = string | Node | Node[] | NodeList;
+    export function appendToElement(el: HTMLElement, content: ElementContent): void;
+    export function prependToElement(parent: HTMLElement, content: ElementContent): void;
+    export function insertAfterElement(refEl: HTMLElement, content: ElementContent): void;
+    export function removeElement(el: HTMLElement): void;
+    export function elementClosest(el: HTMLElement, selector: string): HTMLElement;
+    export function elementMatches(el: HTMLElement, selector: string): HTMLElement;
+    export function findElements(container: HTMLElement[] | HTMLElement | NodeListOf<HTMLElement>, selector: string): HTMLElement[];
+    export function findChildren(parent: HTMLElement[] | HTMLElement, selector?: string): HTMLElement[];
+    export function forceClassName(el: HTMLElement, className: string, bool: any): void;
+    export function applyStyle(el: HTMLElement, props: object): void;
+    export function applyStyleProp(el: HTMLElement, name: string, val: any): void;
+}
+
+declare module '@fullcalendar/core/structs/event-store' {
+    import { EventInput, EventDef, EventDefHash, EventInstanceHash, EventTuple } from '@fullcalendar/core/structs/event';
+    import { EventSource } from '@fullcalendar/core/structs/event-source';
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    export interface EventStore {
+        defs: EventDefHash;
+        instances: EventInstanceHash;
+    }
+    export function parseEvents(rawEvents: EventInput[], sourceId: string, calendar: Calendar, allowOpenRange?: boolean): EventStore;
+    export function eventTupleToStore(tuple: EventTuple, eventStore?: EventStore): EventStore;
+    export function expandRecurring(eventStore: EventStore, framingRange: DateRange, calendar: Calendar): EventStore;
+    export function getRelevantEvents(eventStore: EventStore, instanceId: string): EventStore;
+    export function transformRawEvents(rawEvents: any, eventSource: EventSource, calendar: Calendar): any;
+    export function createEmptyEventStore(): EventStore;
+    export function mergeEventStores(store0: EventStore, store1: EventStore): EventStore;
+    export function filterEventStoreDefs(eventStore: EventStore, filterFunc: (eventDef: EventDef) => boolean): EventStore;
+}
+
+declare module '@fullcalendar/core/component/event-ui' {
+    import { Constraint, AllowFunc, ConstraintInput } from '@fullcalendar/core/validation';
+    import { parseClassName } from '@fullcalendar/core/util/html';
+    import Calendar from '@fullcalendar/core/Calendar';
+    export interface UnscopedEventUiInput {
+        editable?: boolean;
+        startEditable?: boolean;
+        durationEditable?: boolean;
+        constraint?: ConstraintInput;
+        overlap?: boolean;
+        allow?: AllowFunc;
+        className?: string[] | string;
+        classNames?: string[] | string;
+        backgroundColor?: string;
+        borderColor?: string;
+        textColor?: string;
+        color?: string;
+    }
+    export interface EventUi {
+        startEditable: boolean | null;
+        durationEditable: boolean | null;
+        constraints: Constraint[];
+        overlap: boolean | null;
+        allows: AllowFunc[];
+        backgroundColor: string;
+        borderColor: string;
+        textColor: string;
+        classNames: string[];
+    }
+    export type EventUiHash = {
+        [defId: string]: EventUi;
+    };
+    export const UNSCOPED_EVENT_UI_PROPS: {
+        editable: BooleanConstructor;
+        startEditable: BooleanConstructor;
+        durationEditable: BooleanConstructor;
+        constraint: any;
+        overlap: any;
+        allow: any;
+        className: typeof parseClassName;
+        classNames: typeof parseClassName;
+        color: StringConstructor;
+        backgroundColor: StringConstructor;
+        borderColor: StringConstructor;
+        textColor: StringConstructor;
+    };
+    export function processUnscopedUiProps(rawProps: UnscopedEventUiInput, calendar: Calendar, leftovers?: any): EventUi;
+    export function processScopedUiProps(prefix: string, rawScoped: any, calendar: Calendar, leftovers?: any): EventUi;
+    export function combineEventUis(uis: EventUi[]): EventUi;
+}
+
+declare module '@fullcalendar/core/component/event-splitting' {
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventDef } from '@fullcalendar/core/structs/event';
+    import { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    import { EventUiHash, EventUi } from '@fullcalendar/core/component/event-ui';
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    export interface SplittableProps {
+        businessHours: EventStore | null;
+        dateSelection: DateSpan | null;
+        eventStore: EventStore;
+        eventUiBases: EventUiHash;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+    }
+    export { Splitter as default, Splitter };
+    abstract class Splitter<PropsType extends SplittableProps = SplittableProps> {
+        abstract getKeyInfo(props: PropsType): {
+            [key: string]: {
+                ui?: EventUi;
+                businessHours?: EventStore;
+            };
+        };
+        abstract getKeysForDateSpan(dateSpan: DateSpan): string[];
+        abstract getKeysForEventDef(eventDef: EventDef): string[];
+        splitProps(props: PropsType): {
+            [key: string]: SplittableProps;
+        };
+    }
+}
+
+declare module '@fullcalendar/core/component/date-rendering' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import Component, { ComponentContext } from '@fullcalendar/core/component/Component';
+    import { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    export function buildGotoAnchorHtml(component: Component<any>, gotoOptions: any, attrs: any, innerHtml?: any): string;
+    export function getAllDayHtml(component: Component<any>): any;
+    export function getDayClasses(date: DateMarker, dateProfile: DateProfile, context: ComponentContext, noThemeHighlight?: any): any[];
+}
+
+declare module '@fullcalendar/core/util/dom-event' {
+    export function preventDefault(ev: any): void;
+    export function listenBySelector(container: HTMLElement, eventType: string, selector: string, handler: (ev: Event, matchedTarget: HTMLElement) => void): () => void;
+    export function listenToHoverBySelector(container: HTMLElement, selector: string, onMouseEnter: (ev: Event, matchedTarget: HTMLElement) => void, onMouseLeave: (ev: Event, matchedTarget: HTMLElement) => void): () => void;
+    export function whenTransitionDone(el: HTMLElement, callback: (ev: Event) => void): void;
+}
+
+declare module '@fullcalendar/core/util/dom-geom' {
+    import { Rect } from '@fullcalendar/core/util/geom';
+    export interface EdgeInfo {
+        borderLeft: number;
+        borderRight: number;
+        borderTop: number;
+        borderBottom: number;
+        scrollbarLeft: number;
+        scrollbarRight: number;
+        scrollbarBottom: number;
+        paddingLeft?: number;
+        paddingRight?: number;
+        paddingTop?: number;
+        paddingBottom?: number;
+    }
+    export function computeEdges(el: any, getPadding?: boolean): EdgeInfo;
+    export function computeInnerRect(el: any, goWithinPadding?: boolean): {
+        left: number;
+        right: number;
+        top: number;
+        bottom: number;
+    };
+    export function computeRect(el: any): Rect;
+    export function computeHeightAndMargins(el: HTMLElement): number;
+    export function computeVMargins(el: HTMLElement): number;
+    export function getClippingParents(el: HTMLElement): HTMLElement[];
+    export function computeClippingRect(el: HTMLElement): Rect;
+}
+
+declare module '@fullcalendar/core/util/promise' {
+    export function unpromisify(func: any, success: any, failure?: any): void;
+}
+
+declare module '@fullcalendar/core/common/EmitterMixin' {
+    import Mixin from '@fullcalendar/core/common/Mixin';
+    export interface EmitterInterface {
+        on(types: any, handler: any): any;
+        one(types: any, handler: any): any;
+        off(types: any, handler: any): any;
+        trigger(type: any, ...args: any[]): any;
+        triggerWith(type: any, context: any, args: any): any;
+        hasHandlers(type: any): any;
+    }
+    export { EmitterMixin as default, EmitterMixin };
+    class EmitterMixin extends Mixin implements EmitterInterface {
+        _handlers: any;
+        _oneHandlers: any;
+        on(type: any, handler: any): this;
+        one(type: any, handler: any): this;
+        off(type: any, handler?: any): this;
+        trigger(type: any, ...args: any[]): this;
+        triggerWith(type: any, context: any, args: any): this;
+        hasHandlers(type: any): any;
+    }
+}
+
+declare module '@fullcalendar/core/datelib/date-range' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { DateEnv, DateInput } from '@fullcalendar/core/datelib/env';
+    export interface DateRangeInput {
+        start?: DateInput;
+        end?: DateInput;
+    }
+    export interface OpenDateRange {
+        start: DateMarker | null;
+        end: DateMarker | null;
+    }
+    export interface DateRange {
+        start: DateMarker;
+        end: DateMarker;
+    }
+    export function parseRange(input: DateRangeInput, dateEnv: DateEnv): OpenDateRange;
+    export function invertRanges(ranges: DateRange[], constraintRange: DateRange): DateRange[];
+    export function intersectRanges(range0: OpenDateRange, range1: OpenDateRange): OpenDateRange;
+    export function rangesEqual(range0: OpenDateRange, range1: OpenDateRange): boolean;
+    export function rangesIntersect(range0: OpenDateRange, range1: OpenDateRange): boolean;
+    export function rangeContainsRange(outerRange: OpenDateRange, innerRange: OpenDateRange): boolean;
+    export function rangeContainsMarker(range: OpenDateRange, date: DateMarker | number): boolean;
+    export function constrainMarkerToRange(date: DateMarker, range: DateRange): DateMarker;
+}
+
+declare module '@fullcalendar/core/common/Mixin' {
+    export { Mixin as default, Mixin };
+    class Mixin {
+        static mixInto(destClass: any): void;
+        static mixIntoObj(destObj: any): void;
+        static mixOver(destClass: any): void;
+    }
+}
+
+declare module '@fullcalendar/core/common/PositionCache' {
+    export { PositionCache as default, PositionCache };
+    class PositionCache {
+        originClientRect: ClientRect;
+        els: HTMLElement[];
+        originEl: HTMLElement;
+        isHorizontal: boolean;
+        isVertical: boolean;
+        lefts: any;
+        rights: any;
+        tops: any;
+        bottoms: any;
+        constructor(originEl: HTMLElement, els: HTMLElement[], isHorizontal: boolean, isVertical: boolean);
+        build(): void;
+        buildElHorizontals(originClientLeft: number): void;
+        buildElVerticals(originClientTop: number): void;
+        leftToIndex(leftPosition: number): any;
+        topToIndex(topPosition: number): any;
+        getWidth(leftIndex: number): number;
+        getHeight(topIndex: number): number;
+    }
+}
+
+declare module '@fullcalendar/core/common/ScrollComponent' {
+    import { ElementScrollController } from '@fullcalendar/core/common/scroll-controller';
+    export interface ScrollbarWidths {
+        left: number;
+        right: number;
+        bottom: number;
+    }
+    export { ScrollComponent as default, ScrollComponent };
+    class ScrollComponent extends ElementScrollController {
+        overflowX: string;
+        overflowY: string;
+        constructor(overflowX: string, overflowY: string);
+        clear(): void;
+        destroy(): void;
+        applyOverflow(): void;
+        lockOverflow(scrollbarWidths: ScrollbarWidths): void;
+        setHeight(height: number | string): void;
+        getScrollbarWidths(): ScrollbarWidths;
+    }
+}
+
+declare module '@fullcalendar/core/common/scroll-controller' {
+    export abstract class ScrollController {
+        abstract getScrollTop(): number;
+        abstract getScrollLeft(): number;
+        abstract setScrollTop(top: number): void;
+        abstract setScrollLeft(left: number): void;
+        abstract getClientWidth(): number;
+        abstract getClientHeight(): number;
+        abstract getScrollWidth(): number;
+        abstract getScrollHeight(): number;
+        getMaxScrollTop(): number;
+        getMaxScrollLeft(): number;
+        canScrollVertically(): boolean;
+        canScrollHorizontally(): boolean;
+        canScrollUp(): boolean;
+        canScrollDown(): boolean;
+        canScrollLeft(): boolean;
+        canScrollRight(): boolean;
+    }
+    export class ElementScrollController extends ScrollController {
+        el: HTMLElement;
+        constructor(el: HTMLElement);
+        getScrollTop(): number;
+        getScrollLeft(): number;
+        setScrollTop(top: number): void;
+        setScrollLeft(left: number): void;
+        getScrollWidth(): number;
+        getScrollHeight(): number;
+        getClientHeight(): number;
+        getClientWidth(): number;
+    }
+    export class WindowScrollController extends ScrollController {
+        getScrollTop(): number;
+        getScrollLeft(): number;
+        setScrollTop(n: number): void;
+        setScrollLeft(n: number): void;
+        getScrollWidth(): number;
+        getScrollHeight(): number;
+        getClientHeight(): number;
+        getClientWidth(): number;
+    }
+}
+
+declare module '@fullcalendar/core/theme/Theme' {
+    export { Theme as default, Theme };
+    class Theme {
+        calendarOptions: any;
+        classes: any;
+        iconClasses: any;
+        baseIconClass: string;
+        iconOverrideOption: any;
+        iconOverrideCustomButtonOption: any;
+        iconOverridePrefix: string;
+        constructor(calendarOptions: any);
+        processIconOverride(): void;
+        setIconOverride(iconOverrideHash: any): void;
+        applyIconOverridePrefix(className: any): any;
+        getClass(key: any): any;
+        getIconClass(buttonName: any): string;
+        getCustomButtonIconClass(customButtonProps: any): string;
+    }
+    export type ThemeClass = {
+        new (calendarOptions: any): Theme;
+    };
+}
+
+declare module '@fullcalendar/core/component/Component' {
+    import Calendar from '@fullcalendar/core/Calendar';
+    import View from '@fullcalendar/core/View';
+    import Theme from '@fullcalendar/core/theme/Theme';
+    import { DateEnv } from '@fullcalendar/core/datelib/env';
+    export interface ComponentContext {
+        options: any;
+        dateEnv: DateEnv;
+        theme: Theme;
+        calendar: Calendar;
+        view: View;
+    }
+    export type EqualityFuncHash = {
+        [propName: string]: (obj0: any, obj1: any) => boolean;
+    };
+    export { Component as default, Component };
+    class Component<PropsType> {
+        equalityFuncs: EqualityFuncHash;
+        uid: string;
+        props: PropsType | null;
+        context: ComponentContext;
+        dateEnv: DateEnv;
+        theme: Theme;
+        view: View;
+        calendar: Calendar;
+        isRtl: boolean;
+        constructor(context: ComponentContext, isView?: boolean);
+        static addEqualityFuncs(newFuncs: EqualityFuncHash): void;
+        opt(name: any): any;
+        receiveProps(props: PropsType): void;
+        protected render(props: PropsType): void;
+        destroy(): void;
+    }
+}
+
+declare module '@fullcalendar/core/component/DateComponent' {
+    import Component, { ComponentContext } from '@fullcalendar/core/component/Component';
+    import { EventRenderRange } from '@fullcalendar/core/component/event-rendering';
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    import { EventInstanceHash } from '@fullcalendar/core/structs/event';
+    import { Hit } from '@fullcalendar/core/interactions/hit';
+    import FgEventRenderer from '@fullcalendar/core/component/renderers/FgEventRenderer';
+    import FillRenderer from '@fullcalendar/core/component/renderers/FillRenderer';
+    import { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    import { EventHandlerName, EventHandlerArgs } from '@fullcalendar/core/types/input-types';
+    export type DateComponentHash = {
+        [uid: string]: DateComponent<any>;
+    };
+    export interface Seg {
+        component?: DateComponent<any>;
+        isStart: boolean;
+        isEnd: boolean;
+        eventRange?: EventRenderRange;
+        el?: HTMLElement;
+        [otherProp: string]: any;
+    }
+    export interface EventSegUiInteractionState {
+        affectedInstances: EventInstanceHash;
+        segs: Seg[];
+        isEvent: boolean;
+        sourceSeg: any;
+    }
+    export { DateComponent as default, DateComponent };
+    class DateComponent<PropsType> extends Component<PropsType> {
+        fgSegSelector: string;
+        bgSegSelector: string;
+        largeUnit: any;
+        eventRenderer: FgEventRenderer;
+        mirrorRenderer: FgEventRenderer;
+        fillRenderer: FillRenderer;
+        el: HTMLElement;
+        constructor(context: ComponentContext, el: HTMLElement, isView?: boolean);
+        destroy(): void;
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number, elWidth: number, elHeight: number): Hit | null;
+        isInteractionValid(interaction: EventInteractionState): boolean;
+        isDateSelectionValid(selection: DateSpan): boolean;
+        publiclyTrigger<T extends EventHandlerName>(name: T, args?: EventHandlerArgs<T>): any;
+        publiclyTriggerAfterSizing<T extends EventHandlerName>(name: T, args: EventHandlerArgs<T>): void;
+        hasPublicHandlers<T extends EventHandlerName>(name: T): boolean;
+        triggerRenderedSegs(segs: Seg[], isMirrors: boolean): void;
+        triggerWillRemoveSegs(segs: Seg[], isMirrors: boolean): void;
+        isValidSegDownEl(el: HTMLElement): boolean;
+        isValidDateDownEl(el: HTMLElement): boolean;
+        isPopover(): boolean;
+        isInPopover(el: HTMLElement): boolean;
+    }
+}
+
+declare module '@fullcalendar/core/Calendar' {
+    import { EmitterInterface } from '@fullcalendar/core/common/EmitterMixin';
+    import OptionsManager from '@fullcalendar/core/OptionsManager';
+    import View from '@fullcalendar/core/View';
+    import Theme from '@fullcalendar/core/theme/Theme';
+    import { OptionsInput, EventHandlerName, EventHandlerArgs } from '@fullcalendar/core/types/input-types';
+    import { RawLocaleMap } from '@fullcalendar/core/datelib/locale';
+    import { DateEnv, DateInput } from '@fullcalendar/core/datelib/env';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { Duration, DurationInput } from '@fullcalendar/core/datelib/duration';
+    import { DateSpan, DateSpanApi, DatePointApi } from '@fullcalendar/core/structs/date-span';
+    import { DateRangeInput } from '@fullcalendar/core/datelib/date-range';
+    import DateProfileGenerator from '@fullcalendar/core/DateProfileGenerator';
+    import { EventSourceInput } from '@fullcalendar/core/structs/event-source';
+    import { EventInput } from '@fullcalendar/core/structs/event';
+    import { CalendarState, Action } from '@fullcalendar/core/reducers/types';
+    import EventSourceApi from '@fullcalendar/core/api/EventSourceApi';
+    import EventApi from '@fullcalendar/core/api/EventApi';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventUiHash, EventUi } from '@fullcalendar/core/component/event-ui';
+    import { ViewSpecHash, ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    import { PluginSystem } from '@fullcalendar/core/plugin-system';
+    import CalendarComponent from '@fullcalendar/core/CalendarComponent';
+    import DateComponent from '@fullcalendar/core/component/DateComponent';
+    import { PointerDragEvent } from '@fullcalendar/core/interactions/pointer';
+    import { InteractionSettingsInput, Interaction } from '@fullcalendar/core/interactions/interaction';
+    export interface DateClickApi extends DatePointApi {
+        dayEl: HTMLElement;
+        jsEvent: UIEvent;
+        view: View;
+    }
+    export interface DateSelectionApi extends DateSpanApi {
+        jsEvent: UIEvent;
+        view: View;
+    }
+    export type DatePointTransform = (dateSpan: DateSpan, calendar: Calendar) => any;
+    export type DateSpanTransform = (dateSpan: DateSpan, calendar: Calendar) => any;
+    export type CalendarInteraction = {
+        destroy(): any;
+    };
+    export type CalendarInteractionClass = {
+        new (calendar: Calendar): CalendarInteraction;
+    };
+    export type OptionChangeHandler = (propValue: any, calendar: Calendar, deepEqual: any) => void;
+    export type OptionChangeHandlerMap = {
+        [propName: string]: OptionChangeHandler;
+    };
+    export { Calendar as default, Calendar };
+    class Calendar {
+        static on: EmitterInterface['on'];
+        static off: EmitterInterface['off'];
+        static trigger: EmitterInterface['trigger'];
+        on: EmitterInterface['on'];
+        one: EmitterInterface['one'];
+        off: EmitterInterface['off'];
+        trigger: EmitterInterface['trigger'];
+        triggerWith: EmitterInterface['triggerWith'];
+        hasHandlers: EmitterInterface['hasHandlers'];
+        eventUiBases: EventUiHash;
+        selectionConfig: EventUi;
+        optionsManager: OptionsManager;
+        viewSpecs: ViewSpecHash;
+        dateProfileGenerators: {
+            [viewName: string]: DateProfileGenerator;
+        };
+        theme: Theme;
+        dateEnv: DateEnv;
+        availableRawLocales: RawLocaleMap;
+        pluginSystem: PluginSystem;
+        defaultAllDayEventDuration: Duration;
+        defaultTimedEventDuration: Duration;
+        calendarInteractions: CalendarInteraction[];
+        interactionsStore: {
+            [componentUid: string]: Interaction[];
+        };
+        removeNavLinkListener: any;
+        windowResizeProxy: any;
+        isHandlingWindowResize: boolean;
+        state: CalendarState;
+        actionQueue: any[];
+        isReducing: boolean;
+        needsRerender: boolean;
+        needsFullRerender: boolean;
+        isRendering: boolean;
+        renderingPauseDepth: number;
+        renderableEventStore: EventStore;
+        buildDelayedRerender: typeof buildDelayedRerender;
+        delayedRerender: any;
+        afterSizingTriggers: any;
+        isViewUpdated: boolean;
+        isDatesUpdated: boolean;
+        isEventsUpdated: boolean;
+        el: HTMLElement;
+        component: CalendarComponent;
+        constructor(el: HTMLElement, overrides?: OptionsInput);
+        addPluginInputs(pluginInputs: any): void;
+        readonly view: View;
+        render(): void;
+        destroy(): void;
+        bindHandlers(): void;
+        unbindHandlers(): void;
+        hydrate(): void;
+        buildInitialState(): CalendarState;
+        reduce(state: CalendarState, action: Action, calendar: Calendar): CalendarState;
+        requestRerender(needsFull?: boolean): void;
+        tryRerender(): void;
+        batchRendering(func: any): void;
+        executeRender(): void;
+        renderComponent(needsFull: any): void;
+        setOption(name: string, val: any): void;
+        getOption(name: string): any;
+        opt(name: string): any;
+        viewOpt(name: string): any;
+        viewOpts(): any;
+        mutateOptions(updates: any, removals: string[], isDynamic?: boolean, deepEqual?: any): void;
+        handleOptions(options: any): void;
+        getAvailableLocaleCodes(): string[];
+        _buildSelectionConfig(rawOpts: any): EventUi;
+        _buildEventUiSingleBase(rawOpts: any): EventUi;
+        hasPublicHandlers<T extends EventHandlerName>(name: T): boolean;
+        publiclyTrigger<T extends EventHandlerName>(name: T, args?: EventHandlerArgs<T>): any;
+        publiclyTriggerAfterSizing<T extends EventHandlerName>(name: T, args: EventHandlerArgs<T>): void;
+        releaseAfterSizingTriggers(): void;
+        isValidViewType(viewType: string): boolean;
+        changeView(viewType: string, dateOrRange?: DateRangeInput | DateInput): void;
+        zoomTo(dateMarker: DateMarker, viewType?: string): void;
+        getUnitViewSpec(unit: string): ViewSpec | null;
+        getInitialDate(): Date;
+        prev(): void;
+        next(): void;
+        prevYear(): void;
+        nextYear(): void;
+        today(): void;
+        gotoDate(zonedDateInput: any): void;
+        incrementDate(deltaInput: any): void;
+        getDate(): Date;
+        formatDate(d: DateInput, formatter: any): string;
+        formatRange(d0: DateInput, d1: DateInput, settings: any): any;
+        formatIso(d: DateInput, omitTime?: boolean): string;
+        windowResize(ev: Event): void;
+        updateSize(): void;
+        registerInteractiveComponent(component: DateComponent<any>, settingsInput: InteractionSettingsInput): void;
+        unregisterInteractiveComponent(component: DateComponent<any>): void;
+        select(dateOrObj: DateInput | any, endDate?: DateInput): void;
+        unselect(pev?: PointerDragEvent): void;
+        triggerDateSelect(selection: DateSpan, pev?: PointerDragEvent): void;
+        triggerDateUnselect(pev?: PointerDragEvent): void;
+        triggerDateClick(dateSpan: DateSpan, dayEl: HTMLElement, view: View, ev: UIEvent): void;
+        buildDatePointApi(dateSpan: DateSpan): import("@fullcalendar/core/structs/date-span").DatePointApi;
+        buildDateSpanApi(dateSpan: DateSpan): import("@fullcalendar/core/structs/date-span").DateSpanApi;
+        getNow(): DateMarker;
+        getDefaultEventEnd(allDay: boolean, marker: DateMarker): DateMarker;
+        addEvent(eventInput: EventInput, sourceInput?: EventSourceApi | string | number): EventApi | null;
+        getEventById(id: string): EventApi | null;
+        getEvents(): EventApi[];
+        removeAllEvents(): void;
+        rerenderEvents(): void;
+        getEventSources(): EventSourceApi[];
+        getEventSourceById(id: string | number): EventSourceApi | null;
+        addEventSource(sourceInput: EventSourceInput): EventSourceApi;
+        removeAllEventSources(): void;
+        refetchEvents(): void;
+        scrollToTime(timeInput: DurationInput): void;
+    }
+    function buildDelayedRerender(this: Calendar, wait: any): any;
+    export {};
+}
+
+declare module '@fullcalendar/core/View' {
+    import DateProfileGenerator, { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { EmitterInterface } from '@fullcalendar/core/common/EmitterMixin';
+    import { ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    import { ComponentContext } from '@fullcalendar/core/component/Component';
+    import DateComponent from '@fullcalendar/core/component/DateComponent';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventUiHash, EventUi } from '@fullcalendar/core/component/event-ui';
+    import { EventRenderRange } from '@fullcalendar/core/component/event-rendering';
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    import { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    import { EventDef } from '@fullcalendar/core/structs/event';
+    export interface ViewProps {
+        dateProfile: DateProfile;
+        businessHours: EventStore;
+        eventStore: EventStore;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+    }
+    export { View as default, View };
+    abstract class View extends DateComponent<ViewProps> {
+        usesMinMaxTime: boolean;
+        dateProfileGeneratorClass: any;
+        on: EmitterInterface['on'];
+        one: EmitterInterface['one'];
+        off: EmitterInterface['off'];
+        trigger: EmitterInterface['trigger'];
+        triggerWith: EmitterInterface['triggerWith'];
+        hasHandlers: EmitterInterface['hasHandlers'];
+        viewSpec: ViewSpec;
+        dateProfileGenerator: DateProfileGenerator;
+        type: string;
+        title: string;
+        queuedScroll: any;
+        eventOrderSpecs: any;
+        nextDayThreshold: Duration;
+        isNowIndicatorRendered: boolean;
+        initialNowDate: DateMarker;
+        initialNowQueriedMs: number;
+        nowIndicatorTimeoutID: any;
+        nowIndicatorIntervalID: any;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        initialize(): void;
+        readonly activeStart: Date;
+        readonly activeEnd: Date;
+        readonly currentStart: Date;
+        readonly currentEnd: Date;
+        render(props: ViewProps): void;
+        destroy(): void;
+        updateSize(isResize: boolean, viewHeight: number, isAuto: boolean): void;
+        updateBaseSize(isResize: boolean, viewHeight: number, isAuto: boolean): void;
+        renderDatesWrap(dateProfile: DateProfile): void;
+        unrenderDatesWrap(): void;
+        renderDates(dateProfile: DateProfile): void;
+        unrenderDates(): void;
+        renderBusinessHours(businessHours: EventStore): void;
+        unrenderBusinessHours(): void;
+        renderDateSelectionWrap(selection: DateSpan): void;
+        unrenderDateSelectionWrap(selection: DateSpan): void;
+        renderDateSelection(selection: DateSpan): void;
+        unrenderDateSelection(selection: DateSpan): void;
+        renderEvents(eventStore: EventStore): void;
+        unrenderEvents(): void;
+        sliceEvents(eventStore: EventStore, allDay: boolean): EventRenderRange[];
+        computeEventDraggable(eventDef: EventDef, eventUi: EventUi): boolean;
+        computeEventStartResizable(eventDef: EventDef, eventUi: EventUi): any;
+        computeEventEndResizable(eventDef: EventDef, eventUi: EventUi): boolean;
+        renderEventSelectionWrap(instanceId: string): void;
+        unrenderEventSelectionWrap(instanceId: string): void;
+        renderEventSelection(instanceId: string): void;
+        unrenderEventSelection(instanceId: string): void;
+        renderEventDragWrap(state: EventInteractionState): void;
+        unrenderEventDragWrap(state: EventInteractionState): void;
+        renderEventDrag(state: EventInteractionState): void;
+        unrenderEventDrag(state: EventInteractionState): void;
+        renderEventResizeWrap(state: EventInteractionState): void;
+        unrenderEventResizeWrap(state: EventInteractionState): void;
+        renderEventResize(state: EventInteractionState): void;
+        unrenderEventResize(state: EventInteractionState): void;
+        startNowIndicator(dateProfile: DateProfile): void;
+        updateNowIndicator(): void;
+        stopNowIndicator(): void;
+        getNowIndicatorUnit(dateProfile: DateProfile): void;
+        renderNowIndicator(date: any): void;
+        unrenderNowIndicator(): void;
+        addScroll(scroll: any): void;
+        popScroll(isResize: boolean): void;
+        applyQueuedScroll(isResize: boolean): void;
+        queryScroll(): any;
+        applyScroll(scroll: any, isResize: boolean): void;
+        computeDateScroll(duration: Duration): {};
+        queryDateScroll(): {};
+        applyDateScroll(scroll: any): void;
+        scrollToDuration(duration: Duration): void;
+    }
+}
+
+declare module '@fullcalendar/core/component/renderers/FgEventRenderer' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { DateFormatter } from '@fullcalendar/core/datelib/formatting';
+    import { EventUi } from '@fullcalendar/core/component/event-ui';
+    import { EventRenderRange } from '@fullcalendar/core/component/event-rendering';
+    import { Seg } from '@fullcalendar/core/component/DateComponent';
+    import { ComponentContext } from '@fullcalendar/core/component/Component';
+    export { FgEventRenderer as default, FgEventRenderer };
+    abstract class FgEventRenderer {
+        context: ComponentContext;
+        eventTimeFormat: DateFormatter;
+        displayEventTime: boolean;
+        displayEventEnd: boolean;
+        segs: Seg[];
+        isSizeDirty: boolean;
+        constructor(context: ComponentContext);
+        renderSegs(segs: Seg[], mirrorInfo?: any): void;
+        unrender(_segs: Seg[], mirrorInfo?: any): void;
+        abstract renderSegHtml(seg: Seg, mirrorInfo: any): string;
+        abstract attachSegs(segs: Seg[], mirrorInfo: any): any;
+        abstract detachSegs(segs: Seg[]): any;
+        rangeUpdated(): void;
+        renderSegEls(segs: Seg[], mirrorInfo: any): Seg[];
+        getSegClasses(seg: Seg, isDraggable: any, isResizable: any, mirrorInfo: any): string[];
+        getTimeText(eventRange: EventRenderRange, formatter?: any, displayEnd?: any): any;
+        _getTimeText(start: DateMarker, end: DateMarker, allDay: any, formatter?: any, displayEnd?: any, forcedStartTzo?: number, forcedEndTzo?: number): any;
+        computeEventTimeFormat(): any;
+        computeDisplayEventTime(): boolean;
+        computeDisplayEventEnd(): boolean;
+        getSkinCss(ui: EventUi): {
+            'background-color': string;
+            'border-color': string;
+            color: string;
+        };
+        sortEventSegs(segs: any): Seg[];
+        computeSizes(force: boolean): void;
+        assignSizes(force: boolean): void;
+        computeSegSizes(segs: Seg[]): void;
+        assignSegSizes(segs: Seg[]): void;
+        hideByHash(hash: any): void;
+        showByHash(hash: any): void;
+        selectByInstanceId(instanceId: string): void;
+        unselectByInstanceId(instanceId: string): void;
+    }
+    export function buildSegCompareObj(seg: Seg): any;
+}
+
+declare module '@fullcalendar/core/component/renderers/FillRenderer' {
+    import { Seg } from '@fullcalendar/core/component/DateComponent';
+    import { ComponentContext } from '@fullcalendar/core/component/Component';
+    export { FillRenderer as default, FillRenderer };
+    abstract class FillRenderer {
+        context: ComponentContext;
+        fillSegTag: string;
+        containerElsByType: any;
+        segsByType: any;
+        dirtySizeFlags: any;
+        constructor(context: ComponentContext);
+        getSegsByType(type: string): any;
+        renderSegs(type: any, segs: Seg[]): void;
+        unrender(type: any): void;
+        renderSegEls(type: any, segs: Seg[]): Seg[];
+        renderSegHtml(type: any, seg: Seg): string;
+        abstract attachSegs(type: any, segs: Seg[]): HTMLElement[] | void;
+        detachSegs(type: any, segs: Seg[]): void;
+        computeSizes(force: boolean): void;
+        assignSizes(force: boolean): void;
+        computeSegSizes(segs: Seg[]): void;
+        assignSegSizes(segs: Seg[]): void;
+    }
+}
+
+declare module '@fullcalendar/core/DateProfileGenerator' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { DateRange, OpenDateRange } from '@fullcalendar/core/datelib/date-range';
+    import { ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    import { DateEnv } from '@fullcalendar/core/datelib/env';
+    import Calendar from '@fullcalendar/core/Calendar';
+    export interface DateProfile {
+        currentRange: DateRange;
+        currentRangeUnit: string;
+        isRangeAllDay: boolean;
+        validRange: OpenDateRange;
+        activeRange: DateRange;
+        renderRange: DateRange;
+        minTime: Duration;
+        maxTime: Duration;
+        isValid: boolean;
+        dateIncrement: Duration;
+    }
+    export { DateProfileGenerator as default, DateProfileGenerator };
+    class DateProfileGenerator {
+        viewSpec: ViewSpec;
+        options: any;
+        dateEnv: DateEnv;
+        calendar: Calendar;
+        isHiddenDayHash: boolean[];
+        constructor(viewSpec: ViewSpec, calendar: Calendar);
+        buildPrev(currentDateProfile: DateProfile, currentDate: DateMarker): DateProfile;
+        buildNext(currentDateProfile: DateProfile, currentDate: DateMarker): DateProfile;
+        build(currentDate: DateMarker, direction?: any, forceToValid?: boolean): DateProfile;
+        buildValidRange(): OpenDateRange;
+        buildCurrentRangeInfo(date: DateMarker, direction: any): {
+            duration: any;
+            unit: any;
+            range: any;
+        };
+        getFallbackDuration(): Duration;
+        adjustActiveRange(range: DateRange, minTime: Duration, maxTime: Duration): {
+            start: Date;
+            end: Date;
+        };
+        buildRangeFromDuration(date: DateMarker, direction: any, duration: Duration, unit: any): any;
+        buildRangeFromDayCount(date: DateMarker, direction: any, dayCount: any): {
+            start: Date;
+            end: Date;
+        };
+        buildCustomVisibleRange(date: DateMarker): OpenDateRange;
+        buildRenderRange(currentRange: DateRange, currentRangeUnit: any, isRangeAllDay: any): DateRange;
+        buildDateIncrement(fallback: any): Duration;
+        getRangeOption(name: any, ...otherArgs: any[]): OpenDateRange;
+        initHiddenDays(): void;
+        trimHiddenDays(range: DateRange): DateRange | null;
+        isHiddenDay(day: any): boolean;
+        skipHiddenDays(date: DateMarker, inc?: number, isExclusive?: boolean): Date;
+    }
+    export function isDateProfilesEqual(p0: DateProfile, p1: DateProfile): boolean;
+}
+
+declare module '@fullcalendar/core/structs/view-def' {
+    import { ViewClass, ViewConfigHash } from '@fullcalendar/core/structs/view-config';
+    export interface ViewDef {
+        type: string;
+        class: ViewClass;
+        overrides: any;
+        defaults: any;
+    }
+    export type ViewDefHash = {
+        [viewType: string]: ViewDef;
+    };
+    export function compileViewDefs(defaultConfigs: ViewConfigHash, overrideConfigs: ViewConfigHash): ViewDefHash;
+}
+
+declare module '@fullcalendar/core/structs/view-spec' {
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import OptionsManager from '@fullcalendar/core/OptionsManager';
+    import { ViewConfigInputHash, ViewClass } from '@fullcalendar/core/structs/view-config';
+    export interface ViewSpec {
+        type: string;
+        class: ViewClass;
+        duration: Duration;
+        durationUnit: string;
+        singleUnit: string;
+        options: any;
+        buttonTextOverride: string;
+        buttonTextDefault: string;
+    }
+    export type ViewSpecHash = {
+        [viewType: string]: ViewSpec;
+    };
+    export function buildViewSpecs(defaultInputs: ViewConfigInputHash, optionsManager: OptionsManager): ViewSpecHash;
+}
+
+declare module '@fullcalendar/core/structs/date-span' {
+    import { DateRange, OpenDateRange } from '@fullcalendar/core/datelib/date-range';
+    import { DateInput, DateEnv } from '@fullcalendar/core/datelib/env';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { EventRenderRange } from '@fullcalendar/core/component/event-rendering';
+    import { EventUiHash } from '@fullcalendar/core/component/event-ui';
+    import Calendar from '@fullcalendar/core/Calendar';
+    export interface OpenDateSpanInput {
+        start?: DateInput;
+        end?: DateInput;
+        allDay?: boolean;
+        [otherProp: string]: any;
+    }
+    export interface DateSpanInput extends OpenDateSpanInput {
+        start: DateInput;
+        end: DateInput;
+    }
+    export interface OpenDateSpan {
+        range: OpenDateRange;
+        allDay: boolean;
+        [otherProp: string]: any;
+    }
+    export interface DateSpan extends OpenDateSpan {
+        range: DateRange;
+    }
+    export interface DateSpanApi {
+        start: Date;
+        end: Date;
+        startStr: string;
+        endStr: string;
+        allDay: boolean;
+    }
+    export interface DatePointApi {
+        date: Date;
+        dateStr: string;
+        allDay: boolean;
+    }
+    export function parseDateSpan(raw: DateSpanInput, dateEnv: DateEnv, defaultDuration?: Duration): DateSpan | null;
+    export function parseOpenDateSpan(raw: OpenDateSpanInput, dateEnv: DateEnv): OpenDateSpan | null;
+    export function isDateSpansEqual(span0: DateSpan, span1: DateSpan): boolean;
+    export function buildDateSpanApi(span: DateSpan, dateEnv: DateEnv): DateSpanApi;
+    export function buildDatePointApi(span: DateSpan, dateEnv: DateEnv): DatePointApi;
+    export function fabricateEventRange(dateSpan: DateSpan, eventUiBases: EventUiHash, calendar: Calendar): EventRenderRange;
+}
+
+declare module '@fullcalendar/core/datelib/marker' {
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    export type DateMarker = Date;
+    export const DAY_IDS: string[];
+    export function addWeeks(m: DateMarker, n: number): Date;
+    export function addDays(m: DateMarker, n: number): Date;
+    export function addMs(m: DateMarker, n: number): Date;
+    export function diffWeeks(m0: any, m1: any): number;
+    export function diffDays(m0: any, m1: any): number;
+    export function diffHours(m0: any, m1: any): number;
+    export function diffMinutes(m0: any, m1: any): number;
+    export function diffSeconds(m0: any, m1: any): number;
+    export function diffDayAndTime(m0: DateMarker, m1: DateMarker): Duration;
+    export function diffWholeWeeks(m0: DateMarker, m1: DateMarker): number;
+    export function diffWholeDays(m0: DateMarker, m1: DateMarker): number;
+    export function startOfDay(m: DateMarker): DateMarker;
+    export function startOfHour(m: DateMarker): Date;
+    export function startOfMinute(m: DateMarker): Date;
+    export function startOfSecond(m: DateMarker): Date;
+    export function weekOfYear(marker: any, dow: any, doy: any): number;
+    export function dateToLocalArray(date: any): any[];
+    export function arrayToLocalDate(a: any): Date;
+    export function dateToUtcArray(date: any): any[];
+    export function arrayToUtcDate(a: any): Date;
+    export function isValidDate(m: DateMarker): boolean;
+    export function timeAsMs(m: DateMarker): number;
+}
+
+declare module '@fullcalendar/core/datelib/duration' {
+    export type DurationInput = DurationObjectInput | string | number;
+    export interface DurationObjectInput {
+        years?: number;
+        year?: number;
+        months?: number;
+        month?: number;
+        weeks?: number;
+        week?: number;
+        days?: number;
+        day?: number;
+        hours?: number;
+        hour?: number;
+        minutes?: number;
+        minute?: number;
+        seconds?: number;
+        second?: number;
+        milliseconds?: number;
+        millisecond?: number;
+        ms?: number;
+    }
+    export interface Duration {
+        years: number;
+        months: number;
+        days: number;
+        milliseconds: number;
+    }
+    export function createDuration(input: DurationInput, unit?: string): Duration | null;
+    export function getWeeksFromInput(obj: DurationObjectInput): number;
+    export function durationsEqual(d0: Duration, d1: Duration): boolean;
+    export function isSingleDay(dur: Duration): boolean;
+    export function addDurations(d0: Duration, d1: Duration): {
+        years: number;
+        months: number;
+        days: number;
+        milliseconds: number;
+    };
+    export function subtractDurations(d1: Duration, d0: Duration): Duration;
+    export function multiplyDuration(d: Duration, n: number): {
+        years: number;
+        months: number;
+        days: number;
+        milliseconds: number;
+    };
+    export function asRoughYears(dur: Duration): number;
+    export function asRoughMonths(dur: Duration): number;
+    export function asRoughDays(dur: Duration): number;
+    export function asRoughHours(dur: Duration): number;
+    export function asRoughMinutes(dur: Duration): number;
+    export function asRoughSeconds(dur: Duration): number;
+    export function asRoughMs(dur: Duration): number;
+    export function wholeDivideDurations(numerator: Duration, denominator: Duration): number;
+    export function greatestDurationDenominator(dur: Duration, dontReturnWeeks?: boolean): {
+        unit: string;
+        value: number;
+    };
+}
+
+declare module '@fullcalendar/core/datelib/env' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { CalendarSystem } from '@fullcalendar/core/datelib/calendar-system';
+    import { Locale } from '@fullcalendar/core/datelib/locale';
+    import { NamedTimeZoneImpl, NamedTimeZoneImplClass } from '@fullcalendar/core/datelib/timezone';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { DateFormatter } from '@fullcalendar/core/datelib/formatting';
+    import { CmdFormatterFunc } from '@fullcalendar/core/datelib/formatting-cmd';
+    export interface DateEnvSettings {
+        timeZone: string;
+        namedTimeZoneImpl?: NamedTimeZoneImplClass;
+        calendarSystem: string;
+        locale: Locale;
+        weekNumberCalculation?: any;
+        firstDay?: any;
+        weekLabel?: string;
+        cmdFormatter?: CmdFormatterFunc;
+    }
+    export type DateInput = Date | string | number | number[];
+    export interface DateMarkerMeta {
+        marker: DateMarker;
+        isTimeUnspecified: boolean;
+        forcedTzo: number | null;
+    }
+    export class DateEnv {
+        timeZone: string;
+        namedTimeZoneImpl: NamedTimeZoneImpl;
+        canComputeOffset: boolean;
+        calendarSystem: CalendarSystem;
+        locale: Locale;
+        weekDow: number;
+        weekDoy: number;
+        weekNumberFunc: any;
+        weekLabel: string;
+        cmdFormatter?: CmdFormatterFunc;
+        constructor(settings: DateEnvSettings);
+        createMarker(input: DateInput): DateMarker;
+        createNowMarker(): DateMarker;
+        createMarkerMeta(input: DateInput): DateMarkerMeta;
+        parse(s: string): {
+            marker: Date;
+            isTimeUnspecified: boolean;
+            forcedTzo: any;
+        };
+        getYear(marker: DateMarker): number;
+        getMonth(marker: DateMarker): number;
+        add(marker: DateMarker, dur: Duration): DateMarker;
+        subtract(marker: DateMarker, dur: Duration): DateMarker;
+        addYears(marker: DateMarker, n: number): Date;
+        addMonths(marker: DateMarker, n: number): Date;
+        diffWholeYears(m0: DateMarker, m1: DateMarker): number;
+        diffWholeMonths(m0: DateMarker, m1: DateMarker): number;
+        greatestWholeUnit(m0: DateMarker, m1: DateMarker): {
+            unit: string;
+            value: number;
+        };
+        countDurationsBetween(m0: DateMarker, m1: DateMarker, d: Duration): number;
+        startOf(m: DateMarker, unit: string): Date;
+        startOfYear(m: DateMarker): DateMarker;
+        startOfMonth(m: DateMarker): DateMarker;
+        startOfWeek(m: DateMarker): DateMarker;
+        computeWeekNumber(marker: DateMarker): number;
+        format(marker: DateMarker, formatter: DateFormatter, dateOptions?: {
+            forcedTzo?: number;
+        }): any;
+        formatRange(start: DateMarker, end: DateMarker, formatter: DateFormatter, dateOptions?: {
+            forcedStartTzo?: number;
+            forcedEndTzo?: number;
+            isEndExclusive?: boolean;
+        }): any;
+        formatIso(marker: DateMarker, extraOptions?: any): string;
+        timestampToMarker(ms: number): Date;
+        offsetForMarker(m: DateMarker): number;
+        toDate(m: DateMarker, forcedTzo?: number): Date;
+    }
+}
+
+declare module '@fullcalendar/core/datelib/formatting' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { CalendarSystem } from '@fullcalendar/core/datelib/calendar-system';
+    import { Locale } from '@fullcalendar/core/datelib/locale';
+    import { CmdFormatterFunc } from '@fullcalendar/core/datelib/formatting-cmd';
+    import { FuncFormatterFunc } from '@fullcalendar/core/datelib/formatting-func';
+    export interface ZonedMarker {
+        marker: DateMarker;
+        timeZoneOffset: number;
+    }
+    export interface ExpandedZonedMarker extends ZonedMarker {
+        array: number[];
+        year: number;
+        month: number;
+        day: number;
+        hour: number;
+        minute: number;
+        second: number;
+        millisecond: number;
+    }
+    export interface VerboseFormattingArg {
+        date: ExpandedZonedMarker;
+        start: ExpandedZonedMarker;
+        end?: ExpandedZonedMarker;
+        timeZone: string;
+        localeCodes: string[];
+        separator: string;
+    }
+    export interface DateFormattingContext {
+        timeZone: string;
+        locale: Locale;
+        calendarSystem: CalendarSystem;
+        computeWeekNumber: (d: DateMarker) => number;
+        weekLabel: string;
+        cmdFormatter?: CmdFormatterFunc;
+    }
+    export interface DateFormatter {
+        format(date: ZonedMarker, context: DateFormattingContext): any;
+        formatRange(start: ZonedMarker, end: ZonedMarker, context: DateFormattingContext): any;
+    }
+    export type FormatterInput = object | string | FuncFormatterFunc;
+    export function createFormatter(input: FormatterInput, defaultSeparator?: string): DateFormatter;
+    export function buildIsoString(marker: DateMarker, timeZoneOffset?: number, stripZeroTime?: boolean): string;
+    export function formatIsoTimeString(marker: DateMarker): string;
+    export function formatTimeZoneOffset(minutes: number, doIso?: boolean): string;
+    export function createVerboseFormattingArg(start: ZonedMarker, end: ZonedMarker, context: DateFormattingContext, separator?: string): VerboseFormattingArg;
+}
+
+declare module '@fullcalendar/core/datelib/timezone' {
+    export abstract class NamedTimeZoneImpl {
+        timeZoneName: string;
+        constructor(timeZoneName: string);
+        abstract offsetForArray(a: number[]): number;
+        abstract timestampToArray(ms: number): number[];
+    }
+    export type NamedTimeZoneImplClass = {
+        new (timeZoneName: string): NamedTimeZoneImpl;
+    };
+}
+
+declare module '@fullcalendar/core/datelib/parsing' {
+    export function parse(str: any): {
+        marker: Date;
+        isTimeUnspecified: boolean;
+        timeZoneOffset: any;
+    };
+}
+
+declare module '@fullcalendar/core/structs/event-source' {
+    import { EventInput } from '@fullcalendar/core/structs/event';
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { EventSourceFunc } from '@fullcalendar/core/event-sources/func-event-source';
+    import { EventUi } from '@fullcalendar/core/component/event-ui';
+    import { ConstraintInput, AllowFunc } from '@fullcalendar/core/validation';
+    export type EventSourceError = {
+        message: string;
+        response?: any;
+        [otherProp: string]: any;
+    };
+    export type EventInputTransformer = (eventInput: EventInput) => EventInput | null;
+    export type EventSourceSuccessResponseHandler = (rawData: any, response: any) => EventInput[] | void;
+    export type EventSourceErrorResponseHandler = (error: EventSourceError) => void;
+    export interface ExtendedEventSourceInput {
+        id?: string | number;
+        allDayDefault?: boolean;
+        eventDataTransform?: EventInputTransformer;
+        events?: EventInput[] | EventSourceFunc;
+        url?: string;
+        method?: string;
+        extraParams?: object | (() => object);
+        startParam?: string;
+        endParam?: string;
+        timeZoneParam?: string;
+        success?: EventSourceSuccessResponseHandler;
+        failure?: EventSourceErrorResponseHandler;
+        editable?: boolean;
+        startEditable?: boolean;
+        durationEditable?: boolean;
+        constraint?: ConstraintInput;
+        overlap?: boolean;
+        allow?: AllowFunc;
+        className?: string[] | string;
+        classNames?: string[] | string;
+        backgroundColor?: string;
+        borderColor?: string;
+        textColor?: string;
+        color?: string;
+        [otherProp: string]: any;
+    }
+    export type EventSourceInput = ExtendedEventSourceInput | // object in extended form
+    EventSourceFunc | // just a function
+    string;
+    export interface EventSource {
+        _raw: any;
+        sourceId: string;
+        sourceDefId: number;
+        meta: any;
+        publicId: string;
+        isFetching: boolean;
+        latestFetchId: string;
+        fetchRange: DateRange | null;
+        allDayDefault: boolean | null;
+        eventDataTransform: EventInputTransformer;
+        ui: EventUi;
+        success: EventSourceSuccessResponseHandler | null;
+        failure: EventSourceErrorResponseHandler | null;
+        extendedProps: any;
+    }
+    export type EventSourceHash = {
+        [sourceId: string]: EventSource;
+    };
+    export type EventSourceFetcher = (arg: {
+        eventSource: EventSource;
+        calendar: Calendar;
+        range: DateRange;
+    }, success: (res: {
+        rawEvents: EventInput[];
+        xhr?: XMLHttpRequest;
+    }) => void, failure: (error: EventSourceError) => void) => (void | PromiseLike<EventInput[]>);
+    export interface EventSourceDef {
+        ignoreRange?: boolean;
+        parseMeta: (raw: EventSourceInput) => object | null;
+        fetch: EventSourceFetcher;
+    }
+    export function doesSourceNeedRange(eventSource: EventSource, calendar: Calendar): boolean;
+    export function parseEventSource(raw: EventSourceInput, calendar: Calendar): EventSource | null;
+}
+
+declare module '@fullcalendar/core/interactions/interaction' {
+    import DateComponent from '@fullcalendar/core/component/DateComponent';
+    export abstract class Interaction {
+        component: DateComponent<any>;
+        constructor(settings: InteractionSettings);
+        destroy(): void;
+    }
+    export type InteractionClass = {
+        new (settings: InteractionSettings): Interaction;
+    };
+    export interface InteractionSettingsInput {
+        el: HTMLElement;
+        useEventCenter?: boolean;
+    }
+    export interface InteractionSettings {
+        component: DateComponent<any>;
+        el: HTMLElement;
+        useEventCenter: boolean;
+    }
+    export type InteractionSettingsStore = {
+        [componenUid: string]: InteractionSettings;
+    };
+    export function parseInteractionSettings(component: DateComponent<any>, input: InteractionSettingsInput): InteractionSettings;
+    export function interactionSettingsToStore(settings: InteractionSettings): {
+        [x: string]: InteractionSettings;
+    };
+    export let interactionSettingsStore: InteractionSettingsStore;
+}
+
+declare module '@fullcalendar/core/interactions/pointer' {
+    export interface PointerDragEvent {
+        origEvent: UIEvent;
+        isTouch: boolean;
+        subjectEl: EventTarget;
+        pageX: number;
+        pageY: number;
+        deltaX: number;
+        deltaY: number;
+    }
+}
+
+declare module '@fullcalendar/core/interactions/hit' {
+    import DateComponent from '@fullcalendar/core/component/DateComponent';
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    import { Rect } from '@fullcalendar/core/util/geom';
+    export interface Hit {
+        component: DateComponent<any>;
+        dateSpan: DateSpan;
+        dayEl: HTMLElement;
+        rect: Rect;
+        layer: number;
+    }
+}
+
+declare module '@fullcalendar/core/interactions/date-selecting' {
+    import { Hit } from '@fullcalendar/core/interactions/hit';
+    export type dateSelectionJoinTransformer = (hit0: Hit, hit1: Hit) => any;
+}
+
+declare module '@fullcalendar/core/interactions/event-dragging' {
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { EventMutation } from '@fullcalendar/core/structs/event-mutation';
+    import { Hit } from '@fullcalendar/core/interactions/hit';
+    import { EventDef } from '@fullcalendar/core/structs/event';
+    import { EventUi } from '@fullcalendar/core/component/event-ui';
+    import { View } from '@fullcalendar/core';
+    export type eventDragMutationMassager = (mutation: EventMutation, hit0: Hit, hit1: Hit) => void;
+    export type EventDropTransformers = (mutation: EventMutation, calendar: Calendar) => any;
+    export type eventIsDraggableTransformer = (val: boolean, eventDef: EventDef, eventUi: EventUi, view: View) => boolean;
+}
+
+declare module '@fullcalendar/core/interactions/event-resizing' {
+    import { Hit } from '@fullcalendar/core/interactions/hit';
+    export type EventResizeJoinTransforms = (hit0: Hit, hit1: Hit) => false | object;
+}
+
+declare module '@fullcalendar/core/interactions/ElementDragging' {
+    import EmitterMixin from '@fullcalendar/core/common/EmitterMixin';
+    export { ElementDragging as default, ElementDragging };
+    abstract class ElementDragging {
+        emitter: EmitterMixin;
+        constructor(el: HTMLElement);
+        destroy(): void;
+        abstract setIgnoreMove(bool: boolean): void;
+        setMirrorIsVisible(bool: boolean): void;
+        setMirrorNeedsRevert(bool: boolean): void;
+        setAutoScrollEnabled(bool: boolean): void;
+    }
+    export type ElementDraggingClass = {
+        new (el: HTMLElement): ElementDragging;
+    };
+}
+
+declare module '@fullcalendar/core/formatting-api' {
+    import { DateInput } from '@fullcalendar/core/datelib/env';
+    export function formatDate(dateInput: DateInput, settings?: {}): any;
+    export function formatRange(startInput: DateInput, endInput: DateInput, settings: any): any;
+}
+
+declare module '@fullcalendar/core/options' {
+    import { PluginDef } from '@fullcalendar/core/plugin-system';
+    export const config: any;
+    export const globalDefaults: {
+        defaultRangeSeparator: string;
+        titleRangeSeparator: string;
+        defaultTimedEventDuration: string;
+        defaultAllDayEventDuration: {
+            day: number;
+        };
+        forceEventDuration: boolean;
+        nextDayThreshold: string;
+        columnHeader: boolean;
+        defaultView: string;
+        aspectRatio: number;
+        header: {
+            left: string;
+            center: string;
+            right: string;
+        };
+        weekends: boolean;
+        weekNumbers: boolean;
+        weekNumberCalculation: string;
+        editable: boolean;
+        scrollTime: string;
+        minTime: string;
+        maxTime: string;
+        showNonCurrentDates: boolean;
+        lazyFetching: boolean;
+        startParam: string;
+        endParam: string;
+        timeZoneParam: string;
+        timeZone: string;
+        locales: any[];
+        locale: string;
+        timeGridEventMinHeight: number;
+        themeSystem: string;
+        dragRevertDuration: number;
+        dragScroll: boolean;
+        allDayMaintainDuration: boolean;
+        unselectAuto: boolean;
+        dropAccept: string;
+        eventOrder: string;
+        eventLimit: boolean;
+        eventLimitClick: string;
+        dayPopoverFormat: {
+            month: string;
+            day: string;
+            year: string;
+        };
+        handleWindowResize: boolean;
+        windowResizeDelay: number;
+        longPressDelay: number;
+        eventDragMinDistance: number;
+    };
+    export const rtlDefaults: {
+        header: {
+            left: string;
+            center: string;
+            right: string;
+        };
+        buttonIcons: {
+            prev: string;
+            next: string;
+            prevYear: string;
+            nextYear: string;
+        };
+    };
+    export function mergeOptions(optionObjs: any): any;
+    export function refinePluginDefs(pluginInputs: any[]): PluginDef[];
+}
+
+declare module '@fullcalendar/core/structs/recurring-event' {
+    import { EventInput, EventDef } from '@fullcalendar/core/structs/event';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { DateEnv } from '@fullcalendar/core/datelib/env';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    export interface ParsedRecurring {
+        typeData: any;
+        allDayGuess: boolean | null;
+        duration: Duration | null;
+    }
+    export interface RecurringType {
+        parse: (rawEvent: EventInput, leftoverProps: any, dateEnv: DateEnv) => ParsedRecurring | null;
+        expand: (typeData: any, framingRange: DateRange, dateEnv: DateEnv) => DateMarker[];
+    }
+    export function parseRecurring(eventInput: EventInput, allDayDefault: boolean | null, dateEnv: DateEnv, recurringTypes: RecurringType[], leftovers: any): {
+        allDay: any;
+        duration: Duration;
+        typeData: any;
+        typeId: number;
+    };
+    export function expandRecurringRanges(eventDef: EventDef, duration: Duration, framingRange: DateRange, dateEnv: DateEnv, recurringTypes: RecurringType[]): DateMarker[];
+}
+
+declare module '@fullcalendar/core/structs/drag-meta' {
+    import { Duration, DurationInput } from '@fullcalendar/core/datelib/duration';
+    import { EventNonDateInput } from '@fullcalendar/core/structs/event';
+    export interface DragMetaInput extends EventNonDateInput {
+        startTime?: DurationInput;
+        duration?: DurationInput;
+        create?: boolean;
+        sourceId?: string;
+    }
+    export interface DragMeta {
+        startTime: Duration | null;
+        duration: Duration | null;
+        create: boolean;
+        sourceId: string;
+        leftoverProps: object;
+    }
+    export function parseDragMeta(raw: DragMetaInput): DragMeta;
+}
+
+declare module '@fullcalendar/core/plugin-system' {
+    import { reducerFunc } from '@fullcalendar/core/reducers/types';
+    import { eventDefParserFunc } from '@fullcalendar/core/structs/event';
+    import { eventDefMutationApplier } from '@fullcalendar/core/structs/event-mutation';
+    import Calendar, { DatePointTransform, DateSpanTransform, CalendarInteractionClass, OptionChangeHandlerMap } from '@fullcalendar/core/Calendar';
+    import { ViewConfigInputHash } from '@fullcalendar/core/structs/view-config';
+    import { ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    import View, { ViewProps } from '@fullcalendar/core/View';
+    import { CalendarComponentProps } from '@fullcalendar/core/CalendarComponent';
+    import { isPropsValidTester } from '@fullcalendar/core/validation';
+    import { eventDragMutationMassager, eventIsDraggableTransformer, EventDropTransformers } from '@fullcalendar/core/interactions/event-dragging';
+    import { dateSelectionJoinTransformer } from '@fullcalendar/core/interactions/date-selecting';
+    import { EventResizeJoinTransforms } from '@fullcalendar/core/interactions/event-resizing';
+    import { ExternalDefTransform } from '@fullcalendar/core/interactions/external-element-dragging';
+    import { InteractionClass } from '@fullcalendar/core/interactions/interaction';
+    import { ThemeClass } from '@fullcalendar/core/theme/Theme';
+    import { EventSourceDef } from '@fullcalendar/core/structs/event-source';
+    import { CmdFormatterFunc } from '@fullcalendar/core/datelib/formatting-cmd';
+    import { RecurringType } from '@fullcalendar/core/structs/recurring-event';
+    import { NamedTimeZoneImplClass } from '@fullcalendar/core/datelib/timezone';
+    import { ElementDraggingClass } from '@fullcalendar/core/interactions/ElementDragging';
+    export interface PluginDefInput {
+        deps?: PluginDef[];
+        reducers?: reducerFunc[];
+        eventDefParsers?: eventDefParserFunc[];
+        isDraggableTransformers?: eventIsDraggableTransformer[];
+        eventDragMutationMassagers?: eventDragMutationMassager[];
+        eventDefMutationAppliers?: eventDefMutationApplier[];
+        dateSelectionTransformers?: dateSelectionJoinTransformer[];
+        datePointTransforms?: DatePointTransform[];
+        dateSpanTransforms?: DateSpanTransform[];
+        views?: ViewConfigInputHash;
+        viewPropsTransformers?: ViewPropsTransformerClass[];
+        isPropsValid?: isPropsValidTester;
+        externalDefTransforms?: ExternalDefTransform[];
+        eventResizeJoinTransforms?: EventResizeJoinTransforms[];
+        viewContainerModifiers?: ViewContainerModifier[];
+        eventDropTransformers?: EventDropTransformers[];
+        componentInteractions?: InteractionClass[];
+        calendarInteractions?: CalendarInteractionClass[];
+        themeClasses?: {
+            [themeSystemName: string]: ThemeClass;
+        };
+        eventSourceDefs?: EventSourceDef[];
+        cmdFormatter?: CmdFormatterFunc;
+        recurringTypes?: RecurringType[];
+        namedTimeZonedImpl?: NamedTimeZoneImplClass;
+        defaultView?: string;
+        elementDraggingImpl?: ElementDraggingClass;
+        optionChangeHandlers?: OptionChangeHandlerMap;
+    }
+    export interface PluginHooks {
+        reducers: reducerFunc[];
+        eventDefParsers: eventDefParserFunc[];
+        isDraggableTransformers: eventIsDraggableTransformer[];
+        eventDragMutationMassagers: eventDragMutationMassager[];
+        eventDefMutationAppliers: eventDefMutationApplier[];
+        dateSelectionTransformers: dateSelectionJoinTransformer[];
+        datePointTransforms: DatePointTransform[];
+        dateSpanTransforms: DateSpanTransform[];
+        views: ViewConfigInputHash;
+        viewPropsTransformers: ViewPropsTransformerClass[];
+        isPropsValid: isPropsValidTester | null;
+        externalDefTransforms: ExternalDefTransform[];
+        eventResizeJoinTransforms: EventResizeJoinTransforms[];
+        viewContainerModifiers: ViewContainerModifier[];
+        eventDropTransformers: EventDropTransformers[];
+        componentInteractions: InteractionClass[];
+        calendarInteractions: CalendarInteractionClass[];
+        themeClasses: {
+            [themeSystemName: string]: ThemeClass;
+        };
+        eventSourceDefs: EventSourceDef[];
+        cmdFormatter?: CmdFormatterFunc;
+        recurringTypes: RecurringType[];
+        namedTimeZonedImpl?: NamedTimeZoneImplClass;
+        defaultView: string;
+        elementDraggingImpl?: ElementDraggingClass;
+        optionChangeHandlers: OptionChangeHandlerMap;
+    }
+    export interface PluginDef extends PluginHooks {
+        id: string;
+        deps: PluginDef[];
+    }
+    export type ViewPropsTransformerClass = new () => ViewPropsTransformer;
+    export interface ViewPropsTransformer {
+        transform(viewProps: ViewProps, viewSpec: ViewSpec, calendarProps: CalendarComponentProps, view: View): any;
+    }
+    export type ViewContainerModifier = (contentEl: HTMLElement, calendar: Calendar) => void;
+    export function createPlugin(input: PluginDefInput): PluginDef;
+    export class PluginSystem {
+        hooks: PluginHooks;
+        addedHash: {
+            [pluginId: string]: true;
+        };
+        constructor();
+        add(plugin: PluginDef): void;
+    }
+}
+
+declare module '@fullcalendar/core/reducers/types' {
+    import { EventInput, EventInstanceHash } from '@fullcalendar/core/structs/event';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventMutation } from '@fullcalendar/core/structs/event-mutation';
+    import { EventSource, EventSourceHash, EventSourceError } from '@fullcalendar/core/structs/event-source';
+    import { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    import { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    import { DateEnv } from '@fullcalendar/core/datelib/env';
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    export interface CalendarState {
+        eventSources: EventSourceHash;
+        eventSourceLoadingLevel: number;
+        loadingLevel: number;
+        viewType: string;
+        currentDate: DateMarker;
+        dateProfile: DateProfile | null;
+        eventStore: EventStore;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+    }
+    export type reducerFunc = (state: CalendarState, action: Action, calendar: Calendar) => CalendarState;
+    export type Action = {
+        type: 'INIT';
+    } | // wont it create another rerender?
+    {
+        type: 'PREV';
+    } | {
+        type: 'NEXT';
+    } | {
+        type: 'SET_DATE';
+        dateMarker: DateMarker;
+    } | {
+        type: 'SET_VIEW_TYPE';
+        viewType: string;
+        dateMarker?: DateMarker;
+    } | {
+        type: 'SELECT_DATES';
+        selection: DateSpan;
+    } | {
+        type: 'UNSELECT_DATES';
+    } | {
+        type: 'SELECT_EVENT';
+        eventInstanceId: string;
+    } | {
+        type: 'UNSELECT_EVENT';
+    } | {
+        type: 'SET_EVENT_DRAG';
+        state: EventInteractionState;
+    } | {
+        type: 'UNSET_EVENT_DRAG';
+    } | {
+        type: 'SET_EVENT_RESIZE';
+        state: EventInteractionState;
+    } | {
+        type: 'UNSET_EVENT_RESIZE';
+    } | {
+        type: 'ADD_EVENT_SOURCES';
+        sources: EventSource[];
+    } | {
+        type: 'REMOVE_EVENT_SOURCE';
+        sourceId: string;
+    } | {
+        type: 'REMOVE_ALL_EVENT_SOURCES';
+    } | {
+        type: 'FETCH_EVENT_SOURCES';
+        sourceIds?: string[];
+    } | // if no sourceIds, fetch all
+    {
+        type: 'CHANGE_TIMEZONE';
+        oldDateEnv: DateEnv;
+    } | {
+        type: 'RECEIVE_EVENTS';
+        sourceId: string;
+        fetchId: string;
+        fetchRange: DateRange | null;
+        rawEvents: EventInput[];
+    } | {
+        type: 'RECEIVE_EVENT_ERROR';
+        sourceId: string;
+        fetchId: string;
+        fetchRange: DateRange | null;
+        error: EventSourceError;
+    } | // need all these?
+    {
+        type: 'ADD_EVENTS';
+        eventStore: EventStore;
+    } | {
+        type: 'MERGE_EVENTS';
+        eventStore: EventStore;
+    } | {
+        type: 'MUTATE_EVENTS';
+        instanceId: string;
+        mutation: EventMutation;
+        fromApi?: boolean;
+    } | {
+        type: 'REMOVE_EVENT_DEF';
+        defId: string;
+    } | {
+        type: 'REMOVE_EVENT_INSTANCES';
+        instances: EventInstanceHash;
+    } | {
+        type: 'REMOVE_ALL_EVENTS';
+    } | {
+        type: 'RESET_EVENTS';
+    };
+}
+
+declare module '@fullcalendar/core/CalendarComponent' {
+    import Component, { ComponentContext } from '@fullcalendar/core/component/Component';
+    import { ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    import View from '@fullcalendar/core/View';
+    import Toolbar from '@fullcalendar/core/Toolbar';
+    import DateProfileGenerator, { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventUiHash } from '@fullcalendar/core/component/event-ui';
+    import { BusinessHoursInput } from '@fullcalendar/core/structs/business-hours';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { CalendarState } from '@fullcalendar/core/reducers/types';
+    export interface CalendarComponentProps extends CalendarState {
+        viewSpec: ViewSpec;
+        dateProfileGenerator: DateProfileGenerator;
+        eventUiBases: EventUiHash;
+    }
+    export { CalendarComponent as default, CalendarComponent };
+    class CalendarComponent extends Component<CalendarComponentProps> {
+        view: View;
+        header: Toolbar;
+        footer: Toolbar;
+        computeTitle: (dateProfile: any, viewOptions: any) => string;
+        parseBusinessHours: (input: BusinessHoursInput) => EventStore;
+        el: HTMLElement;
+        contentEl: HTMLElement;
+        isHeightAuto: boolean;
+        viewHeight: number;
+        constructor(context: ComponentContext, el: HTMLElement);
+        destroy(): void;
+        toggleElClassNames(bool: boolean): void;
+        render(props: CalendarComponentProps): void;
+        renderToolbars(viewSpec: ViewSpec, dateProfile: DateProfile, currentDate: DateMarker, dateProfileGenerator: DateProfileGenerator, title: string): void;
+        renderView(props: CalendarComponentProps, title: string): void;
+        updateSize(isResize?: boolean): void;
+        computeHeightVars(): void;
+        queryToolbarsHeight(): number;
+        freezeHeight(): void;
+        thawHeight(): void;
+    }
+}
+
+declare module '@fullcalendar/core/common/DayHeader' {
+    import Component, { ComponentContext } from '@fullcalendar/core/component/Component';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    export interface DayTableHeaderProps {
+        dates: DateMarker[];
+        dateProfile: DateProfile;
+        datesRepDistinctDays: boolean;
+        renderIntroHtml?: () => string;
+    }
+    export { DayHeader as default, DayHeader };
+    class DayHeader extends Component<DayTableHeaderProps> {
+        el: HTMLElement;
+        thead: HTMLElement;
+        constructor(context: ComponentContext, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: DayTableHeaderProps): void;
+    }
+}
+
+declare module '@fullcalendar/core/common/table-utils' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    import { ComponentContext } from '@fullcalendar/core/component/Component';
+    export function computeFallbackHeaderFormat(datesRepDistinctDays: boolean, dayCnt: number): {
+        weekday: string;
+        month?: undefined;
+        day?: undefined;
+        omitCommas?: undefined;
+    } | {
+        weekday: string;
+        month: string;
+        day: string;
+        omitCommas: boolean;
+    };
+    export function renderDateCell(dateMarker: DateMarker, dateProfile: DateProfile, datesRepDistinctDays: any, colCnt: any, colHeadFormat: any, context: ComponentContext, colspan?: any, otherAttrs?: any): string;
+}
+
+declare module '@fullcalendar/core/common/DaySeries' {
+    import DateProfileGenerator from '@fullcalendar/core/DateProfileGenerator';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    export interface DaySeriesSeg {
+        firstIndex: number;
+        lastIndex: number;
+        isStart: boolean;
+        isEnd: boolean;
+    }
+    export { DaySeries as default, DaySeries };
+    class DaySeries {
+        cnt: number;
+        dates: DateMarker[];
+        indices: number[];
+        constructor(range: DateRange, dateProfileGenerator: DateProfileGenerator);
+        sliceRange(range: DateRange): DaySeriesSeg | null;
+    }
+}
+
+declare module '@fullcalendar/core/interactions/event-interaction-state' {
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { Seg } from '@fullcalendar/core/component/DateComponent';
+    export interface EventInteractionState {
+        affectedEvents: EventStore;
+        mutatedEvents: EventStore;
+        isEvent: boolean;
+        origSeg: Seg | null;
+    }
+}
+
+declare module '@fullcalendar/core/component/event-rendering' {
+    import { EventDef, EventTuple, EventDefHash } from '@fullcalendar/core/structs/event';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { Seg } from '@fullcalendar/core/component/DateComponent';
+    import View from '@fullcalendar/core/View';
+    import { EventUi, EventUiHash } from '@fullcalendar/core/component/event-ui';
+    export interface EventRenderRange extends EventTuple {
+        ui: EventUi;
+        range: DateRange;
+        isStart: boolean;
+        isEnd: boolean;
+    }
+    export function sliceEventStore(eventStore: EventStore, eventUiBases: EventUiHash, framingRange: DateRange, nextDayThreshold?: Duration): {
+        bg: EventRenderRange[];
+        fg: EventRenderRange[];
+    };
+    export function hasBgRendering(def: EventDef): boolean;
+    export function filterSegsViaEls(view: View, segs: Seg[], isMirror: boolean): Seg[];
+    export function getElSeg(el: HTMLElement): Seg | null;
+    export function compileEventUis(eventDefs: EventDefHash, eventUiBases: EventUiHash): {
+        [key: string]: EventUi;
+    };
+    export function compileEventUi(eventDef: EventDef, eventUiBases: EventUiHash): EventUi;
+}
+
+declare module '@fullcalendar/core/common/DayTable' {
+    import DaySeries from '@fullcalendar/core/common/DaySeries';
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    import { Seg } from '@fullcalendar/core/component/DateComponent';
+    export interface DayTableSeg extends Seg {
+        row: number;
+        firstCol: number;
+        lastCol: number;
+    }
+    export interface DayTableCell {
+        date: DateMarker;
+        htmlAttrs?: string;
+    }
+    export { DayTable as default, DayTable };
+    class DayTable {
+        rowCnt: number;
+        colCnt: number;
+        cells: DayTableCell[][];
+        headerDates: DateMarker[];
+        constructor(daySeries: DaySeries, breakOnWeeks: boolean);
+        sliceRange(range: DateRange): DayTableSeg[];
+    }
+}
+
+declare module '@fullcalendar/core/common/slicing-utils' {
+    import { DateRange } from '@fullcalendar/core/datelib/date-range';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventUiHash } from '@fullcalendar/core/component/event-ui';
+    import { DateProfile } from '@fullcalendar/core/DateProfileGenerator';
+    import DateComponent, { Seg, EventSegUiInteractionState } from '@fullcalendar/core/component/DateComponent';
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    import { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    export interface SliceableProps {
+        dateSelection: DateSpan;
+        businessHours: EventStore;
+        eventStore: EventStore;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+        eventSelection: string;
+        eventUiBases: EventUiHash;
+    }
+    export interface SlicedProps<SegType extends Seg> {
+        dateSelectionSegs: SegType[];
+        businessHourSegs: SegType[];
+        fgEventSegs: SegType[];
+        bgEventSegs: SegType[];
+        eventDrag: EventSegUiInteractionState | null;
+        eventResize: EventSegUiInteractionState | null;
+        eventSelection: string;
+    }
+    export { Slicer as default, Slicer };
+    abstract class Slicer<SegType extends Seg, ExtraArgs extends any[] = []> {
+        abstract sliceRange(dateRange: DateRange, ...extraArgs: ExtraArgs): SegType[];
+        sliceProps(props: SliceableProps, dateProfile: DateProfile, nextDayThreshold: Duration | null, component: DateComponent<any>, // TODO: kill
+        ...extraArgs: ExtraArgs): SlicedProps<SegType>;
+        sliceNowDate(// does not memoize
+        date: DateMarker, component: DateComponent<any>, // TODO: kill
+        ...extraArgs: ExtraArgs): SegType[];
+    }
+}
+
+declare module '@fullcalendar/core/structs/event-mutation' {
+    import { Duration } from '@fullcalendar/core/datelib/duration';
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import { EventDef } from '@fullcalendar/core/structs/event';
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { EventUiHash } from '@fullcalendar/core/component/event-ui';
+    export interface EventMutation {
+        datesDelta?: Duration;
+        startDelta?: Duration;
+        endDelta?: Duration;
+        standardProps?: any;
+        extendedProps?: any;
+    }
+    export function applyMutationToEventStore(eventStore: EventStore, eventConfigBase: EventUiHash, mutation: EventMutation, calendar: Calendar): EventStore;
+    export type eventDefMutationApplier = (eventDef: EventDef, mutation: EventMutation, calendar: Calendar) => void;
+}
+
+declare module '@fullcalendar/core/validation' {
+    import { EventStore } from '@fullcalendar/core/structs/event-store';
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { DateSpan, DateSpanApi } from '@fullcalendar/core/structs/date-span';
+    import EventApi from '@fullcalendar/core/api/EventApi';
+    import { EventInput } from '@fullcalendar/core/structs/event';
+    import { EventInteractionState } from '@fullcalendar/core/interactions/event-interaction-state';
+    import { SplittableProps } from '@fullcalendar/core/component/event-splitting';
+    export type ConstraintInput = 'businessHours' | string | EventInput | EventInput[];
+    export type Constraint = 'businessHours' | string | EventStore | false;
+    export type OverlapFunc = ((stillEvent: EventApi, movingEvent: EventApi | null) => boolean);
+    export type AllowFunc = (span: DateSpanApi, movingEvent: EventApi | null) => boolean;
+    export type isPropsValidTester = (props: SplittableProps, calendar: Calendar) => boolean;
+    export function isInteractionValid(interaction: EventInteractionState, calendar: Calendar): boolean;
+    export function isDateSelectionValid(dateSelection: DateSpan, calendar: Calendar): boolean;
+    export function isPropsValid(state: SplittableProps, calendar: Calendar, dateSpanMeta?: {}, filterConfig?: any): boolean;
+    export function normalizeConstraint(input: ConstraintInput, calendar: Calendar): Constraint | null;
+}
+
+declare module '@fullcalendar/core/api/EventApi' {
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { EventDef, EventInstance } from '@fullcalendar/core/structs/event';
+    import { EventMutation } from '@fullcalendar/core/structs/event-mutation';
+    import { DateInput } from '@fullcalendar/core/datelib/env';
+    import { DurationInput } from '@fullcalendar/core/datelib/duration';
+    import { FormatterInput } from '@fullcalendar/core/datelib/formatting';
+    import EventSourceApi from '@fullcalendar/core/api/EventSourceApi';
+    export { EventApi as default, EventApi };
+    class EventApi {
+        _calendar: Calendar;
+        _def: EventDef;
+        _instance: EventInstance | null;
+        constructor(calendar: Calendar, def: EventDef, instance?: EventInstance);
+        setProp(name: string, val: string): void;
+        setExtendedProp(name: string, val: any): void;
+        setStart(startInput: DateInput, options?: {
+            granularity?: string;
+            maintainDuration?: boolean;
+        }): void;
+        setEnd(endInput: DateInput | null, options?: {
+            granularity?: string;
+        }): void;
+        setDates(startInput: DateInput, endInput: DateInput | null, options?: {
+            allDay?: boolean;
+            granularity?: string;
+        }): void;
+        moveStart(deltaInput: DurationInput): void;
+        moveEnd(deltaInput: DurationInput): void;
+        moveDates(deltaInput: DurationInput): void;
+        setAllDay(allDay: boolean, options?: {
+            maintainDuration?: boolean;
+        }): void;
+        formatRange(formatInput: FormatterInput): any;
+        mutate(mutation: EventMutation): void;
+        remove(): void;
+        readonly source: EventSourceApi | null;
+        readonly start: Date | null;
+        readonly end: Date | null;
+        readonly id: string;
+        readonly groupId: string;
+        readonly allDay: boolean;
+        readonly title: string;
+        readonly url: string;
+        readonly rendering: string;
+        readonly startEditable: boolean;
+        readonly durationEditable: boolean;
+        readonly constraint: any;
+        readonly overlap: any;
+        readonly allow: any;
+        readonly backgroundColor: string;
+        readonly borderColor: string;
+        readonly textColor: string;
+        readonly classNames: string[];
+        readonly extendedProps: any;
+    }
+}
+
+declare module '@fullcalendar/core/util/requestJson' {
+    export default function requestJson(method: string, url: string, params: object, successCallback: any, failureCallback: any): void;
+}
+
+declare module '@fullcalendar/core/datelib/locale' {
+    export type LocaleCodeArg = string | string[];
+    export type LocaleSingularArg = LocaleCodeArg | RawLocale;
+    export interface Locale {
+        codeArg: LocaleCodeArg;
+        codes: string[];
+        week: {
+            dow: number;
+            doy: number;
+        };
+        simpleNumberFormat: Intl.NumberFormat;
+        options: any;
+    }
+    export interface RawLocale {
+        code: string;
+        [otherProp: string]: any;
+    }
+    export type RawLocaleMap = {
+        [code: string]: RawLocale;
+    };
+    export interface RawLocaleInfo {
+        map: RawLocaleMap;
+        defaultCode: string;
+    }
+    export function parseRawLocales(explicitRawLocales: RawLocale[]): RawLocaleInfo;
+    export function buildLocale(inputSingular: LocaleSingularArg, available: RawLocaleMap): Locale;
+}
+
+declare module '@fullcalendar/core/OptionsManager' {
+    export { OptionsManager as default, OptionsManager };
+    class OptionsManager {
+        dirDefaults: any;
+        localeDefaults: any;
+        overrides: any;
+        dynamicOverrides: any;
+        computed: any;
+        constructor(overrides: any);
+        mutate(updates: any, removals: string[], isDynamic?: boolean): void;
+        compute(): void;
+    }
+}
+
+declare module '@fullcalendar/core/api/EventSourceApi' {
+    import Calendar from '@fullcalendar/core/Calendar';
+    import { EventSource } from '@fullcalendar/core/structs/event-source';
+    export { EventSourceApi as default, EventSourceApi };
+    class EventSourceApi {
+        calendar: Calendar;
+        internalEventSource: EventSource;
+        constructor(calendar: Calendar, internalEventSource: EventSource);
+        remove(): void;
+        refetch(): void;
+        readonly id: string;
+        readonly url: string;
+    }
+}
+
+declare module '@fullcalendar/core/structs/view-config' {
+    import View from '@fullcalendar/core/View';
+    import { ViewSpec } from '@fullcalendar/core/structs/view-spec';
+    import { ComponentContext } from '@fullcalendar/core/component/Component';
+    import DateProfileGenerator from '@fullcalendar/core/DateProfileGenerator';
+    export type ViewClass = new (context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement) => View;
+    export interface ViewConfigObjectInput {
+        type?: string;
+        class?: ViewClass;
+        [optionName: string]: any;
+    }
+    export type ViewConfigInput = ViewClass | ViewConfigObjectInput;
+    export type ViewConfigInputHash = {
+        [viewType: string]: ViewConfigInput;
+    };
+    export interface ViewConfig {
+        superType: string;
+        class: ViewClass | null;
+        options: any;
+    }
+    export type ViewConfigHash = {
+        [viewType: string]: ViewConfig;
+    };
+    export function parseViewConfigs(inputs: ViewConfigInputHash): ViewConfigHash;
+}
+
+declare module '@fullcalendar/core/datelib/calendar-system' {
+    import { DateMarker } from '@fullcalendar/core/datelib/marker';
+    export interface CalendarSystem {
+        getMarkerYear(d: DateMarker): number;
+        getMarkerMonth(d: DateMarker): number;
+        getMarkerDay(d: DateMarker): number;
+        arrayToMarker(arr: number[]): DateMarker;
+        markerToArray(d: DateMarker): number[];
+    }
+    export function registerCalendarSystem(name: any, theClass: any): void;
+    export function createCalendarSystem(name: any): any;
+}
+
+declare module '@fullcalendar/core/datelib/formatting-cmd' {
+    import { DateFormatter, DateFormattingContext, ZonedMarker, VerboseFormattingArg } from '@fullcalendar/core/datelib/formatting';
+    export type CmdFormatterFunc = (cmd: string, arg: VerboseFormattingArg) => string;
+    export class CmdFormatter implements DateFormatter {
+        cmdStr: string;
+        separator: string;
+        constructor(cmdStr: string, separator?: string);
+        format(date: ZonedMarker, context: DateFormattingContext): string;
+        formatRange(start: ZonedMarker, end: ZonedMarker, context: DateFormattingContext): string;
+    }
+}
+
+declare module '@fullcalendar/core/datelib/formatting-func' {
+    import { DateFormatter, DateFormattingContext, ZonedMarker, VerboseFormattingArg } from '@fullcalendar/core/datelib/formatting';
+    export type FuncFormatterFunc = (arg: VerboseFormattingArg) => string;
+    export class FuncFormatter implements DateFormatter {
+        func: FuncFormatterFunc;
+        constructor(func: FuncFormatterFunc);
+        format(date: ZonedMarker, context: DateFormattingContext): string;
+        formatRange(start: ZonedMarker, end: ZonedMarker, context: DateFormattingContext): string;
+    }
+}
+
+declare module '@fullcalendar/core/event-sources/func-event-source' {
+    import { EventSourceError } from '@fullcalendar/core/structs/event-source';
+    import { EventInput } from '@fullcalendar/core/structs/event';
+    export type EventSourceFunc = (arg: {
+        start: Date;
+        end: Date;
+        timeZone: string;
+    }, successCallback: (events: EventInput[]) => void, failureCallback: (error: EventSourceError) => void) => (void | PromiseLike<EventInput[]>);
+    const _default: import("@fullcalendar/core/plugin-system").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/core/interactions/external-element-dragging' {
+    import { DateSpan } from '@fullcalendar/core/structs/date-span';
+    import { DragMeta } from '@fullcalendar/core/structs/drag-meta';
+    export type ExternalDefTransform = (dateSpan: DateSpan, dragMeta: DragMeta) => any;
+}
+
+declare module '@fullcalendar/core/Toolbar' {
+    import Component, { ComponentContext } from '@fullcalendar/core/component/Component';
+    export interface ToolbarRenderProps {
+        layout: any;
+        title: string;
+        activeButton: string;
+        isTodayEnabled: boolean;
+        isPrevEnabled: boolean;
+        isNextEnabled: boolean;
+    }
+    export { Toolbar as default, Toolbar };
+    class Toolbar extends Component<ToolbarRenderProps> {
+        el: HTMLElement;
+        viewsWithButtons: any;
+        constructor(context: ComponentContext, extraClassName: any);
+        destroy(): void;
+        render(props: ToolbarRenderProps): void;
+        renderLayout(layout: any): void;
+        unrenderLayout(): void;
+        renderSection(position: any, buttonStr: any): HTMLElement;
+        updateToday(isTodayEnabled: any): void;
+        updatePrev(isPrevEnabled: any): void;
+        updateNext(isNextEnabled: any): void;
+        updateTitle(text: any): void;
+        updateActiveButton(buttonName?: any): void;
+        toggleButtonEnabled(buttonName: any, bool: any): void;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/core/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..b3d27d75214f1e2ba49dfb371ad9e479afc4c4e4
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/main.esm.js
@@ -0,0 +1,8558 @@
+/*!
+FullCalendar Core Package v4.3.1
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+// Creating
+// ----------------------------------------------------------------------------------------------------------------
+var elementPropHash = {
+    className: true,
+    colSpan: true,
+    rowSpan: true
+};
+var containerTagHash = {
+    '<tr': 'tbody',
+    '<td': 'tr'
+};
+function createElement(tagName, attrs, content) {
+    var el = document.createElement(tagName);
+    if (attrs) {
+        for (var attrName in attrs) {
+            if (attrName === 'style') {
+                applyStyle(el, attrs[attrName]);
+            }
+            else if (elementPropHash[attrName]) {
+                el[attrName] = attrs[attrName];
+            }
+            else {
+                el.setAttribute(attrName, attrs[attrName]);
+            }
+        }
+    }
+    if (typeof content === 'string') {
+        el.innerHTML = content; // shortcut. no need to process HTML in any way
+    }
+    else if (content != null) {
+        appendToElement(el, content);
+    }
+    return el;
+}
+function htmlToElement(html) {
+    html = html.trim();
+    var container = document.createElement(computeContainerTag(html));
+    container.innerHTML = html;
+    return container.firstChild;
+}
+function htmlToElements(html) {
+    return Array.prototype.slice.call(htmlToNodeList(html));
+}
+function htmlToNodeList(html) {
+    html = html.trim();
+    var container = document.createElement(computeContainerTag(html));
+    container.innerHTML = html;
+    return container.childNodes;
+}
+// assumes html already trimmed and tag names are lowercase
+function computeContainerTag(html) {
+    return containerTagHash[html.substr(0, 3) // faster than using regex
+    ] || 'div';
+}
+function appendToElement(el, content) {
+    var childNodes = normalizeContent(content);
+    for (var i = 0; i < childNodes.length; i++) {
+        el.appendChild(childNodes[i]);
+    }
+}
+function prependToElement(parent, content) {
+    var newEls = normalizeContent(content);
+    var afterEl = parent.firstChild || null; // if no firstChild, will append to end, but that's okay, b/c there were no children
+    for (var i = 0; i < newEls.length; i++) {
+        parent.insertBefore(newEls[i], afterEl);
+    }
+}
+function insertAfterElement(refEl, content) {
+    var newEls = normalizeContent(content);
+    var afterEl = refEl.nextSibling || null;
+    for (var i = 0; i < newEls.length; i++) {
+        refEl.parentNode.insertBefore(newEls[i], afterEl);
+    }
+}
+function normalizeContent(content) {
+    var els;
+    if (typeof content === 'string') {
+        els = htmlToElements(content);
+    }
+    else if (content instanceof Node) {
+        els = [content];
+    }
+    else { // Node[] or NodeList
+        els = Array.prototype.slice.call(content);
+    }
+    return els;
+}
+function removeElement(el) {
+    if (el.parentNode) {
+        el.parentNode.removeChild(el);
+    }
+}
+// Querying
+// ----------------------------------------------------------------------------------------------------------------
+// from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
+var matchesMethod = Element.prototype.matches ||
+    Element.prototype.matchesSelector ||
+    Element.prototype.msMatchesSelector;
+var closestMethod = Element.prototype.closest || function (selector) {
+    // polyfill
+    var el = this;
+    if (!document.documentElement.contains(el)) {
+        return null;
+    }
+    do {
+        if (elementMatches(el, selector)) {
+            return el;
+        }
+        el = el.parentElement || el.parentNode;
+    } while (el !== null && el.nodeType === 1);
+    return null;
+};
+function elementClosest(el, selector) {
+    return closestMethod.call(el, selector);
+}
+function elementMatches(el, selector) {
+    return matchesMethod.call(el, selector);
+}
+// accepts multiple subject els
+// returns a real array. good for methods like forEach
+function findElements(container, selector) {
+    var containers = container instanceof HTMLElement ? [container] : container;
+    var allMatches = [];
+    for (var i = 0; i < containers.length; i++) {
+        var matches = containers[i].querySelectorAll(selector);
+        for (var j = 0; j < matches.length; j++) {
+            allMatches.push(matches[j]);
+        }
+    }
+    return allMatches;
+}
+// accepts multiple subject els
+// only queries direct child elements
+function findChildren(parent, selector) {
+    var parents = parent instanceof HTMLElement ? [parent] : parent;
+    var allMatches = [];
+    for (var i = 0; i < parents.length; i++) {
+        var childNodes = parents[i].children; // only ever elements
+        for (var j = 0; j < childNodes.length; j++) {
+            var childNode = childNodes[j];
+            if (!selector || elementMatches(childNode, selector)) {
+                allMatches.push(childNode);
+            }
+        }
+    }
+    return allMatches;
+}
+// Attributes
+// ----------------------------------------------------------------------------------------------------------------
+function forceClassName(el, className, bool) {
+    if (bool) {
+        el.classList.add(className);
+    }
+    else {
+        el.classList.remove(className);
+    }
+}
+// Style
+// ----------------------------------------------------------------------------------------------------------------
+var PIXEL_PROP_RE = /(top|left|right|bottom|width|height)$/i;
+function applyStyle(el, props) {
+    for (var propName in props) {
+        applyStyleProp(el, propName, props[propName]);
+    }
+}
+function applyStyleProp(el, name, val) {
+    if (val == null) {
+        el.style[name] = '';
+    }
+    else if (typeof val === 'number' && PIXEL_PROP_RE.test(name)) {
+        el.style[name] = val + 'px';
+    }
+    else {
+        el.style[name] = val;
+    }
+}
+
+function pointInsideRect(point, rect) {
+    return point.left >= rect.left &&
+        point.left < rect.right &&
+        point.top >= rect.top &&
+        point.top < rect.bottom;
+}
+// Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
+function intersectRects(rect1, rect2) {
+    var res = {
+        left: Math.max(rect1.left, rect2.left),
+        right: Math.min(rect1.right, rect2.right),
+        top: Math.max(rect1.top, rect2.top),
+        bottom: Math.min(rect1.bottom, rect2.bottom)
+    };
+    if (res.left < res.right && res.top < res.bottom) {
+        return res;
+    }
+    return false;
+}
+function translateRect(rect, deltaX, deltaY) {
+    return {
+        left: rect.left + deltaX,
+        right: rect.right + deltaX,
+        top: rect.top + deltaY,
+        bottom: rect.bottom + deltaY
+    };
+}
+// Returns a new point that will have been moved to reside within the given rectangle
+function constrainPoint(point, rect) {
+    return {
+        left: Math.min(Math.max(point.left, rect.left), rect.right),
+        top: Math.min(Math.max(point.top, rect.top), rect.bottom)
+    };
+}
+// Returns a point that is the center of the given rectangle
+function getRectCenter(rect) {
+    return {
+        left: (rect.left + rect.right) / 2,
+        top: (rect.top + rect.bottom) / 2
+    };
+}
+// Subtracts point2's coordinates from point1's coordinates, returning a delta
+function diffPoints(point1, point2) {
+    return {
+        left: point1.left - point2.left,
+        top: point1.top - point2.top
+    };
+}
+
+// Logic for determining if, when the element is right-to-left, the scrollbar appears on the left side
+var isRtlScrollbarOnLeft = null;
+function getIsRtlScrollbarOnLeft() {
+    if (isRtlScrollbarOnLeft === null) {
+        isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
+    }
+    return isRtlScrollbarOnLeft;
+}
+function computeIsRtlScrollbarOnLeft() {
+    var outerEl = createElement('div', {
+        style: {
+            position: 'absolute',
+            top: -1000,
+            left: 0,
+            border: 0,
+            padding: 0,
+            overflow: 'scroll',
+            direction: 'rtl'
+        }
+    }, '<div></div>');
+    document.body.appendChild(outerEl);
+    var innerEl = outerEl.firstChild;
+    var res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
+    removeElement(outerEl);
+    return res;
+}
+// The scrollbar width computations in computeEdges are sometimes flawed when it comes to
+// retina displays, rounding, and IE11. Massage them into a usable value.
+function sanitizeScrollbarWidth(width) {
+    width = Math.max(0, width); // no negatives
+    width = Math.round(width);
+    return width;
+}
+
+function computeEdges(el, getPadding) {
+    if (getPadding === void 0) { getPadding = false; }
+    var computedStyle = window.getComputedStyle(el);
+    var borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
+    var borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
+    var borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
+    var borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
+    // must use offset(Width|Height) because compatible with client(Width|Height)
+    var scrollbarLeftRight = sanitizeScrollbarWidth(el.offsetWidth - el.clientWidth - borderLeft - borderRight);
+    var scrollbarBottom = sanitizeScrollbarWidth(el.offsetHeight - el.clientHeight - borderTop - borderBottom);
+    var res = {
+        borderLeft: borderLeft,
+        borderRight: borderRight,
+        borderTop: borderTop,
+        borderBottom: borderBottom,
+        scrollbarBottom: scrollbarBottom,
+        scrollbarLeft: 0,
+        scrollbarRight: 0
+    };
+    if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
+        res.scrollbarLeft = scrollbarLeftRight;
+    }
+    else {
+        res.scrollbarRight = scrollbarLeftRight;
+    }
+    if (getPadding) {
+        res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
+        res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
+        res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
+        res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
+    }
+    return res;
+}
+function computeInnerRect(el, goWithinPadding) {
+    if (goWithinPadding === void 0) { goWithinPadding = false; }
+    var outerRect = computeRect(el);
+    var edges = computeEdges(el, goWithinPadding);
+    var res = {
+        left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
+        right: outerRect.right - edges.borderRight - edges.scrollbarRight,
+        top: outerRect.top + edges.borderTop,
+        bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom
+    };
+    if (goWithinPadding) {
+        res.left += edges.paddingLeft;
+        res.right -= edges.paddingRight;
+        res.top += edges.paddingTop;
+        res.bottom -= edges.paddingBottom;
+    }
+    return res;
+}
+function computeRect(el) {
+    var rect = el.getBoundingClientRect();
+    return {
+        left: rect.left + window.pageXOffset,
+        top: rect.top + window.pageYOffset,
+        right: rect.right + window.pageXOffset,
+        bottom: rect.bottom + window.pageYOffset
+    };
+}
+function computeViewportRect() {
+    return {
+        left: window.pageXOffset,
+        right: window.pageXOffset + document.documentElement.clientWidth,
+        top: window.pageYOffset,
+        bottom: window.pageYOffset + document.documentElement.clientHeight
+    };
+}
+function computeHeightAndMargins(el) {
+    return el.getBoundingClientRect().height + computeVMargins(el);
+}
+function computeVMargins(el) {
+    var computed = window.getComputedStyle(el);
+    return parseInt(computed.marginTop, 10) +
+        parseInt(computed.marginBottom, 10);
+}
+// does not return window
+function getClippingParents(el) {
+    var parents = [];
+    while (el instanceof HTMLElement) { // will stop when gets to document or null
+        var computedStyle = window.getComputedStyle(el);
+        if (computedStyle.position === 'fixed') {
+            break;
+        }
+        if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
+            parents.push(el);
+        }
+        el = el.parentNode;
+    }
+    return parents;
+}
+function computeClippingRect(el) {
+    return getClippingParents(el)
+        .map(function (el) {
+        return computeInnerRect(el);
+    })
+        .concat(computeViewportRect())
+        .reduce(function (rect0, rect1) {
+        return intersectRects(rect0, rect1) || rect1; // should always intersect
+    });
+}
+
+// Stops a mouse/touch event from doing it's native browser action
+function preventDefault(ev) {
+    ev.preventDefault();
+}
+// Event Delegation
+// ----------------------------------------------------------------------------------------------------------------
+function listenBySelector(container, eventType, selector, handler) {
+    function realHandler(ev) {
+        var matchedChild = elementClosest(ev.target, selector);
+        if (matchedChild) {
+            handler.call(matchedChild, ev, matchedChild);
+        }
+    }
+    container.addEventListener(eventType, realHandler);
+    return function () {
+        container.removeEventListener(eventType, realHandler);
+    };
+}
+function listenToHoverBySelector(container, selector, onMouseEnter, onMouseLeave) {
+    var currentMatchedChild;
+    return listenBySelector(container, 'mouseover', selector, function (ev, matchedChild) {
+        if (matchedChild !== currentMatchedChild) {
+            currentMatchedChild = matchedChild;
+            onMouseEnter(ev, matchedChild);
+            var realOnMouseLeave_1 = function (ev) {
+                currentMatchedChild = null;
+                onMouseLeave(ev, matchedChild);
+                matchedChild.removeEventListener('mouseleave', realOnMouseLeave_1);
+            };
+            // listen to the next mouseleave, and then unattach
+            matchedChild.addEventListener('mouseleave', realOnMouseLeave_1);
+        }
+    });
+}
+// Animation
+// ----------------------------------------------------------------------------------------------------------------
+var transitionEventNames = [
+    'webkitTransitionEnd',
+    'otransitionend',
+    'oTransitionEnd',
+    'msTransitionEnd',
+    'transitionend'
+];
+// triggered only when the next single subsequent transition finishes
+function whenTransitionDone(el, callback) {
+    var realCallback = function (ev) {
+        callback(ev);
+        transitionEventNames.forEach(function (eventName) {
+            el.removeEventListener(eventName, realCallback);
+        });
+    };
+    transitionEventNames.forEach(function (eventName) {
+        el.addEventListener(eventName, realCallback); // cross-browser way to determine when the transition finishes
+    });
+}
+
+var DAY_IDS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
+// Adding
+function addWeeks(m, n) {
+    var a = dateToUtcArray(m);
+    a[2] += n * 7;
+    return arrayToUtcDate(a);
+}
+function addDays(m, n) {
+    var a = dateToUtcArray(m);
+    a[2] += n;
+    return arrayToUtcDate(a);
+}
+function addMs(m, n) {
+    var a = dateToUtcArray(m);
+    a[6] += n;
+    return arrayToUtcDate(a);
+}
+// Diffing (all return floats)
+function diffWeeks(m0, m1) {
+    return diffDays(m0, m1) / 7;
+}
+function diffDays(m0, m1) {
+    return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60 * 24);
+}
+function diffHours(m0, m1) {
+    return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60);
+}
+function diffMinutes(m0, m1) {
+    return (m1.valueOf() - m0.valueOf()) / (1000 * 60);
+}
+function diffSeconds(m0, m1) {
+    return (m1.valueOf() - m0.valueOf()) / 1000;
+}
+function diffDayAndTime(m0, m1) {
+    var m0day = startOfDay(m0);
+    var m1day = startOfDay(m1);
+    return {
+        years: 0,
+        months: 0,
+        days: Math.round(diffDays(m0day, m1day)),
+        milliseconds: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf())
+    };
+}
+// Diffing Whole Units
+function diffWholeWeeks(m0, m1) {
+    var d = diffWholeDays(m0, m1);
+    if (d !== null && d % 7 === 0) {
+        return d / 7;
+    }
+    return null;
+}
+function diffWholeDays(m0, m1) {
+    if (timeAsMs(m0) === timeAsMs(m1)) {
+        return Math.round(diffDays(m0, m1));
+    }
+    return null;
+}
+// Start-Of
+function startOfDay(m) {
+    return arrayToUtcDate([
+        m.getUTCFullYear(),
+        m.getUTCMonth(),
+        m.getUTCDate()
+    ]);
+}
+function startOfHour(m) {
+    return arrayToUtcDate([
+        m.getUTCFullYear(),
+        m.getUTCMonth(),
+        m.getUTCDate(),
+        m.getUTCHours()
+    ]);
+}
+function startOfMinute(m) {
+    return arrayToUtcDate([
+        m.getUTCFullYear(),
+        m.getUTCMonth(),
+        m.getUTCDate(),
+        m.getUTCHours(),
+        m.getUTCMinutes()
+    ]);
+}
+function startOfSecond(m) {
+    return arrayToUtcDate([
+        m.getUTCFullYear(),
+        m.getUTCMonth(),
+        m.getUTCDate(),
+        m.getUTCHours(),
+        m.getUTCMinutes(),
+        m.getUTCSeconds()
+    ]);
+}
+// Week Computation
+function weekOfYear(marker, dow, doy) {
+    var y = marker.getUTCFullYear();
+    var w = weekOfGivenYear(marker, y, dow, doy);
+    if (w < 1) {
+        return weekOfGivenYear(marker, y - 1, dow, doy);
+    }
+    var nextW = weekOfGivenYear(marker, y + 1, dow, doy);
+    if (nextW >= 1) {
+        return Math.min(w, nextW);
+    }
+    return w;
+}
+function weekOfGivenYear(marker, year, dow, doy) {
+    var firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]);
+    var dayStart = startOfDay(marker);
+    var days = Math.round(diffDays(firstWeekStart, dayStart));
+    return Math.floor(days / 7) + 1; // zero-indexed
+}
+// start-of-first-week - start-of-year
+function firstWeekOffset(year, dow, doy) {
+    // first-week day -- which january is always in the first week (4 for iso, 1 for other)
+    var fwd = 7 + dow - doy;
+    // first-week day local weekday -- which local weekday is fwd
+    var fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7;
+    return -fwdlw + fwd - 1;
+}
+// Array Conversion
+function dateToLocalArray(date) {
+    return [
+        date.getFullYear(),
+        date.getMonth(),
+        date.getDate(),
+        date.getHours(),
+        date.getMinutes(),
+        date.getSeconds(),
+        date.getMilliseconds()
+    ];
+}
+function arrayToLocalDate(a) {
+    return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month
+    a[3] || 0, a[4] || 0, a[5] || 0);
+}
+function dateToUtcArray(date) {
+    return [
+        date.getUTCFullYear(),
+        date.getUTCMonth(),
+        date.getUTCDate(),
+        date.getUTCHours(),
+        date.getUTCMinutes(),
+        date.getUTCSeconds(),
+        date.getUTCMilliseconds()
+    ];
+}
+function arrayToUtcDate(a) {
+    // according to web standards (and Safari), a month index is required.
+    // massage if only given a year.
+    if (a.length === 1) {
+        a = a.concat([0]);
+    }
+    return new Date(Date.UTC.apply(Date, a));
+}
+// Other Utils
+function isValidDate(m) {
+    return !isNaN(m.valueOf());
+}
+function timeAsMs(m) {
+    return m.getUTCHours() * 1000 * 60 * 60 +
+        m.getUTCMinutes() * 1000 * 60 +
+        m.getUTCSeconds() * 1000 +
+        m.getUTCMilliseconds();
+}
+
+var INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds'];
+var PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;
+// Parsing and Creation
+function createDuration(input, unit) {
+    var _a;
+    if (typeof input === 'string') {
+        return parseString(input);
+    }
+    else if (typeof input === 'object' && input) { // non-null object
+        return normalizeObject(input);
+    }
+    else if (typeof input === 'number') {
+        return normalizeObject((_a = {}, _a[unit || 'milliseconds'] = input, _a));
+    }
+    else {
+        return null;
+    }
+}
+function parseString(s) {
+    var m = PARSE_RE.exec(s);
+    if (m) {
+        var sign = m[1] ? -1 : 1;
+        return {
+            years: 0,
+            months: 0,
+            days: sign * (m[2] ? parseInt(m[2], 10) : 0),
+            milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours
+                (m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes
+                (m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds
+                (m[6] ? parseInt(m[6], 10) : 0) // ms
+            )
+        };
+    }
+    return null;
+}
+function normalizeObject(obj) {
+    return {
+        years: obj.years || obj.year || 0,
+        months: obj.months || obj.month || 0,
+        days: (obj.days || obj.day || 0) +
+            getWeeksFromInput(obj) * 7,
+        milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours
+            (obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes
+            (obj.seconds || obj.second || 0) * 1000 + // seconds
+            (obj.milliseconds || obj.millisecond || obj.ms || 0) // ms
+    };
+}
+function getWeeksFromInput(obj) {
+    return obj.weeks || obj.week || 0;
+}
+// Equality
+function durationsEqual(d0, d1) {
+    return d0.years === d1.years &&
+        d0.months === d1.months &&
+        d0.days === d1.days &&
+        d0.milliseconds === d1.milliseconds;
+}
+function isSingleDay(dur) {
+    return dur.years === 0 && dur.months === 0 && dur.days === 1 && dur.milliseconds === 0;
+}
+// Simple Math
+function addDurations(d0, d1) {
+    return {
+        years: d0.years + d1.years,
+        months: d0.months + d1.months,
+        days: d0.days + d1.days,
+        milliseconds: d0.milliseconds + d1.milliseconds
+    };
+}
+function subtractDurations(d1, d0) {
+    return {
+        years: d1.years - d0.years,
+        months: d1.months - d0.months,
+        days: d1.days - d0.days,
+        milliseconds: d1.milliseconds - d0.milliseconds
+    };
+}
+function multiplyDuration(d, n) {
+    return {
+        years: d.years * n,
+        months: d.months * n,
+        days: d.days * n,
+        milliseconds: d.milliseconds * n
+    };
+}
+// Conversions
+// "Rough" because they are based on average-case Gregorian months/years
+function asRoughYears(dur) {
+    return asRoughDays(dur) / 365;
+}
+function asRoughMonths(dur) {
+    return asRoughDays(dur) / 30;
+}
+function asRoughDays(dur) {
+    return asRoughMs(dur) / 864e5;
+}
+function asRoughMinutes(dur) {
+    return asRoughMs(dur) / (1000 * 60);
+}
+function asRoughSeconds(dur) {
+    return asRoughMs(dur) / 1000;
+}
+function asRoughMs(dur) {
+    return dur.years * (365 * 864e5) +
+        dur.months * (30 * 864e5) +
+        dur.days * 864e5 +
+        dur.milliseconds;
+}
+// Advanced Math
+function wholeDivideDurations(numerator, denominator) {
+    var res = null;
+    for (var i = 0; i < INTERNAL_UNITS.length; i++) {
+        var unit = INTERNAL_UNITS[i];
+        if (denominator[unit]) {
+            var localRes = numerator[unit] / denominator[unit];
+            if (!isInt(localRes) || (res !== null && res !== localRes)) {
+                return null;
+            }
+            res = localRes;
+        }
+        else if (numerator[unit]) {
+            // needs to divide by something but can't!
+            return null;
+        }
+    }
+    return res;
+}
+function greatestDurationDenominator(dur, dontReturnWeeks) {
+    var ms = dur.milliseconds;
+    if (ms) {
+        if (ms % 1000 !== 0) {
+            return { unit: 'millisecond', value: ms };
+        }
+        if (ms % (1000 * 60) !== 0) {
+            return { unit: 'second', value: ms / 1000 };
+        }
+        if (ms % (1000 * 60 * 60) !== 0) {
+            return { unit: 'minute', value: ms / (1000 * 60) };
+        }
+        if (ms) {
+            return { unit: 'hour', value: ms / (1000 * 60 * 60) };
+        }
+    }
+    if (dur.days) {
+        if (!dontReturnWeeks && dur.days % 7 === 0) {
+            return { unit: 'week', value: dur.days / 7 };
+        }
+        return { unit: 'day', value: dur.days };
+    }
+    if (dur.months) {
+        return { unit: 'month', value: dur.months };
+    }
+    if (dur.years) {
+        return { unit: 'year', value: dur.years };
+    }
+    return { unit: 'millisecond', value: 0 };
+}
+
+/* FullCalendar-specific DOM Utilities
+----------------------------------------------------------------------------------------------------------------------*/
+// Given the scrollbar widths of some other container, create borders/margins on rowEls in order to match the left
+// and right space that was offset by the scrollbars. A 1-pixel border first, then margin beyond that.
+function compensateScroll(rowEl, scrollbarWidths) {
+    if (scrollbarWidths.left) {
+        applyStyle(rowEl, {
+            borderLeftWidth: 1,
+            marginLeft: scrollbarWidths.left - 1
+        });
+    }
+    if (scrollbarWidths.right) {
+        applyStyle(rowEl, {
+            borderRightWidth: 1,
+            marginRight: scrollbarWidths.right - 1
+        });
+    }
+}
+// Undoes compensateScroll and restores all borders/margins
+function uncompensateScroll(rowEl) {
+    applyStyle(rowEl, {
+        marginLeft: '',
+        marginRight: '',
+        borderLeftWidth: '',
+        borderRightWidth: ''
+    });
+}
+// Make the mouse cursor express that an event is not allowed in the current area
+function disableCursor() {
+    document.body.classList.add('fc-not-allowed');
+}
+// Returns the mouse cursor to its original look
+function enableCursor() {
+    document.body.classList.remove('fc-not-allowed');
+}
+// Given a total available height to fill, have `els` (essentially child rows) expand to accomodate.
+// By default, all elements that are shorter than the recommended height are expanded uniformly, not considering
+// any other els that are already too tall. if `shouldRedistribute` is on, it considers these tall rows and
+// reduces the available height.
+function distributeHeight(els, availableHeight, shouldRedistribute) {
+    // *FLOORING NOTE*: we floor in certain places because zoom can give inaccurate floating-point dimensions,
+    // and it is better to be shorter than taller, to avoid creating unnecessary scrollbars.
+    var minOffset1 = Math.floor(availableHeight / els.length); // for non-last element
+    var minOffset2 = Math.floor(availableHeight - minOffset1 * (els.length - 1)); // for last element *FLOORING NOTE*
+    var flexEls = []; // elements that are allowed to expand. array of DOM nodes
+    var flexOffsets = []; // amount of vertical space it takes up
+    var flexHeights = []; // actual css height
+    var usedHeight = 0;
+    undistributeHeight(els); // give all elements their natural height
+    // find elements that are below the recommended height (expandable).
+    // important to query for heights in a single first pass (to avoid reflow oscillation).
+    els.forEach(function (el, i) {
+        var minOffset = i === els.length - 1 ? minOffset2 : minOffset1;
+        var naturalHeight = el.getBoundingClientRect().height;
+        var naturalOffset = naturalHeight + computeVMargins(el);
+        if (naturalOffset < minOffset) {
+            flexEls.push(el);
+            flexOffsets.push(naturalOffset);
+            flexHeights.push(naturalHeight);
+        }
+        else {
+            // this element stretches past recommended height (non-expandable). mark the space as occupied.
+            usedHeight += naturalOffset;
+        }
+    });
+    // readjust the recommended height to only consider the height available to non-maxed-out rows.
+    if (shouldRedistribute) {
+        availableHeight -= usedHeight;
+        minOffset1 = Math.floor(availableHeight / flexEls.length);
+        minOffset2 = Math.floor(availableHeight - minOffset1 * (flexEls.length - 1)); // *FLOORING NOTE*
+    }
+    // assign heights to all expandable elements
+    flexEls.forEach(function (el, i) {
+        var minOffset = i === flexEls.length - 1 ? minOffset2 : minOffset1;
+        var naturalOffset = flexOffsets[i];
+        var naturalHeight = flexHeights[i];
+        var newHeight = minOffset - (naturalOffset - naturalHeight); // subtract the margin/padding
+        if (naturalOffset < minOffset) { // we check this again because redistribution might have changed things
+            el.style.height = newHeight + 'px';
+        }
+    });
+}
+// Undoes distrubuteHeight, restoring all els to their natural height
+function undistributeHeight(els) {
+    els.forEach(function (el) {
+        el.style.height = '';
+    });
+}
+// Given `els`, a set of <td> cells, find the cell with the largest natural width and set the widths of all the
+// cells to be that width.
+// PREREQUISITE: if you want a cell to take up width, it needs to have a single inner element w/ display:inline
+function matchCellWidths(els) {
+    var maxInnerWidth = 0;
+    els.forEach(function (el) {
+        var innerEl = el.firstChild; // hopefully an element
+        if (innerEl instanceof HTMLElement) {
+            var innerWidth_1 = innerEl.getBoundingClientRect().width;
+            if (innerWidth_1 > maxInnerWidth) {
+                maxInnerWidth = innerWidth_1;
+            }
+        }
+    });
+    maxInnerWidth++; // sometimes not accurate of width the text needs to stay on one line. insurance
+    els.forEach(function (el) {
+        el.style.width = maxInnerWidth + 'px';
+    });
+    return maxInnerWidth;
+}
+// Given one element that resides inside another,
+// Subtracts the height of the inner element from the outer element.
+function subtractInnerElHeight(outerEl, innerEl) {
+    // effin' IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked
+    var reflowStyleProps = {
+        position: 'relative',
+        left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll
+    };
+    applyStyle(outerEl, reflowStyleProps);
+    applyStyle(innerEl, reflowStyleProps);
+    var diff = // grab the dimensions
+     outerEl.getBoundingClientRect().height -
+        innerEl.getBoundingClientRect().height;
+    // undo hack
+    var resetStyleProps = { position: '', left: '' };
+    applyStyle(outerEl, resetStyleProps);
+    applyStyle(innerEl, resetStyleProps);
+    return diff;
+}
+/* Selection
+----------------------------------------------------------------------------------------------------------------------*/
+function preventSelection(el) {
+    el.classList.add('fc-unselectable');
+    el.addEventListener('selectstart', preventDefault);
+}
+function allowSelection(el) {
+    el.classList.remove('fc-unselectable');
+    el.removeEventListener('selectstart', preventDefault);
+}
+/* Context Menu
+----------------------------------------------------------------------------------------------------------------------*/
+function preventContextMenu(el) {
+    el.addEventListener('contextmenu', preventDefault);
+}
+function allowContextMenu(el) {
+    el.removeEventListener('contextmenu', preventDefault);
+}
+/* Object Ordering by Field
+----------------------------------------------------------------------------------------------------------------------*/
+function parseFieldSpecs(input) {
+    var specs = [];
+    var tokens = [];
+    var i;
+    var token;
+    if (typeof input === 'string') {
+        tokens = input.split(/\s*,\s*/);
+    }
+    else if (typeof input === 'function') {
+        tokens = [input];
+    }
+    else if (Array.isArray(input)) {
+        tokens = input;
+    }
+    for (i = 0; i < tokens.length; i++) {
+        token = tokens[i];
+        if (typeof token === 'string') {
+            specs.push(token.charAt(0) === '-' ?
+                { field: token.substring(1), order: -1 } :
+                { field: token, order: 1 });
+        }
+        else if (typeof token === 'function') {
+            specs.push({ func: token });
+        }
+    }
+    return specs;
+}
+function compareByFieldSpecs(obj0, obj1, fieldSpecs) {
+    var i;
+    var cmp;
+    for (i = 0; i < fieldSpecs.length; i++) {
+        cmp = compareByFieldSpec(obj0, obj1, fieldSpecs[i]);
+        if (cmp) {
+            return cmp;
+        }
+    }
+    return 0;
+}
+function compareByFieldSpec(obj0, obj1, fieldSpec) {
+    if (fieldSpec.func) {
+        return fieldSpec.func(obj0, obj1);
+    }
+    return flexibleCompare(obj0[fieldSpec.field], obj1[fieldSpec.field])
+        * (fieldSpec.order || 1);
+}
+function flexibleCompare(a, b) {
+    if (!a && !b) {
+        return 0;
+    }
+    if (b == null) {
+        return -1;
+    }
+    if (a == null) {
+        return 1;
+    }
+    if (typeof a === 'string' || typeof b === 'string') {
+        return String(a).localeCompare(String(b));
+    }
+    return a - b;
+}
+/* String Utilities
+----------------------------------------------------------------------------------------------------------------------*/
+function capitaliseFirstLetter(str) {
+    return str.charAt(0).toUpperCase() + str.slice(1);
+}
+function padStart(val, len) {
+    var s = String(val);
+    return '000'.substr(0, len - s.length) + s;
+}
+/* Number Utilities
+----------------------------------------------------------------------------------------------------------------------*/
+function compareNumbers(a, b) {
+    return a - b;
+}
+function isInt(n) {
+    return n % 1 === 0;
+}
+/* Weird Utilities
+----------------------------------------------------------------------------------------------------------------------*/
+function applyAll(functions, thisObj, args) {
+    if (typeof functions === 'function') { // supplied a single function
+        functions = [functions];
+    }
+    if (functions) {
+        var i = void 0;
+        var ret = void 0;
+        for (i = 0; i < functions.length; i++) {
+            ret = functions[i].apply(thisObj, args) || ret;
+        }
+        return ret;
+    }
+}
+function firstDefined() {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        if (args[i] !== undefined) {
+            return args[i];
+        }
+    }
+}
+// Returns a function, that, as long as it continues to be invoked, will not
+// be triggered. The function will be called after it stops being called for
+// N milliseconds. If `immediate` is passed, trigger the function on the
+// leading edge, instead of the trailing.
+// https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714
+function debounce(func, wait) {
+    var timeout;
+    var args;
+    var context;
+    var timestamp;
+    var result;
+    var later = function () {
+        var last = new Date().valueOf() - timestamp;
+        if (last < wait) {
+            timeout = setTimeout(later, wait - last);
+        }
+        else {
+            timeout = null;
+            result = func.apply(context, args);
+            context = args = null;
+        }
+    };
+    return function () {
+        context = this;
+        args = arguments;
+        timestamp = new Date().valueOf();
+        if (!timeout) {
+            timeout = setTimeout(later, wait);
+        }
+        return result;
+    };
+}
+// Number and Boolean are only types that defaults or not computed for
+// TODO: write more comments
+function refineProps(rawProps, processors, defaults, leftoverProps) {
+    if (defaults === void 0) { defaults = {}; }
+    var refined = {};
+    for (var key in processors) {
+        var processor = processors[key];
+        if (rawProps[key] !== undefined) {
+            // found
+            if (processor === Function) {
+                refined[key] = typeof rawProps[key] === 'function' ? rawProps[key] : null;
+            }
+            else if (processor) { // a refining function?
+                refined[key] = processor(rawProps[key]);
+            }
+            else {
+                refined[key] = rawProps[key];
+            }
+        }
+        else if (defaults[key] !== undefined) {
+            // there's an explicit default
+            refined[key] = defaults[key];
+        }
+        else {
+            // must compute a default
+            if (processor === String) {
+                refined[key] = ''; // empty string is default for String
+            }
+            else if (!processor || processor === Number || processor === Boolean || processor === Function) {
+                refined[key] = null; // assign null for other non-custom processor funcs
+            }
+            else {
+                refined[key] = processor(null); // run the custom processor func
+            }
+        }
+    }
+    if (leftoverProps) {
+        for (var key in rawProps) {
+            if (processors[key] === undefined) {
+                leftoverProps[key] = rawProps[key];
+            }
+        }
+    }
+    return refined;
+}
+/* Date stuff that doesn't belong in datelib core
+----------------------------------------------------------------------------------------------------------------------*/
+// given a timed range, computes an all-day range that has the same exact duration,
+// but whose start time is aligned with the start of the day.
+function computeAlignedDayRange(timedRange) {
+    var dayCnt = Math.floor(diffDays(timedRange.start, timedRange.end)) || 1;
+    var start = startOfDay(timedRange.start);
+    var end = addDays(start, dayCnt);
+    return { start: start, end: end };
+}
+// given a timed range, computes an all-day range based on how for the end date bleeds into the next day
+// TODO: give nextDayThreshold a default arg
+function computeVisibleDayRange(timedRange, nextDayThreshold) {
+    if (nextDayThreshold === void 0) { nextDayThreshold = createDuration(0); }
+    var startDay = null;
+    var endDay = null;
+    if (timedRange.end) {
+        endDay = startOfDay(timedRange.end);
+        var endTimeMS = timedRange.end.valueOf() - endDay.valueOf(); // # of milliseconds into `endDay`
+        // If the end time is actually inclusively part of the next day and is equal to or
+        // beyond the next day threshold, adjust the end to be the exclusive end of `endDay`.
+        // Otherwise, leaving it as inclusive will cause it to exclude `endDay`.
+        if (endTimeMS && endTimeMS >= asRoughMs(nextDayThreshold)) {
+            endDay = addDays(endDay, 1);
+        }
+    }
+    if (timedRange.start) {
+        startDay = startOfDay(timedRange.start); // the beginning of the day the range starts
+        // If end is within `startDay` but not past nextDayThreshold, assign the default duration of one day.
+        if (endDay && endDay <= startDay) {
+            endDay = addDays(startDay, 1);
+        }
+    }
+    return { start: startDay, end: endDay };
+}
+// spans from one day into another?
+function isMultiDayRange(range) {
+    var visibleRange = computeVisibleDayRange(range);
+    return diffDays(visibleRange.start, visibleRange.end) > 1;
+}
+function diffDates(date0, date1, dateEnv, largeUnit) {
+    if (largeUnit === 'year') {
+        return createDuration(dateEnv.diffWholeYears(date0, date1), 'year');
+    }
+    else if (largeUnit === 'month') {
+        return createDuration(dateEnv.diffWholeMonths(date0, date1), 'month');
+    }
+    else {
+        return diffDayAndTime(date0, date1); // returns a duration
+    }
+}
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+function parseRecurring(eventInput, allDayDefault, dateEnv, recurringTypes, leftovers) {
+    for (var i = 0; i < recurringTypes.length; i++) {
+        var localLeftovers = {};
+        var parsed = recurringTypes[i].parse(eventInput, localLeftovers, dateEnv);
+        if (parsed) {
+            var allDay = localLeftovers.allDay;
+            delete localLeftovers.allDay; // remove from leftovers
+            if (allDay == null) {
+                allDay = allDayDefault;
+                if (allDay == null) {
+                    allDay = parsed.allDayGuess;
+                    if (allDay == null) {
+                        allDay = false;
+                    }
+                }
+            }
+            __assign(leftovers, localLeftovers);
+            return {
+                allDay: allDay,
+                duration: parsed.duration,
+                typeData: parsed.typeData,
+                typeId: i
+            };
+        }
+    }
+    return null;
+}
+/*
+Event MUST have a recurringDef
+*/
+function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) {
+    var typeDef = recurringTypes[eventDef.recurringDef.typeId];
+    var markers = typeDef.expand(eventDef.recurringDef.typeData, {
+        start: dateEnv.subtract(framingRange.start, duration),
+        end: framingRange.end
+    }, dateEnv);
+    // the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to
+    if (eventDef.allDay) {
+        markers = markers.map(startOfDay);
+    }
+    return markers;
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+// Merges an array of objects into a single object.
+// The second argument allows for an array of property names who's object values will be merged together.
+function mergeProps(propObjs, complexProps) {
+    var dest = {};
+    var i;
+    var name;
+    var complexObjs;
+    var j;
+    var val;
+    var props;
+    if (complexProps) {
+        for (i = 0; i < complexProps.length; i++) {
+            name = complexProps[i];
+            complexObjs = [];
+            // collect the trailing object values, stopping when a non-object is discovered
+            for (j = propObjs.length - 1; j >= 0; j--) {
+                val = propObjs[j][name];
+                if (typeof val === 'object' && val) { // non-null object
+                    complexObjs.unshift(val);
+                }
+                else if (val !== undefined) {
+                    dest[name] = val; // if there were no objects, this value will be used
+                    break;
+                }
+            }
+            // if the trailing values were objects, use the merged value
+            if (complexObjs.length) {
+                dest[name] = mergeProps(complexObjs);
+            }
+        }
+    }
+    // copy values into the destination, going from last to first
+    for (i = propObjs.length - 1; i >= 0; i--) {
+        props = propObjs[i];
+        for (name in props) {
+            if (!(name in dest)) { // if already assigned by previous props or complex props, don't reassign
+                dest[name] = props[name];
+            }
+        }
+    }
+    return dest;
+}
+function filterHash(hash, func) {
+    var filtered = {};
+    for (var key in hash) {
+        if (func(hash[key], key)) {
+            filtered[key] = hash[key];
+        }
+    }
+    return filtered;
+}
+function mapHash(hash, func) {
+    var newHash = {};
+    for (var key in hash) {
+        newHash[key] = func(hash[key], key);
+    }
+    return newHash;
+}
+function arrayToHash(a) {
+    var hash = {};
+    for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
+        var item = a_1[_i];
+        hash[item] = true;
+    }
+    return hash;
+}
+function hashValuesToArray(obj) {
+    var a = [];
+    for (var key in obj) {
+        a.push(obj[key]);
+    }
+    return a;
+}
+function isPropsEqual(obj0, obj1) {
+    for (var key in obj0) {
+        if (hasOwnProperty.call(obj0, key)) {
+            if (!(key in obj1)) {
+                return false;
+            }
+        }
+    }
+    for (var key in obj1) {
+        if (hasOwnProperty.call(obj1, key)) {
+            if (obj0[key] !== obj1[key]) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+function parseEvents(rawEvents, sourceId, calendar, allowOpenRange) {
+    var eventStore = createEmptyEventStore();
+    for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) {
+        var rawEvent = rawEvents_1[_i];
+        var tuple = parseEvent(rawEvent, sourceId, calendar, allowOpenRange);
+        if (tuple) {
+            eventTupleToStore(tuple, eventStore);
+        }
+    }
+    return eventStore;
+}
+function eventTupleToStore(tuple, eventStore) {
+    if (eventStore === void 0) { eventStore = createEmptyEventStore(); }
+    eventStore.defs[tuple.def.defId] = tuple.def;
+    if (tuple.instance) {
+        eventStore.instances[tuple.instance.instanceId] = tuple.instance;
+    }
+    return eventStore;
+}
+function expandRecurring(eventStore, framingRange, calendar) {
+    var dateEnv = calendar.dateEnv;
+    var defs = eventStore.defs, instances = eventStore.instances;
+    // remove existing recurring instances
+    instances = filterHash(instances, function (instance) {
+        return !defs[instance.defId].recurringDef;
+    });
+    for (var defId in defs) {
+        var def = defs[defId];
+        if (def.recurringDef) {
+            var duration = def.recurringDef.duration;
+            if (!duration) {
+                duration = def.allDay ?
+                    calendar.defaultAllDayEventDuration :
+                    calendar.defaultTimedEventDuration;
+            }
+            var starts = expandRecurringRanges(def, duration, framingRange, calendar.dateEnv, calendar.pluginSystem.hooks.recurringTypes);
+            for (var _i = 0, starts_1 = starts; _i < starts_1.length; _i++) {
+                var start = starts_1[_i];
+                var instance = createEventInstance(defId, {
+                    start: start,
+                    end: dateEnv.add(start, duration)
+                });
+                instances[instance.instanceId] = instance;
+            }
+        }
+    }
+    return { defs: defs, instances: instances };
+}
+// retrieves events that have the same groupId as the instance specified by `instanceId`
+// or they are the same as the instance.
+// why might instanceId not be in the store? an event from another calendar?
+function getRelevantEvents(eventStore, instanceId) {
+    var instance = eventStore.instances[instanceId];
+    if (instance) {
+        var def_1 = eventStore.defs[instance.defId];
+        // get events/instances with same group
+        var newStore = filterEventStoreDefs(eventStore, function (lookDef) {
+            return isEventDefsGrouped(def_1, lookDef);
+        });
+        // add the original
+        // TODO: wish we could use eventTupleToStore or something like it
+        newStore.defs[def_1.defId] = def_1;
+        newStore.instances[instance.instanceId] = instance;
+        return newStore;
+    }
+    return createEmptyEventStore();
+}
+function isEventDefsGrouped(def0, def1) {
+    return Boolean(def0.groupId && def0.groupId === def1.groupId);
+}
+function transformRawEvents(rawEvents, eventSource, calendar) {
+    var calEachTransform = calendar.opt('eventDataTransform');
+    var sourceEachTransform = eventSource ? eventSource.eventDataTransform : null;
+    if (sourceEachTransform) {
+        rawEvents = transformEachRawEvent(rawEvents, sourceEachTransform);
+    }
+    if (calEachTransform) {
+        rawEvents = transformEachRawEvent(rawEvents, calEachTransform);
+    }
+    return rawEvents;
+}
+function transformEachRawEvent(rawEvents, func) {
+    var refinedEvents;
+    if (!func) {
+        refinedEvents = rawEvents;
+    }
+    else {
+        refinedEvents = [];
+        for (var _i = 0, rawEvents_2 = rawEvents; _i < rawEvents_2.length; _i++) {
+            var rawEvent = rawEvents_2[_i];
+            var refinedEvent = func(rawEvent);
+            if (refinedEvent) {
+                refinedEvents.push(refinedEvent);
+            }
+            else if (refinedEvent == null) {
+                refinedEvents.push(rawEvent);
+            } // if a different falsy value, do nothing
+        }
+    }
+    return refinedEvents;
+}
+function createEmptyEventStore() {
+    return { defs: {}, instances: {} };
+}
+function mergeEventStores(store0, store1) {
+    return {
+        defs: __assign({}, store0.defs, store1.defs),
+        instances: __assign({}, store0.instances, store1.instances)
+    };
+}
+function filterEventStoreDefs(eventStore, filterFunc) {
+    var defs = filterHash(eventStore.defs, filterFunc);
+    var instances = filterHash(eventStore.instances, function (instance) {
+        return defs[instance.defId]; // still exists?
+    });
+    return { defs: defs, instances: instances };
+}
+
+function parseRange(input, dateEnv) {
+    var start = null;
+    var end = null;
+    if (input.start) {
+        start = dateEnv.createMarker(input.start);
+    }
+    if (input.end) {
+        end = dateEnv.createMarker(input.end);
+    }
+    if (!start && !end) {
+        return null;
+    }
+    if (start && end && end < start) {
+        return null;
+    }
+    return { start: start, end: end };
+}
+// SIDE-EFFECT: will mutate ranges.
+// Will return a new array result.
+function invertRanges(ranges, constraintRange) {
+    var invertedRanges = [];
+    var start = constraintRange.start; // the end of the previous range. the start of the new range
+    var i;
+    var dateRange;
+    // ranges need to be in order. required for our date-walking algorithm
+    ranges.sort(compareRanges);
+    for (i = 0; i < ranges.length; i++) {
+        dateRange = ranges[i];
+        // add the span of time before the event (if there is any)
+        if (dateRange.start > start) { // compare millisecond time (skip any ambig logic)
+            invertedRanges.push({ start: start, end: dateRange.start });
+        }
+        if (dateRange.end > start) {
+            start = dateRange.end;
+        }
+    }
+    // add the span of time after the last event (if there is any)
+    if (start < constraintRange.end) { // compare millisecond time (skip any ambig logic)
+        invertedRanges.push({ start: start, end: constraintRange.end });
+    }
+    return invertedRanges;
+}
+function compareRanges(range0, range1) {
+    return range0.start.valueOf() - range1.start.valueOf(); // earlier ranges go first
+}
+function intersectRanges(range0, range1) {
+    var start = range0.start;
+    var end = range0.end;
+    var newRange = null;
+    if (range1.start !== null) {
+        if (start === null) {
+            start = range1.start;
+        }
+        else {
+            start = new Date(Math.max(start.valueOf(), range1.start.valueOf()));
+        }
+    }
+    if (range1.end != null) {
+        if (end === null) {
+            end = range1.end;
+        }
+        else {
+            end = new Date(Math.min(end.valueOf(), range1.end.valueOf()));
+        }
+    }
+    if (start === null || end === null || start < end) {
+        newRange = { start: start, end: end };
+    }
+    return newRange;
+}
+function rangesEqual(range0, range1) {
+    return (range0.start === null ? null : range0.start.valueOf()) === (range1.start === null ? null : range1.start.valueOf()) &&
+        (range0.end === null ? null : range0.end.valueOf()) === (range1.end === null ? null : range1.end.valueOf());
+}
+function rangesIntersect(range0, range1) {
+    return (range0.end === null || range1.start === null || range0.end > range1.start) &&
+        (range0.start === null || range1.end === null || range0.start < range1.end);
+}
+function rangeContainsRange(outerRange, innerRange) {
+    return (outerRange.start === null || (innerRange.start !== null && innerRange.start >= outerRange.start)) &&
+        (outerRange.end === null || (innerRange.end !== null && innerRange.end <= outerRange.end));
+}
+function rangeContainsMarker(range, date) {
+    return (range.start === null || date >= range.start) &&
+        (range.end === null || date < range.end);
+}
+// If the given date is not within the given range, move it inside.
+// (If it's past the end, make it one millisecond before the end).
+function constrainMarkerToRange(date, range) {
+    if (range.start != null && date < range.start) {
+        return range.start;
+    }
+    if (range.end != null && date >= range.end) {
+        return new Date(range.end.valueOf() - 1);
+    }
+    return date;
+}
+
+function removeExact(array, exactVal) {
+    var removeCnt = 0;
+    var i = 0;
+    while (i < array.length) {
+        if (array[i] === exactVal) {
+            array.splice(i, 1);
+            removeCnt++;
+        }
+        else {
+            i++;
+        }
+    }
+    return removeCnt;
+}
+function isArraysEqual(a0, a1) {
+    var len = a0.length;
+    var i;
+    if (len !== a1.length) { // not array? or not same length?
+        return false;
+    }
+    for (i = 0; i < len; i++) {
+        if (a0[i] !== a1[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+function memoize(workerFunc) {
+    var args;
+    var res;
+    return function () {
+        if (!args || !isArraysEqual(args, arguments)) {
+            args = arguments;
+            res = workerFunc.apply(this, arguments);
+        }
+        return res;
+    };
+}
+/*
+always executes the workerFunc, but if the result is equal to the previous result,
+return the previous result instead.
+*/
+function memoizeOutput(workerFunc, equalityFunc) {
+    var cachedRes = null;
+    return function () {
+        var newRes = workerFunc.apply(this, arguments);
+        if (cachedRes === null || !(cachedRes === newRes || equalityFunc(cachedRes, newRes))) {
+            cachedRes = newRes;
+        }
+        return cachedRes;
+    };
+}
+
+var EXTENDED_SETTINGS_AND_SEVERITIES = {
+    week: 3,
+    separator: 0,
+    omitZeroMinute: 0,
+    meridiem: 0,
+    omitCommas: 0
+};
+var STANDARD_DATE_PROP_SEVERITIES = {
+    timeZoneName: 7,
+    era: 6,
+    year: 5,
+    month: 4,
+    day: 2,
+    weekday: 2,
+    hour: 1,
+    minute: 1,
+    second: 1
+};
+var MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too
+var COMMA_RE = /,/g; // we need re for globalness
+var MULTI_SPACE_RE = /\s+/g;
+var LTR_RE = /\u200e/g; // control character
+var UTC_RE = /UTC|GMT/;
+var NativeFormatter = /** @class */ (function () {
+    function NativeFormatter(formatSettings) {
+        var standardDateProps = {};
+        var extendedSettings = {};
+        var severity = 0;
+        for (var name_1 in formatSettings) {
+            if (name_1 in EXTENDED_SETTINGS_AND_SEVERITIES) {
+                extendedSettings[name_1] = formatSettings[name_1];
+                severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name_1], severity);
+            }
+            else {
+                standardDateProps[name_1] = formatSettings[name_1];
+                if (name_1 in STANDARD_DATE_PROP_SEVERITIES) {
+                    severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name_1], severity);
+                }
+            }
+        }
+        this.standardDateProps = standardDateProps;
+        this.extendedSettings = extendedSettings;
+        this.severity = severity;
+        this.buildFormattingFunc = memoize(buildFormattingFunc);
+    }
+    NativeFormatter.prototype.format = function (date, context) {
+        return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date);
+    };
+    NativeFormatter.prototype.formatRange = function (start, end, context) {
+        var _a = this, standardDateProps = _a.standardDateProps, extendedSettings = _a.extendedSettings;
+        var diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem);
+        if (!diffSeverity) {
+            return this.format(start, context);
+        }
+        var biggestUnitForPartial = diffSeverity;
+        if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time
+            (standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') &&
+            (standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') &&
+            (standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) {
+            biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time
+        }
+        var full0 = this.format(start, context);
+        var full1 = this.format(end, context);
+        if (full0 === full1) {
+            return full0;
+        }
+        var partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial);
+        var partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context);
+        var partial0 = partialFormattingFunc(start);
+        var partial1 = partialFormattingFunc(end);
+        var insertion = findCommonInsertion(full0, partial0, full1, partial1);
+        var separator = extendedSettings.separator || '';
+        if (insertion) {
+            return insertion.before + partial0 + separator + partial1 + insertion.after;
+        }
+        return full0 + separator + full1;
+    };
+    NativeFormatter.prototype.getLargestUnit = function () {
+        switch (this.severity) {
+            case 7:
+            case 6:
+            case 5:
+                return 'year';
+            case 4:
+                return 'month';
+            case 3:
+                return 'week';
+            default:
+                return 'day';
+        }
+    };
+    return NativeFormatter;
+}());
+function buildFormattingFunc(standardDateProps, extendedSettings, context) {
+    var standardDatePropCnt = Object.keys(standardDateProps).length;
+    if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') {
+        return function (date) {
+            return formatTimeZoneOffset(date.timeZoneOffset);
+        };
+    }
+    if (standardDatePropCnt === 0 && extendedSettings.week) {
+        return function (date) {
+            return formatWeekNumber(context.computeWeekNumber(date.marker), context.weekLabel, context.locale, extendedSettings.week);
+        };
+    }
+    return buildNativeFormattingFunc(standardDateProps, extendedSettings, context);
+}
+function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) {
+    standardDateProps = __assign({}, standardDateProps); // copy
+    extendedSettings = __assign({}, extendedSettings); // copy
+    sanitizeSettings(standardDateProps, extendedSettings);
+    standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers
+    var normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps);
+    var zeroFormat; // needed?
+    if (extendedSettings.omitZeroMinute) {
+        var zeroProps = __assign({}, standardDateProps);
+        delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings
+        zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps);
+    }
+    return function (date) {
+        var marker = date.marker;
+        var format;
+        if (zeroFormat && !marker.getUTCMinutes()) {
+            format = zeroFormat;
+        }
+        else {
+            format = normalFormat;
+        }
+        var s = format.format(marker);
+        return postProcess(s, date, standardDateProps, extendedSettings, context);
+    };
+}
+function sanitizeSettings(standardDateProps, extendedSettings) {
+    // deal with a browser inconsistency where formatting the timezone
+    // requires that the hour/minute be present.
+    if (standardDateProps.timeZoneName) {
+        if (!standardDateProps.hour) {
+            standardDateProps.hour = '2-digit';
+        }
+        if (!standardDateProps.minute) {
+            standardDateProps.minute = '2-digit';
+        }
+    }
+    // only support short timezone names
+    if (standardDateProps.timeZoneName === 'long') {
+        standardDateProps.timeZoneName = 'short';
+    }
+    // if requesting to display seconds, MUST display minutes
+    if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) {
+        delete extendedSettings.omitZeroMinute;
+    }
+}
+function postProcess(s, date, standardDateProps, extendedSettings, context) {
+    s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes
+    if (standardDateProps.timeZoneName === 'short') {
+        s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ?
+            'UTC' : // important to normalize for IE, which does "GMT"
+            formatTimeZoneOffset(date.timeZoneOffset));
+    }
+    if (extendedSettings.omitCommas) {
+        s = s.replace(COMMA_RE, '').trim();
+    }
+    if (extendedSettings.omitZeroMinute) {
+        s = s.replace(':00', ''); // zeroFormat doesn't always achieve this
+    }
+    // ^ do anything that might create adjacent spaces before this point,
+    // because MERIDIEM_RE likes to eat up loading spaces
+    if (extendedSettings.meridiem === false) {
+        s = s.replace(MERIDIEM_RE, '').trim();
+    }
+    else if (extendedSettings.meridiem === 'narrow') { // a/p
+        s = s.replace(MERIDIEM_RE, function (m0, m1) {
+            return m1.toLocaleLowerCase();
+        });
+    }
+    else if (extendedSettings.meridiem === 'short') { // am/pm
+        s = s.replace(MERIDIEM_RE, function (m0, m1) {
+            return m1.toLocaleLowerCase() + 'm';
+        });
+    }
+    else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase
+        s = s.replace(MERIDIEM_RE, function (m0) {
+            return m0.toLocaleLowerCase();
+        });
+    }
+    s = s.replace(MULTI_SPACE_RE, ' ');
+    s = s.trim();
+    return s;
+}
+function injectTzoStr(s, tzoStr) {
+    var replaced = false;
+    s = s.replace(UTC_RE, function () {
+        replaced = true;
+        return tzoStr;
+    });
+    // IE11 doesn't include UTC/GMT in the original string, so append to end
+    if (!replaced) {
+        s += ' ' + tzoStr;
+    }
+    return s;
+}
+function formatWeekNumber(num, weekLabel, locale, display) {
+    var parts = [];
+    if (display === 'narrow') {
+        parts.push(weekLabel);
+    }
+    else if (display === 'short') {
+        parts.push(weekLabel, ' ');
+    }
+    // otherwise, considered 'numeric'
+    parts.push(locale.simpleNumberFormat.format(num));
+    if (locale.options.isRtl) { // TODO: use control characters instead?
+        parts.reverse();
+    }
+    return parts.join('');
+}
+// Range Formatting Utils
+// 0 = exactly the same
+// 1 = different by time
+// and bigger
+function computeMarkerDiffSeverity(d0, d1, ca) {
+    if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) {
+        return 5;
+    }
+    if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) {
+        return 4;
+    }
+    if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) {
+        return 2;
+    }
+    if (timeAsMs(d0) !== timeAsMs(d1)) {
+        return 1;
+    }
+    return 0;
+}
+function computePartialFormattingOptions(options, biggestUnit) {
+    var partialOptions = {};
+    for (var name_2 in options) {
+        if (!(name_2 in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone)
+            STANDARD_DATE_PROP_SEVERITIES[name_2] <= biggestUnit) {
+            partialOptions[name_2] = options[name_2];
+        }
+    }
+    return partialOptions;
+}
+function findCommonInsertion(full0, partial0, full1, partial1) {
+    var i0 = 0;
+    while (i0 < full0.length) {
+        var found0 = full0.indexOf(partial0, i0);
+        if (found0 === -1) {
+            break;
+        }
+        var before0 = full0.substr(0, found0);
+        i0 = found0 + partial0.length;
+        var after0 = full0.substr(i0);
+        var i1 = 0;
+        while (i1 < full1.length) {
+            var found1 = full1.indexOf(partial1, i1);
+            if (found1 === -1) {
+                break;
+            }
+            var before1 = full1.substr(0, found1);
+            i1 = found1 + partial1.length;
+            var after1 = full1.substr(i1);
+            if (before0 === before1 && after0 === after1) {
+                return {
+                    before: before0,
+                    after: after0
+                };
+            }
+        }
+    }
+    return null;
+}
+
+/*
+TODO: fix the terminology of "formatter" vs "formatting func"
+*/
+/*
+At the time of instantiation, this object does not know which cmd-formatting system it will use.
+It receives this at the time of formatting, as a setting.
+*/
+var CmdFormatter = /** @class */ (function () {
+    function CmdFormatter(cmdStr, separator) {
+        this.cmdStr = cmdStr;
+        this.separator = separator;
+    }
+    CmdFormatter.prototype.format = function (date, context) {
+        return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, this.separator));
+    };
+    CmdFormatter.prototype.formatRange = function (start, end, context) {
+        return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, this.separator));
+    };
+    return CmdFormatter;
+}());
+
+var FuncFormatter = /** @class */ (function () {
+    function FuncFormatter(func) {
+        this.func = func;
+    }
+    FuncFormatter.prototype.format = function (date, context) {
+        return this.func(createVerboseFormattingArg(date, null, context));
+    };
+    FuncFormatter.prototype.formatRange = function (start, end, context) {
+        return this.func(createVerboseFormattingArg(start, end, context));
+    };
+    return FuncFormatter;
+}());
+
+// Formatter Object Creation
+function createFormatter(input, defaultSeparator) {
+    if (typeof input === 'object' && input) { // non-null object
+        if (typeof defaultSeparator === 'string') {
+            input = __assign({ separator: defaultSeparator }, input);
+        }
+        return new NativeFormatter(input);
+    }
+    else if (typeof input === 'string') {
+        return new CmdFormatter(input, defaultSeparator);
+    }
+    else if (typeof input === 'function') {
+        return new FuncFormatter(input);
+    }
+}
+// String Utils
+// timeZoneOffset is in minutes
+function buildIsoString(marker, timeZoneOffset, stripZeroTime) {
+    if (stripZeroTime === void 0) { stripZeroTime = false; }
+    var s = marker.toISOString();
+    s = s.replace('.000', '');
+    if (stripZeroTime) {
+        s = s.replace('T00:00:00Z', '');
+    }
+    if (s.length > 10) { // time part wasn't stripped, can add timezone info
+        if (timeZoneOffset == null) {
+            s = s.replace('Z', '');
+        }
+        else if (timeZoneOffset !== 0) {
+            s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true));
+        }
+        // otherwise, its UTC-0 and we want to keep the Z
+    }
+    return s;
+}
+function formatIsoTimeString(marker) {
+    return padStart(marker.getUTCHours(), 2) + ':' +
+        padStart(marker.getUTCMinutes(), 2) + ':' +
+        padStart(marker.getUTCSeconds(), 2);
+}
+function formatTimeZoneOffset(minutes, doIso) {
+    if (doIso === void 0) { doIso = false; }
+    var sign = minutes < 0 ? '-' : '+';
+    var abs = Math.abs(minutes);
+    var hours = Math.floor(abs / 60);
+    var mins = Math.round(abs % 60);
+    if (doIso) {
+        return sign + padStart(hours, 2) + ':' + padStart(mins, 2);
+    }
+    else {
+        return 'GMT' + sign + hours + (mins ? ':' + padStart(mins, 2) : '');
+    }
+}
+// Arg Utils
+function createVerboseFormattingArg(start, end, context, separator) {
+    var startInfo = expandZonedMarker(start, context.calendarSystem);
+    var endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null;
+    return {
+        date: startInfo,
+        start: startInfo,
+        end: endInfo,
+        timeZone: context.timeZone,
+        localeCodes: context.locale.codes,
+        separator: separator
+    };
+}
+function expandZonedMarker(dateInfo, calendarSystem) {
+    var a = calendarSystem.markerToArray(dateInfo.marker);
+    return {
+        marker: dateInfo.marker,
+        timeZoneOffset: dateInfo.timeZoneOffset,
+        array: a,
+        year: a[0],
+        month: a[1],
+        day: a[2],
+        hour: a[3],
+        minute: a[4],
+        second: a[5],
+        millisecond: a[6]
+    };
+}
+
+var EventSourceApi = /** @class */ (function () {
+    function EventSourceApi(calendar, internalEventSource) {
+        this.calendar = calendar;
+        this.internalEventSource = internalEventSource;
+    }
+    EventSourceApi.prototype.remove = function () {
+        this.calendar.dispatch({
+            type: 'REMOVE_EVENT_SOURCE',
+            sourceId: this.internalEventSource.sourceId
+        });
+    };
+    EventSourceApi.prototype.refetch = function () {
+        this.calendar.dispatch({
+            type: 'FETCH_EVENT_SOURCES',
+            sourceIds: [this.internalEventSource.sourceId]
+        });
+    };
+    Object.defineProperty(EventSourceApi.prototype, "id", {
+        get: function () {
+            return this.internalEventSource.publicId;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventSourceApi.prototype, "url", {
+        // only relevant to json-feed event sources
+        get: function () {
+            return this.internalEventSource.meta.url;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    return EventSourceApi;
+}());
+
+var EventApi = /** @class */ (function () {
+    function EventApi(calendar, def, instance) {
+        this._calendar = calendar;
+        this._def = def;
+        this._instance = instance || null;
+    }
+    /*
+    TODO: make event struct more responsible for this
+    */
+    EventApi.prototype.setProp = function (name, val) {
+        var _a, _b;
+        if (name in DATE_PROPS) ;
+        else if (name in NON_DATE_PROPS) {
+            if (typeof NON_DATE_PROPS[name] === 'function') {
+                val = NON_DATE_PROPS[name](val);
+            }
+            this.mutate({
+                standardProps: (_a = {}, _a[name] = val, _a)
+            });
+        }
+        else if (name in UNSCOPED_EVENT_UI_PROPS) {
+            var ui = void 0;
+            if (typeof UNSCOPED_EVENT_UI_PROPS[name] === 'function') {
+                val = UNSCOPED_EVENT_UI_PROPS[name](val);
+            }
+            if (name === 'color') {
+                ui = { backgroundColor: val, borderColor: val };
+            }
+            else if (name === 'editable') {
+                ui = { startEditable: val, durationEditable: val };
+            }
+            else {
+                ui = (_b = {}, _b[name] = val, _b);
+            }
+            this.mutate({
+                standardProps: { ui: ui }
+            });
+        }
+    };
+    EventApi.prototype.setExtendedProp = function (name, val) {
+        var _a;
+        this.mutate({
+            extendedProps: (_a = {}, _a[name] = val, _a)
+        });
+    };
+    EventApi.prototype.setStart = function (startInput, options) {
+        if (options === void 0) { options = {}; }
+        var dateEnv = this._calendar.dateEnv;
+        var start = dateEnv.createMarker(startInput);
+        if (start && this._instance) { // TODO: warning if parsed bad
+            var instanceRange = this._instance.range;
+            var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); // what if parsed bad!?
+            if (options.maintainDuration) {
+                this.mutate({ datesDelta: startDelta });
+            }
+            else {
+                this.mutate({ startDelta: startDelta });
+            }
+        }
+    };
+    EventApi.prototype.setEnd = function (endInput, options) {
+        if (options === void 0) { options = {}; }
+        var dateEnv = this._calendar.dateEnv;
+        var end;
+        if (endInput != null) {
+            end = dateEnv.createMarker(endInput);
+            if (!end) {
+                return; // TODO: warning if parsed bad
+            }
+        }
+        if (this._instance) {
+            if (end) {
+                var endDelta = diffDates(this._instance.range.end, end, dateEnv, options.granularity);
+                this.mutate({ endDelta: endDelta });
+            }
+            else {
+                this.mutate({ standardProps: { hasEnd: false } });
+            }
+        }
+    };
+    EventApi.prototype.setDates = function (startInput, endInput, options) {
+        if (options === void 0) { options = {}; }
+        var dateEnv = this._calendar.dateEnv;
+        var standardProps = { allDay: options.allDay };
+        var start = dateEnv.createMarker(startInput);
+        var end;
+        if (!start) {
+            return; // TODO: warning if parsed bad
+        }
+        if (endInput != null) {
+            end = dateEnv.createMarker(endInput);
+            if (!end) { // TODO: warning if parsed bad
+                return;
+            }
+        }
+        if (this._instance) {
+            var instanceRange = this._instance.range;
+            // when computing the diff for an event being converted to all-day,
+            // compute diff off of the all-day values the way event-mutation does.
+            if (options.allDay === true) {
+                instanceRange = computeAlignedDayRange(instanceRange);
+            }
+            var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity);
+            if (end) {
+                var endDelta = diffDates(instanceRange.end, end, dateEnv, options.granularity);
+                if (durationsEqual(startDelta, endDelta)) {
+                    this.mutate({ datesDelta: startDelta, standardProps: standardProps });
+                }
+                else {
+                    this.mutate({ startDelta: startDelta, endDelta: endDelta, standardProps: standardProps });
+                }
+            }
+            else { // means "clear the end"
+                standardProps.hasEnd = false;
+                this.mutate({ datesDelta: startDelta, standardProps: standardProps });
+            }
+        }
+    };
+    EventApi.prototype.moveStart = function (deltaInput) {
+        var delta = createDuration(deltaInput);
+        if (delta) { // TODO: warning if parsed bad
+            this.mutate({ startDelta: delta });
+        }
+    };
+    EventApi.prototype.moveEnd = function (deltaInput) {
+        var delta = createDuration(deltaInput);
+        if (delta) { // TODO: warning if parsed bad
+            this.mutate({ endDelta: delta });
+        }
+    };
+    EventApi.prototype.moveDates = function (deltaInput) {
+        var delta = createDuration(deltaInput);
+        if (delta) { // TODO: warning if parsed bad
+            this.mutate({ datesDelta: delta });
+        }
+    };
+    EventApi.prototype.setAllDay = function (allDay, options) {
+        if (options === void 0) { options = {}; }
+        var standardProps = { allDay: allDay };
+        var maintainDuration = options.maintainDuration;
+        if (maintainDuration == null) {
+            maintainDuration = this._calendar.opt('allDayMaintainDuration');
+        }
+        if (this._def.allDay !== allDay) {
+            standardProps.hasEnd = maintainDuration;
+        }
+        this.mutate({ standardProps: standardProps });
+    };
+    EventApi.prototype.formatRange = function (formatInput) {
+        var dateEnv = this._calendar.dateEnv;
+        var instance = this._instance;
+        var formatter = createFormatter(formatInput, this._calendar.opt('defaultRangeSeparator'));
+        if (this._def.hasEnd) {
+            return dateEnv.formatRange(instance.range.start, instance.range.end, formatter, {
+                forcedStartTzo: instance.forcedStartTzo,
+                forcedEndTzo: instance.forcedEndTzo
+            });
+        }
+        else {
+            return dateEnv.format(instance.range.start, formatter, {
+                forcedTzo: instance.forcedStartTzo
+            });
+        }
+    };
+    EventApi.prototype.mutate = function (mutation) {
+        var def = this._def;
+        var instance = this._instance;
+        if (instance) {
+            this._calendar.dispatch({
+                type: 'MUTATE_EVENTS',
+                instanceId: instance.instanceId,
+                mutation: mutation,
+                fromApi: true
+            });
+            var eventStore = this._calendar.state.eventStore;
+            this._def = eventStore.defs[def.defId];
+            this._instance = eventStore.instances[instance.instanceId];
+        }
+    };
+    EventApi.prototype.remove = function () {
+        this._calendar.dispatch({
+            type: 'REMOVE_EVENT_DEF',
+            defId: this._def.defId
+        });
+    };
+    Object.defineProperty(EventApi.prototype, "source", {
+        get: function () {
+            var sourceId = this._def.sourceId;
+            if (sourceId) {
+                return new EventSourceApi(this._calendar, this._calendar.state.eventSources[sourceId]);
+            }
+            return null;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "start", {
+        get: function () {
+            return this._instance ?
+                this._calendar.dateEnv.toDate(this._instance.range.start) :
+                null;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "end", {
+        get: function () {
+            return (this._instance && this._def.hasEnd) ?
+                this._calendar.dateEnv.toDate(this._instance.range.end) :
+                null;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "id", {
+        // computable props that all access the def
+        // TODO: find a TypeScript-compatible way to do this at scale
+        get: function () { return this._def.publicId; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "groupId", {
+        get: function () { return this._def.groupId; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "allDay", {
+        get: function () { return this._def.allDay; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "title", {
+        get: function () { return this._def.title; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "url", {
+        get: function () { return this._def.url; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "rendering", {
+        get: function () { return this._def.rendering; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "startEditable", {
+        get: function () { return this._def.ui.startEditable; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "durationEditable", {
+        get: function () { return this._def.ui.durationEditable; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "constraint", {
+        get: function () { return this._def.ui.constraints[0] || null; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "overlap", {
+        get: function () { return this._def.ui.overlap; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "allow", {
+        get: function () { return this._def.ui.allows[0] || null; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "backgroundColor", {
+        get: function () { return this._def.ui.backgroundColor; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "borderColor", {
+        get: function () { return this._def.ui.borderColor; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "textColor", {
+        get: function () { return this._def.ui.textColor; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "classNames", {
+        // NOTE: user can't modify these because Object.freeze was called in event-def parsing
+        get: function () { return this._def.ui.classNames; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(EventApi.prototype, "extendedProps", {
+        get: function () { return this._def.extendedProps; },
+        enumerable: true,
+        configurable: true
+    });
+    return EventApi;
+}());
+
+/*
+Specifying nextDayThreshold signals that all-day ranges should be sliced.
+*/
+function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshold) {
+    var inverseBgByGroupId = {};
+    var inverseBgByDefId = {};
+    var defByGroupId = {};
+    var bgRanges = [];
+    var fgRanges = [];
+    var eventUis = compileEventUis(eventStore.defs, eventUiBases);
+    for (var defId in eventStore.defs) {
+        var def = eventStore.defs[defId];
+        if (def.rendering === 'inverse-background') {
+            if (def.groupId) {
+                inverseBgByGroupId[def.groupId] = [];
+                if (!defByGroupId[def.groupId]) {
+                    defByGroupId[def.groupId] = def;
+                }
+            }
+            else {
+                inverseBgByDefId[defId] = [];
+            }
+        }
+    }
+    for (var instanceId in eventStore.instances) {
+        var instance = eventStore.instances[instanceId];
+        var def = eventStore.defs[instance.defId];
+        var ui = eventUis[def.defId];
+        var origRange = instance.range;
+        var normalRange = (!def.allDay && nextDayThreshold) ?
+            computeVisibleDayRange(origRange, nextDayThreshold) :
+            origRange;
+        var slicedRange = intersectRanges(normalRange, framingRange);
+        if (slicedRange) {
+            if (def.rendering === 'inverse-background') {
+                if (def.groupId) {
+                    inverseBgByGroupId[def.groupId].push(slicedRange);
+                }
+                else {
+                    inverseBgByDefId[instance.defId].push(slicedRange);
+                }
+            }
+            else {
+                (def.rendering === 'background' ? bgRanges : fgRanges).push({
+                    def: def,
+                    ui: ui,
+                    instance: instance,
+                    range: slicedRange,
+                    isStart: normalRange.start && normalRange.start.valueOf() === slicedRange.start.valueOf(),
+                    isEnd: normalRange.end && normalRange.end.valueOf() === slicedRange.end.valueOf()
+                });
+            }
+        }
+    }
+    for (var groupId in inverseBgByGroupId) { // BY GROUP
+        var ranges = inverseBgByGroupId[groupId];
+        var invertedRanges = invertRanges(ranges, framingRange);
+        for (var _i = 0, invertedRanges_1 = invertedRanges; _i < invertedRanges_1.length; _i++) {
+            var invertedRange = invertedRanges_1[_i];
+            var def = defByGroupId[groupId];
+            var ui = eventUis[def.defId];
+            bgRanges.push({
+                def: def,
+                ui: ui,
+                instance: null,
+                range: invertedRange,
+                isStart: false,
+                isEnd: false
+            });
+        }
+    }
+    for (var defId in inverseBgByDefId) {
+        var ranges = inverseBgByDefId[defId];
+        var invertedRanges = invertRanges(ranges, framingRange);
+        for (var _a = 0, invertedRanges_2 = invertedRanges; _a < invertedRanges_2.length; _a++) {
+            var invertedRange = invertedRanges_2[_a];
+            bgRanges.push({
+                def: eventStore.defs[defId],
+                ui: eventUis[defId],
+                instance: null,
+                range: invertedRange,
+                isStart: false,
+                isEnd: false
+            });
+        }
+    }
+    return { bg: bgRanges, fg: fgRanges };
+}
+function hasBgRendering(def) {
+    return def.rendering === 'background' || def.rendering === 'inverse-background';
+}
+function filterSegsViaEls(view, segs, isMirror) {
+    if (view.hasPublicHandlers('eventRender')) {
+        segs = segs.filter(function (seg) {
+            var custom = view.publiclyTrigger('eventRender', [
+                {
+                    event: new EventApi(view.calendar, seg.eventRange.def, seg.eventRange.instance),
+                    isMirror: isMirror,
+                    isStart: seg.isStart,
+                    isEnd: seg.isEnd,
+                    // TODO: include seg.range once all components consistently generate it
+                    el: seg.el,
+                    view: view
+                }
+            ]);
+            if (custom === false) { // means don't render at all
+                return false;
+            }
+            else if (custom && custom !== true) {
+                seg.el = custom;
+            }
+            return true;
+        });
+    }
+    for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+        var seg = segs_1[_i];
+        setElSeg(seg.el, seg);
+    }
+    return segs;
+}
+function setElSeg(el, seg) {
+    el.fcSeg = seg;
+}
+function getElSeg(el) {
+    return el.fcSeg || null;
+}
+// event ui computation
+function compileEventUis(eventDefs, eventUiBases) {
+    return mapHash(eventDefs, function (eventDef) {
+        return compileEventUi(eventDef, eventUiBases);
+    });
+}
+function compileEventUi(eventDef, eventUiBases) {
+    var uis = [];
+    if (eventUiBases['']) {
+        uis.push(eventUiBases['']);
+    }
+    if (eventUiBases[eventDef.defId]) {
+        uis.push(eventUiBases[eventDef.defId]);
+    }
+    uis.push(eventDef.ui);
+    return combineEventUis(uis);
+}
+
+// applies the mutation to ALL defs/instances within the event store
+function applyMutationToEventStore(eventStore, eventConfigBase, mutation, calendar) {
+    var eventConfigs = compileEventUis(eventStore.defs, eventConfigBase);
+    var dest = createEmptyEventStore();
+    for (var defId in eventStore.defs) {
+        var def = eventStore.defs[defId];
+        dest.defs[defId] = applyMutationToEventDef(def, eventConfigs[defId], mutation, calendar.pluginSystem.hooks.eventDefMutationAppliers, calendar);
+    }
+    for (var instanceId in eventStore.instances) {
+        var instance = eventStore.instances[instanceId];
+        var def = dest.defs[instance.defId]; // important to grab the newly modified def
+        dest.instances[instanceId] = applyMutationToEventInstance(instance, def, eventConfigs[instance.defId], mutation, calendar);
+    }
+    return dest;
+}
+function applyMutationToEventDef(eventDef, eventConfig, mutation, appliers, calendar) {
+    var standardProps = mutation.standardProps || {};
+    // if hasEnd has not been specified, guess a good value based on deltas.
+    // if duration will change, there's no way the default duration will persist,
+    // and thus, we need to mark the event as having a real end
+    if (standardProps.hasEnd == null &&
+        eventConfig.durationEditable &&
+        (mutation.startDelta || mutation.endDelta)) {
+        standardProps.hasEnd = true; // TODO: is this mutation okay?
+    }
+    var copy = __assign({}, eventDef, standardProps, { ui: __assign({}, eventDef.ui, standardProps.ui) });
+    if (mutation.extendedProps) {
+        copy.extendedProps = __assign({}, copy.extendedProps, mutation.extendedProps);
+    }
+    for (var _i = 0, appliers_1 = appliers; _i < appliers_1.length; _i++) {
+        var applier = appliers_1[_i];
+        applier(copy, mutation, calendar);
+    }
+    if (!copy.hasEnd && calendar.opt('forceEventDuration')) {
+        copy.hasEnd = true;
+    }
+    return copy;
+}
+function applyMutationToEventInstance(eventInstance, eventDef, // must first be modified by applyMutationToEventDef
+eventConfig, mutation, calendar) {
+    var dateEnv = calendar.dateEnv;
+    var forceAllDay = mutation.standardProps && mutation.standardProps.allDay === true;
+    var clearEnd = mutation.standardProps && mutation.standardProps.hasEnd === false;
+    var copy = __assign({}, eventInstance);
+    if (forceAllDay) {
+        copy.range = computeAlignedDayRange(copy.range);
+    }
+    if (mutation.datesDelta && eventConfig.startEditable) {
+        copy.range = {
+            start: dateEnv.add(copy.range.start, mutation.datesDelta),
+            end: dateEnv.add(copy.range.end, mutation.datesDelta)
+        };
+    }
+    if (mutation.startDelta && eventConfig.durationEditable) {
+        copy.range = {
+            start: dateEnv.add(copy.range.start, mutation.startDelta),
+            end: copy.range.end
+        };
+    }
+    if (mutation.endDelta && eventConfig.durationEditable) {
+        copy.range = {
+            start: copy.range.start,
+            end: dateEnv.add(copy.range.end, mutation.endDelta)
+        };
+    }
+    if (clearEnd) {
+        copy.range = {
+            start: copy.range.start,
+            end: calendar.getDefaultEventEnd(eventDef.allDay, copy.range.start)
+        };
+    }
+    // in case event was all-day but the supplied deltas were not
+    // better util for this?
+    if (eventDef.allDay) {
+        copy.range = {
+            start: startOfDay(copy.range.start),
+            end: startOfDay(copy.range.end)
+        };
+    }
+    // handle invalid durations
+    if (copy.range.end < copy.range.start) {
+        copy.range.end = calendar.getDefaultEventEnd(eventDef.allDay, copy.range.start);
+    }
+    return copy;
+}
+
+function reduceEventStore (eventStore, action, eventSources, dateProfile, calendar) {
+    switch (action.type) {
+        case 'RECEIVE_EVENTS': // raw
+            return receiveRawEvents(eventStore, eventSources[action.sourceId], action.fetchId, action.fetchRange, action.rawEvents, calendar);
+        case 'ADD_EVENTS': // already parsed, but not expanded
+            return addEvent(eventStore, action.eventStore, // new ones
+            dateProfile ? dateProfile.activeRange : null, calendar);
+        case 'MERGE_EVENTS': // already parsed and expanded
+            return mergeEventStores(eventStore, action.eventStore);
+        case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
+        case 'NEXT':
+        case 'SET_DATE':
+        case 'SET_VIEW_TYPE':
+            if (dateProfile) {
+                return expandRecurring(eventStore, dateProfile.activeRange, calendar);
+            }
+            else {
+                return eventStore;
+            }
+        case 'CHANGE_TIMEZONE':
+            return rezoneDates(eventStore, action.oldDateEnv, calendar.dateEnv);
+        case 'MUTATE_EVENTS':
+            return applyMutationToRelated(eventStore, action.instanceId, action.mutation, action.fromApi, calendar);
+        case 'REMOVE_EVENT_INSTANCES':
+            return excludeInstances(eventStore, action.instances);
+        case 'REMOVE_EVENT_DEF':
+            return filterEventStoreDefs(eventStore, function (eventDef) {
+                return eventDef.defId !== action.defId;
+            });
+        case 'REMOVE_EVENT_SOURCE':
+            return excludeEventsBySourceId(eventStore, action.sourceId);
+        case 'REMOVE_ALL_EVENT_SOURCES':
+            return filterEventStoreDefs(eventStore, function (eventDef) {
+                return !eventDef.sourceId; // only keep events with no source id
+            });
+        case 'REMOVE_ALL_EVENTS':
+            return createEmptyEventStore();
+        case 'RESET_EVENTS':
+            return {
+                defs: eventStore.defs,
+                instances: eventStore.instances
+            };
+        default:
+            return eventStore;
+    }
+}
+function receiveRawEvents(eventStore, eventSource, fetchId, fetchRange, rawEvents, calendar) {
+    if (eventSource && // not already removed
+        fetchId === eventSource.latestFetchId // TODO: wish this logic was always in event-sources
+    ) {
+        var subset = parseEvents(transformRawEvents(rawEvents, eventSource, calendar), eventSource.sourceId, calendar);
+        if (fetchRange) {
+            subset = expandRecurring(subset, fetchRange, calendar);
+        }
+        return mergeEventStores(excludeEventsBySourceId(eventStore, eventSource.sourceId), subset);
+    }
+    return eventStore;
+}
+function addEvent(eventStore, subset, expandRange, calendar) {
+    if (expandRange) {
+        subset = expandRecurring(subset, expandRange, calendar);
+    }
+    return mergeEventStores(eventStore, subset);
+}
+function rezoneDates(eventStore, oldDateEnv, newDateEnv) {
+    var defs = eventStore.defs;
+    var instances = mapHash(eventStore.instances, function (instance) {
+        var def = defs[instance.defId];
+        if (def.allDay || def.recurringDef) {
+            return instance; // isn't dependent on timezone
+        }
+        else {
+            return __assign({}, instance, { range: {
+                    start: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.start, instance.forcedStartTzo)),
+                    end: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.end, instance.forcedEndTzo))
+                }, forcedStartTzo: newDateEnv.canComputeOffset ? null : instance.forcedStartTzo, forcedEndTzo: newDateEnv.canComputeOffset ? null : instance.forcedEndTzo });
+        }
+    });
+    return { defs: defs, instances: instances };
+}
+function applyMutationToRelated(eventStore, instanceId, mutation, fromApi, calendar) {
+    var relevant = getRelevantEvents(eventStore, instanceId);
+    var eventConfigBase = fromApi ?
+        { '': {
+                startEditable: true,
+                durationEditable: true,
+                constraints: [],
+                overlap: null,
+                allows: [],
+                backgroundColor: '',
+                borderColor: '',
+                textColor: '',
+                classNames: []
+            } } :
+        calendar.eventUiBases;
+    relevant = applyMutationToEventStore(relevant, eventConfigBase, mutation, calendar);
+    return mergeEventStores(eventStore, relevant);
+}
+function excludeEventsBySourceId(eventStore, sourceId) {
+    return filterEventStoreDefs(eventStore, function (eventDef) {
+        return eventDef.sourceId !== sourceId;
+    });
+}
+// QUESTION: why not just return instances? do a general object-property-exclusion util
+function excludeInstances(eventStore, removals) {
+    return {
+        defs: eventStore.defs,
+        instances: filterHash(eventStore.instances, function (instance) {
+            return !removals[instance.instanceId];
+        })
+    };
+}
+
+// high-level segmenting-aware tester functions
+// ------------------------------------------------------------------------------------------------------------------------
+function isInteractionValid(interaction, calendar) {
+    return isNewPropsValid({ eventDrag: interaction }, calendar); // HACK: the eventDrag props is used for ALL interactions
+}
+function isDateSelectionValid(dateSelection, calendar) {
+    return isNewPropsValid({ dateSelection: dateSelection }, calendar);
+}
+function isNewPropsValid(newProps, calendar) {
+    var view = calendar.view;
+    var props = __assign({ businessHours: view ? view.props.businessHours : createEmptyEventStore(), dateSelection: '', eventStore: calendar.state.eventStore, eventUiBases: calendar.eventUiBases, eventSelection: '', eventDrag: null, eventResize: null }, newProps);
+    return (calendar.pluginSystem.hooks.isPropsValid || isPropsValid)(props, calendar);
+}
+function isPropsValid(state, calendar, dateSpanMeta, filterConfig) {
+    if (dateSpanMeta === void 0) { dateSpanMeta = {}; }
+    if (state.eventDrag && !isInteractionPropsValid(state, calendar, dateSpanMeta, filterConfig)) {
+        return false;
+    }
+    if (state.dateSelection && !isDateSelectionPropsValid(state, calendar, dateSpanMeta, filterConfig)) {
+        return false;
+    }
+    return true;
+}
+// Moving Event Validation
+// ------------------------------------------------------------------------------------------------------------------------
+function isInteractionPropsValid(state, calendar, dateSpanMeta, filterConfig) {
+    var interaction = state.eventDrag; // HACK: the eventDrag props is used for ALL interactions
+    var subjectEventStore = interaction.mutatedEvents;
+    var subjectDefs = subjectEventStore.defs;
+    var subjectInstances = subjectEventStore.instances;
+    var subjectConfigs = compileEventUis(subjectDefs, interaction.isEvent ?
+        state.eventUiBases :
+        { '': calendar.selectionConfig } // if not a real event, validate as a selection
+    );
+    if (filterConfig) {
+        subjectConfigs = mapHash(subjectConfigs, filterConfig);
+    }
+    var otherEventStore = excludeInstances(state.eventStore, interaction.affectedEvents.instances); // exclude the subject events. TODO: exclude defs too?
+    var otherDefs = otherEventStore.defs;
+    var otherInstances = otherEventStore.instances;
+    var otherConfigs = compileEventUis(otherDefs, state.eventUiBases);
+    for (var subjectInstanceId in subjectInstances) {
+        var subjectInstance = subjectInstances[subjectInstanceId];
+        var subjectRange = subjectInstance.range;
+        var subjectConfig = subjectConfigs[subjectInstance.defId];
+        var subjectDef = subjectDefs[subjectInstance.defId];
+        // constraint
+        if (!allConstraintsPass(subjectConfig.constraints, subjectRange, otherEventStore, state.businessHours, calendar)) {
+            return false;
+        }
+        // overlap
+        var overlapFunc = calendar.opt('eventOverlap');
+        if (typeof overlapFunc !== 'function') {
+            overlapFunc = null;
+        }
+        for (var otherInstanceId in otherInstances) {
+            var otherInstance = otherInstances[otherInstanceId];
+            // intersect! evaluate
+            if (rangesIntersect(subjectRange, otherInstance.range)) {
+                var otherOverlap = otherConfigs[otherInstance.defId].overlap;
+                // consider the other event's overlap. only do this if the subject event is a "real" event
+                if (otherOverlap === false && interaction.isEvent) {
+                    return false;
+                }
+                if (subjectConfig.overlap === false) {
+                    return false;
+                }
+                if (overlapFunc && !overlapFunc(new EventApi(calendar, otherDefs[otherInstance.defId], otherInstance), // still event
+                new EventApi(calendar, subjectDef, subjectInstance) // moving event
+                )) {
+                    return false;
+                }
+            }
+        }
+        // allow (a function)
+        var calendarEventStore = calendar.state.eventStore; // need global-to-calendar, not local to component (splittable)state
+        for (var _i = 0, _a = subjectConfig.allows; _i < _a.length; _i++) {
+            var subjectAllow = _a[_i];
+            var subjectDateSpan = __assign({}, dateSpanMeta, { range: subjectInstance.range, allDay: subjectDef.allDay });
+            var origDef = calendarEventStore.defs[subjectDef.defId];
+            var origInstance = calendarEventStore.instances[subjectInstanceId];
+            var eventApi = void 0;
+            if (origDef) { // was previously in the calendar
+                eventApi = new EventApi(calendar, origDef, origInstance);
+            }
+            else { // was an external event
+                eventApi = new EventApi(calendar, subjectDef); // no instance, because had no dates
+            }
+            if (!subjectAllow(calendar.buildDateSpanApi(subjectDateSpan), eventApi)) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+// Date Selection Validation
+// ------------------------------------------------------------------------------------------------------------------------
+function isDateSelectionPropsValid(state, calendar, dateSpanMeta, filterConfig) {
+    var relevantEventStore = state.eventStore;
+    var relevantDefs = relevantEventStore.defs;
+    var relevantInstances = relevantEventStore.instances;
+    var selection = state.dateSelection;
+    var selectionRange = selection.range;
+    var selectionConfig = calendar.selectionConfig;
+    if (filterConfig) {
+        selectionConfig = filterConfig(selectionConfig);
+    }
+    // constraint
+    if (!allConstraintsPass(selectionConfig.constraints, selectionRange, relevantEventStore, state.businessHours, calendar)) {
+        return false;
+    }
+    // overlap
+    var overlapFunc = calendar.opt('selectOverlap');
+    if (typeof overlapFunc !== 'function') {
+        overlapFunc = null;
+    }
+    for (var relevantInstanceId in relevantInstances) {
+        var relevantInstance = relevantInstances[relevantInstanceId];
+        // intersect! evaluate
+        if (rangesIntersect(selectionRange, relevantInstance.range)) {
+            if (selectionConfig.overlap === false) {
+                return false;
+            }
+            if (overlapFunc && !overlapFunc(new EventApi(calendar, relevantDefs[relevantInstance.defId], relevantInstance))) {
+                return false;
+            }
+        }
+    }
+    // allow (a function)
+    for (var _i = 0, _a = selectionConfig.allows; _i < _a.length; _i++) {
+        var selectionAllow = _a[_i];
+        var fullDateSpan = __assign({}, dateSpanMeta, selection);
+        if (!selectionAllow(calendar.buildDateSpanApi(fullDateSpan), null)) {
+            return false;
+        }
+    }
+    return true;
+}
+// Constraint Utils
+// ------------------------------------------------------------------------------------------------------------------------
+function allConstraintsPass(constraints, subjectRange, otherEventStore, businessHoursUnexpanded, calendar) {
+    for (var _i = 0, constraints_1 = constraints; _i < constraints_1.length; _i++) {
+        var constraint = constraints_1[_i];
+        if (!anyRangesContainRange(constraintToRanges(constraint, subjectRange, otherEventStore, businessHoursUnexpanded, calendar), subjectRange)) {
+            return false;
+        }
+    }
+    return true;
+}
+function constraintToRanges(constraint, subjectRange, // for expanding a recurring constraint, or expanding business hours
+otherEventStore, // for if constraint is an even group ID
+businessHoursUnexpanded, // for if constraint is 'businessHours'
+calendar // for expanding businesshours
+) {
+    if (constraint === 'businessHours') {
+        return eventStoreToRanges(expandRecurring(businessHoursUnexpanded, subjectRange, calendar));
+    }
+    else if (typeof constraint === 'string') { // an group ID
+        return eventStoreToRanges(filterEventStoreDefs(otherEventStore, function (eventDef) {
+            return eventDef.groupId === constraint;
+        }));
+    }
+    else if (typeof constraint === 'object' && constraint) { // non-null object
+        return eventStoreToRanges(expandRecurring(constraint, subjectRange, calendar));
+    }
+    return []; // if it's false
+}
+// TODO: move to event-store file?
+function eventStoreToRanges(eventStore) {
+    var instances = eventStore.instances;
+    var ranges = [];
+    for (var instanceId in instances) {
+        ranges.push(instances[instanceId].range);
+    }
+    return ranges;
+}
+// TODO: move to geom file?
+function anyRangesContainRange(outerRanges, innerRange) {
+    for (var _i = 0, outerRanges_1 = outerRanges; _i < outerRanges_1.length; _i++) {
+        var outerRange = outerRanges_1[_i];
+        if (rangeContainsRange(outerRange, innerRange)) {
+            return true;
+        }
+    }
+    return false;
+}
+// Parsing
+// ------------------------------------------------------------------------------------------------------------------------
+function normalizeConstraint(input, calendar) {
+    if (Array.isArray(input)) {
+        return parseEvents(input, '', calendar, true); // allowOpenRange=true
+    }
+    else if (typeof input === 'object' && input) { // non-null object
+        return parseEvents([input], '', calendar, true); // allowOpenRange=true
+    }
+    else if (input != null) {
+        return String(input);
+    }
+    else {
+        return null;
+    }
+}
+
+function htmlEscape(s) {
+    return (s + '').replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/'/g, '&#039;')
+        .replace(/"/g, '&quot;')
+        .replace(/\n/g, '<br />');
+}
+// Given a hash of CSS properties, returns a string of CSS.
+// Uses property names as-is (no camel-case conversion). Will not make statements for null/undefined values.
+function cssToStr(cssProps) {
+    var statements = [];
+    for (var name_1 in cssProps) {
+        var val = cssProps[name_1];
+        if (val != null && val !== '') {
+            statements.push(name_1 + ':' + val);
+        }
+    }
+    return statements.join(';');
+}
+// Given an object hash of HTML attribute names to values,
+// generates a string that can be injected between < > in HTML
+function attrsToStr(attrs) {
+    var parts = [];
+    for (var name_2 in attrs) {
+        var val = attrs[name_2];
+        if (val != null) {
+            parts.push(name_2 + '="' + htmlEscape(val) + '"');
+        }
+    }
+    return parts.join(' ');
+}
+function parseClassName(raw) {
+    if (Array.isArray(raw)) {
+        return raw;
+    }
+    else if (typeof raw === 'string') {
+        return raw.split(/\s+/);
+    }
+    else {
+        return [];
+    }
+}
+
+var UNSCOPED_EVENT_UI_PROPS = {
+    editable: Boolean,
+    startEditable: Boolean,
+    durationEditable: Boolean,
+    constraint: null,
+    overlap: null,
+    allow: null,
+    className: parseClassName,
+    classNames: parseClassName,
+    color: String,
+    backgroundColor: String,
+    borderColor: String,
+    textColor: String
+};
+function processUnscopedUiProps(rawProps, calendar, leftovers) {
+    var props = refineProps(rawProps, UNSCOPED_EVENT_UI_PROPS, {}, leftovers);
+    var constraint = normalizeConstraint(props.constraint, calendar);
+    return {
+        startEditable: props.startEditable != null ? props.startEditable : props.editable,
+        durationEditable: props.durationEditable != null ? props.durationEditable : props.editable,
+        constraints: constraint != null ? [constraint] : [],
+        overlap: props.overlap,
+        allows: props.allow != null ? [props.allow] : [],
+        backgroundColor: props.backgroundColor || props.color,
+        borderColor: props.borderColor || props.color,
+        textColor: props.textColor,
+        classNames: props.classNames.concat(props.className)
+    };
+}
+function processScopedUiProps(prefix, rawScoped, calendar, leftovers) {
+    var rawUnscoped = {};
+    var wasFound = {};
+    for (var key in UNSCOPED_EVENT_UI_PROPS) {
+        var scopedKey = prefix + capitaliseFirstLetter(key);
+        rawUnscoped[key] = rawScoped[scopedKey];
+        wasFound[scopedKey] = true;
+    }
+    if (prefix === 'event') {
+        rawUnscoped.editable = rawScoped.editable; // special case. there is no 'eventEditable', just 'editable'
+    }
+    if (leftovers) {
+        for (var key in rawScoped) {
+            if (!wasFound[key]) {
+                leftovers[key] = rawScoped[key];
+            }
+        }
+    }
+    return processUnscopedUiProps(rawUnscoped, calendar);
+}
+var EMPTY_EVENT_UI = {
+    startEditable: null,
+    durationEditable: null,
+    constraints: [],
+    overlap: null,
+    allows: [],
+    backgroundColor: '',
+    borderColor: '',
+    textColor: '',
+    classNames: []
+};
+// prevent against problems with <2 args!
+function combineEventUis(uis) {
+    return uis.reduce(combineTwoEventUis, EMPTY_EVENT_UI);
+}
+function combineTwoEventUis(item0, item1) {
+    return {
+        startEditable: item1.startEditable != null ? item1.startEditable : item0.startEditable,
+        durationEditable: item1.durationEditable != null ? item1.durationEditable : item0.durationEditable,
+        constraints: item0.constraints.concat(item1.constraints),
+        overlap: typeof item1.overlap === 'boolean' ? item1.overlap : item0.overlap,
+        allows: item0.allows.concat(item1.allows),
+        backgroundColor: item1.backgroundColor || item0.backgroundColor,
+        borderColor: item1.borderColor || item0.borderColor,
+        textColor: item1.textColor || item0.textColor,
+        classNames: item0.classNames.concat(item1.classNames)
+    };
+}
+
+var NON_DATE_PROPS = {
+    id: String,
+    groupId: String,
+    title: String,
+    url: String,
+    rendering: String,
+    extendedProps: null
+};
+var DATE_PROPS = {
+    start: null,
+    date: null,
+    end: null,
+    allDay: null
+};
+var uid = 0;
+function parseEvent(raw, sourceId, calendar, allowOpenRange) {
+    var allDayDefault = computeIsAllDayDefault(sourceId, calendar);
+    var leftovers0 = {};
+    var recurringRes = parseRecurring(raw, // raw, but with single-event stuff stripped out
+    allDayDefault, calendar.dateEnv, calendar.pluginSystem.hooks.recurringTypes, leftovers0 // will populate with non-recurring props
+    );
+    if (recurringRes) {
+        var def = parseEventDef(leftovers0, sourceId, recurringRes.allDay, Boolean(recurringRes.duration), calendar);
+        def.recurringDef = {
+            typeId: recurringRes.typeId,
+            typeData: recurringRes.typeData,
+            duration: recurringRes.duration
+        };
+        return { def: def, instance: null };
+    }
+    else {
+        var leftovers1 = {};
+        var singleRes = parseSingle(raw, allDayDefault, calendar, leftovers1, allowOpenRange);
+        if (singleRes) {
+            var def = parseEventDef(leftovers1, sourceId, singleRes.allDay, singleRes.hasEnd, calendar);
+            var instance = createEventInstance(def.defId, singleRes.range, singleRes.forcedStartTzo, singleRes.forcedEndTzo);
+            return { def: def, instance: instance };
+        }
+    }
+    return null;
+}
+/*
+Will NOT populate extendedProps with the leftover properties.
+Will NOT populate date-related props.
+The EventNonDateInput has been normalized (id => publicId, etc).
+*/
+function parseEventDef(raw, sourceId, allDay, hasEnd, calendar) {
+    var leftovers = {};
+    var def = pluckNonDateProps(raw, calendar, leftovers);
+    def.defId = String(uid++);
+    def.sourceId = sourceId;
+    def.allDay = allDay;
+    def.hasEnd = hasEnd;
+    for (var _i = 0, _a = calendar.pluginSystem.hooks.eventDefParsers; _i < _a.length; _i++) {
+        var eventDefParser = _a[_i];
+        var newLeftovers = {};
+        eventDefParser(def, leftovers, newLeftovers);
+        leftovers = newLeftovers;
+    }
+    def.extendedProps = __assign(leftovers, def.extendedProps || {});
+    // help out EventApi from having user modify props
+    Object.freeze(def.ui.classNames);
+    Object.freeze(def.extendedProps);
+    return def;
+}
+function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) {
+    return {
+        instanceId: String(uid++),
+        defId: defId,
+        range: range,
+        forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo,
+        forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo
+    };
+}
+function parseSingle(raw, allDayDefault, calendar, leftovers, allowOpenRange) {
+    var props = pluckDateProps(raw, leftovers);
+    var allDay = props.allDay;
+    var startMeta;
+    var startMarker = null;
+    var hasEnd = false;
+    var endMeta;
+    var endMarker = null;
+    startMeta = calendar.dateEnv.createMarkerMeta(props.start);
+    if (startMeta) {
+        startMarker = startMeta.marker;
+    }
+    else if (!allowOpenRange) {
+        return null;
+    }
+    if (props.end != null) {
+        endMeta = calendar.dateEnv.createMarkerMeta(props.end);
+    }
+    if (allDay == null) {
+        if (allDayDefault != null) {
+            allDay = allDayDefault;
+        }
+        else {
+            // fall back to the date props LAST
+            allDay = (!startMeta || startMeta.isTimeUnspecified) &&
+                (!endMeta || endMeta.isTimeUnspecified);
+        }
+    }
+    if (allDay && startMarker) {
+        startMarker = startOfDay(startMarker);
+    }
+    if (endMeta) {
+        endMarker = endMeta.marker;
+        if (allDay) {
+            endMarker = startOfDay(endMarker);
+        }
+        if (startMarker && endMarker <= startMarker) {
+            endMarker = null;
+        }
+    }
+    if (endMarker) {
+        hasEnd = true;
+    }
+    else if (!allowOpenRange) {
+        hasEnd = calendar.opt('forceEventDuration') || false;
+        endMarker = calendar.dateEnv.add(startMarker, allDay ?
+            calendar.defaultAllDayEventDuration :
+            calendar.defaultTimedEventDuration);
+    }
+    return {
+        allDay: allDay,
+        hasEnd: hasEnd,
+        range: { start: startMarker, end: endMarker },
+        forcedStartTzo: startMeta ? startMeta.forcedTzo : null,
+        forcedEndTzo: endMeta ? endMeta.forcedTzo : null
+    };
+}
+function pluckDateProps(raw, leftovers) {
+    var props = refineProps(raw, DATE_PROPS, {}, leftovers);
+    props.start = (props.start !== null) ? props.start : props.date;
+    delete props.date;
+    return props;
+}
+function pluckNonDateProps(raw, calendar, leftovers) {
+    var preLeftovers = {};
+    var props = refineProps(raw, NON_DATE_PROPS, {}, preLeftovers);
+    var ui = processUnscopedUiProps(preLeftovers, calendar, leftovers);
+    props.publicId = props.id;
+    delete props.id;
+    props.ui = ui;
+    return props;
+}
+function computeIsAllDayDefault(sourceId, calendar) {
+    var res = null;
+    if (sourceId) {
+        var source = calendar.state.eventSources[sourceId];
+        res = source.allDayDefault;
+    }
+    if (res == null) {
+        res = calendar.opt('allDayDefault');
+    }
+    return res;
+}
+
+var DEF_DEFAULTS = {
+    startTime: '09:00',
+    endTime: '17:00',
+    daysOfWeek: [1, 2, 3, 4, 5],
+    rendering: 'inverse-background',
+    classNames: 'fc-nonbusiness',
+    groupId: '_businessHours' // so multiple defs get grouped
+};
+/*
+TODO: pass around as EventDefHash!!!
+*/
+function parseBusinessHours(input, calendar) {
+    return parseEvents(refineInputs(input), '', calendar);
+}
+function refineInputs(input) {
+    var rawDefs;
+    if (input === true) {
+        rawDefs = [{}]; // will get DEF_DEFAULTS verbatim
+    }
+    else if (Array.isArray(input)) {
+        // if specifying an array, every sub-definition NEEDS a day-of-week
+        rawDefs = input.filter(function (rawDef) {
+            return rawDef.daysOfWeek;
+        });
+    }
+    else if (typeof input === 'object' && input) { // non-null object
+        rawDefs = [input];
+    }
+    else { // is probably false
+        rawDefs = [];
+    }
+    rawDefs = rawDefs.map(function (rawDef) {
+        return __assign({}, DEF_DEFAULTS, rawDef);
+    });
+    return rawDefs;
+}
+
+function memoizeRendering(renderFunc, unrenderFunc, dependencies) {
+    if (dependencies === void 0) { dependencies = []; }
+    var dependents = [];
+    var thisContext;
+    var prevArgs;
+    function unrender() {
+        if (prevArgs) {
+            for (var _i = 0, dependents_1 = dependents; _i < dependents_1.length; _i++) {
+                var dependent = dependents_1[_i];
+                dependent.unrender();
+            }
+            if (unrenderFunc) {
+                unrenderFunc.apply(thisContext, prevArgs);
+            }
+            prevArgs = null;
+        }
+    }
+    function res() {
+        if (!prevArgs || !isArraysEqual(prevArgs, arguments)) {
+            unrender();
+            thisContext = this;
+            prevArgs = arguments;
+            renderFunc.apply(this, arguments);
+        }
+    }
+    res.dependents = dependents;
+    res.unrender = unrender;
+    for (var _i = 0, dependencies_1 = dependencies; _i < dependencies_1.length; _i++) {
+        var dependency = dependencies_1[_i];
+        dependency.dependents.push(res);
+    }
+    return res;
+}
+
+var EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
+var Splitter = /** @class */ (function () {
+    function Splitter() {
+        this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
+        this.splitDateSelection = memoize(this._splitDateSpan);
+        this.splitEventStore = memoize(this._splitEventStore);
+        this.splitIndividualUi = memoize(this._splitIndividualUi);
+        this.splitEventDrag = memoize(this._splitInteraction);
+        this.splitEventResize = memoize(this._splitInteraction);
+        this.eventUiBuilders = {}; // TODO: typescript protection
+    }
+    Splitter.prototype.splitProps = function (props) {
+        var _this = this;
+        var keyInfos = this.getKeyInfo(props);
+        var defKeys = this.getKeysForEventDefs(props.eventStore);
+        var dateSelections = this.splitDateSelection(props.dateSelection);
+        var individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
+        var eventStores = this.splitEventStore(props.eventStore, defKeys);
+        var eventDrags = this.splitEventDrag(props.eventDrag);
+        var eventResizes = this.splitEventResize(props.eventResize);
+        var splitProps = {};
+        this.eventUiBuilders = mapHash(keyInfos, function (info, key) {
+            return _this.eventUiBuilders[key] || memoize(buildEventUiForKey);
+        });
+        for (var key in keyInfos) {
+            var keyInfo = keyInfos[key];
+            var eventStore = eventStores[key] || EMPTY_EVENT_STORE;
+            var buildEventUi = this.eventUiBuilders[key];
+            splitProps[key] = {
+                businessHours: keyInfo.businessHours || props.businessHours,
+                dateSelection: dateSelections[key] || null,
+                eventStore: eventStore,
+                eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
+                eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
+                eventDrag: eventDrags[key] || null,
+                eventResize: eventResizes[key] || null
+            };
+        }
+        return splitProps;
+    };
+    Splitter.prototype._splitDateSpan = function (dateSpan) {
+        var dateSpans = {};
+        if (dateSpan) {
+            var keys = this.getKeysForDateSpan(dateSpan);
+            for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
+                var key = keys_1[_i];
+                dateSpans[key] = dateSpan;
+            }
+        }
+        return dateSpans;
+    };
+    Splitter.prototype._getKeysForEventDefs = function (eventStore) {
+        var _this = this;
+        return mapHash(eventStore.defs, function (eventDef) {
+            return _this.getKeysForEventDef(eventDef);
+        });
+    };
+    Splitter.prototype._splitEventStore = function (eventStore, defKeys) {
+        var defs = eventStore.defs, instances = eventStore.instances;
+        var splitStores = {};
+        for (var defId in defs) {
+            for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) {
+                var key = _a[_i];
+                if (!splitStores[key]) {
+                    splitStores[key] = createEmptyEventStore();
+                }
+                splitStores[key].defs[defId] = defs[defId];
+            }
+        }
+        for (var instanceId in instances) {
+            var instance = instances[instanceId];
+            for (var _b = 0, _c = defKeys[instance.defId]; _b < _c.length; _b++) {
+                var key = _c[_b];
+                if (splitStores[key]) { // must have already been created
+                    splitStores[key].instances[instanceId] = instance;
+                }
+            }
+        }
+        return splitStores;
+    };
+    Splitter.prototype._splitIndividualUi = function (eventUiBases, defKeys) {
+        var splitHashes = {};
+        for (var defId in eventUiBases) {
+            if (defId) { // not the '' key
+                for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) {
+                    var key = _a[_i];
+                    if (!splitHashes[key]) {
+                        splitHashes[key] = {};
+                    }
+                    splitHashes[key][defId] = eventUiBases[defId];
+                }
+            }
+        }
+        return splitHashes;
+    };
+    Splitter.prototype._splitInteraction = function (interaction) {
+        var splitStates = {};
+        if (interaction) {
+            var affectedStores_1 = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents) // can't use cached. might be events from other calendar
+            );
+            // can't rely on defKeys because event data is mutated
+            var mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
+            var mutatedStores_1 = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
+            var populate = function (key) {
+                if (!splitStates[key]) {
+                    splitStates[key] = {
+                        affectedEvents: affectedStores_1[key] || EMPTY_EVENT_STORE,
+                        mutatedEvents: mutatedStores_1[key] || EMPTY_EVENT_STORE,
+                        isEvent: interaction.isEvent,
+                        origSeg: interaction.origSeg
+                    };
+                }
+            };
+            for (var key in affectedStores_1) {
+                populate(key);
+            }
+            for (var key in mutatedStores_1) {
+                populate(key);
+            }
+        }
+        return splitStates;
+    };
+    return Splitter;
+}());
+function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
+    var baseParts = [];
+    if (allUi) {
+        baseParts.push(allUi);
+    }
+    if (eventUiForKey) {
+        baseParts.push(eventUiForKey);
+    }
+    var stuff = {
+        '': combineEventUis(baseParts)
+    };
+    if (individualUi) {
+        __assign(stuff, individualUi);
+    }
+    return stuff;
+}
+
+// Generates HTML for an anchor to another view into the calendar.
+// Will either generate an <a> tag or a non-clickable <span> tag, depending on enabled settings.
+// `gotoOptions` can either be a DateMarker, or an object with the form:
+// { date, type, forceOff }
+// `type` is a view-type like "day" or "week". default value is "day".
+// `attrs` and `innerHtml` are use to generate the rest of the HTML tag.
+function buildGotoAnchorHtml(component, gotoOptions, attrs, innerHtml) {
+    var dateEnv = component.dateEnv;
+    var date;
+    var type;
+    var forceOff;
+    var finalOptions;
+    if (gotoOptions instanceof Date) {
+        date = gotoOptions; // a single date-like input
+    }
+    else {
+        date = gotoOptions.date;
+        type = gotoOptions.type;
+        forceOff = gotoOptions.forceOff;
+    }
+    finalOptions = {
+        date: dateEnv.formatIso(date, { omitTime: true }),
+        type: type || 'day'
+    };
+    if (typeof attrs === 'string') {
+        innerHtml = attrs;
+        attrs = null;
+    }
+    attrs = attrs ? ' ' + attrsToStr(attrs) : ''; // will have a leading space
+    innerHtml = innerHtml || '';
+    if (!forceOff && component.opt('navLinks')) {
+        return '<a' + attrs +
+            ' data-goto="' + htmlEscape(JSON.stringify(finalOptions)) + '">' +
+            innerHtml +
+            '</a>';
+    }
+    else {
+        return '<span' + attrs + '>' +
+            innerHtml +
+            '</span>';
+    }
+}
+function getAllDayHtml(component) {
+    return component.opt('allDayHtml') || htmlEscape(component.opt('allDayText'));
+}
+// Computes HTML classNames for a single-day element
+function getDayClasses(date, dateProfile, context, noThemeHighlight) {
+    var calendar = context.calendar, view = context.view, theme = context.theme, dateEnv = context.dateEnv;
+    var classes = [];
+    var todayStart;
+    var todayEnd;
+    if (!rangeContainsMarker(dateProfile.activeRange, date)) {
+        classes.push('fc-disabled-day');
+    }
+    else {
+        classes.push('fc-' + DAY_IDS[date.getUTCDay()]);
+        if (view.opt('monthMode') &&
+            dateEnv.getMonth(date) !== dateEnv.getMonth(dateProfile.currentRange.start)) {
+            classes.push('fc-other-month');
+        }
+        todayStart = startOfDay(calendar.getNow());
+        todayEnd = addDays(todayStart, 1);
+        if (date < todayStart) {
+            classes.push('fc-past');
+        }
+        else if (date >= todayEnd) {
+            classes.push('fc-future');
+        }
+        else {
+            classes.push('fc-today');
+            if (noThemeHighlight !== true) {
+                classes.push(theme.getClass('today'));
+            }
+        }
+    }
+    return classes;
+}
+
+// given a function that resolves a result asynchronously.
+// the function can either call passed-in success and failure callbacks,
+// or it can return a promise.
+// if you need to pass additional params to func, bind them first.
+function unpromisify(func, success, failure) {
+    // guard against success/failure callbacks being called more than once
+    // and guard against a promise AND callback being used together.
+    var isResolved = false;
+    var wrappedSuccess = function () {
+        if (!isResolved) {
+            isResolved = true;
+            success.apply(this, arguments);
+        }
+    };
+    var wrappedFailure = function () {
+        if (!isResolved) {
+            isResolved = true;
+            if (failure) {
+                failure.apply(this, arguments);
+            }
+        }
+    };
+    var res = func(wrappedSuccess, wrappedFailure);
+    if (res && typeof res.then === 'function') {
+        res.then(wrappedSuccess, wrappedFailure);
+    }
+}
+
+var Mixin = /** @class */ (function () {
+    function Mixin() {
+    }
+    // mix into a CLASS
+    Mixin.mixInto = function (destClass) {
+        this.mixIntoObj(destClass.prototype);
+    };
+    // mix into ANY object
+    Mixin.mixIntoObj = function (destObj) {
+        var _this = this;
+        Object.getOwnPropertyNames(this.prototype).forEach(function (name) {
+            if (!destObj[name]) { // if destination doesn't already define it
+                destObj[name] = _this.prototype[name];
+            }
+        });
+    };
+    /*
+    will override existing methods
+    TODO: remove! not used anymore
+    */
+    Mixin.mixOver = function (destClass) {
+        var _this = this;
+        Object.getOwnPropertyNames(this.prototype).forEach(function (name) {
+            destClass.prototype[name] = _this.prototype[name];
+        });
+    };
+    return Mixin;
+}());
+
+/*
+USAGE:
+  import { default as EmitterMixin, EmitterInterface } from './EmitterMixin'
+in class:
+  on: EmitterInterface['on']
+  one: EmitterInterface['one']
+  off: EmitterInterface['off']
+  trigger: EmitterInterface['trigger']
+  triggerWith: EmitterInterface['triggerWith']
+  hasHandlers: EmitterInterface['hasHandlers']
+after class:
+  EmitterMixin.mixInto(TheClass)
+*/
+var EmitterMixin = /** @class */ (function (_super) {
+    __extends(EmitterMixin, _super);
+    function EmitterMixin() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    EmitterMixin.prototype.on = function (type, handler) {
+        addToHash(this._handlers || (this._handlers = {}), type, handler);
+        return this; // for chaining
+    };
+    // todo: add comments
+    EmitterMixin.prototype.one = function (type, handler) {
+        addToHash(this._oneHandlers || (this._oneHandlers = {}), type, handler);
+        return this; // for chaining
+    };
+    EmitterMixin.prototype.off = function (type, handler) {
+        if (this._handlers) {
+            removeFromHash(this._handlers, type, handler);
+        }
+        if (this._oneHandlers) {
+            removeFromHash(this._oneHandlers, type, handler);
+        }
+        return this; // for chaining
+    };
+    EmitterMixin.prototype.trigger = function (type) {
+        var args = [];
+        for (var _i = 1; _i < arguments.length; _i++) {
+            args[_i - 1] = arguments[_i];
+        }
+        this.triggerWith(type, this, args);
+        return this; // for chaining
+    };
+    EmitterMixin.prototype.triggerWith = function (type, context, args) {
+        if (this._handlers) {
+            applyAll(this._handlers[type], context, args);
+        }
+        if (this._oneHandlers) {
+            applyAll(this._oneHandlers[type], context, args);
+            delete this._oneHandlers[type]; // will never fire again
+        }
+        return this; // for chaining
+    };
+    EmitterMixin.prototype.hasHandlers = function (type) {
+        return (this._handlers && this._handlers[type] && this._handlers[type].length) ||
+            (this._oneHandlers && this._oneHandlers[type] && this._oneHandlers[type].length);
+    };
+    return EmitterMixin;
+}(Mixin));
+function addToHash(hash, type, handler) {
+    (hash[type] || (hash[type] = []))
+        .push(handler);
+}
+function removeFromHash(hash, type, handler) {
+    if (handler) {
+        if (hash[type]) {
+            hash[type] = hash[type].filter(function (func) {
+                return func !== handler;
+            });
+        }
+    }
+    else {
+        delete hash[type]; // remove all handler funcs for this type
+    }
+}
+
+/*
+Records offset information for a set of elements, relative to an origin element.
+Can record the left/right OR the top/bottom OR both.
+Provides methods for querying the cache by position.
+*/
+var PositionCache = /** @class */ (function () {
+    function PositionCache(originEl, els, isHorizontal, isVertical) {
+        this.originEl = originEl;
+        this.els = els;
+        this.isHorizontal = isHorizontal;
+        this.isVertical = isVertical;
+    }
+    // Queries the els for coordinates and stores them.
+    // Call this method before using and of the get* methods below.
+    PositionCache.prototype.build = function () {
+        var originEl = this.originEl;
+        var originClientRect = this.originClientRect =
+            originEl.getBoundingClientRect(); // relative to viewport top-left
+        if (this.isHorizontal) {
+            this.buildElHorizontals(originClientRect.left);
+        }
+        if (this.isVertical) {
+            this.buildElVerticals(originClientRect.top);
+        }
+    };
+    // Populates the left/right internal coordinate arrays
+    PositionCache.prototype.buildElHorizontals = function (originClientLeft) {
+        var lefts = [];
+        var rights = [];
+        for (var _i = 0, _a = this.els; _i < _a.length; _i++) {
+            var el = _a[_i];
+            var rect = el.getBoundingClientRect();
+            lefts.push(rect.left - originClientLeft);
+            rights.push(rect.right - originClientLeft);
+        }
+        this.lefts = lefts;
+        this.rights = rights;
+    };
+    // Populates the top/bottom internal coordinate arrays
+    PositionCache.prototype.buildElVerticals = function (originClientTop) {
+        var tops = [];
+        var bottoms = [];
+        for (var _i = 0, _a = this.els; _i < _a.length; _i++) {
+            var el = _a[_i];
+            var rect = el.getBoundingClientRect();
+            tops.push(rect.top - originClientTop);
+            bottoms.push(rect.bottom - originClientTop);
+        }
+        this.tops = tops;
+        this.bottoms = bottoms;
+    };
+    // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
+    // If no intersection is made, returns undefined.
+    PositionCache.prototype.leftToIndex = function (leftPosition) {
+        var lefts = this.lefts;
+        var rights = this.rights;
+        var len = lefts.length;
+        var i;
+        for (i = 0; i < len; i++) {
+            if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
+                return i;
+            }
+        }
+    };
+    // Given a top offset (from document top), returns the index of the el that it vertically intersects.
+    // If no intersection is made, returns undefined.
+    PositionCache.prototype.topToIndex = function (topPosition) {
+        var tops = this.tops;
+        var bottoms = this.bottoms;
+        var len = tops.length;
+        var i;
+        for (i = 0; i < len; i++) {
+            if (topPosition >= tops[i] && topPosition < bottoms[i]) {
+                return i;
+            }
+        }
+    };
+    // Gets the width of the element at the given index
+    PositionCache.prototype.getWidth = function (leftIndex) {
+        return this.rights[leftIndex] - this.lefts[leftIndex];
+    };
+    // Gets the height of the element at the given index
+    PositionCache.prototype.getHeight = function (topIndex) {
+        return this.bottoms[topIndex] - this.tops[topIndex];
+    };
+    return PositionCache;
+}());
+
+/*
+An object for getting/setting scroll-related information for an element.
+Internally, this is done very differently for window versus DOM element,
+so this object serves as a common interface.
+*/
+var ScrollController = /** @class */ (function () {
+    function ScrollController() {
+    }
+    ScrollController.prototype.getMaxScrollTop = function () {
+        return this.getScrollHeight() - this.getClientHeight();
+    };
+    ScrollController.prototype.getMaxScrollLeft = function () {
+        return this.getScrollWidth() - this.getClientWidth();
+    };
+    ScrollController.prototype.canScrollVertically = function () {
+        return this.getMaxScrollTop() > 0;
+    };
+    ScrollController.prototype.canScrollHorizontally = function () {
+        return this.getMaxScrollLeft() > 0;
+    };
+    ScrollController.prototype.canScrollUp = function () {
+        return this.getScrollTop() > 0;
+    };
+    ScrollController.prototype.canScrollDown = function () {
+        return this.getScrollTop() < this.getMaxScrollTop();
+    };
+    ScrollController.prototype.canScrollLeft = function () {
+        return this.getScrollLeft() > 0;
+    };
+    ScrollController.prototype.canScrollRight = function () {
+        return this.getScrollLeft() < this.getMaxScrollLeft();
+    };
+    return ScrollController;
+}());
+var ElementScrollController = /** @class */ (function (_super) {
+    __extends(ElementScrollController, _super);
+    function ElementScrollController(el) {
+        var _this = _super.call(this) || this;
+        _this.el = el;
+        return _this;
+    }
+    ElementScrollController.prototype.getScrollTop = function () {
+        return this.el.scrollTop;
+    };
+    ElementScrollController.prototype.getScrollLeft = function () {
+        return this.el.scrollLeft;
+    };
+    ElementScrollController.prototype.setScrollTop = function (top) {
+        this.el.scrollTop = top;
+    };
+    ElementScrollController.prototype.setScrollLeft = function (left) {
+        this.el.scrollLeft = left;
+    };
+    ElementScrollController.prototype.getScrollWidth = function () {
+        return this.el.scrollWidth;
+    };
+    ElementScrollController.prototype.getScrollHeight = function () {
+        return this.el.scrollHeight;
+    };
+    ElementScrollController.prototype.getClientHeight = function () {
+        return this.el.clientHeight;
+    };
+    ElementScrollController.prototype.getClientWidth = function () {
+        return this.el.clientWidth;
+    };
+    return ElementScrollController;
+}(ScrollController));
+var WindowScrollController = /** @class */ (function (_super) {
+    __extends(WindowScrollController, _super);
+    function WindowScrollController() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    WindowScrollController.prototype.getScrollTop = function () {
+        return window.pageYOffset;
+    };
+    WindowScrollController.prototype.getScrollLeft = function () {
+        return window.pageXOffset;
+    };
+    WindowScrollController.prototype.setScrollTop = function (n) {
+        window.scroll(window.pageXOffset, n);
+    };
+    WindowScrollController.prototype.setScrollLeft = function (n) {
+        window.scroll(n, window.pageYOffset);
+    };
+    WindowScrollController.prototype.getScrollWidth = function () {
+        return document.documentElement.scrollWidth;
+    };
+    WindowScrollController.prototype.getScrollHeight = function () {
+        return document.documentElement.scrollHeight;
+    };
+    WindowScrollController.prototype.getClientHeight = function () {
+        return document.documentElement.clientHeight;
+    };
+    WindowScrollController.prototype.getClientWidth = function () {
+        return document.documentElement.clientWidth;
+    };
+    return WindowScrollController;
+}(ScrollController));
+
+/*
+Embodies a div that has potential scrollbars
+*/
+var ScrollComponent = /** @class */ (function (_super) {
+    __extends(ScrollComponent, _super);
+    function ScrollComponent(overflowX, overflowY) {
+        var _this = _super.call(this, createElement('div', {
+            className: 'fc-scroller'
+        })) || this;
+        _this.overflowX = overflowX;
+        _this.overflowY = overflowY;
+        _this.applyOverflow();
+        return _this;
+    }
+    // sets to natural height, unlocks overflow
+    ScrollComponent.prototype.clear = function () {
+        this.setHeight('auto');
+        this.applyOverflow();
+    };
+    ScrollComponent.prototype.destroy = function () {
+        removeElement(this.el);
+    };
+    // Overflow
+    // -----------------------------------------------------------------------------------------------------------------
+    ScrollComponent.prototype.applyOverflow = function () {
+        applyStyle(this.el, {
+            overflowX: this.overflowX,
+            overflowY: this.overflowY
+        });
+    };
+    // Causes any 'auto' overflow values to resolves to 'scroll' or 'hidden'.
+    // Useful for preserving scrollbar widths regardless of future resizes.
+    // Can pass in scrollbarWidths for optimization.
+    ScrollComponent.prototype.lockOverflow = function (scrollbarWidths) {
+        var overflowX = this.overflowX;
+        var overflowY = this.overflowY;
+        scrollbarWidths = scrollbarWidths || this.getScrollbarWidths();
+        if (overflowX === 'auto') {
+            overflowX = (scrollbarWidths.bottom || // horizontal scrollbars?
+                this.canScrollHorizontally() // OR scrolling pane with massless scrollbars?
+            ) ? 'scroll' : 'hidden';
+        }
+        if (overflowY === 'auto') {
+            overflowY = (scrollbarWidths.left || scrollbarWidths.right || // horizontal scrollbars?
+                this.canScrollVertically() // OR scrolling pane with massless scrollbars?
+            ) ? 'scroll' : 'hidden';
+        }
+        applyStyle(this.el, { overflowX: overflowX, overflowY: overflowY });
+    };
+    ScrollComponent.prototype.setHeight = function (height) {
+        applyStyleProp(this.el, 'height', height);
+    };
+    ScrollComponent.prototype.getScrollbarWidths = function () {
+        var edges = computeEdges(this.el);
+        return {
+            left: edges.scrollbarLeft,
+            right: edges.scrollbarRight,
+            bottom: edges.scrollbarBottom
+        };
+    };
+    return ScrollComponent;
+}(ElementScrollController));
+
+var Theme = /** @class */ (function () {
+    function Theme(calendarOptions) {
+        this.calendarOptions = calendarOptions;
+        this.processIconOverride();
+    }
+    Theme.prototype.processIconOverride = function () {
+        if (this.iconOverrideOption) {
+            this.setIconOverride(this.calendarOptions[this.iconOverrideOption]);
+        }
+    };
+    Theme.prototype.setIconOverride = function (iconOverrideHash) {
+        var iconClassesCopy;
+        var buttonName;
+        if (typeof iconOverrideHash === 'object' && iconOverrideHash) { // non-null object
+            iconClassesCopy = __assign({}, this.iconClasses);
+            for (buttonName in iconOverrideHash) {
+                iconClassesCopy[buttonName] = this.applyIconOverridePrefix(iconOverrideHash[buttonName]);
+            }
+            this.iconClasses = iconClassesCopy;
+        }
+        else if (iconOverrideHash === false) {
+            this.iconClasses = {};
+        }
+    };
+    Theme.prototype.applyIconOverridePrefix = function (className) {
+        var prefix = this.iconOverridePrefix;
+        if (prefix && className.indexOf(prefix) !== 0) { // if not already present
+            className = prefix + className;
+        }
+        return className;
+    };
+    Theme.prototype.getClass = function (key) {
+        return this.classes[key] || '';
+    };
+    Theme.prototype.getIconClass = function (buttonName) {
+        var className = this.iconClasses[buttonName];
+        if (className) {
+            return this.baseIconClass + ' ' + className;
+        }
+        return '';
+    };
+    Theme.prototype.getCustomButtonIconClass = function (customButtonProps) {
+        var className;
+        if (this.iconOverrideCustomButtonOption) {
+            className = customButtonProps[this.iconOverrideCustomButtonOption];
+            if (className) {
+                return this.baseIconClass + ' ' + this.applyIconOverridePrefix(className);
+            }
+        }
+        return '';
+    };
+    return Theme;
+}());
+Theme.prototype.classes = {};
+Theme.prototype.iconClasses = {};
+Theme.prototype.baseIconClass = '';
+Theme.prototype.iconOverridePrefix = '';
+
+var guid = 0;
+var Component = /** @class */ (function () {
+    function Component(context, isView) {
+        // HACK to populate view at top of component instantiation call chain
+        if (isView) {
+            context.view = this;
+        }
+        this.uid = String(guid++);
+        this.context = context;
+        this.dateEnv = context.dateEnv;
+        this.theme = context.theme;
+        this.view = context.view;
+        this.calendar = context.calendar;
+        this.isRtl = this.opt('dir') === 'rtl';
+    }
+    Component.addEqualityFuncs = function (newFuncs) {
+        this.prototype.equalityFuncs = __assign({}, this.prototype.equalityFuncs, newFuncs);
+    };
+    Component.prototype.opt = function (name) {
+        return this.context.options[name];
+    };
+    Component.prototype.receiveProps = function (props) {
+        var _a = recycleProps(this.props || {}, props, this.equalityFuncs), anyChanges = _a.anyChanges, comboProps = _a.comboProps;
+        this.props = comboProps;
+        if (anyChanges) {
+            this.render(comboProps);
+        }
+    };
+    Component.prototype.render = function (props) {
+    };
+    // after destroy is called, this component won't ever be used again
+    Component.prototype.destroy = function () {
+    };
+    return Component;
+}());
+Component.prototype.equalityFuncs = {};
+/*
+Reuses old values when equal. If anything is unequal, returns newProps as-is.
+Great for PureComponent, but won't be feasible with React, so just eliminate and use React's DOM diffing.
+*/
+function recycleProps(oldProps, newProps, equalityFuncs) {
+    var comboProps = {}; // some old, some new
+    var anyChanges = false;
+    for (var key in newProps) {
+        if (key in oldProps && (oldProps[key] === newProps[key] ||
+            (equalityFuncs[key] && equalityFuncs[key](oldProps[key], newProps[key])))) {
+            // equal to old? use old prop
+            comboProps[key] = oldProps[key];
+        }
+        else {
+            comboProps[key] = newProps[key];
+            anyChanges = true;
+        }
+    }
+    for (var key in oldProps) {
+        if (!(key in newProps)) {
+            anyChanges = true;
+            break;
+        }
+    }
+    return { anyChanges: anyChanges, comboProps: comboProps };
+}
+
+/*
+PURPOSES:
+- hook up to fg, fill, and mirror renderers
+- interface for dragging and hits
+*/
+var DateComponent = /** @class */ (function (_super) {
+    __extends(DateComponent, _super);
+    function DateComponent(context, el, isView) {
+        var _this = _super.call(this, context, isView) || this;
+        _this.el = el;
+        return _this;
+    }
+    DateComponent.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        removeElement(this.el);
+    };
+    // TODO: WHAT ABOUT (sourceSeg && sourceSeg.component.doesDragMirror)
+    //
+    // Event Drag-n-Drop Rendering (for both events and external elements)
+    // ---------------------------------------------------------------------------------------------------------------
+    /*
+    renderEventDragSegs(state: EventSegUiInteractionState) {
+      if (state) {
+        let { isEvent, segs, sourceSeg } = state
+  
+        if (this.eventRenderer) {
+          this.eventRenderer.hideByHash(state.affectedInstances)
+        }
+  
+        // if the user is dragging something that is considered an event with real event data,
+        // and this component likes to do drag mirrors OR the component where the seg came from
+        // likes to do drag mirrors, then render a drag mirror.
+        if (isEvent && (this.doesDragMirror || sourceSeg && sourceSeg.component.doesDragMirror)) {
+          if (this.mirrorRenderer) {
+            this.mirrorRenderer.renderSegs(segs, { isDragging: true, sourceSeg })
+          }
+        }
+  
+        // if it would be impossible to render a drag mirror OR this component likes to render
+        // highlights, then render a highlight.
+        if (!isEvent || this.doesDragHighlight) {
+          if (this.fillRenderer) {
+            this.fillRenderer.renderSegs('highlight', segs)
+          }
+        }
+      }
+    }
+    */
+    // Hit System
+    // -----------------------------------------------------------------------------------------------------------------
+    DateComponent.prototype.buildPositionCaches = function () {
+    };
+    DateComponent.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+        return null; // this should be abstract
+    };
+    // Validation
+    // -----------------------------------------------------------------------------------------------------------------
+    DateComponent.prototype.isInteractionValid = function (interaction) {
+        var calendar = this.calendar;
+        var dateProfile = this.props.dateProfile; // HACK
+        var instances = interaction.mutatedEvents.instances;
+        if (dateProfile) { // HACK for DayTile
+            for (var instanceId in instances) {
+                if (!rangeContainsRange(dateProfile.validRange, instances[instanceId].range)) {
+                    return false;
+                }
+            }
+        }
+        return isInteractionValid(interaction, calendar);
+    };
+    DateComponent.prototype.isDateSelectionValid = function (selection) {
+        var dateProfile = this.props.dateProfile; // HACK
+        if (dateProfile && // HACK for DayTile
+            !rangeContainsRange(dateProfile.validRange, selection.range)) {
+            return false;
+        }
+        return isDateSelectionValid(selection, this.calendar);
+    };
+    // Triggering
+    // -----------------------------------------------------------------------------------------------------------------
+    // TODO: move to Calendar
+    DateComponent.prototype.publiclyTrigger = function (name, args) {
+        var calendar = this.calendar;
+        return calendar.publiclyTrigger(name, args);
+    };
+    DateComponent.prototype.publiclyTriggerAfterSizing = function (name, args) {
+        var calendar = this.calendar;
+        return calendar.publiclyTriggerAfterSizing(name, args);
+    };
+    DateComponent.prototype.hasPublicHandlers = function (name) {
+        var calendar = this.calendar;
+        return calendar.hasPublicHandlers(name);
+    };
+    DateComponent.prototype.triggerRenderedSegs = function (segs, isMirrors) {
+        var calendar = this.calendar;
+        if (this.hasPublicHandlers('eventPositioned')) {
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                this.publiclyTriggerAfterSizing('eventPositioned', [
+                    {
+                        event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance),
+                        isMirror: isMirrors,
+                        isStart: seg.isStart,
+                        isEnd: seg.isEnd,
+                        el: seg.el,
+                        view: this // safe to cast because this method is only called on context.view
+                    }
+                ]);
+            }
+        }
+        if (!calendar.state.loadingLevel) { // avoid initial empty state while pending
+            calendar.afterSizingTriggers._eventsPositioned = [null]; // fire once
+        }
+    };
+    DateComponent.prototype.triggerWillRemoveSegs = function (segs, isMirrors) {
+        var calendar = this.calendar;
+        for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+            var seg = segs_2[_i];
+            calendar.trigger('eventElRemove', seg.el);
+        }
+        if (this.hasPublicHandlers('eventDestroy')) {
+            for (var _a = 0, segs_3 = segs; _a < segs_3.length; _a++) {
+                var seg = segs_3[_a];
+                this.publiclyTrigger('eventDestroy', [
+                    {
+                        event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance),
+                        isMirror: isMirrors,
+                        el: seg.el,
+                        view: this // safe to cast because this method is only called on context.view
+                    }
+                ]);
+            }
+        }
+    };
+    // Pointer Interaction Utils
+    // -----------------------------------------------------------------------------------------------------------------
+    DateComponent.prototype.isValidSegDownEl = function (el) {
+        return !this.props.eventDrag && // HACK
+            !this.props.eventResize && // HACK
+            !elementClosest(el, '.fc-mirror') &&
+            (this.isPopover() || !this.isInPopover(el));
+        // ^above line ensures we don't detect a seg interaction within a nested component.
+        // it's a HACK because it only supports a popover as the nested component.
+    };
+    DateComponent.prototype.isValidDateDownEl = function (el) {
+        var segEl = elementClosest(el, this.fgSegSelector);
+        return (!segEl || segEl.classList.contains('fc-mirror')) &&
+            !elementClosest(el, '.fc-more') && // a "more.." link
+            !elementClosest(el, 'a[data-goto]') && // a clickable nav link
+            !this.isInPopover(el);
+    };
+    DateComponent.prototype.isPopover = function () {
+        return this.el.classList.contains('fc-popover');
+    };
+    DateComponent.prototype.isInPopover = function (el) {
+        return Boolean(elementClosest(el, '.fc-popover'));
+    };
+    return DateComponent;
+}(Component));
+DateComponent.prototype.fgSegSelector = '.fc-event-container > *';
+DateComponent.prototype.bgSegSelector = '.fc-bgevent:not(.fc-nonbusiness)';
+
+var uid$1 = 0;
+function createPlugin(input) {
+    return {
+        id: String(uid$1++),
+        deps: input.deps || [],
+        reducers: input.reducers || [],
+        eventDefParsers: input.eventDefParsers || [],
+        isDraggableTransformers: input.isDraggableTransformers || [],
+        eventDragMutationMassagers: input.eventDragMutationMassagers || [],
+        eventDefMutationAppliers: input.eventDefMutationAppliers || [],
+        dateSelectionTransformers: input.dateSelectionTransformers || [],
+        datePointTransforms: input.datePointTransforms || [],
+        dateSpanTransforms: input.dateSpanTransforms || [],
+        views: input.views || {},
+        viewPropsTransformers: input.viewPropsTransformers || [],
+        isPropsValid: input.isPropsValid || null,
+        externalDefTransforms: input.externalDefTransforms || [],
+        eventResizeJoinTransforms: input.eventResizeJoinTransforms || [],
+        viewContainerModifiers: input.viewContainerModifiers || [],
+        eventDropTransformers: input.eventDropTransformers || [],
+        componentInteractions: input.componentInteractions || [],
+        calendarInteractions: input.calendarInteractions || [],
+        themeClasses: input.themeClasses || {},
+        eventSourceDefs: input.eventSourceDefs || [],
+        cmdFormatter: input.cmdFormatter,
+        recurringTypes: input.recurringTypes || [],
+        namedTimeZonedImpl: input.namedTimeZonedImpl,
+        defaultView: input.defaultView || '',
+        elementDraggingImpl: input.elementDraggingImpl,
+        optionChangeHandlers: input.optionChangeHandlers || {}
+    };
+}
+var PluginSystem = /** @class */ (function () {
+    function PluginSystem() {
+        this.hooks = {
+            reducers: [],
+            eventDefParsers: [],
+            isDraggableTransformers: [],
+            eventDragMutationMassagers: [],
+            eventDefMutationAppliers: [],
+            dateSelectionTransformers: [],
+            datePointTransforms: [],
+            dateSpanTransforms: [],
+            views: {},
+            viewPropsTransformers: [],
+            isPropsValid: null,
+            externalDefTransforms: [],
+            eventResizeJoinTransforms: [],
+            viewContainerModifiers: [],
+            eventDropTransformers: [],
+            componentInteractions: [],
+            calendarInteractions: [],
+            themeClasses: {},
+            eventSourceDefs: [],
+            cmdFormatter: null,
+            recurringTypes: [],
+            namedTimeZonedImpl: null,
+            defaultView: '',
+            elementDraggingImpl: null,
+            optionChangeHandlers: {}
+        };
+        this.addedHash = {};
+    }
+    PluginSystem.prototype.add = function (plugin) {
+        if (!this.addedHash[plugin.id]) {
+            this.addedHash[plugin.id] = true;
+            for (var _i = 0, _a = plugin.deps; _i < _a.length; _i++) {
+                var dep = _a[_i];
+                this.add(dep);
+            }
+            this.hooks = combineHooks(this.hooks, plugin);
+        }
+    };
+    return PluginSystem;
+}());
+function combineHooks(hooks0, hooks1) {
+    return {
+        reducers: hooks0.reducers.concat(hooks1.reducers),
+        eventDefParsers: hooks0.eventDefParsers.concat(hooks1.eventDefParsers),
+        isDraggableTransformers: hooks0.isDraggableTransformers.concat(hooks1.isDraggableTransformers),
+        eventDragMutationMassagers: hooks0.eventDragMutationMassagers.concat(hooks1.eventDragMutationMassagers),
+        eventDefMutationAppliers: hooks0.eventDefMutationAppliers.concat(hooks1.eventDefMutationAppliers),
+        dateSelectionTransformers: hooks0.dateSelectionTransformers.concat(hooks1.dateSelectionTransformers),
+        datePointTransforms: hooks0.datePointTransforms.concat(hooks1.datePointTransforms),
+        dateSpanTransforms: hooks0.dateSpanTransforms.concat(hooks1.dateSpanTransforms),
+        views: __assign({}, hooks0.views, hooks1.views),
+        viewPropsTransformers: hooks0.viewPropsTransformers.concat(hooks1.viewPropsTransformers),
+        isPropsValid: hooks1.isPropsValid || hooks0.isPropsValid,
+        externalDefTransforms: hooks0.externalDefTransforms.concat(hooks1.externalDefTransforms),
+        eventResizeJoinTransforms: hooks0.eventResizeJoinTransforms.concat(hooks1.eventResizeJoinTransforms),
+        viewContainerModifiers: hooks0.viewContainerModifiers.concat(hooks1.viewContainerModifiers),
+        eventDropTransformers: hooks0.eventDropTransformers.concat(hooks1.eventDropTransformers),
+        calendarInteractions: hooks0.calendarInteractions.concat(hooks1.calendarInteractions),
+        componentInteractions: hooks0.componentInteractions.concat(hooks1.componentInteractions),
+        themeClasses: __assign({}, hooks0.themeClasses, hooks1.themeClasses),
+        eventSourceDefs: hooks0.eventSourceDefs.concat(hooks1.eventSourceDefs),
+        cmdFormatter: hooks1.cmdFormatter || hooks0.cmdFormatter,
+        recurringTypes: hooks0.recurringTypes.concat(hooks1.recurringTypes),
+        namedTimeZonedImpl: hooks1.namedTimeZonedImpl || hooks0.namedTimeZonedImpl,
+        defaultView: hooks0.defaultView || hooks1.defaultView,
+        elementDraggingImpl: hooks0.elementDraggingImpl || hooks1.elementDraggingImpl,
+        optionChangeHandlers: __assign({}, hooks0.optionChangeHandlers, hooks1.optionChangeHandlers)
+    };
+}
+
+var eventSourceDef = {
+    ignoreRange: true,
+    parseMeta: function (raw) {
+        if (Array.isArray(raw)) { // short form
+            return raw;
+        }
+        else if (Array.isArray(raw.events)) {
+            return raw.events;
+        }
+        return null;
+    },
+    fetch: function (arg, success) {
+        success({
+            rawEvents: arg.eventSource.meta
+        });
+    }
+};
+var ArrayEventSourcePlugin = createPlugin({
+    eventSourceDefs: [eventSourceDef]
+});
+
+var eventSourceDef$1 = {
+    parseMeta: function (raw) {
+        if (typeof raw === 'function') { // short form
+            return raw;
+        }
+        else if (typeof raw.events === 'function') {
+            return raw.events;
+        }
+        return null;
+    },
+    fetch: function (arg, success, failure) {
+        var dateEnv = arg.calendar.dateEnv;
+        var func = arg.eventSource.meta;
+        unpromisify(func.bind(null, {
+            start: dateEnv.toDate(arg.range.start),
+            end: dateEnv.toDate(arg.range.end),
+            startStr: dateEnv.formatIso(arg.range.start),
+            endStr: dateEnv.formatIso(arg.range.end),
+            timeZone: dateEnv.timeZone
+        }), function (rawEvents) {
+            success({ rawEvents: rawEvents }); // needs an object response
+        }, failure // send errorObj directly to failure callback
+        );
+    }
+};
+var FuncEventSourcePlugin = createPlugin({
+    eventSourceDefs: [eventSourceDef$1]
+});
+
+function requestJson(method, url, params, successCallback, failureCallback) {
+    method = method.toUpperCase();
+    var body = null;
+    if (method === 'GET') {
+        url = injectQueryStringParams(url, params);
+    }
+    else {
+        body = encodeParams(params);
+    }
+    var xhr = new XMLHttpRequest();
+    xhr.open(method, url, true);
+    if (method !== 'GET') {
+        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+    }
+    xhr.onload = function () {
+        if (xhr.status >= 200 && xhr.status < 400) {
+            try {
+                var res = JSON.parse(xhr.responseText);
+                successCallback(res, xhr);
+            }
+            catch (err) {
+                failureCallback('Failure parsing JSON', xhr);
+            }
+        }
+        else {
+            failureCallback('Request failed', xhr);
+        }
+    };
+    xhr.onerror = function () {
+        failureCallback('Request failed', xhr);
+    };
+    xhr.send(body);
+}
+function injectQueryStringParams(url, params) {
+    return url +
+        (url.indexOf('?') === -1 ? '?' : '&') +
+        encodeParams(params);
+}
+function encodeParams(params) {
+    var parts = [];
+    for (var key in params) {
+        parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
+    }
+    return parts.join('&');
+}
+
+var eventSourceDef$2 = {
+    parseMeta: function (raw) {
+        if (typeof raw === 'string') { // short form
+            raw = { url: raw };
+        }
+        else if (!raw || typeof raw !== 'object' || !raw.url) {
+            return null;
+        }
+        return {
+            url: raw.url,
+            method: (raw.method || 'GET').toUpperCase(),
+            extraParams: raw.extraParams,
+            startParam: raw.startParam,
+            endParam: raw.endParam,
+            timeZoneParam: raw.timeZoneParam
+        };
+    },
+    fetch: function (arg, success, failure) {
+        var meta = arg.eventSource.meta;
+        var requestParams = buildRequestParams(meta, arg.range, arg.calendar);
+        requestJson(meta.method, meta.url, requestParams, function (rawEvents, xhr) {
+            success({ rawEvents: rawEvents, xhr: xhr });
+        }, function (errorMessage, xhr) {
+            failure({ message: errorMessage, xhr: xhr });
+        });
+    }
+};
+var JsonFeedEventSourcePlugin = createPlugin({
+    eventSourceDefs: [eventSourceDef$2]
+});
+function buildRequestParams(meta, range, calendar) {
+    var dateEnv = calendar.dateEnv;
+    var startParam;
+    var endParam;
+    var timeZoneParam;
+    var customRequestParams;
+    var params = {};
+    startParam = meta.startParam;
+    if (startParam == null) {
+        startParam = calendar.opt('startParam');
+    }
+    endParam = meta.endParam;
+    if (endParam == null) {
+        endParam = calendar.opt('endParam');
+    }
+    timeZoneParam = meta.timeZoneParam;
+    if (timeZoneParam == null) {
+        timeZoneParam = calendar.opt('timeZoneParam');
+    }
+    // retrieve any outbound GET/POST data from the options
+    if (typeof meta.extraParams === 'function') {
+        // supplied as a function that returns a key/value object
+        customRequestParams = meta.extraParams();
+    }
+    else {
+        // probably supplied as a straight key/value object
+        customRequestParams = meta.extraParams || {};
+    }
+    __assign(params, customRequestParams);
+    params[startParam] = dateEnv.formatIso(range.start);
+    params[endParam] = dateEnv.formatIso(range.end);
+    if (dateEnv.timeZone !== 'local') {
+        params[timeZoneParam] = dateEnv.timeZone;
+    }
+    return params;
+}
+
+var recurring = {
+    parse: function (rawEvent, leftoverProps, dateEnv) {
+        var createMarker = dateEnv.createMarker.bind(dateEnv);
+        var processors = {
+            daysOfWeek: null,
+            startTime: createDuration,
+            endTime: createDuration,
+            startRecur: createMarker,
+            endRecur: createMarker
+        };
+        var props = refineProps(rawEvent, processors, {}, leftoverProps);
+        var anyValid = false;
+        for (var propName in props) {
+            if (props[propName] != null) {
+                anyValid = true;
+                break;
+            }
+        }
+        if (anyValid) {
+            var duration = null;
+            if ('duration' in leftoverProps) {
+                duration = createDuration(leftoverProps.duration);
+                delete leftoverProps.duration;
+            }
+            if (!duration && props.startTime && props.endTime) {
+                duration = subtractDurations(props.endTime, props.startTime);
+            }
+            return {
+                allDayGuess: Boolean(!props.startTime && !props.endTime),
+                duration: duration,
+                typeData: props // doesn't need endTime anymore but oh well
+            };
+        }
+        return null;
+    },
+    expand: function (typeData, framingRange, dateEnv) {
+        var clippedFramingRange = intersectRanges(framingRange, { start: typeData.startRecur, end: typeData.endRecur });
+        if (clippedFramingRange) {
+            return expandRanges(typeData.daysOfWeek, typeData.startTime, clippedFramingRange, dateEnv);
+        }
+        else {
+            return [];
+        }
+    }
+};
+var SimpleRecurrencePlugin = createPlugin({
+    recurringTypes: [recurring]
+});
+function expandRanges(daysOfWeek, startTime, framingRange, dateEnv) {
+    var dowHash = daysOfWeek ? arrayToHash(daysOfWeek) : null;
+    var dayMarker = startOfDay(framingRange.start);
+    var endMarker = framingRange.end;
+    var instanceStarts = [];
+    while (dayMarker < endMarker) {
+        var instanceStart 
+        // if everyday, or this particular day-of-week
+        = void 0;
+        // if everyday, or this particular day-of-week
+        if (!dowHash || dowHash[dayMarker.getUTCDay()]) {
+            if (startTime) {
+                instanceStart = dateEnv.add(dayMarker, startTime);
+            }
+            else {
+                instanceStart = dayMarker;
+            }
+            instanceStarts.push(instanceStart);
+        }
+        dayMarker = addDays(dayMarker, 1);
+    }
+    return instanceStarts;
+}
+
+var DefaultOptionChangeHandlers = createPlugin({
+    optionChangeHandlers: {
+        events: function (events, calendar, deepEqual) {
+            handleEventSources([events], calendar, deepEqual);
+        },
+        eventSources: handleEventSources,
+        plugins: handlePlugins
+    }
+});
+function handleEventSources(inputs, calendar, deepEqual) {
+    var unfoundSources = hashValuesToArray(calendar.state.eventSources);
+    var newInputs = [];
+    for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) {
+        var input = inputs_1[_i];
+        var inputFound = false;
+        for (var i = 0; i < unfoundSources.length; i++) {
+            if (deepEqual(unfoundSources[i]._raw, input)) {
+                unfoundSources.splice(i, 1); // delete
+                inputFound = true;
+                break;
+            }
+        }
+        if (!inputFound) {
+            newInputs.push(input);
+        }
+    }
+    for (var _a = 0, unfoundSources_1 = unfoundSources; _a < unfoundSources_1.length; _a++) {
+        var unfoundSource = unfoundSources_1[_a];
+        calendar.dispatch({
+            type: 'REMOVE_EVENT_SOURCE',
+            sourceId: unfoundSource.sourceId
+        });
+    }
+    for (var _b = 0, newInputs_1 = newInputs; _b < newInputs_1.length; _b++) {
+        var newInput = newInputs_1[_b];
+        calendar.addEventSource(newInput);
+    }
+}
+// shortcoming: won't remove plugins
+function handlePlugins(inputs, calendar) {
+    calendar.addPluginInputs(inputs); // will gracefully handle duplicates
+}
+
+var config = {}; // TODO: make these options
+var globalDefaults = {
+    defaultRangeSeparator: ' - ',
+    titleRangeSeparator: ' \u2013 ',
+    defaultTimedEventDuration: '01:00:00',
+    defaultAllDayEventDuration: { day: 1 },
+    forceEventDuration: false,
+    nextDayThreshold: '00:00:00',
+    // display
+    columnHeader: true,
+    defaultView: '',
+    aspectRatio: 1.35,
+    header: {
+        left: 'title',
+        center: '',
+        right: 'today prev,next'
+    },
+    weekends: true,
+    weekNumbers: false,
+    weekNumberCalculation: 'local',
+    editable: false,
+    // nowIndicator: false,
+    scrollTime: '06:00:00',
+    minTime: '00:00:00',
+    maxTime: '24:00:00',
+    showNonCurrentDates: true,
+    // event ajax
+    lazyFetching: true,
+    startParam: 'start',
+    endParam: 'end',
+    timeZoneParam: 'timeZone',
+    timeZone: 'local',
+    // allDayDefault: undefined,
+    // locale
+    locales: [],
+    locale: '',
+    // dir: will get this from the default locale
+    // buttonIcons: null,
+    // allows setting a min-height to the event segment to prevent short events overlapping each other
+    timeGridEventMinHeight: 0,
+    themeSystem: 'standard',
+    // eventResizableFromStart: false,
+    dragRevertDuration: 500,
+    dragScroll: true,
+    allDayMaintainDuration: false,
+    // selectable: false,
+    unselectAuto: true,
+    // selectMinDistance: 0,
+    dropAccept: '*',
+    eventOrder: 'start,-duration,allDay,title',
+    // ^ if start tie, longer events go before shorter. final tie-breaker is title text
+    // rerenderDelay: null,
+    eventLimit: false,
+    eventLimitClick: 'popover',
+    dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' },
+    handleWindowResize: true,
+    windowResizeDelay: 100,
+    longPressDelay: 1000,
+    eventDragMinDistance: 5 // only applies to mouse
+};
+var rtlDefaults = {
+    header: {
+        left: 'next,prev today',
+        center: '',
+        right: 'title'
+    },
+    buttonIcons: {
+        // TODO: make RTL support the responibility of the theme
+        prev: 'fc-icon-chevron-right',
+        next: 'fc-icon-chevron-left',
+        prevYear: 'fc-icon-chevrons-right',
+        nextYear: 'fc-icon-chevrons-left'
+    }
+};
+var complexOptions = [
+    'header',
+    'footer',
+    'buttonText',
+    'buttonIcons'
+];
+// Merges an array of option objects into a single object
+function mergeOptions(optionObjs) {
+    return mergeProps(optionObjs, complexOptions);
+}
+// TODO: move this stuff to a "plugin"-related file...
+var INTERNAL_PLUGINS = [
+    ArrayEventSourcePlugin,
+    FuncEventSourcePlugin,
+    JsonFeedEventSourcePlugin,
+    SimpleRecurrencePlugin,
+    DefaultOptionChangeHandlers
+];
+function refinePluginDefs(pluginInputs) {
+    var plugins = [];
+    for (var _i = 0, pluginInputs_1 = pluginInputs; _i < pluginInputs_1.length; _i++) {
+        var pluginInput = pluginInputs_1[_i];
+        if (typeof pluginInput === 'string') {
+            var globalName = 'FullCalendar' + capitaliseFirstLetter(pluginInput);
+            if (!window[globalName]) {
+                console.warn('Plugin file not loaded for ' + pluginInput);
+            }
+            else {
+                plugins.push(window[globalName].default); // is an ES6 module
+            }
+        }
+        else {
+            plugins.push(pluginInput);
+        }
+    }
+    return INTERNAL_PLUGINS.concat(plugins);
+}
+
+var RAW_EN_LOCALE = {
+    code: 'en',
+    week: {
+        dow: 0,
+        doy: 4 // 4 days need to be within the year to be considered the first week
+    },
+    dir: 'ltr',
+    buttonText: {
+        prev: 'prev',
+        next: 'next',
+        prevYear: 'prev year',
+        nextYear: 'next year',
+        year: 'year',
+        today: 'today',
+        month: 'month',
+        week: 'week',
+        day: 'day',
+        list: 'list'
+    },
+    weekLabel: 'W',
+    allDayText: 'all-day',
+    eventLimitText: 'more',
+    noEventsMessage: 'No events to display'
+};
+function parseRawLocales(explicitRawLocales) {
+    var defaultCode = explicitRawLocales.length > 0 ? explicitRawLocales[0].code : 'en';
+    var globalArray = window['FullCalendarLocalesAll'] || []; // from locales-all.js
+    var globalObject = window['FullCalendarLocales'] || {}; // from locales/*.js. keys are meaningless
+    var allRawLocales = globalArray.concat(// globalArray is low prio
+    hashValuesToArray(globalObject), // medium prio
+    explicitRawLocales // highest prio
+    );
+    var rawLocaleMap = {
+        en: RAW_EN_LOCALE // necessary?
+    };
+    for (var _i = 0, allRawLocales_1 = allRawLocales; _i < allRawLocales_1.length; _i++) {
+        var rawLocale = allRawLocales_1[_i];
+        rawLocaleMap[rawLocale.code] = rawLocale;
+    }
+    return {
+        map: rawLocaleMap,
+        defaultCode: defaultCode
+    };
+}
+function buildLocale(inputSingular, available) {
+    if (typeof inputSingular === 'object' && !Array.isArray(inputSingular)) {
+        return parseLocale(inputSingular.code, [inputSingular.code], inputSingular);
+    }
+    else {
+        return queryLocale(inputSingular, available);
+    }
+}
+function queryLocale(codeArg, available) {
+    var codes = [].concat(codeArg || []); // will convert to array
+    var raw = queryRawLocale(codes, available) || RAW_EN_LOCALE;
+    return parseLocale(codeArg, codes, raw);
+}
+function queryRawLocale(codes, available) {
+    for (var i = 0; i < codes.length; i++) {
+        var parts = codes[i].toLocaleLowerCase().split('-');
+        for (var j = parts.length; j > 0; j--) {
+            var simpleId = parts.slice(0, j).join('-');
+            if (available[simpleId]) {
+                return available[simpleId];
+            }
+        }
+    }
+    return null;
+}
+function parseLocale(codeArg, codes, raw) {
+    var merged = mergeProps([RAW_EN_LOCALE, raw], ['buttonText']);
+    delete merged.code; // don't want this part of the options
+    var week = merged.week;
+    delete merged.week;
+    return {
+        codeArg: codeArg,
+        codes: codes,
+        week: week,
+        simpleNumberFormat: new Intl.NumberFormat(codeArg),
+        options: merged
+    };
+}
+
+var OptionsManager = /** @class */ (function () {
+    function OptionsManager(overrides) {
+        this.overrides = __assign({}, overrides); // make a copy
+        this.dynamicOverrides = {};
+        this.compute();
+    }
+    OptionsManager.prototype.mutate = function (updates, removals, isDynamic) {
+        var overrideHash = isDynamic ? this.dynamicOverrides : this.overrides;
+        __assign(overrideHash, updates);
+        for (var _i = 0, removals_1 = removals; _i < removals_1.length; _i++) {
+            var propName = removals_1[_i];
+            delete overrideHash[propName];
+        }
+        this.compute();
+    };
+    // Computes the flattened options hash for the calendar and assigns to `this.options`.
+    // Assumes this.overrides and this.dynamicOverrides have already been initialized.
+    OptionsManager.prototype.compute = function () {
+        // TODO: not a very efficient system
+        var locales = firstDefined(// explicit locale option given?
+        this.dynamicOverrides.locales, this.overrides.locales, globalDefaults.locales);
+        var locale = firstDefined(// explicit locales option given?
+        this.dynamicOverrides.locale, this.overrides.locale, globalDefaults.locale);
+        var available = parseRawLocales(locales);
+        var localeDefaults = buildLocale(locale || available.defaultCode, available.map).options;
+        var dir = firstDefined(// based on options computed so far, is direction RTL?
+        this.dynamicOverrides.dir, this.overrides.dir, localeDefaults.dir);
+        var dirDefaults = dir === 'rtl' ? rtlDefaults : {};
+        this.dirDefaults = dirDefaults;
+        this.localeDefaults = localeDefaults;
+        this.computed = mergeOptions([
+            globalDefaults,
+            dirDefaults,
+            localeDefaults,
+            this.overrides,
+            this.dynamicOverrides
+        ]);
+    };
+    return OptionsManager;
+}());
+
+var calendarSystemClassMap = {};
+function registerCalendarSystem(name, theClass) {
+    calendarSystemClassMap[name] = theClass;
+}
+function createCalendarSystem(name) {
+    return new calendarSystemClassMap[name]();
+}
+var GregorianCalendarSystem = /** @class */ (function () {
+    function GregorianCalendarSystem() {
+    }
+    GregorianCalendarSystem.prototype.getMarkerYear = function (d) {
+        return d.getUTCFullYear();
+    };
+    GregorianCalendarSystem.prototype.getMarkerMonth = function (d) {
+        return d.getUTCMonth();
+    };
+    GregorianCalendarSystem.prototype.getMarkerDay = function (d) {
+        return d.getUTCDate();
+    };
+    GregorianCalendarSystem.prototype.arrayToMarker = function (arr) {
+        return arrayToUtcDate(arr);
+    };
+    GregorianCalendarSystem.prototype.markerToArray = function (marker) {
+        return dateToUtcArray(marker);
+    };
+    return GregorianCalendarSystem;
+}());
+registerCalendarSystem('gregory', GregorianCalendarSystem);
+
+var ISO_RE = /^\s*(\d{4})(-(\d{2})(-(\d{2})([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/;
+function parse(str) {
+    var m = ISO_RE.exec(str);
+    if (m) {
+        var marker = new Date(Date.UTC(Number(m[1]), m[3] ? Number(m[3]) - 1 : 0, Number(m[5] || 1), Number(m[7] || 0), Number(m[8] || 0), Number(m[10] || 0), m[12] ? Number('0.' + m[12]) * 1000 : 0));
+        if (isValidDate(marker)) {
+            var timeZoneOffset = null;
+            if (m[13]) {
+                timeZoneOffset = (m[15] === '-' ? -1 : 1) * (Number(m[16] || 0) * 60 +
+                    Number(m[18] || 0));
+            }
+            return {
+                marker: marker,
+                isTimeUnspecified: !m[6],
+                timeZoneOffset: timeZoneOffset
+            };
+        }
+    }
+    return null;
+}
+
+var DateEnv = /** @class */ (function () {
+    function DateEnv(settings) {
+        var timeZone = this.timeZone = settings.timeZone;
+        var isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC';
+        if (settings.namedTimeZoneImpl && isNamedTimeZone) {
+            this.namedTimeZoneImpl = new settings.namedTimeZoneImpl(timeZone);
+        }
+        this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl);
+        this.calendarSystem = createCalendarSystem(settings.calendarSystem);
+        this.locale = settings.locale;
+        this.weekDow = settings.locale.week.dow;
+        this.weekDoy = settings.locale.week.doy;
+        if (settings.weekNumberCalculation === 'ISO') {
+            this.weekDow = 1;
+            this.weekDoy = 4;
+        }
+        if (typeof settings.firstDay === 'number') {
+            this.weekDow = settings.firstDay;
+        }
+        if (typeof settings.weekNumberCalculation === 'function') {
+            this.weekNumberFunc = settings.weekNumberCalculation;
+        }
+        this.weekLabel = settings.weekLabel != null ? settings.weekLabel : settings.locale.options.weekLabel;
+        this.cmdFormatter = settings.cmdFormatter;
+    }
+    // Creating / Parsing
+    DateEnv.prototype.createMarker = function (input) {
+        var meta = this.createMarkerMeta(input);
+        if (meta === null) {
+            return null;
+        }
+        return meta.marker;
+    };
+    DateEnv.prototype.createNowMarker = function () {
+        if (this.canComputeOffset) {
+            return this.timestampToMarker(new Date().valueOf());
+        }
+        else {
+            // if we can't compute the current date val for a timezone,
+            // better to give the current local date vals than UTC
+            return arrayToUtcDate(dateToLocalArray(new Date()));
+        }
+    };
+    DateEnv.prototype.createMarkerMeta = function (input) {
+        if (typeof input === 'string') {
+            return this.parse(input);
+        }
+        var marker = null;
+        if (typeof input === 'number') {
+            marker = this.timestampToMarker(input);
+        }
+        else if (input instanceof Date) {
+            input = input.valueOf();
+            if (!isNaN(input)) {
+                marker = this.timestampToMarker(input);
+            }
+        }
+        else if (Array.isArray(input)) {
+            marker = arrayToUtcDate(input);
+        }
+        if (marker === null || !isValidDate(marker)) {
+            return null;
+        }
+        return { marker: marker, isTimeUnspecified: false, forcedTzo: null };
+    };
+    DateEnv.prototype.parse = function (s) {
+        var parts = parse(s);
+        if (parts === null) {
+            return null;
+        }
+        var marker = parts.marker;
+        var forcedTzo = null;
+        if (parts.timeZoneOffset !== null) {
+            if (this.canComputeOffset) {
+                marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000);
+            }
+            else {
+                forcedTzo = parts.timeZoneOffset;
+            }
+        }
+        return { marker: marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTzo: forcedTzo };
+    };
+    // Accessors
+    DateEnv.prototype.getYear = function (marker) {
+        return this.calendarSystem.getMarkerYear(marker);
+    };
+    DateEnv.prototype.getMonth = function (marker) {
+        return this.calendarSystem.getMarkerMonth(marker);
+    };
+    // Adding / Subtracting
+    DateEnv.prototype.add = function (marker, dur) {
+        var a = this.calendarSystem.markerToArray(marker);
+        a[0] += dur.years;
+        a[1] += dur.months;
+        a[2] += dur.days;
+        a[6] += dur.milliseconds;
+        return this.calendarSystem.arrayToMarker(a);
+    };
+    DateEnv.prototype.subtract = function (marker, dur) {
+        var a = this.calendarSystem.markerToArray(marker);
+        a[0] -= dur.years;
+        a[1] -= dur.months;
+        a[2] -= dur.days;
+        a[6] -= dur.milliseconds;
+        return this.calendarSystem.arrayToMarker(a);
+    };
+    DateEnv.prototype.addYears = function (marker, n) {
+        var a = this.calendarSystem.markerToArray(marker);
+        a[0] += n;
+        return this.calendarSystem.arrayToMarker(a);
+    };
+    DateEnv.prototype.addMonths = function (marker, n) {
+        var a = this.calendarSystem.markerToArray(marker);
+        a[1] += n;
+        return this.calendarSystem.arrayToMarker(a);
+    };
+    // Diffing Whole Units
+    DateEnv.prototype.diffWholeYears = function (m0, m1) {
+        var calendarSystem = this.calendarSystem;
+        if (timeAsMs(m0) === timeAsMs(m1) &&
+            calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) &&
+            calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)) {
+            return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0);
+        }
+        return null;
+    };
+    DateEnv.prototype.diffWholeMonths = function (m0, m1) {
+        var calendarSystem = this.calendarSystem;
+        if (timeAsMs(m0) === timeAsMs(m1) &&
+            calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)) {
+            return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) +
+                (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12;
+        }
+        return null;
+    };
+    // Range / Duration
+    DateEnv.prototype.greatestWholeUnit = function (m0, m1) {
+        var n = this.diffWholeYears(m0, m1);
+        if (n !== null) {
+            return { unit: 'year', value: n };
+        }
+        n = this.diffWholeMonths(m0, m1);
+        if (n !== null) {
+            return { unit: 'month', value: n };
+        }
+        n = diffWholeWeeks(m0, m1);
+        if (n !== null) {
+            return { unit: 'week', value: n };
+        }
+        n = diffWholeDays(m0, m1);
+        if (n !== null) {
+            return { unit: 'day', value: n };
+        }
+        n = diffHours(m0, m1);
+        if (isInt(n)) {
+            return { unit: 'hour', value: n };
+        }
+        n = diffMinutes(m0, m1);
+        if (isInt(n)) {
+            return { unit: 'minute', value: n };
+        }
+        n = diffSeconds(m0, m1);
+        if (isInt(n)) {
+            return { unit: 'second', value: n };
+        }
+        return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() };
+    };
+    DateEnv.prototype.countDurationsBetween = function (m0, m1, d) {
+        // TODO: can use greatestWholeUnit
+        var diff;
+        if (d.years) {
+            diff = this.diffWholeYears(m0, m1);
+            if (diff !== null) {
+                return diff / asRoughYears(d);
+            }
+        }
+        if (d.months) {
+            diff = this.diffWholeMonths(m0, m1);
+            if (diff !== null) {
+                return diff / asRoughMonths(d);
+            }
+        }
+        if (d.days) {
+            diff = diffWholeDays(m0, m1);
+            if (diff !== null) {
+                return diff / asRoughDays(d);
+            }
+        }
+        return (m1.valueOf() - m0.valueOf()) / asRoughMs(d);
+    };
+    // Start-Of
+    DateEnv.prototype.startOf = function (m, unit) {
+        if (unit === 'year') {
+            return this.startOfYear(m);
+        }
+        else if (unit === 'month') {
+            return this.startOfMonth(m);
+        }
+        else if (unit === 'week') {
+            return this.startOfWeek(m);
+        }
+        else if (unit === 'day') {
+            return startOfDay(m);
+        }
+        else if (unit === 'hour') {
+            return startOfHour(m);
+        }
+        else if (unit === 'minute') {
+            return startOfMinute(m);
+        }
+        else if (unit === 'second') {
+            return startOfSecond(m);
+        }
+    };
+    DateEnv.prototype.startOfYear = function (m) {
+        return this.calendarSystem.arrayToMarker([
+            this.calendarSystem.getMarkerYear(m)
+        ]);
+    };
+    DateEnv.prototype.startOfMonth = function (m) {
+        return this.calendarSystem.arrayToMarker([
+            this.calendarSystem.getMarkerYear(m),
+            this.calendarSystem.getMarkerMonth(m)
+        ]);
+    };
+    DateEnv.prototype.startOfWeek = function (m) {
+        return this.calendarSystem.arrayToMarker([
+            this.calendarSystem.getMarkerYear(m),
+            this.calendarSystem.getMarkerMonth(m),
+            m.getUTCDate() - ((m.getUTCDay() - this.weekDow + 7) % 7)
+        ]);
+    };
+    // Week Number
+    DateEnv.prototype.computeWeekNumber = function (marker) {
+        if (this.weekNumberFunc) {
+            return this.weekNumberFunc(this.toDate(marker));
+        }
+        else {
+            return weekOfYear(marker, this.weekDow, this.weekDoy);
+        }
+    };
+    // TODO: choke on timeZoneName: long
+    DateEnv.prototype.format = function (marker, formatter, dateOptions) {
+        if (dateOptions === void 0) { dateOptions = {}; }
+        return formatter.format({
+            marker: marker,
+            timeZoneOffset: dateOptions.forcedTzo != null ?
+                dateOptions.forcedTzo :
+                this.offsetForMarker(marker)
+        }, this);
+    };
+    DateEnv.prototype.formatRange = function (start, end, formatter, dateOptions) {
+        if (dateOptions === void 0) { dateOptions = {}; }
+        if (dateOptions.isEndExclusive) {
+            end = addMs(end, -1);
+        }
+        return formatter.formatRange({
+            marker: start,
+            timeZoneOffset: dateOptions.forcedStartTzo != null ?
+                dateOptions.forcedStartTzo :
+                this.offsetForMarker(start)
+        }, {
+            marker: end,
+            timeZoneOffset: dateOptions.forcedEndTzo != null ?
+                dateOptions.forcedEndTzo :
+                this.offsetForMarker(end)
+        }, this);
+    };
+    DateEnv.prototype.formatIso = function (marker, extraOptions) {
+        if (extraOptions === void 0) { extraOptions = {}; }
+        var timeZoneOffset = null;
+        if (!extraOptions.omitTimeZoneOffset) {
+            if (extraOptions.forcedTzo != null) {
+                timeZoneOffset = extraOptions.forcedTzo;
+            }
+            else {
+                timeZoneOffset = this.offsetForMarker(marker);
+            }
+        }
+        return buildIsoString(marker, timeZoneOffset, extraOptions.omitTime);
+    };
+    // TimeZone
+    DateEnv.prototype.timestampToMarker = function (ms) {
+        if (this.timeZone === 'local') {
+            return arrayToUtcDate(dateToLocalArray(new Date(ms)));
+        }
+        else if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) {
+            return new Date(ms);
+        }
+        else {
+            return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms));
+        }
+    };
+    DateEnv.prototype.offsetForMarker = function (m) {
+        if (this.timeZone === 'local') {
+            return -arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset(); // convert "inverse" offset to "normal" offset
+        }
+        else if (this.timeZone === 'UTC') {
+            return 0;
+        }
+        else if (this.namedTimeZoneImpl) {
+            return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m));
+        }
+        return null;
+    };
+    // Conversion
+    DateEnv.prototype.toDate = function (m, forcedTzo) {
+        if (this.timeZone === 'local') {
+            return arrayToLocalDate(dateToUtcArray(m));
+        }
+        else if (this.timeZone === 'UTC') {
+            return new Date(m.valueOf()); // make sure it's a copy
+        }
+        else if (!this.namedTimeZoneImpl) {
+            return new Date(m.valueOf() - (forcedTzo || 0));
+        }
+        else {
+            return new Date(m.valueOf() -
+                this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)) * 1000 * 60 // convert minutes -> ms
+            );
+        }
+    };
+    return DateEnv;
+}());
+
+var SIMPLE_SOURCE_PROPS = {
+    id: String,
+    allDayDefault: Boolean,
+    eventDataTransform: Function,
+    success: Function,
+    failure: Function
+};
+var uid$2 = 0;
+function doesSourceNeedRange(eventSource, calendar) {
+    var defs = calendar.pluginSystem.hooks.eventSourceDefs;
+    return !defs[eventSource.sourceDefId].ignoreRange;
+}
+function parseEventSource(raw, calendar) {
+    var defs = calendar.pluginSystem.hooks.eventSourceDefs;
+    for (var i = defs.length - 1; i >= 0; i--) { // later-added plugins take precedence
+        var def = defs[i];
+        var meta = def.parseMeta(raw);
+        if (meta) {
+            var res = parseEventSourceProps(typeof raw === 'object' ? raw : {}, meta, i, calendar);
+            res._raw = raw;
+            return res;
+        }
+    }
+    return null;
+}
+function parseEventSourceProps(raw, meta, sourceDefId, calendar) {
+    var leftovers0 = {};
+    var props = refineProps(raw, SIMPLE_SOURCE_PROPS, {}, leftovers0);
+    var leftovers1 = {};
+    var ui = processUnscopedUiProps(leftovers0, calendar, leftovers1);
+    props.isFetching = false;
+    props.latestFetchId = '';
+    props.fetchRange = null;
+    props.publicId = String(raw.id || '');
+    props.sourceId = String(uid$2++);
+    props.sourceDefId = sourceDefId;
+    props.meta = meta;
+    props.ui = ui;
+    props.extendedProps = leftovers1;
+    return props;
+}
+
+function reduceEventSources (eventSources, action, dateProfile, calendar) {
+    switch (action.type) {
+        case 'ADD_EVENT_SOURCES': // already parsed
+            return addSources(eventSources, action.sources, dateProfile ? dateProfile.activeRange : null, calendar);
+        case 'REMOVE_EVENT_SOURCE':
+            return removeSource(eventSources, action.sourceId);
+        case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
+        case 'NEXT':
+        case 'SET_DATE':
+        case 'SET_VIEW_TYPE':
+            if (dateProfile) {
+                return fetchDirtySources(eventSources, dateProfile.activeRange, calendar);
+            }
+            else {
+                return eventSources;
+            }
+        case 'FETCH_EVENT_SOURCES':
+        case 'CHANGE_TIMEZONE':
+            return fetchSourcesByIds(eventSources, action.sourceIds ?
+                arrayToHash(action.sourceIds) :
+                excludeStaticSources(eventSources, calendar), dateProfile ? dateProfile.activeRange : null, calendar);
+        case 'RECEIVE_EVENTS':
+        case 'RECEIVE_EVENT_ERROR':
+            return receiveResponse(eventSources, action.sourceId, action.fetchId, action.fetchRange);
+        case 'REMOVE_ALL_EVENT_SOURCES':
+            return {};
+        default:
+            return eventSources;
+    }
+}
+var uid$3 = 0;
+function addSources(eventSourceHash, sources, fetchRange, calendar) {
+    var hash = {};
+    for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) {
+        var source = sources_1[_i];
+        hash[source.sourceId] = source;
+    }
+    if (fetchRange) {
+        hash = fetchDirtySources(hash, fetchRange, calendar);
+    }
+    return __assign({}, eventSourceHash, hash);
+}
+function removeSource(eventSourceHash, sourceId) {
+    return filterHash(eventSourceHash, function (eventSource) {
+        return eventSource.sourceId !== sourceId;
+    });
+}
+function fetchDirtySources(sourceHash, fetchRange, calendar) {
+    return fetchSourcesByIds(sourceHash, filterHash(sourceHash, function (eventSource) {
+        return isSourceDirty(eventSource, fetchRange, calendar);
+    }), fetchRange, calendar);
+}
+function isSourceDirty(eventSource, fetchRange, calendar) {
+    if (!doesSourceNeedRange(eventSource, calendar)) {
+        return !eventSource.latestFetchId;
+    }
+    else {
+        return !calendar.opt('lazyFetching') ||
+            !eventSource.fetchRange ||
+            fetchRange.start < eventSource.fetchRange.start ||
+            fetchRange.end > eventSource.fetchRange.end;
+    }
+}
+function fetchSourcesByIds(prevSources, sourceIdHash, fetchRange, calendar) {
+    var nextSources = {};
+    for (var sourceId in prevSources) {
+        var source = prevSources[sourceId];
+        if (sourceIdHash[sourceId]) {
+            nextSources[sourceId] = fetchSource(source, fetchRange, calendar);
+        }
+        else {
+            nextSources[sourceId] = source;
+        }
+    }
+    return nextSources;
+}
+function fetchSource(eventSource, fetchRange, calendar) {
+    var sourceDef = calendar.pluginSystem.hooks.eventSourceDefs[eventSource.sourceDefId];
+    var fetchId = String(uid$3++);
+    sourceDef.fetch({
+        eventSource: eventSource,
+        calendar: calendar,
+        range: fetchRange
+    }, function (res) {
+        var rawEvents = res.rawEvents;
+        var calSuccess = calendar.opt('eventSourceSuccess');
+        var calSuccessRes;
+        var sourceSuccessRes;
+        if (eventSource.success) {
+            sourceSuccessRes = eventSource.success(rawEvents, res.xhr);
+        }
+        if (calSuccess) {
+            calSuccessRes = calSuccess(rawEvents, res.xhr);
+        }
+        rawEvents = sourceSuccessRes || calSuccessRes || rawEvents;
+        calendar.dispatch({
+            type: 'RECEIVE_EVENTS',
+            sourceId: eventSource.sourceId,
+            fetchId: fetchId,
+            fetchRange: fetchRange,
+            rawEvents: rawEvents
+        });
+    }, function (error) {
+        var callFailure = calendar.opt('eventSourceFailure');
+        console.warn(error.message, error);
+        if (eventSource.failure) {
+            eventSource.failure(error);
+        }
+        if (callFailure) {
+            callFailure(error);
+        }
+        calendar.dispatch({
+            type: 'RECEIVE_EVENT_ERROR',
+            sourceId: eventSource.sourceId,
+            fetchId: fetchId,
+            fetchRange: fetchRange,
+            error: error
+        });
+    });
+    return __assign({}, eventSource, { isFetching: true, latestFetchId: fetchId });
+}
+function receiveResponse(sourceHash, sourceId, fetchId, fetchRange) {
+    var _a;
+    var eventSource = sourceHash[sourceId];
+    if (eventSource && // not already removed
+        fetchId === eventSource.latestFetchId) {
+        return __assign({}, sourceHash, (_a = {}, _a[sourceId] = __assign({}, eventSource, { isFetching: false, fetchRange: fetchRange }), _a));
+    }
+    return sourceHash;
+}
+function excludeStaticSources(eventSources, calendar) {
+    return filterHash(eventSources, function (eventSource) {
+        return doesSourceNeedRange(eventSource, calendar);
+    });
+}
+
+var DateProfileGenerator = /** @class */ (function () {
+    function DateProfileGenerator(viewSpec, calendar) {
+        this.viewSpec = viewSpec;
+        this.options = viewSpec.options;
+        this.dateEnv = calendar.dateEnv;
+        this.calendar = calendar;
+        this.initHiddenDays();
+    }
+    /* Date Range Computation
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Builds a structure with info about what the dates/ranges will be for the "prev" view.
+    DateProfileGenerator.prototype.buildPrev = function (currentDateProfile, currentDate) {
+        var dateEnv = this.dateEnv;
+        var prevDate = dateEnv.subtract(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
+        currentDateProfile.dateIncrement);
+        return this.build(prevDate, -1);
+    };
+    // Builds a structure with info about what the dates/ranges will be for the "next" view.
+    DateProfileGenerator.prototype.buildNext = function (currentDateProfile, currentDate) {
+        var dateEnv = this.dateEnv;
+        var nextDate = dateEnv.add(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
+        currentDateProfile.dateIncrement);
+        return this.build(nextDate, 1);
+    };
+    // Builds a structure holding dates/ranges for rendering around the given date.
+    // Optional direction param indicates whether the date is being incremented/decremented
+    // from its previous value. decremented = -1, incremented = 1 (default).
+    DateProfileGenerator.prototype.build = function (currentDate, direction, forceToValid) {
+        if (forceToValid === void 0) { forceToValid = false; }
+        var validRange;
+        var minTime = null;
+        var maxTime = null;
+        var currentInfo;
+        var isRangeAllDay;
+        var renderRange;
+        var activeRange;
+        var isValid;
+        validRange = this.buildValidRange();
+        validRange = this.trimHiddenDays(validRange);
+        if (forceToValid) {
+            currentDate = constrainMarkerToRange(currentDate, validRange);
+        }
+        currentInfo = this.buildCurrentRangeInfo(currentDate, direction);
+        isRangeAllDay = /^(year|month|week|day)$/.test(currentInfo.unit);
+        renderRange = this.buildRenderRange(this.trimHiddenDays(currentInfo.range), currentInfo.unit, isRangeAllDay);
+        renderRange = this.trimHiddenDays(renderRange);
+        activeRange = renderRange;
+        if (!this.options.showNonCurrentDates) {
+            activeRange = intersectRanges(activeRange, currentInfo.range);
+        }
+        minTime = createDuration(this.options.minTime);
+        maxTime = createDuration(this.options.maxTime);
+        activeRange = this.adjustActiveRange(activeRange, minTime, maxTime);
+        activeRange = intersectRanges(activeRange, validRange); // might return null
+        // it's invalid if the originally requested date is not contained,
+        // or if the range is completely outside of the valid range.
+        isValid = rangesIntersect(currentInfo.range, validRange);
+        return {
+            // constraint for where prev/next operations can go and where events can be dragged/resized to.
+            // an object with optional start and end properties.
+            validRange: validRange,
+            // range the view is formally responsible for.
+            // for example, a month view might have 1st-31st, excluding padded dates
+            currentRange: currentInfo.range,
+            // name of largest unit being displayed, like "month" or "week"
+            currentRangeUnit: currentInfo.unit,
+            isRangeAllDay: isRangeAllDay,
+            // dates that display events and accept drag-n-drop
+            // will be `null` if no dates accept events
+            activeRange: activeRange,
+            // date range with a rendered skeleton
+            // includes not-active days that need some sort of DOM
+            renderRange: renderRange,
+            // Duration object that denotes the first visible time of any given day
+            minTime: minTime,
+            // Duration object that denotes the exclusive visible end time of any given day
+            maxTime: maxTime,
+            isValid: isValid,
+            // how far the current date will move for a prev/next operation
+            dateIncrement: this.buildDateIncrement(currentInfo.duration)
+            // pass a fallback (might be null) ^
+        };
+    };
+    // Builds an object with optional start/end properties.
+    // Indicates the minimum/maximum dates to display.
+    // not responsible for trimming hidden days.
+    DateProfileGenerator.prototype.buildValidRange = function () {
+        return this.getRangeOption('validRange', this.calendar.getNow()) ||
+            { start: null, end: null }; // completely open-ended
+    };
+    // Builds a structure with info about the "current" range, the range that is
+    // highlighted as being the current month for example.
+    // See build() for a description of `direction`.
+    // Guaranteed to have `range` and `unit` properties. `duration` is optional.
+    DateProfileGenerator.prototype.buildCurrentRangeInfo = function (date, direction) {
+        var _a = this, viewSpec = _a.viewSpec, dateEnv = _a.dateEnv;
+        var duration = null;
+        var unit = null;
+        var range = null;
+        var dayCount;
+        if (viewSpec.duration) {
+            duration = viewSpec.duration;
+            unit = viewSpec.durationUnit;
+            range = this.buildRangeFromDuration(date, direction, duration, unit);
+        }
+        else if ((dayCount = this.options.dayCount)) {
+            unit = 'day';
+            range = this.buildRangeFromDayCount(date, direction, dayCount);
+        }
+        else if ((range = this.buildCustomVisibleRange(date))) {
+            unit = dateEnv.greatestWholeUnit(range.start, range.end).unit;
+        }
+        else {
+            duration = this.getFallbackDuration();
+            unit = greatestDurationDenominator(duration).unit;
+            range = this.buildRangeFromDuration(date, direction, duration, unit);
+        }
+        return { duration: duration, unit: unit, range: range };
+    };
+    DateProfileGenerator.prototype.getFallbackDuration = function () {
+        return createDuration({ day: 1 });
+    };
+    // Returns a new activeRange to have time values (un-ambiguate)
+    // minTime or maxTime causes the range to expand.
+    DateProfileGenerator.prototype.adjustActiveRange = function (range, minTime, maxTime) {
+        var dateEnv = this.dateEnv;
+        var start = range.start;
+        var end = range.end;
+        if (this.viewSpec.class.prototype.usesMinMaxTime) {
+            // expand active range if minTime is negative (why not when positive?)
+            if (asRoughDays(minTime) < 0) {
+                start = startOfDay(start); // necessary?
+                start = dateEnv.add(start, minTime);
+            }
+            // expand active range if maxTime is beyond one day (why not when positive?)
+            if (asRoughDays(maxTime) > 1) {
+                end = startOfDay(end); // necessary?
+                end = addDays(end, -1);
+                end = dateEnv.add(end, maxTime);
+            }
+        }
+        return { start: start, end: end };
+    };
+    // Builds the "current" range when it is specified as an explicit duration.
+    // `unit` is the already-computed greatestDurationDenominator unit of duration.
+    DateProfileGenerator.prototype.buildRangeFromDuration = function (date, direction, duration, unit) {
+        var dateEnv = this.dateEnv;
+        var alignment = this.options.dateAlignment;
+        var dateIncrementInput;
+        var dateIncrementDuration;
+        var start;
+        var end;
+        var res;
+        // compute what the alignment should be
+        if (!alignment) {
+            dateIncrementInput = this.options.dateIncrement;
+            if (dateIncrementInput) {
+                dateIncrementDuration = createDuration(dateIncrementInput);
+                // use the smaller of the two units
+                if (asRoughMs(dateIncrementDuration) < asRoughMs(duration)) {
+                    alignment = greatestDurationDenominator(dateIncrementDuration, !getWeeksFromInput(dateIncrementInput)).unit;
+                }
+                else {
+                    alignment = unit;
+                }
+            }
+            else {
+                alignment = unit;
+            }
+        }
+        // if the view displays a single day or smaller
+        if (asRoughDays(duration) <= 1) {
+            if (this.isHiddenDay(start)) {
+                start = this.skipHiddenDays(start, direction);
+                start = startOfDay(start);
+            }
+        }
+        function computeRes() {
+            start = dateEnv.startOf(date, alignment);
+            end = dateEnv.add(start, duration);
+            res = { start: start, end: end };
+        }
+        computeRes();
+        // if range is completely enveloped by hidden days, go past the hidden days
+        if (!this.trimHiddenDays(res)) {
+            date = this.skipHiddenDays(date, direction);
+            computeRes();
+        }
+        return res;
+    };
+    // Builds the "current" range when a dayCount is specified.
+    DateProfileGenerator.prototype.buildRangeFromDayCount = function (date, direction, dayCount) {
+        var dateEnv = this.dateEnv;
+        var customAlignment = this.options.dateAlignment;
+        var runningCount = 0;
+        var start = date;
+        var end;
+        if (customAlignment) {
+            start = dateEnv.startOf(start, customAlignment);
+        }
+        start = startOfDay(start);
+        start = this.skipHiddenDays(start, direction);
+        end = start;
+        do {
+            end = addDays(end, 1);
+            if (!this.isHiddenDay(end)) {
+                runningCount++;
+            }
+        } while (runningCount < dayCount);
+        return { start: start, end: end };
+    };
+    // Builds a normalized range object for the "visible" range,
+    // which is a way to define the currentRange and activeRange at the same time.
+    DateProfileGenerator.prototype.buildCustomVisibleRange = function (date) {
+        var dateEnv = this.dateEnv;
+        var visibleRange = this.getRangeOption('visibleRange', dateEnv.toDate(date));
+        if (visibleRange && (visibleRange.start == null || visibleRange.end == null)) {
+            return null;
+        }
+        return visibleRange;
+    };
+    // Computes the range that will represent the element/cells for *rendering*,
+    // but which may have voided days/times.
+    // not responsible for trimming hidden days.
+    DateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) {
+        return currentRange;
+    };
+    // Compute the duration value that should be added/substracted to the current date
+    // when a prev/next operation happens.
+    DateProfileGenerator.prototype.buildDateIncrement = function (fallback) {
+        var dateIncrementInput = this.options.dateIncrement;
+        var customAlignment;
+        if (dateIncrementInput) {
+            return createDuration(dateIncrementInput);
+        }
+        else if ((customAlignment = this.options.dateAlignment)) {
+            return createDuration(1, customAlignment);
+        }
+        else if (fallback) {
+            return fallback;
+        }
+        else {
+            return createDuration({ days: 1 });
+        }
+    };
+    // Arguments after name will be forwarded to a hypothetical function value
+    // WARNING: passed-in arguments will be given to generator functions as-is and can cause side-effects.
+    // Always clone your objects if you fear mutation.
+    DateProfileGenerator.prototype.getRangeOption = function (name) {
+        var otherArgs = [];
+        for (var _i = 1; _i < arguments.length; _i++) {
+            otherArgs[_i - 1] = arguments[_i];
+        }
+        var val = this.options[name];
+        if (typeof val === 'function') {
+            val = val.apply(null, otherArgs);
+        }
+        if (val) {
+            val = parseRange(val, this.dateEnv);
+        }
+        if (val) {
+            val = computeVisibleDayRange(val);
+        }
+        return val;
+    };
+    /* Hidden Days
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Initializes internal variables related to calculating hidden days-of-week
+    DateProfileGenerator.prototype.initHiddenDays = function () {
+        var hiddenDays = this.options.hiddenDays || []; // array of day-of-week indices that are hidden
+        var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool)
+        var dayCnt = 0;
+        var i;
+        if (this.options.weekends === false) {
+            hiddenDays.push(0, 6); // 0=sunday, 6=saturday
+        }
+        for (i = 0; i < 7; i++) {
+            if (!(isHiddenDayHash[i] = hiddenDays.indexOf(i) !== -1)) {
+                dayCnt++;
+            }
+        }
+        if (!dayCnt) {
+            throw new Error('invalid hiddenDays'); // all days were hidden? bad.
+        }
+        this.isHiddenDayHash = isHiddenDayHash;
+    };
+    // Remove days from the beginning and end of the range that are computed as hidden.
+    // If the whole range is trimmed off, returns null
+    DateProfileGenerator.prototype.trimHiddenDays = function (range) {
+        var start = range.start;
+        var end = range.end;
+        if (start) {
+            start = this.skipHiddenDays(start);
+        }
+        if (end) {
+            end = this.skipHiddenDays(end, -1, true);
+        }
+        if (start == null || end == null || start < end) {
+            return { start: start, end: end };
+        }
+        return null;
+    };
+    // Is the current day hidden?
+    // `day` is a day-of-week index (0-6), or a Date (used for UTC)
+    DateProfileGenerator.prototype.isHiddenDay = function (day) {
+        if (day instanceof Date) {
+            day = day.getUTCDay();
+        }
+        return this.isHiddenDayHash[day];
+    };
+    // Incrementing the current day until it is no longer a hidden day, returning a copy.
+    // DOES NOT CONSIDER validRange!
+    // If the initial value of `date` is not a hidden day, don't do anything.
+    // Pass `isExclusive` as `true` if you are dealing with an end date.
+    // `inc` defaults to `1` (increment one day forward each time)
+    DateProfileGenerator.prototype.skipHiddenDays = function (date, inc, isExclusive) {
+        if (inc === void 0) { inc = 1; }
+        if (isExclusive === void 0) { isExclusive = false; }
+        while (this.isHiddenDayHash[(date.getUTCDay() + (isExclusive ? inc : 0) + 7) % 7]) {
+            date = addDays(date, inc);
+        }
+        return date;
+    };
+    return DateProfileGenerator;
+}());
+// TODO: find a way to avoid comparing DateProfiles. it's tedious
+function isDateProfilesEqual(p0, p1) {
+    return rangesEqual(p0.validRange, p1.validRange) &&
+        rangesEqual(p0.activeRange, p1.activeRange) &&
+        rangesEqual(p0.renderRange, p1.renderRange) &&
+        durationsEqual(p0.minTime, p1.minTime) &&
+        durationsEqual(p0.maxTime, p1.maxTime);
+    /*
+    TODO: compare more?
+      currentRange: DateRange
+      currentRangeUnit: string
+      isRangeAllDay: boolean
+      isValid: boolean
+      dateIncrement: Duration
+    */
+}
+
+function reduce (state, action, calendar) {
+    var viewType = reduceViewType(state.viewType, action);
+    var dateProfile = reduceDateProfile(state.dateProfile, action, state.currentDate, viewType, calendar);
+    var eventSources = reduceEventSources(state.eventSources, action, dateProfile, calendar);
+    var nextState = __assign({}, state, { viewType: viewType,
+        dateProfile: dateProfile, currentDate: reduceCurrentDate(state.currentDate, action, dateProfile), eventSources: eventSources, eventStore: reduceEventStore(state.eventStore, action, eventSources, dateProfile, calendar), dateSelection: reduceDateSelection(state.dateSelection, action, calendar), eventSelection: reduceSelectedEvent(state.eventSelection, action), eventDrag: reduceEventDrag(state.eventDrag, action, eventSources, calendar), eventResize: reduceEventResize(state.eventResize, action, eventSources, calendar), eventSourceLoadingLevel: computeLoadingLevel(eventSources), loadingLevel: computeLoadingLevel(eventSources) });
+    for (var _i = 0, _a = calendar.pluginSystem.hooks.reducers; _i < _a.length; _i++) {
+        var reducerFunc = _a[_i];
+        nextState = reducerFunc(nextState, action, calendar);
+    }
+    // console.log(action.type, nextState)
+    return nextState;
+}
+function reduceViewType(currentViewType, action) {
+    switch (action.type) {
+        case 'SET_VIEW_TYPE':
+            return action.viewType;
+        default:
+            return currentViewType;
+    }
+}
+function reduceDateProfile(currentDateProfile, action, currentDate, viewType, calendar) {
+    var newDateProfile;
+    switch (action.type) {
+        case 'PREV':
+            newDateProfile = calendar.dateProfileGenerators[viewType].buildPrev(currentDateProfile, currentDate);
+            break;
+        case 'NEXT':
+            newDateProfile = calendar.dateProfileGenerators[viewType].buildNext(currentDateProfile, currentDate);
+            break;
+        case 'SET_DATE':
+            if (!currentDateProfile.activeRange ||
+                !rangeContainsMarker(currentDateProfile.currentRange, action.dateMarker)) {
+                newDateProfile = calendar.dateProfileGenerators[viewType].build(action.dateMarker, undefined, true // forceToValid
+                );
+            }
+            break;
+        case 'SET_VIEW_TYPE':
+            var generator = calendar.dateProfileGenerators[viewType];
+            if (!generator) {
+                throw new Error(viewType ?
+                    'The FullCalendar view "' + viewType + '" does not exist. Make sure your plugins are loaded correctly.' :
+                    'No available FullCalendar view plugins.');
+            }
+            newDateProfile = generator.build(action.dateMarker || currentDate, undefined, true // forceToValid
+            );
+            break;
+    }
+    if (newDateProfile &&
+        newDateProfile.isValid &&
+        !(currentDateProfile && isDateProfilesEqual(currentDateProfile, newDateProfile))) {
+        return newDateProfile;
+    }
+    else {
+        return currentDateProfile;
+    }
+}
+function reduceCurrentDate(currentDate, action, dateProfile) {
+    switch (action.type) {
+        case 'PREV':
+        case 'NEXT':
+            if (!rangeContainsMarker(dateProfile.currentRange, currentDate)) {
+                return dateProfile.currentRange.start;
+            }
+            else {
+                return currentDate;
+            }
+        case 'SET_DATE':
+        case 'SET_VIEW_TYPE':
+            var newDate = action.dateMarker || currentDate;
+            if (dateProfile.activeRange && !rangeContainsMarker(dateProfile.activeRange, newDate)) {
+                return dateProfile.currentRange.start;
+            }
+            else {
+                return newDate;
+            }
+        default:
+            return currentDate;
+    }
+}
+function reduceDateSelection(currentSelection, action, calendar) {
+    switch (action.type) {
+        case 'SELECT_DATES':
+            return action.selection;
+        case 'UNSELECT_DATES':
+            return null;
+        default:
+            return currentSelection;
+    }
+}
+function reduceSelectedEvent(currentInstanceId, action) {
+    switch (action.type) {
+        case 'SELECT_EVENT':
+            return action.eventInstanceId;
+        case 'UNSELECT_EVENT':
+            return '';
+        default:
+            return currentInstanceId;
+    }
+}
+function reduceEventDrag(currentDrag, action, sources, calendar) {
+    switch (action.type) {
+        case 'SET_EVENT_DRAG':
+            var newDrag = action.state;
+            return {
+                affectedEvents: newDrag.affectedEvents,
+                mutatedEvents: newDrag.mutatedEvents,
+                isEvent: newDrag.isEvent,
+                origSeg: newDrag.origSeg
+            };
+        case 'UNSET_EVENT_DRAG':
+            return null;
+        default:
+            return currentDrag;
+    }
+}
+function reduceEventResize(currentResize, action, sources, calendar) {
+    switch (action.type) {
+        case 'SET_EVENT_RESIZE':
+            var newResize = action.state;
+            return {
+                affectedEvents: newResize.affectedEvents,
+                mutatedEvents: newResize.mutatedEvents,
+                isEvent: newResize.isEvent,
+                origSeg: newResize.origSeg
+            };
+        case 'UNSET_EVENT_RESIZE':
+            return null;
+        default:
+            return currentResize;
+    }
+}
+function computeLoadingLevel(eventSources) {
+    var cnt = 0;
+    for (var sourceId in eventSources) {
+        if (eventSources[sourceId].isFetching) {
+            cnt++;
+        }
+    }
+    return cnt;
+}
+
+var STANDARD_PROPS = {
+    start: null,
+    end: null,
+    allDay: Boolean
+};
+function parseDateSpan(raw, dateEnv, defaultDuration) {
+    var span = parseOpenDateSpan(raw, dateEnv);
+    var range = span.range;
+    if (!range.start) {
+        return null;
+    }
+    if (!range.end) {
+        if (defaultDuration == null) {
+            return null;
+        }
+        else {
+            range.end = dateEnv.add(range.start, defaultDuration);
+        }
+    }
+    return span;
+}
+/*
+TODO: somehow combine with parseRange?
+Will return null if the start/end props were present but parsed invalidly.
+*/
+function parseOpenDateSpan(raw, dateEnv) {
+    var leftovers = {};
+    var standardProps = refineProps(raw, STANDARD_PROPS, {}, leftovers);
+    var startMeta = standardProps.start ? dateEnv.createMarkerMeta(standardProps.start) : null;
+    var endMeta = standardProps.end ? dateEnv.createMarkerMeta(standardProps.end) : null;
+    var allDay = standardProps.allDay;
+    if (allDay == null) {
+        allDay = (startMeta && startMeta.isTimeUnspecified) &&
+            (!endMeta || endMeta.isTimeUnspecified);
+    }
+    // use this leftover object as the selection object
+    leftovers.range = {
+        start: startMeta ? startMeta.marker : null,
+        end: endMeta ? endMeta.marker : null
+    };
+    leftovers.allDay = allDay;
+    return leftovers;
+}
+function isDateSpansEqual(span0, span1) {
+    return rangesEqual(span0.range, span1.range) &&
+        span0.allDay === span1.allDay &&
+        isSpanPropsEqual(span0, span1);
+}
+// the NON-DATE-RELATED props
+function isSpanPropsEqual(span0, span1) {
+    for (var propName in span1) {
+        if (propName !== 'range' && propName !== 'allDay') {
+            if (span0[propName] !== span1[propName]) {
+                return false;
+            }
+        }
+    }
+    // are there any props that span0 has that span1 DOESN'T have?
+    // both have range/allDay, so no need to special-case.
+    for (var propName in span0) {
+        if (!(propName in span1)) {
+            return false;
+        }
+    }
+    return true;
+}
+function buildDateSpanApi(span, dateEnv) {
+    return {
+        start: dateEnv.toDate(span.range.start),
+        end: dateEnv.toDate(span.range.end),
+        startStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
+        endStr: dateEnv.formatIso(span.range.end, { omitTime: span.allDay }),
+        allDay: span.allDay
+    };
+}
+function buildDatePointApi(span, dateEnv) {
+    return {
+        date: dateEnv.toDate(span.range.start),
+        dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
+        allDay: span.allDay
+    };
+}
+function fabricateEventRange(dateSpan, eventUiBases, calendar) {
+    var def = parseEventDef({ editable: false }, '', // sourceId
+    dateSpan.allDay, true, // hasEnd
+    calendar);
+    return {
+        def: def,
+        ui: compileEventUi(def, eventUiBases),
+        instance: createEventInstance(def.defId, dateSpan.range),
+        range: dateSpan.range,
+        isStart: true,
+        isEnd: true
+    };
+}
+
+function compileViewDefs(defaultConfigs, overrideConfigs) {
+    var hash = {};
+    var viewType;
+    for (viewType in defaultConfigs) {
+        ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs);
+    }
+    for (viewType in overrideConfigs) {
+        ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs);
+    }
+    return hash;
+}
+function ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs) {
+    if (hash[viewType]) {
+        return hash[viewType];
+    }
+    var viewDef = buildViewDef(viewType, hash, defaultConfigs, overrideConfigs);
+    if (viewDef) {
+        hash[viewType] = viewDef;
+    }
+    return viewDef;
+}
+function buildViewDef(viewType, hash, defaultConfigs, overrideConfigs) {
+    var defaultConfig = defaultConfigs[viewType];
+    var overrideConfig = overrideConfigs[viewType];
+    var queryProp = function (name) {
+        return (defaultConfig && defaultConfig[name] !== null) ? defaultConfig[name] :
+            ((overrideConfig && overrideConfig[name] !== null) ? overrideConfig[name] : null);
+    };
+    var theClass = queryProp('class');
+    var superType = queryProp('superType');
+    if (!superType && theClass) {
+        superType =
+            findViewNameBySubclass(theClass, overrideConfigs) ||
+                findViewNameBySubclass(theClass, defaultConfigs);
+    }
+    var superDef = null;
+    if (superType) {
+        if (superType === viewType) {
+            throw new Error('Can\'t have a custom view type that references itself');
+        }
+        superDef = ensureViewDef(superType, hash, defaultConfigs, overrideConfigs);
+    }
+    if (!theClass && superDef) {
+        theClass = superDef.class;
+    }
+    if (!theClass) {
+        return null; // don't throw a warning, might be settings for a single-unit view
+    }
+    return {
+        type: viewType,
+        class: theClass,
+        defaults: __assign({}, (superDef ? superDef.defaults : {}), (defaultConfig ? defaultConfig.options : {})),
+        overrides: __assign({}, (superDef ? superDef.overrides : {}), (overrideConfig ? overrideConfig.options : {}))
+    };
+}
+function findViewNameBySubclass(viewSubclass, configs) {
+    var superProto = Object.getPrototypeOf(viewSubclass.prototype);
+    for (var viewType in configs) {
+        var parsed = configs[viewType];
+        // need DIRECT subclass, so instanceof won't do it
+        if (parsed.class && parsed.class.prototype === superProto) {
+            return viewType;
+        }
+    }
+    return '';
+}
+
+function parseViewConfigs(inputs) {
+    return mapHash(inputs, parseViewConfig);
+}
+var VIEW_DEF_PROPS = {
+    type: String,
+    class: null
+};
+function parseViewConfig(input) {
+    if (typeof input === 'function') {
+        input = { class: input };
+    }
+    var options = {};
+    var props = refineProps(input, VIEW_DEF_PROPS, {}, options);
+    return {
+        superType: props.type,
+        class: props.class,
+        options: options
+    };
+}
+
+function buildViewSpecs(defaultInputs, optionsManager) {
+    var defaultConfigs = parseViewConfigs(defaultInputs);
+    var overrideConfigs = parseViewConfigs(optionsManager.overrides.views);
+    var viewDefs = compileViewDefs(defaultConfigs, overrideConfigs);
+    return mapHash(viewDefs, function (viewDef) {
+        return buildViewSpec(viewDef, overrideConfigs, optionsManager);
+    });
+}
+function buildViewSpec(viewDef, overrideConfigs, optionsManager) {
+    var durationInput = viewDef.overrides.duration ||
+        viewDef.defaults.duration ||
+        optionsManager.dynamicOverrides.duration ||
+        optionsManager.overrides.duration;
+    var duration = null;
+    var durationUnit = '';
+    var singleUnit = '';
+    var singleUnitOverrides = {};
+    if (durationInput) {
+        duration = createDuration(durationInput);
+        if (duration) { // valid?
+            var denom = greatestDurationDenominator(duration, !getWeeksFromInput(durationInput));
+            durationUnit = denom.unit;
+            if (denom.value === 1) {
+                singleUnit = durationUnit;
+                singleUnitOverrides = overrideConfigs[durationUnit] ? overrideConfigs[durationUnit].options : {};
+            }
+        }
+    }
+    var queryButtonText = function (options) {
+        var buttonTextMap = options.buttonText || {};
+        var buttonTextKey = viewDef.defaults.buttonTextKey;
+        if (buttonTextKey != null && buttonTextMap[buttonTextKey] != null) {
+            return buttonTextMap[buttonTextKey];
+        }
+        if (buttonTextMap[viewDef.type] != null) {
+            return buttonTextMap[viewDef.type];
+        }
+        if (buttonTextMap[singleUnit] != null) {
+            return buttonTextMap[singleUnit];
+        }
+    };
+    return {
+        type: viewDef.type,
+        class: viewDef.class,
+        duration: duration,
+        durationUnit: durationUnit,
+        singleUnit: singleUnit,
+        options: __assign({}, globalDefaults, viewDef.defaults, optionsManager.dirDefaults, optionsManager.localeDefaults, optionsManager.overrides, singleUnitOverrides, viewDef.overrides, optionsManager.dynamicOverrides),
+        buttonTextOverride: queryButtonText(optionsManager.dynamicOverrides) ||
+            queryButtonText(optionsManager.overrides) || // constructor-specified buttonText lookup hash takes precedence
+            viewDef.overrides.buttonText,
+        buttonTextDefault: queryButtonText(optionsManager.localeDefaults) ||
+            queryButtonText(optionsManager.dirDefaults) ||
+            viewDef.defaults.buttonText ||
+            queryButtonText(globalDefaults) ||
+            viewDef.type // fall back to given view name
+    };
+}
+
+var Toolbar = /** @class */ (function (_super) {
+    __extends(Toolbar, _super);
+    function Toolbar(context, extraClassName) {
+        var _this = _super.call(this, context) || this;
+        _this._renderLayout = memoizeRendering(_this.renderLayout, _this.unrenderLayout);
+        _this._updateTitle = memoizeRendering(_this.updateTitle, null, [_this._renderLayout]);
+        _this._updateActiveButton = memoizeRendering(_this.updateActiveButton, null, [_this._renderLayout]);
+        _this._updateToday = memoizeRendering(_this.updateToday, null, [_this._renderLayout]);
+        _this._updatePrev = memoizeRendering(_this.updatePrev, null, [_this._renderLayout]);
+        _this._updateNext = memoizeRendering(_this.updateNext, null, [_this._renderLayout]);
+        _this.el = createElement('div', { className: 'fc-toolbar ' + extraClassName });
+        return _this;
+    }
+    Toolbar.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this._renderLayout.unrender(); // should unrender everything else
+        removeElement(this.el);
+    };
+    Toolbar.prototype.render = function (props) {
+        this._renderLayout(props.layout);
+        this._updateTitle(props.title);
+        this._updateActiveButton(props.activeButton);
+        this._updateToday(props.isTodayEnabled);
+        this._updatePrev(props.isPrevEnabled);
+        this._updateNext(props.isNextEnabled);
+    };
+    Toolbar.prototype.renderLayout = function (layout) {
+        var el = this.el;
+        this.viewsWithButtons = [];
+        appendToElement(el, this.renderSection('left', layout.left));
+        appendToElement(el, this.renderSection('center', layout.center));
+        appendToElement(el, this.renderSection('right', layout.right));
+    };
+    Toolbar.prototype.unrenderLayout = function () {
+        this.el.innerHTML = '';
+    };
+    Toolbar.prototype.renderSection = function (position, buttonStr) {
+        var _this = this;
+        var _a = this, theme = _a.theme, calendar = _a.calendar;
+        var optionsManager = calendar.optionsManager;
+        var viewSpecs = calendar.viewSpecs;
+        var sectionEl = createElement('div', { className: 'fc-' + position });
+        var calendarCustomButtons = optionsManager.computed.customButtons || {};
+        var calendarButtonTextOverrides = optionsManager.overrides.buttonText || {};
+        var calendarButtonText = optionsManager.computed.buttonText || {};
+        if (buttonStr) {
+            buttonStr.split(' ').forEach(function (buttonGroupStr, i) {
+                var groupChildren = [];
+                var isOnlyButtons = true;
+                var groupEl;
+                buttonGroupStr.split(',').forEach(function (buttonName, j) {
+                    var customButtonProps;
+                    var viewSpec;
+                    var buttonClick;
+                    var buttonIcon; // only one of these will be set
+                    var buttonText; // "
+                    var buttonInnerHtml;
+                    var buttonClasses;
+                    var buttonEl;
+                    var buttonAriaAttr;
+                    if (buttonName === 'title') {
+                        groupChildren.push(htmlToElement('<h2>&nbsp;</h2>')); // we always want it to take up height
+                        isOnlyButtons = false;
+                    }
+                    else {
+                        if ((customButtonProps = calendarCustomButtons[buttonName])) {
+                            buttonClick = function (ev) {
+                                if (customButtonProps.click) {
+                                    customButtonProps.click.call(buttonEl, ev);
+                                }
+                            };
+                            (buttonIcon = theme.getCustomButtonIconClass(customButtonProps)) ||
+                                (buttonIcon = theme.getIconClass(buttonName)) ||
+                                (buttonText = customButtonProps.text);
+                        }
+                        else if ((viewSpec = viewSpecs[buttonName])) {
+                            _this.viewsWithButtons.push(buttonName);
+                            buttonClick = function () {
+                                calendar.changeView(buttonName);
+                            };
+                            (buttonText = viewSpec.buttonTextOverride) ||
+                                (buttonIcon = theme.getIconClass(buttonName)) ||
+                                (buttonText = viewSpec.buttonTextDefault);
+                        }
+                        else if (calendar[buttonName]) { // a calendar method
+                            buttonClick = function () {
+                                calendar[buttonName]();
+                            };
+                            (buttonText = calendarButtonTextOverrides[buttonName]) ||
+                                (buttonIcon = theme.getIconClass(buttonName)) ||
+                                (buttonText = calendarButtonText[buttonName]);
+                            //            ^ everything else is considered default
+                        }
+                        if (buttonClick) {
+                            buttonClasses = [
+                                'fc-' + buttonName + '-button',
+                                theme.getClass('button')
+                            ];
+                            if (buttonText) {
+                                buttonInnerHtml = htmlEscape(buttonText);
+                                buttonAriaAttr = '';
+                            }
+                            else if (buttonIcon) {
+                                buttonInnerHtml = "<span class='" + buttonIcon + "'></span>";
+                                buttonAriaAttr = ' aria-label="' + buttonName + '"';
+                            }
+                            buttonEl = htmlToElement(// type="button" so that it doesn't submit a form
+                            '<button type="button" class="' + buttonClasses.join(' ') + '"' +
+                                buttonAriaAttr +
+                                '>' + buttonInnerHtml + '</button>');
+                            buttonEl.addEventListener('click', buttonClick);
+                            groupChildren.push(buttonEl);
+                        }
+                    }
+                });
+                if (groupChildren.length > 1) {
+                    groupEl = document.createElement('div');
+                    var buttonGroupClassName = theme.getClass('buttonGroup');
+                    if (isOnlyButtons && buttonGroupClassName) {
+                        groupEl.classList.add(buttonGroupClassName);
+                    }
+                    appendToElement(groupEl, groupChildren);
+                    sectionEl.appendChild(groupEl);
+                }
+                else {
+                    appendToElement(sectionEl, groupChildren); // 1 or 0 children
+                }
+            });
+        }
+        return sectionEl;
+    };
+    Toolbar.prototype.updateToday = function (isTodayEnabled) {
+        this.toggleButtonEnabled('today', isTodayEnabled);
+    };
+    Toolbar.prototype.updatePrev = function (isPrevEnabled) {
+        this.toggleButtonEnabled('prev', isPrevEnabled);
+    };
+    Toolbar.prototype.updateNext = function (isNextEnabled) {
+        this.toggleButtonEnabled('next', isNextEnabled);
+    };
+    Toolbar.prototype.updateTitle = function (text) {
+        findElements(this.el, 'h2').forEach(function (titleEl) {
+            titleEl.innerText = text;
+        });
+    };
+    Toolbar.prototype.updateActiveButton = function (buttonName) {
+        var className = this.theme.getClass('buttonActive');
+        findElements(this.el, 'button').forEach(function (buttonEl) {
+            if (buttonName && buttonEl.classList.contains('fc-' + buttonName + '-button')) {
+                buttonEl.classList.add(className);
+            }
+            else {
+                buttonEl.classList.remove(className);
+            }
+        });
+    };
+    Toolbar.prototype.toggleButtonEnabled = function (buttonName, bool) {
+        findElements(this.el, '.fc-' + buttonName + '-button').forEach(function (buttonEl) {
+            buttonEl.disabled = !bool;
+        });
+    };
+    return Toolbar;
+}(Component));
+
+var CalendarComponent = /** @class */ (function (_super) {
+    __extends(CalendarComponent, _super);
+    function CalendarComponent(context, el) {
+        var _this = _super.call(this, context) || this;
+        _this._renderToolbars = memoizeRendering(_this.renderToolbars);
+        _this.buildViewPropTransformers = memoize(buildViewPropTransformers);
+        _this.el = el;
+        prependToElement(el, _this.contentEl = createElement('div', { className: 'fc-view-container' }));
+        var calendar = _this.calendar;
+        for (var _i = 0, _a = calendar.pluginSystem.hooks.viewContainerModifiers; _i < _a.length; _i++) {
+            var modifyViewContainer = _a[_i];
+            modifyViewContainer(_this.contentEl, calendar);
+        }
+        _this.toggleElClassNames(true);
+        _this.computeTitle = memoize(computeTitle);
+        _this.parseBusinessHours = memoize(function (input) {
+            return parseBusinessHours(input, _this.calendar);
+        });
+        return _this;
+    }
+    CalendarComponent.prototype.destroy = function () {
+        if (this.header) {
+            this.header.destroy();
+        }
+        if (this.footer) {
+            this.footer.destroy();
+        }
+        if (this.view) {
+            this.view.destroy();
+        }
+        removeElement(this.contentEl);
+        this.toggleElClassNames(false);
+        _super.prototype.destroy.call(this);
+    };
+    CalendarComponent.prototype.toggleElClassNames = function (bool) {
+        var classList = this.el.classList;
+        var dirClassName = 'fc-' + this.opt('dir');
+        var themeClassName = this.theme.getClass('widget');
+        if (bool) {
+            classList.add('fc');
+            classList.add(dirClassName);
+            classList.add(themeClassName);
+        }
+        else {
+            classList.remove('fc');
+            classList.remove(dirClassName);
+            classList.remove(themeClassName);
+        }
+    };
+    CalendarComponent.prototype.render = function (props) {
+        this.freezeHeight();
+        var title = this.computeTitle(props.dateProfile, props.viewSpec.options);
+        this._renderToolbars(props.viewSpec, props.dateProfile, props.currentDate, props.dateProfileGenerator, title);
+        this.renderView(props, title);
+        this.updateSize();
+        this.thawHeight();
+    };
+    CalendarComponent.prototype.renderToolbars = function (viewSpec, dateProfile, currentDate, dateProfileGenerator, title) {
+        var headerLayout = this.opt('header');
+        var footerLayout = this.opt('footer');
+        var now = this.calendar.getNow();
+        var todayInfo = dateProfileGenerator.build(now);
+        var prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate);
+        var nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate);
+        var toolbarProps = {
+            title: title,
+            activeButton: viewSpec.type,
+            isTodayEnabled: todayInfo.isValid && !rangeContainsMarker(dateProfile.currentRange, now),
+            isPrevEnabled: prevInfo.isValid,
+            isNextEnabled: nextInfo.isValid
+        };
+        if (headerLayout) {
+            if (!this.header) {
+                this.header = new Toolbar(this.context, 'fc-header-toolbar');
+                prependToElement(this.el, this.header.el);
+            }
+            this.header.receiveProps(__assign({ layout: headerLayout }, toolbarProps));
+        }
+        else if (this.header) {
+            this.header.destroy();
+            this.header = null;
+        }
+        if (footerLayout) {
+            if (!this.footer) {
+                this.footer = new Toolbar(this.context, 'fc-footer-toolbar');
+                appendToElement(this.el, this.footer.el);
+            }
+            this.footer.receiveProps(__assign({ layout: footerLayout }, toolbarProps));
+        }
+        else if (this.footer) {
+            this.footer.destroy();
+            this.footer = null;
+        }
+    };
+    CalendarComponent.prototype.renderView = function (props, title) {
+        var view = this.view;
+        var viewSpec = props.viewSpec, dateProfileGenerator = props.dateProfileGenerator;
+        if (!view || view.viewSpec !== viewSpec) {
+            if (view) {
+                view.destroy();
+            }
+            view = this.view = new viewSpec['class']({
+                calendar: this.calendar,
+                view: null,
+                dateEnv: this.dateEnv,
+                theme: this.theme,
+                options: viewSpec.options
+            }, viewSpec, dateProfileGenerator, this.contentEl);
+        }
+        else {
+            view.addScroll(view.queryScroll());
+        }
+        view.title = title; // for the API
+        var viewProps = {
+            dateProfile: props.dateProfile,
+            businessHours: this.parseBusinessHours(viewSpec.options.businessHours),
+            eventStore: props.eventStore,
+            eventUiBases: props.eventUiBases,
+            dateSelection: props.dateSelection,
+            eventSelection: props.eventSelection,
+            eventDrag: props.eventDrag,
+            eventResize: props.eventResize
+        };
+        var transformers = this.buildViewPropTransformers(this.calendar.pluginSystem.hooks.viewPropsTransformers);
+        for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) {
+            var transformer = transformers_1[_i];
+            __assign(viewProps, transformer.transform(viewProps, viewSpec, props, view));
+        }
+        view.receiveProps(viewProps);
+    };
+    // Sizing
+    // -----------------------------------------------------------------------------------------------------------------
+    CalendarComponent.prototype.updateSize = function (isResize) {
+        if (isResize === void 0) { isResize = false; }
+        var view = this.view;
+        if (isResize) {
+            view.addScroll(view.queryScroll());
+        }
+        if (isResize || this.isHeightAuto == null) {
+            this.computeHeightVars();
+        }
+        view.updateSize(isResize, this.viewHeight, this.isHeightAuto);
+        view.updateNowIndicator(); // we need to guarantee this will run after updateSize
+        view.popScroll(isResize);
+    };
+    CalendarComponent.prototype.computeHeightVars = function () {
+        var calendar = this.calendar; // yuck. need to handle dynamic options
+        var heightInput = calendar.opt('height');
+        var contentHeightInput = calendar.opt('contentHeight');
+        this.isHeightAuto = heightInput === 'auto' || contentHeightInput === 'auto';
+        if (typeof contentHeightInput === 'number') { // exists and not 'auto'
+            this.viewHeight = contentHeightInput;
+        }
+        else if (typeof contentHeightInput === 'function') { // exists and is a function
+            this.viewHeight = contentHeightInput();
+        }
+        else if (typeof heightInput === 'number') { // exists and not 'auto'
+            this.viewHeight = heightInput - this.queryToolbarsHeight();
+        }
+        else if (typeof heightInput === 'function') { // exists and is a function
+            this.viewHeight = heightInput() - this.queryToolbarsHeight();
+        }
+        else if (heightInput === 'parent') { // set to height of parent element
+            var parentEl = this.el.parentNode;
+            this.viewHeight = parentEl.getBoundingClientRect().height - this.queryToolbarsHeight();
+        }
+        else {
+            this.viewHeight = Math.round(this.contentEl.getBoundingClientRect().width /
+                Math.max(calendar.opt('aspectRatio'), .5));
+        }
+    };
+    CalendarComponent.prototype.queryToolbarsHeight = function () {
+        var height = 0;
+        if (this.header) {
+            height += computeHeightAndMargins(this.header.el);
+        }
+        if (this.footer) {
+            height += computeHeightAndMargins(this.footer.el);
+        }
+        return height;
+    };
+    // Height "Freezing"
+    // -----------------------------------------------------------------------------------------------------------------
+    CalendarComponent.prototype.freezeHeight = function () {
+        applyStyle(this.el, {
+            height: this.el.getBoundingClientRect().height,
+            overflow: 'hidden'
+        });
+    };
+    CalendarComponent.prototype.thawHeight = function () {
+        applyStyle(this.el, {
+            height: '',
+            overflow: ''
+        });
+    };
+    return CalendarComponent;
+}(Component));
+// Title and Date Formatting
+// -----------------------------------------------------------------------------------------------------------------
+// Computes what the title at the top of the calendar should be for this view
+function computeTitle(dateProfile, viewOptions) {
+    var range;
+    // for views that span a large unit of time, show the proper interval, ignoring stray days before and after
+    if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) {
+        range = dateProfile.currentRange;
+    }
+    else { // for day units or smaller, use the actual day range
+        range = dateProfile.activeRange;
+    }
+    return this.dateEnv.formatRange(range.start, range.end, createFormatter(viewOptions.titleFormat || computeTitleFormat(dateProfile), viewOptions.titleRangeSeparator), { isEndExclusive: dateProfile.isRangeAllDay });
+}
+// Generates the format string that should be used to generate the title for the current date range.
+// Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
+function computeTitleFormat(dateProfile) {
+    var currentRangeUnit = dateProfile.currentRangeUnit;
+    if (currentRangeUnit === 'year') {
+        return { year: 'numeric' };
+    }
+    else if (currentRangeUnit === 'month') {
+        return { year: 'numeric', month: 'long' }; // like "September 2014"
+    }
+    else {
+        var days = diffWholeDays(dateProfile.currentRange.start, dateProfile.currentRange.end);
+        if (days !== null && days > 1) {
+            // multi-day range. shorter, like "Sep 9 - 10 2014"
+            return { year: 'numeric', month: 'short', day: 'numeric' };
+        }
+        else {
+            // one day. longer, like "September 9 2014"
+            return { year: 'numeric', month: 'long', day: 'numeric' };
+        }
+    }
+}
+// Plugin
+// -----------------------------------------------------------------------------------------------------------------
+function buildViewPropTransformers(theClasses) {
+    return theClasses.map(function (theClass) {
+        return new theClass();
+    });
+}
+
+var Interaction = /** @class */ (function () {
+    function Interaction(settings) {
+        this.component = settings.component;
+    }
+    Interaction.prototype.destroy = function () {
+    };
+    return Interaction;
+}());
+function parseInteractionSettings(component, input) {
+    return {
+        component: component,
+        el: input.el,
+        useEventCenter: input.useEventCenter != null ? input.useEventCenter : true
+    };
+}
+function interactionSettingsToStore(settings) {
+    var _a;
+    return _a = {},
+        _a[settings.component.uid] = settings,
+        _a;
+}
+// global state
+var interactionSettingsStore = {};
+
+/*
+Detects when the user clicks on an event within a DateComponent
+*/
+var EventClicking = /** @class */ (function (_super) {
+    __extends(EventClicking, _super);
+    function EventClicking(settings) {
+        var _this = _super.call(this, settings) || this;
+        _this.handleSegClick = function (ev, segEl) {
+            var component = _this.component;
+            var seg = getElSeg(segEl);
+            if (seg && // might be the <div> surrounding the more link
+                component.isValidSegDownEl(ev.target)) {
+                // our way to simulate a link click for elements that can't be <a> tags
+                // grab before trigger fired in case trigger trashes DOM thru rerendering
+                var hasUrlContainer = elementClosest(ev.target, '.fc-has-url');
+                var url = hasUrlContainer ? hasUrlContainer.querySelector('a[href]').href : '';
+                component.publiclyTrigger('eventClick', [
+                    {
+                        el: segEl,
+                        event: new EventApi(component.calendar, seg.eventRange.def, seg.eventRange.instance),
+                        jsEvent: ev,
+                        view: component.view
+                    }
+                ]);
+                if (url && !ev.defaultPrevented) {
+                    window.location.href = url;
+                }
+            }
+        };
+        var component = settings.component;
+        _this.destroy = listenBySelector(component.el, 'click', component.fgSegSelector + ',' + component.bgSegSelector, _this.handleSegClick);
+        return _this;
+    }
+    return EventClicking;
+}(Interaction));
+
+/*
+Triggers events and adds/removes core classNames when the user's pointer
+enters/leaves event-elements of a component.
+*/
+var EventHovering = /** @class */ (function (_super) {
+    __extends(EventHovering, _super);
+    function EventHovering(settings) {
+        var _this = _super.call(this, settings) || this;
+        // for simulating an eventMouseLeave when the event el is destroyed while mouse is over it
+        _this.handleEventElRemove = function (el) {
+            if (el === _this.currentSegEl) {
+                _this.handleSegLeave(null, _this.currentSegEl);
+            }
+        };
+        _this.handleSegEnter = function (ev, segEl) {
+            if (getElSeg(segEl)) { // TODO: better way to make sure not hovering over more+ link or its wrapper
+                segEl.classList.add('fc-allow-mouse-resize');
+                _this.currentSegEl = segEl;
+                _this.triggerEvent('eventMouseEnter', ev, segEl);
+            }
+        };
+        _this.handleSegLeave = function (ev, segEl) {
+            if (_this.currentSegEl) {
+                segEl.classList.remove('fc-allow-mouse-resize');
+                _this.currentSegEl = null;
+                _this.triggerEvent('eventMouseLeave', ev, segEl);
+            }
+        };
+        var component = settings.component;
+        _this.removeHoverListeners = listenToHoverBySelector(component.el, component.fgSegSelector + ',' + component.bgSegSelector, _this.handleSegEnter, _this.handleSegLeave);
+        component.calendar.on('eventElRemove', _this.handleEventElRemove);
+        return _this;
+    }
+    EventHovering.prototype.destroy = function () {
+        this.removeHoverListeners();
+        this.component.calendar.off('eventElRemove', this.handleEventElRemove);
+    };
+    EventHovering.prototype.triggerEvent = function (publicEvName, ev, segEl) {
+        var component = this.component;
+        var seg = getElSeg(segEl);
+        if (!ev || component.isValidSegDownEl(ev.target)) {
+            component.publiclyTrigger(publicEvName, [
+                {
+                    el: segEl,
+                    event: new EventApi(this.component.calendar, seg.eventRange.def, seg.eventRange.instance),
+                    jsEvent: ev,
+                    view: component.view
+                }
+            ]);
+        }
+    };
+    return EventHovering;
+}(Interaction));
+
+var StandardTheme = /** @class */ (function (_super) {
+    __extends(StandardTheme, _super);
+    function StandardTheme() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    return StandardTheme;
+}(Theme));
+StandardTheme.prototype.classes = {
+    widget: 'fc-unthemed',
+    widgetHeader: 'fc-widget-header',
+    widgetContent: 'fc-widget-content',
+    buttonGroup: 'fc-button-group',
+    button: 'fc-button fc-button-primary',
+    buttonActive: 'fc-button-active',
+    popoverHeader: 'fc-widget-header',
+    popoverContent: 'fc-widget-content',
+    // day grid
+    headerRow: 'fc-widget-header',
+    dayRow: 'fc-widget-content',
+    // list view
+    listView: 'fc-widget-content'
+};
+StandardTheme.prototype.baseIconClass = 'fc-icon';
+StandardTheme.prototype.iconClasses = {
+    close: 'fc-icon-x',
+    prev: 'fc-icon-chevron-left',
+    next: 'fc-icon-chevron-right',
+    prevYear: 'fc-icon-chevrons-left',
+    nextYear: 'fc-icon-chevrons-right'
+};
+StandardTheme.prototype.iconOverrideOption = 'buttonIcons';
+StandardTheme.prototype.iconOverrideCustomButtonOption = 'icon';
+StandardTheme.prototype.iconOverridePrefix = 'fc-icon-';
+
+var Calendar = /** @class */ (function () {
+    function Calendar(el, overrides) {
+        var _this = this;
+        this.parseRawLocales = memoize(parseRawLocales);
+        this.buildLocale = memoize(buildLocale);
+        this.buildDateEnv = memoize(buildDateEnv);
+        this.buildTheme = memoize(buildTheme);
+        this.buildEventUiSingleBase = memoize(this._buildEventUiSingleBase);
+        this.buildSelectionConfig = memoize(this._buildSelectionConfig);
+        this.buildEventUiBySource = memoizeOutput(buildEventUiBySource, isPropsEqual);
+        this.buildEventUiBases = memoize(buildEventUiBases);
+        this.interactionsStore = {};
+        this.actionQueue = [];
+        this.isReducing = false;
+        // isDisplaying: boolean = false // installed in DOM? accepting renders?
+        this.needsRerender = false; // needs a render?
+        this.needsFullRerender = false;
+        this.isRendering = false; // currently in the executeRender function?
+        this.renderingPauseDepth = 0;
+        this.buildDelayedRerender = memoize(buildDelayedRerender);
+        this.afterSizingTriggers = {};
+        this.isViewUpdated = false;
+        this.isDatesUpdated = false;
+        this.isEventsUpdated = false;
+        this.el = el;
+        this.optionsManager = new OptionsManager(overrides || {});
+        this.pluginSystem = new PluginSystem();
+        // only do once. don't do in handleOptions. because can't remove plugins
+        this.addPluginInputs(this.optionsManager.computed.plugins || []);
+        this.handleOptions(this.optionsManager.computed);
+        this.publiclyTrigger('_init'); // for tests
+        this.hydrate();
+        this.calendarInteractions = this.pluginSystem.hooks.calendarInteractions
+            .map(function (calendarInteractionClass) {
+            return new calendarInteractionClass(_this);
+        });
+    }
+    Calendar.prototype.addPluginInputs = function (pluginInputs) {
+        var pluginDefs = refinePluginDefs(pluginInputs);
+        for (var _i = 0, pluginDefs_1 = pluginDefs; _i < pluginDefs_1.length; _i++) {
+            var pluginDef = pluginDefs_1[_i];
+            this.pluginSystem.add(pluginDef);
+        }
+    };
+    Object.defineProperty(Calendar.prototype, "view", {
+        // public API
+        get: function () {
+            return this.component ? this.component.view : null;
+        },
+        enumerable: true,
+        configurable: true
+    });
+    // Public API for rendering
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.render = function () {
+        if (!this.component) {
+            this.renderableEventStore = createEmptyEventStore();
+            this.bindHandlers();
+            this.executeRender();
+        }
+        else {
+            this.requestRerender(true);
+        }
+    };
+    Calendar.prototype.destroy = function () {
+        if (this.component) {
+            this.unbindHandlers();
+            this.component.destroy(); // don't null-out. in case API needs access
+            this.component = null; // umm ???
+            for (var _i = 0, _a = this.calendarInteractions; _i < _a.length; _i++) {
+                var interaction = _a[_i];
+                interaction.destroy();
+            }
+            this.publiclyTrigger('_destroyed');
+        }
+    };
+    // Handlers
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.bindHandlers = function () {
+        var _this = this;
+        // event delegation for nav links
+        this.removeNavLinkListener = listenBySelector(this.el, 'click', 'a[data-goto]', function (ev, anchorEl) {
+            var gotoOptions = anchorEl.getAttribute('data-goto');
+            gotoOptions = gotoOptions ? JSON.parse(gotoOptions) : {};
+            var dateEnv = _this.dateEnv;
+            var dateMarker = dateEnv.createMarker(gotoOptions.date);
+            var viewType = gotoOptions.type;
+            // property like "navLinkDayClick". might be a string or a function
+            var customAction = _this.viewOpt('navLink' + capitaliseFirstLetter(viewType) + 'Click');
+            if (typeof customAction === 'function') {
+                customAction(dateEnv.toDate(dateMarker), ev);
+            }
+            else {
+                if (typeof customAction === 'string') {
+                    viewType = customAction;
+                }
+                _this.zoomTo(dateMarker, viewType);
+            }
+        });
+        if (this.opt('handleWindowResize')) {
+            window.addEventListener('resize', this.windowResizeProxy = debounce(// prevents rapid calls
+            this.windowResize.bind(this), this.opt('windowResizeDelay')));
+        }
+    };
+    Calendar.prototype.unbindHandlers = function () {
+        this.removeNavLinkListener();
+        if (this.windowResizeProxy) {
+            window.removeEventListener('resize', this.windowResizeProxy);
+            this.windowResizeProxy = null;
+        }
+    };
+    // Dispatcher
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.hydrate = function () {
+        var _this = this;
+        this.state = this.buildInitialState();
+        var rawSources = this.opt('eventSources') || [];
+        var singleRawSource = this.opt('events');
+        var sources = []; // parsed
+        if (singleRawSource) {
+            rawSources.unshift(singleRawSource);
+        }
+        for (var _i = 0, rawSources_1 = rawSources; _i < rawSources_1.length; _i++) {
+            var rawSource = rawSources_1[_i];
+            var source = parseEventSource(rawSource, this);
+            if (source) {
+                sources.push(source);
+            }
+        }
+        this.batchRendering(function () {
+            _this.dispatch({ type: 'INIT' }); // pass in sources here?
+            _this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: sources });
+            _this.dispatch({
+                type: 'SET_VIEW_TYPE',
+                viewType: _this.opt('defaultView') || _this.pluginSystem.hooks.defaultView
+            });
+        });
+    };
+    Calendar.prototype.buildInitialState = function () {
+        return {
+            viewType: null,
+            loadingLevel: 0,
+            eventSourceLoadingLevel: 0,
+            currentDate: this.getInitialDate(),
+            dateProfile: null,
+            eventSources: {},
+            eventStore: createEmptyEventStore(),
+            dateSelection: null,
+            eventSelection: '',
+            eventDrag: null,
+            eventResize: null
+        };
+    };
+    Calendar.prototype.dispatch = function (action) {
+        this.actionQueue.push(action);
+        if (!this.isReducing) {
+            this.isReducing = true;
+            var oldState = this.state;
+            while (this.actionQueue.length) {
+                this.state = this.reduce(this.state, this.actionQueue.shift(), this);
+            }
+            var newState = this.state;
+            this.isReducing = false;
+            if (!oldState.loadingLevel && newState.loadingLevel) {
+                this.publiclyTrigger('loading', [true]);
+            }
+            else if (oldState.loadingLevel && !newState.loadingLevel) {
+                this.publiclyTrigger('loading', [false]);
+            }
+            var view = this.component && this.component.view;
+            if (oldState.eventStore !== newState.eventStore || this.needsFullRerender) {
+                if (oldState.eventStore) {
+                    this.isEventsUpdated = true;
+                }
+            }
+            if (oldState.dateProfile !== newState.dateProfile || this.needsFullRerender) {
+                if (oldState.dateProfile && view) { // why would view be null!?
+                    this.publiclyTrigger('datesDestroy', [
+                        {
+                            view: view,
+                            el: view.el
+                        }
+                    ]);
+                }
+                this.isDatesUpdated = true;
+            }
+            if (oldState.viewType !== newState.viewType || this.needsFullRerender) {
+                if (oldState.viewType && view) { // why would view be null!?
+                    this.publiclyTrigger('viewSkeletonDestroy', [
+                        {
+                            view: view,
+                            el: view.el
+                        }
+                    ]);
+                }
+                this.isViewUpdated = true;
+            }
+            this.requestRerender();
+        }
+    };
+    Calendar.prototype.reduce = function (state, action, calendar) {
+        return reduce(state, action, calendar);
+    };
+    // Render Queue
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.requestRerender = function (needsFull) {
+        if (needsFull === void 0) { needsFull = false; }
+        this.needsRerender = true;
+        this.needsFullRerender = this.needsFullRerender || needsFull;
+        this.delayedRerender(); // will call a debounced-version of tryRerender
+    };
+    Calendar.prototype.tryRerender = function () {
+        if (this.component && // must be accepting renders
+            this.needsRerender && // indicates that a rerender was requested
+            !this.renderingPauseDepth && // not paused
+            !this.isRendering // not currently in the render loop
+        ) {
+            this.executeRender();
+        }
+    };
+    Calendar.prototype.batchRendering = function (func) {
+        this.renderingPauseDepth++;
+        func();
+        this.renderingPauseDepth--;
+        if (this.needsRerender) {
+            this.requestRerender();
+        }
+    };
+    // Rendering
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.executeRender = function () {
+        var needsFullRerender = this.needsFullRerender; // save before clearing
+        // clear these BEFORE the render so that new values will accumulate during render
+        this.needsRerender = false;
+        this.needsFullRerender = false;
+        this.isRendering = true;
+        this.renderComponent(needsFullRerender);
+        this.isRendering = false;
+        // received a rerender request while rendering
+        if (this.needsRerender) {
+            this.delayedRerender();
+        }
+    };
+    /*
+    don't call this directly. use executeRender instead
+    */
+    Calendar.prototype.renderComponent = function (needsFull) {
+        var _a = this, state = _a.state, component = _a.component;
+        var viewType = state.viewType;
+        var viewSpec = this.viewSpecs[viewType];
+        var savedScroll = (needsFull && component) ? component.view.queryScroll() : null;
+        if (!viewSpec) {
+            throw new Error("View type \"" + viewType + "\" is not valid");
+        }
+        // if event sources are still loading and progressive rendering hasn't been enabled,
+        // keep rendering the last fully loaded set of events
+        var renderableEventStore = this.renderableEventStore =
+            (state.eventSourceLoadingLevel && !this.opt('progressiveEventRendering')) ?
+                this.renderableEventStore :
+                state.eventStore;
+        var eventUiSingleBase = this.buildEventUiSingleBase(viewSpec.options);
+        var eventUiBySource = this.buildEventUiBySource(state.eventSources);
+        var eventUiBases = this.eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource);
+        if (needsFull || !component) {
+            if (component) {
+                component.freezeHeight(); // next component will unfreeze it
+                component.destroy();
+            }
+            component = this.component = new CalendarComponent({
+                calendar: this,
+                view: null,
+                dateEnv: this.dateEnv,
+                theme: this.theme,
+                options: this.optionsManager.computed
+            }, this.el);
+            this.isViewUpdated = true;
+            this.isDatesUpdated = true;
+            this.isEventsUpdated = true;
+        }
+        component.receiveProps(__assign({}, state, { viewSpec: viewSpec, dateProfile: state.dateProfile, dateProfileGenerator: this.dateProfileGenerators[viewType], eventStore: renderableEventStore, eventUiBases: eventUiBases, dateSelection: state.dateSelection, eventSelection: state.eventSelection, eventDrag: state.eventDrag, eventResize: state.eventResize }));
+        if (savedScroll) {
+            component.view.applyScroll(savedScroll, false);
+        }
+        if (this.isViewUpdated) {
+            this.isViewUpdated = false;
+            this.publiclyTrigger('viewSkeletonRender', [
+                {
+                    view: component.view,
+                    el: component.view.el
+                }
+            ]);
+        }
+        if (this.isDatesUpdated) {
+            this.isDatesUpdated = false;
+            this.publiclyTrigger('datesRender', [
+                {
+                    view: component.view,
+                    el: component.view.el
+                }
+            ]);
+        }
+        if (this.isEventsUpdated) {
+            this.isEventsUpdated = false;
+        }
+        this.releaseAfterSizingTriggers();
+    };
+    // Options
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.setOption = function (name, val) {
+        var _a;
+        this.mutateOptions((_a = {}, _a[name] = val, _a), [], true);
+    };
+    Calendar.prototype.getOption = function (name) {
+        return this.optionsManager.computed[name];
+    };
+    Calendar.prototype.opt = function (name) {
+        return this.optionsManager.computed[name];
+    };
+    Calendar.prototype.viewOpt = function (name) {
+        return this.viewOpts()[name];
+    };
+    Calendar.prototype.viewOpts = function () {
+        return this.viewSpecs[this.state.viewType].options;
+    };
+    /*
+    handles option changes (like a diff)
+    */
+    Calendar.prototype.mutateOptions = function (updates, removals, isDynamic, deepEqual) {
+        var _this = this;
+        var changeHandlers = this.pluginSystem.hooks.optionChangeHandlers;
+        var normalUpdates = {};
+        var specialUpdates = {};
+        var oldDateEnv = this.dateEnv; // do this before handleOptions
+        var isTimeZoneDirty = false;
+        var isSizeDirty = false;
+        var anyDifficultOptions = Boolean(removals.length);
+        for (var name_1 in updates) {
+            if (changeHandlers[name_1]) {
+                specialUpdates[name_1] = updates[name_1];
+            }
+            else {
+                normalUpdates[name_1] = updates[name_1];
+            }
+        }
+        for (var name_2 in normalUpdates) {
+            if (/^(height|contentHeight|aspectRatio)$/.test(name_2)) {
+                isSizeDirty = true;
+            }
+            else if (/^(defaultDate|defaultView)$/.test(name_2)) ;
+            else {
+                anyDifficultOptions = true;
+                if (name_2 === 'timeZone') {
+                    isTimeZoneDirty = true;
+                }
+            }
+        }
+        this.optionsManager.mutate(normalUpdates, removals, isDynamic);
+        if (anyDifficultOptions) {
+            this.handleOptions(this.optionsManager.computed);
+            this.needsFullRerender = true;
+        }
+        this.batchRendering(function () {
+            if (anyDifficultOptions) {
+                if (isTimeZoneDirty) {
+                    _this.dispatch({
+                        type: 'CHANGE_TIMEZONE',
+                        oldDateEnv: oldDateEnv
+                    });
+                }
+                /* HACK
+                has the same effect as calling this.requestRerender(true)
+                but recomputes the state's dateProfile
+                */
+                _this.dispatch({
+                    type: 'SET_VIEW_TYPE',
+                    viewType: _this.state.viewType
+                });
+            }
+            else if (isSizeDirty) {
+                _this.updateSize();
+            }
+            // special updates
+            if (deepEqual) {
+                for (var name_3 in specialUpdates) {
+                    changeHandlers[name_3](specialUpdates[name_3], _this, deepEqual);
+                }
+            }
+        });
+    };
+    /*
+    rebuilds things based off of a complete set of refined options
+    */
+    Calendar.prototype.handleOptions = function (options) {
+        var _this = this;
+        var pluginHooks = this.pluginSystem.hooks;
+        this.defaultAllDayEventDuration = createDuration(options.defaultAllDayEventDuration);
+        this.defaultTimedEventDuration = createDuration(options.defaultTimedEventDuration);
+        this.delayedRerender = this.buildDelayedRerender(options.rerenderDelay);
+        this.theme = this.buildTheme(options);
+        var available = this.parseRawLocales(options.locales);
+        this.availableRawLocales = available.map;
+        var locale = this.buildLocale(options.locale || available.defaultCode, available.map);
+        this.dateEnv = this.buildDateEnv(locale, options.timeZone, pluginHooks.namedTimeZonedImpl, options.firstDay, options.weekNumberCalculation, options.weekLabel, pluginHooks.cmdFormatter);
+        this.selectionConfig = this.buildSelectionConfig(options); // needs dateEnv. do after :(
+        // ineffecient to do every time?
+        this.viewSpecs = buildViewSpecs(pluginHooks.views, this.optionsManager);
+        // ineffecient to do every time?
+        this.dateProfileGenerators = mapHash(this.viewSpecs, function (viewSpec) {
+            return new viewSpec.class.prototype.dateProfileGeneratorClass(viewSpec, _this);
+        });
+    };
+    Calendar.prototype.getAvailableLocaleCodes = function () {
+        return Object.keys(this.availableRawLocales);
+    };
+    Calendar.prototype._buildSelectionConfig = function (rawOpts) {
+        return processScopedUiProps('select', rawOpts, this);
+    };
+    Calendar.prototype._buildEventUiSingleBase = function (rawOpts) {
+        if (rawOpts.editable) { // so 'editable' affected events
+            rawOpts = __assign({}, rawOpts, { eventEditable: true });
+        }
+        return processScopedUiProps('event', rawOpts, this);
+    };
+    // Trigger
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.hasPublicHandlers = function (name) {
+        return this.hasHandlers(name) ||
+            this.opt(name); // handler specified in options
+    };
+    Calendar.prototype.publiclyTrigger = function (name, args) {
+        var optHandler = this.opt(name);
+        this.triggerWith(name, this, args);
+        if (optHandler) {
+            return optHandler.apply(this, args);
+        }
+    };
+    Calendar.prototype.publiclyTriggerAfterSizing = function (name, args) {
+        var afterSizingTriggers = this.afterSizingTriggers;
+        (afterSizingTriggers[name] || (afterSizingTriggers[name] = [])).push(args);
+    };
+    Calendar.prototype.releaseAfterSizingTriggers = function () {
+        var afterSizingTriggers = this.afterSizingTriggers;
+        for (var name_4 in afterSizingTriggers) {
+            for (var _i = 0, _a = afterSizingTriggers[name_4]; _i < _a.length; _i++) {
+                var args = _a[_i];
+                this.publiclyTrigger(name_4, args);
+            }
+        }
+        this.afterSizingTriggers = {};
+    };
+    // View
+    // -----------------------------------------------------------------------------------------------------------------
+    // Returns a boolean about whether the view is okay to instantiate at some point
+    Calendar.prototype.isValidViewType = function (viewType) {
+        return Boolean(this.viewSpecs[viewType]);
+    };
+    Calendar.prototype.changeView = function (viewType, dateOrRange) {
+        var dateMarker = null;
+        if (dateOrRange) {
+            if (dateOrRange.start && dateOrRange.end) { // a range
+                this.optionsManager.mutate({ visibleRange: dateOrRange }, []); // will not rerender
+                this.handleOptions(this.optionsManager.computed); // ...but yuck
+            }
+            else { // a date
+                dateMarker = this.dateEnv.createMarker(dateOrRange); // just like gotoDate
+            }
+        }
+        this.unselect();
+        this.dispatch({
+            type: 'SET_VIEW_TYPE',
+            viewType: viewType,
+            dateMarker: dateMarker
+        });
+    };
+    // Forces navigation to a view for the given date.
+    // `viewType` can be a specific view name or a generic one like "week" or "day".
+    // needs to change
+    Calendar.prototype.zoomTo = function (dateMarker, viewType) {
+        var spec;
+        viewType = viewType || 'day'; // day is default zoom
+        spec = this.viewSpecs[viewType] ||
+            this.getUnitViewSpec(viewType);
+        this.unselect();
+        if (spec) {
+            this.dispatch({
+                type: 'SET_VIEW_TYPE',
+                viewType: spec.type,
+                dateMarker: dateMarker
+            });
+        }
+        else {
+            this.dispatch({
+                type: 'SET_DATE',
+                dateMarker: dateMarker
+            });
+        }
+    };
+    // Given a duration singular unit, like "week" or "day", finds a matching view spec.
+    // Preference is given to views that have corresponding buttons.
+    Calendar.prototype.getUnitViewSpec = function (unit) {
+        var component = this.component;
+        var viewTypes = [];
+        var i;
+        var spec;
+        // put views that have buttons first. there will be duplicates, but oh
+        if (component.header) {
+            viewTypes.push.apply(viewTypes, component.header.viewsWithButtons);
+        }
+        if (component.footer) {
+            viewTypes.push.apply(viewTypes, component.footer.viewsWithButtons);
+        }
+        for (var viewType in this.viewSpecs) {
+            viewTypes.push(viewType);
+        }
+        for (i = 0; i < viewTypes.length; i++) {
+            spec = this.viewSpecs[viewTypes[i]];
+            if (spec) {
+                if (spec.singleUnit === unit) {
+                    return spec;
+                }
+            }
+        }
+    };
+    // Current Date
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.getInitialDate = function () {
+        var defaultDateInput = this.opt('defaultDate');
+        // compute the initial ambig-timezone date
+        if (defaultDateInput != null) {
+            return this.dateEnv.createMarker(defaultDateInput);
+        }
+        else {
+            return this.getNow(); // getNow already returns unzoned
+        }
+    };
+    Calendar.prototype.prev = function () {
+        this.unselect();
+        this.dispatch({ type: 'PREV' });
+    };
+    Calendar.prototype.next = function () {
+        this.unselect();
+        this.dispatch({ type: 'NEXT' });
+    };
+    Calendar.prototype.prevYear = function () {
+        this.unselect();
+        this.dispatch({
+            type: 'SET_DATE',
+            dateMarker: this.dateEnv.addYears(this.state.currentDate, -1)
+        });
+    };
+    Calendar.prototype.nextYear = function () {
+        this.unselect();
+        this.dispatch({
+            type: 'SET_DATE',
+            dateMarker: this.dateEnv.addYears(this.state.currentDate, 1)
+        });
+    };
+    Calendar.prototype.today = function () {
+        this.unselect();
+        this.dispatch({
+            type: 'SET_DATE',
+            dateMarker: this.getNow()
+        });
+    };
+    Calendar.prototype.gotoDate = function (zonedDateInput) {
+        this.unselect();
+        this.dispatch({
+            type: 'SET_DATE',
+            dateMarker: this.dateEnv.createMarker(zonedDateInput)
+        });
+    };
+    Calendar.prototype.incrementDate = function (deltaInput) {
+        var delta = createDuration(deltaInput);
+        if (delta) { // else, warn about invalid input?
+            this.unselect();
+            this.dispatch({
+                type: 'SET_DATE',
+                dateMarker: this.dateEnv.add(this.state.currentDate, delta)
+            });
+        }
+    };
+    // for external API
+    Calendar.prototype.getDate = function () {
+        return this.dateEnv.toDate(this.state.currentDate);
+    };
+    // Date Formatting Utils
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.formatDate = function (d, formatter) {
+        var dateEnv = this.dateEnv;
+        return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
+    };
+    // `settings` is for formatter AND isEndExclusive
+    Calendar.prototype.formatRange = function (d0, d1, settings) {
+        var dateEnv = this.dateEnv;
+        return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings, this.opt('defaultRangeSeparator')), settings);
+    };
+    Calendar.prototype.formatIso = function (d, omitTime) {
+        var dateEnv = this.dateEnv;
+        return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime: omitTime });
+    };
+    // Sizing
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.windowResize = function (ev) {
+        if (!this.isHandlingWindowResize &&
+            this.component && // why?
+            ev.target === window // not a jqui resize event
+        ) {
+            this.isHandlingWindowResize = true;
+            this.updateSize();
+            this.publiclyTrigger('windowResize', [this.view]);
+            this.isHandlingWindowResize = false;
+        }
+    };
+    Calendar.prototype.updateSize = function () {
+        if (this.component) { // when?
+            this.component.updateSize(true);
+        }
+    };
+    // Component Registration
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.registerInteractiveComponent = function (component, settingsInput) {
+        var settings = parseInteractionSettings(component, settingsInput);
+        var DEFAULT_INTERACTIONS = [
+            EventClicking,
+            EventHovering
+        ];
+        var interactionClasses = DEFAULT_INTERACTIONS.concat(this.pluginSystem.hooks.componentInteractions);
+        var interactions = interactionClasses.map(function (interactionClass) {
+            return new interactionClass(settings);
+        });
+        this.interactionsStore[component.uid] = interactions;
+        interactionSettingsStore[component.uid] = settings;
+    };
+    Calendar.prototype.unregisterInteractiveComponent = function (component) {
+        for (var _i = 0, _a = this.interactionsStore[component.uid]; _i < _a.length; _i++) {
+            var listener = _a[_i];
+            listener.destroy();
+        }
+        delete this.interactionsStore[component.uid];
+        delete interactionSettingsStore[component.uid];
+    };
+    // Date Selection / Event Selection / DayClick
+    // -----------------------------------------------------------------------------------------------------------------
+    // this public method receives start/end dates in any format, with any timezone
+    // NOTE: args were changed from v3
+    Calendar.prototype.select = function (dateOrObj, endDate) {
+        var selectionInput;
+        if (endDate == null) {
+            if (dateOrObj.start != null) {
+                selectionInput = dateOrObj;
+            }
+            else {
+                selectionInput = {
+                    start: dateOrObj,
+                    end: null
+                };
+            }
+        }
+        else {
+            selectionInput = {
+                start: dateOrObj,
+                end: endDate
+            };
+        }
+        var selection = parseDateSpan(selectionInput, this.dateEnv, createDuration({ days: 1 }) // TODO: cache this?
+        );
+        if (selection) { // throw parse error otherwise?
+            this.dispatch({ type: 'SELECT_DATES', selection: selection });
+            this.triggerDateSelect(selection);
+        }
+    };
+    // public method
+    Calendar.prototype.unselect = function (pev) {
+        if (this.state.dateSelection) {
+            this.dispatch({ type: 'UNSELECT_DATES' });
+            this.triggerDateUnselect(pev);
+        }
+    };
+    Calendar.prototype.triggerDateSelect = function (selection, pev) {
+        var arg = __assign({}, this.buildDateSpanApi(selection), { jsEvent: pev ? pev.origEvent : null, view: this.view });
+        this.publiclyTrigger('select', [arg]);
+    };
+    Calendar.prototype.triggerDateUnselect = function (pev) {
+        this.publiclyTrigger('unselect', [
+            {
+                jsEvent: pev ? pev.origEvent : null,
+                view: this.view
+            }
+        ]);
+    };
+    // TODO: receive pev?
+    Calendar.prototype.triggerDateClick = function (dateSpan, dayEl, view, ev) {
+        var arg = __assign({}, this.buildDatePointApi(dateSpan), { dayEl: dayEl, jsEvent: ev, // Is this always a mouse event? See #4655
+            view: view });
+        this.publiclyTrigger('dateClick', [arg]);
+    };
+    Calendar.prototype.buildDatePointApi = function (dateSpan) {
+        var props = {};
+        for (var _i = 0, _a = this.pluginSystem.hooks.datePointTransforms; _i < _a.length; _i++) {
+            var transform = _a[_i];
+            __assign(props, transform(dateSpan, this));
+        }
+        __assign(props, buildDatePointApi(dateSpan, this.dateEnv));
+        return props;
+    };
+    Calendar.prototype.buildDateSpanApi = function (dateSpan) {
+        var props = {};
+        for (var _i = 0, _a = this.pluginSystem.hooks.dateSpanTransforms; _i < _a.length; _i++) {
+            var transform = _a[_i];
+            __assign(props, transform(dateSpan, this));
+        }
+        __assign(props, buildDateSpanApi(dateSpan, this.dateEnv));
+        return props;
+    };
+    // Date Utils
+    // -----------------------------------------------------------------------------------------------------------------
+    // Returns a DateMarker for the current date, as defined by the client's computer or from the `now` option
+    Calendar.prototype.getNow = function () {
+        var now = this.opt('now');
+        if (typeof now === 'function') {
+            now = now();
+        }
+        if (now == null) {
+            return this.dateEnv.createNowMarker();
+        }
+        return this.dateEnv.createMarker(now);
+    };
+    // Event-Date Utilities
+    // -----------------------------------------------------------------------------------------------------------------
+    // Given an event's allDay status and start date, return what its fallback end date should be.
+    // TODO: rename to computeDefaultEventEnd
+    Calendar.prototype.getDefaultEventEnd = function (allDay, marker) {
+        var end = marker;
+        if (allDay) {
+            end = startOfDay(end);
+            end = this.dateEnv.add(end, this.defaultAllDayEventDuration);
+        }
+        else {
+            end = this.dateEnv.add(end, this.defaultTimedEventDuration);
+        }
+        return end;
+    };
+    // Public Events API
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.addEvent = function (eventInput, sourceInput) {
+        if (eventInput instanceof EventApi) {
+            var def = eventInput._def;
+            var instance = eventInput._instance;
+            // not already present? don't want to add an old snapshot
+            if (!this.state.eventStore.defs[def.defId]) {
+                this.dispatch({
+                    type: 'ADD_EVENTS',
+                    eventStore: eventTupleToStore({ def: def, instance: instance }) // TODO: better util for two args?
+                });
+            }
+            return eventInput;
+        }
+        var sourceId;
+        if (sourceInput instanceof EventSourceApi) {
+            sourceId = sourceInput.internalEventSource.sourceId;
+        }
+        else if (sourceInput != null) {
+            var sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
+            if (!sourceApi) {
+                console.warn('Could not find an event source with ID "' + sourceInput + '"'); // TODO: test
+                return null;
+            }
+            else {
+                sourceId = sourceApi.internalEventSource.sourceId;
+            }
+        }
+        var tuple = parseEvent(eventInput, sourceId, this);
+        if (tuple) {
+            this.dispatch({
+                type: 'ADD_EVENTS',
+                eventStore: eventTupleToStore(tuple)
+            });
+            return new EventApi(this, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
+        }
+        return null;
+    };
+    // TODO: optimize
+    Calendar.prototype.getEventById = function (id) {
+        var _a = this.state.eventStore, defs = _a.defs, instances = _a.instances;
+        id = String(id);
+        for (var defId in defs) {
+            var def = defs[defId];
+            if (def.publicId === id) {
+                if (def.recurringDef) {
+                    return new EventApi(this, def, null);
+                }
+                else {
+                    for (var instanceId in instances) {
+                        var instance = instances[instanceId];
+                        if (instance.defId === def.defId) {
+                            return new EventApi(this, def, instance);
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    };
+    Calendar.prototype.getEvents = function () {
+        var _a = this.state.eventStore, defs = _a.defs, instances = _a.instances;
+        var eventApis = [];
+        for (var id in instances) {
+            var instance = instances[id];
+            var def = defs[instance.defId];
+            eventApis.push(new EventApi(this, def, instance));
+        }
+        return eventApis;
+    };
+    Calendar.prototype.removeAllEvents = function () {
+        this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
+    };
+    Calendar.prototype.rerenderEvents = function () {
+        this.dispatch({ type: 'RESET_EVENTS' });
+    };
+    // Public Event Sources API
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.getEventSources = function () {
+        var sourceHash = this.state.eventSources;
+        var sourceApis = [];
+        for (var internalId in sourceHash) {
+            sourceApis.push(new EventSourceApi(this, sourceHash[internalId]));
+        }
+        return sourceApis;
+    };
+    Calendar.prototype.getEventSourceById = function (id) {
+        var sourceHash = this.state.eventSources;
+        id = String(id);
+        for (var sourceId in sourceHash) {
+            if (sourceHash[sourceId].publicId === id) {
+                return new EventSourceApi(this, sourceHash[sourceId]);
+            }
+        }
+        return null;
+    };
+    Calendar.prototype.addEventSource = function (sourceInput) {
+        if (sourceInput instanceof EventSourceApi) {
+            // not already present? don't want to add an old snapshot
+            if (!this.state.eventSources[sourceInput.internalEventSource.sourceId]) {
+                this.dispatch({
+                    type: 'ADD_EVENT_SOURCES',
+                    sources: [sourceInput.internalEventSource]
+                });
+            }
+            return sourceInput;
+        }
+        var eventSource = parseEventSource(sourceInput, this);
+        if (eventSource) { // TODO: error otherwise?
+            this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
+            return new EventSourceApi(this, eventSource);
+        }
+        return null;
+    };
+    Calendar.prototype.removeAllEventSources = function () {
+        this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
+    };
+    Calendar.prototype.refetchEvents = function () {
+        this.dispatch({ type: 'FETCH_EVENT_SOURCES' });
+    };
+    // Scroll
+    // -----------------------------------------------------------------------------------------------------------------
+    Calendar.prototype.scrollToTime = function (timeInput) {
+        var duration = createDuration(timeInput);
+        if (duration) {
+            this.component.view.scrollToDuration(duration);
+        }
+    };
+    return Calendar;
+}());
+EmitterMixin.mixInto(Calendar);
+// for memoizers
+// -----------------------------------------------------------------------------------------------------------------
+function buildDateEnv(locale, timeZone, namedTimeZoneImpl, firstDay, weekNumberCalculation, weekLabel, cmdFormatter) {
+    return new DateEnv({
+        calendarSystem: 'gregory',
+        timeZone: timeZone,
+        namedTimeZoneImpl: namedTimeZoneImpl,
+        locale: locale,
+        weekNumberCalculation: weekNumberCalculation,
+        firstDay: firstDay,
+        weekLabel: weekLabel,
+        cmdFormatter: cmdFormatter
+    });
+}
+function buildTheme(calendarOptions) {
+    var themeClass = this.pluginSystem.hooks.themeClasses[calendarOptions.themeSystem] || StandardTheme;
+    return new themeClass(calendarOptions);
+}
+function buildDelayedRerender(wait) {
+    var func = this.tryRerender.bind(this);
+    if (wait != null) {
+        func = debounce(func, wait);
+    }
+    return func;
+}
+function buildEventUiBySource(eventSources) {
+    return mapHash(eventSources, function (eventSource) {
+        return eventSource.ui;
+    });
+}
+function buildEventUiBases(eventDefs, eventUiSingleBase, eventUiBySource) {
+    var eventUiBases = { '': eventUiSingleBase };
+    for (var defId in eventDefs) {
+        var def = eventDefs[defId];
+        if (def.sourceId && eventUiBySource[def.sourceId]) {
+            eventUiBases[defId] = eventUiBySource[def.sourceId];
+        }
+    }
+    return eventUiBases;
+}
+
+var View = /** @class */ (function (_super) {
+    __extends(View, _super);
+    function View(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, createElement('div', { className: 'fc-view fc-' + viewSpec.type + '-view' }), true // isView (HACK)
+        ) || this;
+        _this.renderDatesMem = memoizeRendering(_this.renderDatesWrap, _this.unrenderDatesWrap);
+        _this.renderBusinessHoursMem = memoizeRendering(_this.renderBusinessHours, _this.unrenderBusinessHours, [_this.renderDatesMem]);
+        _this.renderDateSelectionMem = memoizeRendering(_this.renderDateSelectionWrap, _this.unrenderDateSelectionWrap, [_this.renderDatesMem]);
+        _this.renderEventsMem = memoizeRendering(_this.renderEvents, _this.unrenderEvents, [_this.renderDatesMem]);
+        _this.renderEventSelectionMem = memoizeRendering(_this.renderEventSelectionWrap, _this.unrenderEventSelectionWrap, [_this.renderEventsMem]);
+        _this.renderEventDragMem = memoizeRendering(_this.renderEventDragWrap, _this.unrenderEventDragWrap, [_this.renderDatesMem]);
+        _this.renderEventResizeMem = memoizeRendering(_this.renderEventResizeWrap, _this.unrenderEventResizeWrap, [_this.renderDatesMem]);
+        _this.viewSpec = viewSpec;
+        _this.dateProfileGenerator = dateProfileGenerator;
+        _this.type = viewSpec.type;
+        _this.eventOrderSpecs = parseFieldSpecs(_this.opt('eventOrder'));
+        _this.nextDayThreshold = createDuration(_this.opt('nextDayThreshold'));
+        parentEl.appendChild(_this.el);
+        _this.initialize();
+        return _this;
+    }
+    View.prototype.initialize = function () {
+    };
+    Object.defineProperty(View.prototype, "activeStart", {
+        // Date Setting/Unsetting
+        // -----------------------------------------------------------------------------------------------------------------
+        get: function () {
+            return this.dateEnv.toDate(this.props.dateProfile.activeRange.start);
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(View.prototype, "activeEnd", {
+        get: function () {
+            return this.dateEnv.toDate(this.props.dateProfile.activeRange.end);
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(View.prototype, "currentStart", {
+        get: function () {
+            return this.dateEnv.toDate(this.props.dateProfile.currentRange.start);
+        },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(View.prototype, "currentEnd", {
+        get: function () {
+            return this.dateEnv.toDate(this.props.dateProfile.currentRange.end);
+        },
+        enumerable: true,
+        configurable: true
+    });
+    // General Rendering
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.render = function (props) {
+        this.renderDatesMem(props.dateProfile);
+        this.renderBusinessHoursMem(props.businessHours);
+        this.renderDateSelectionMem(props.dateSelection);
+        this.renderEventsMem(props.eventStore);
+        this.renderEventSelectionMem(props.eventSelection);
+        this.renderEventDragMem(props.eventDrag);
+        this.renderEventResizeMem(props.eventResize);
+    };
+    View.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.renderDatesMem.unrender(); // should unrender everything else
+    };
+    // Sizing
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+        var calendar = this.calendar;
+        if (isResize || // HACKS...
+            calendar.isViewUpdated ||
+            calendar.isDatesUpdated ||
+            calendar.isEventsUpdated) {
+            // sort of the catch-all sizing
+            // anything that might cause dimension changes
+            this.updateBaseSize(isResize, viewHeight, isAuto);
+        }
+    };
+    View.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
+    };
+    // Date Rendering
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderDatesWrap = function (dateProfile) {
+        this.renderDates(dateProfile);
+        this.addScroll({
+            duration: createDuration(this.opt('scrollTime'))
+        });
+        this.startNowIndicator(dateProfile); // shouldn't render yet because updateSize will be called soon
+    };
+    View.prototype.unrenderDatesWrap = function () {
+        this.stopNowIndicator();
+        this.unrenderDates();
+    };
+    View.prototype.renderDates = function (dateProfile) { };
+    View.prototype.unrenderDates = function () { };
+    // Business Hours
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderBusinessHours = function (businessHours) { };
+    View.prototype.unrenderBusinessHours = function () { };
+    // Date Selection
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderDateSelectionWrap = function (selection) {
+        if (selection) {
+            this.renderDateSelection(selection);
+        }
+    };
+    View.prototype.unrenderDateSelectionWrap = function (selection) {
+        if (selection) {
+            this.unrenderDateSelection(selection);
+        }
+    };
+    View.prototype.renderDateSelection = function (selection) { };
+    View.prototype.unrenderDateSelection = function (selection) { };
+    // Event Rendering
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderEvents = function (eventStore) { };
+    View.prototype.unrenderEvents = function () { };
+    // util for subclasses
+    View.prototype.sliceEvents = function (eventStore, allDay) {
+        var props = this.props;
+        return sliceEventStore(eventStore, props.eventUiBases, props.dateProfile.activeRange, allDay ? this.nextDayThreshold : null).fg;
+    };
+    View.prototype.computeEventDraggable = function (eventDef, eventUi) {
+        var transformers = this.calendar.pluginSystem.hooks.isDraggableTransformers;
+        var val = eventUi.startEditable;
+        for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) {
+            var transformer = transformers_1[_i];
+            val = transformer(val, eventDef, eventUi, this);
+        }
+        return val;
+    };
+    View.prototype.computeEventStartResizable = function (eventDef, eventUi) {
+        return eventUi.durationEditable && this.opt('eventResizableFromStart');
+    };
+    View.prototype.computeEventEndResizable = function (eventDef, eventUi) {
+        return eventUi.durationEditable;
+    };
+    // Event Selection
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderEventSelectionWrap = function (instanceId) {
+        if (instanceId) {
+            this.renderEventSelection(instanceId);
+        }
+    };
+    View.prototype.unrenderEventSelectionWrap = function (instanceId) {
+        if (instanceId) {
+            this.unrenderEventSelection(instanceId);
+        }
+    };
+    View.prototype.renderEventSelection = function (instanceId) { };
+    View.prototype.unrenderEventSelection = function (instanceId) { };
+    // Event Drag
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderEventDragWrap = function (state) {
+        if (state) {
+            this.renderEventDrag(state);
+        }
+    };
+    View.prototype.unrenderEventDragWrap = function (state) {
+        if (state) {
+            this.unrenderEventDrag(state);
+        }
+    };
+    View.prototype.renderEventDrag = function (state) { };
+    View.prototype.unrenderEventDrag = function (state) { };
+    // Event Resize
+    // -----------------------------------------------------------------------------------------------------------------
+    View.prototype.renderEventResizeWrap = function (state) {
+        if (state) {
+            this.renderEventResize(state);
+        }
+    };
+    View.prototype.unrenderEventResizeWrap = function (state) {
+        if (state) {
+            this.unrenderEventResize(state);
+        }
+    };
+    View.prototype.renderEventResize = function (state) { };
+    View.prototype.unrenderEventResize = function (state) { };
+    /* Now Indicator
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Immediately render the current time indicator and begins re-rendering it at an interval,
+    // which is defined by this.getNowIndicatorUnit().
+    // TODO: somehow do this for the current whole day's background too
+    View.prototype.startNowIndicator = function (dateProfile) {
+        var _this = this;
+        var dateEnv = this.dateEnv;
+        var unit;
+        var update;
+        var delay; // ms wait value
+        if (this.opt('nowIndicator')) {
+            unit = this.getNowIndicatorUnit(dateProfile);
+            if (unit) {
+                update = this.updateNowIndicator.bind(this);
+                this.initialNowDate = this.calendar.getNow();
+                this.initialNowQueriedMs = new Date().valueOf();
+                // wait until the beginning of the next interval
+                delay = dateEnv.add(dateEnv.startOf(this.initialNowDate, unit), createDuration(1, unit)).valueOf() - this.initialNowDate.valueOf();
+                // TODO: maybe always use setTimeout, waiting until start of next unit
+                this.nowIndicatorTimeoutID = setTimeout(function () {
+                    _this.nowIndicatorTimeoutID = null;
+                    update();
+                    if (unit === 'second') {
+                        delay = 1000; // every second
+                    }
+                    else {
+                        delay = 1000 * 60; // otherwise, every minute
+                    }
+                    _this.nowIndicatorIntervalID = setInterval(update, delay); // update every interval
+                }, delay);
+            }
+            // rendering will be initiated in updateSize
+        }
+    };
+    // rerenders the now indicator, computing the new current time from the amount of time that has passed
+    // since the initial getNow call.
+    View.prototype.updateNowIndicator = function () {
+        if (this.props.dateProfile && // a way to determine if dates were rendered yet
+            this.initialNowDate // activated before?
+        ) {
+            this.unrenderNowIndicator(); // won't unrender if unnecessary
+            this.renderNowIndicator(addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs));
+            this.isNowIndicatorRendered = true;
+        }
+    };
+    // Immediately unrenders the view's current time indicator and stops any re-rendering timers.
+    // Won't cause side effects if indicator isn't rendered.
+    View.prototype.stopNowIndicator = function () {
+        if (this.isNowIndicatorRendered) {
+            if (this.nowIndicatorTimeoutID) {
+                clearTimeout(this.nowIndicatorTimeoutID);
+                this.nowIndicatorTimeoutID = null;
+            }
+            if (this.nowIndicatorIntervalID) {
+                clearInterval(this.nowIndicatorIntervalID);
+                this.nowIndicatorIntervalID = null;
+            }
+            this.unrenderNowIndicator();
+            this.isNowIndicatorRendered = false;
+        }
+    };
+    View.prototype.getNowIndicatorUnit = function (dateProfile) {
+        // subclasses should implement
+    };
+    // Renders a current time indicator at the given datetime
+    View.prototype.renderNowIndicator = function (date) {
+        // SUBCLASSES MUST PASS TO CHILDREN!
+    };
+    // Undoes the rendering actions from renderNowIndicator
+    View.prototype.unrenderNowIndicator = function () {
+        // SUBCLASSES MUST PASS TO CHILDREN!
+    };
+    /* Scroller
+    ------------------------------------------------------------------------------------------------------------------*/
+    View.prototype.addScroll = function (scroll) {
+        var queuedScroll = this.queuedScroll || (this.queuedScroll = {});
+        __assign(queuedScroll, scroll);
+    };
+    View.prototype.popScroll = function (isResize) {
+        this.applyQueuedScroll(isResize);
+        this.queuedScroll = null;
+    };
+    View.prototype.applyQueuedScroll = function (isResize) {
+        this.applyScroll(this.queuedScroll || {}, isResize);
+    };
+    View.prototype.queryScroll = function () {
+        var scroll = {};
+        if (this.props.dateProfile) { // dates rendered yet?
+            __assign(scroll, this.queryDateScroll());
+        }
+        return scroll;
+    };
+    View.prototype.applyScroll = function (scroll, isResize) {
+        var duration = scroll.duration;
+        if (duration != null) {
+            delete scroll.duration;
+            if (this.props.dateProfile) { // dates rendered yet?
+                __assign(scroll, this.computeDateScroll(duration));
+            }
+        }
+        if (this.props.dateProfile) { // dates rendered yet?
+            this.applyDateScroll(scroll);
+        }
+    };
+    View.prototype.computeDateScroll = function (duration) {
+        return {}; // subclasses must implement
+    };
+    View.prototype.queryDateScroll = function () {
+        return {}; // subclasses must implement
+    };
+    View.prototype.applyDateScroll = function (scroll) {
+        // subclasses must implement
+    };
+    // for API
+    View.prototype.scrollToDuration = function (duration) {
+        this.applyScroll({ duration: duration }, false);
+    };
+    return View;
+}(DateComponent));
+EmitterMixin.mixInto(View);
+View.prototype.usesMinMaxTime = false;
+View.prototype.dateProfileGeneratorClass = DateProfileGenerator;
+
+var FgEventRenderer = /** @class */ (function () {
+    function FgEventRenderer(context) {
+        this.segs = [];
+        this.isSizeDirty = false;
+        this.context = context;
+    }
+    FgEventRenderer.prototype.renderSegs = function (segs, mirrorInfo) {
+        this.rangeUpdated(); // called too frequently :(
+        // render an `.el` on each seg
+        // returns a subset of the segs. segs that were actually rendered
+        segs = this.renderSegEls(segs, mirrorInfo);
+        this.segs = segs;
+        this.attachSegs(segs, mirrorInfo);
+        this.isSizeDirty = true;
+        this.context.view.triggerRenderedSegs(this.segs, Boolean(mirrorInfo));
+    };
+    FgEventRenderer.prototype.unrender = function (_segs, mirrorInfo) {
+        this.context.view.triggerWillRemoveSegs(this.segs, Boolean(mirrorInfo));
+        this.detachSegs(this.segs);
+        this.segs = [];
+    };
+    // Updates values that rely on options and also relate to range
+    FgEventRenderer.prototype.rangeUpdated = function () {
+        var options = this.context.options;
+        var displayEventTime;
+        var displayEventEnd;
+        this.eventTimeFormat = createFormatter(options.eventTimeFormat || this.computeEventTimeFormat(), options.defaultRangeSeparator);
+        displayEventTime = options.displayEventTime;
+        if (displayEventTime == null) {
+            displayEventTime = this.computeDisplayEventTime(); // might be based off of range
+        }
+        displayEventEnd = options.displayEventEnd;
+        if (displayEventEnd == null) {
+            displayEventEnd = this.computeDisplayEventEnd(); // might be based off of range
+        }
+        this.displayEventTime = displayEventTime;
+        this.displayEventEnd = displayEventEnd;
+    };
+    // Renders and assigns an `el` property for each foreground event segment.
+    // Only returns segments that successfully rendered.
+    FgEventRenderer.prototype.renderSegEls = function (segs, mirrorInfo) {
+        var html = '';
+        var i;
+        if (segs.length) { // don't build an empty html string
+            // build a large concatenation of event segment HTML
+            for (i = 0; i < segs.length; i++) {
+                html += this.renderSegHtml(segs[i], mirrorInfo);
+            }
+            // Grab individual elements from the combined HTML string. Use each as the default rendering.
+            // Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false.
+            htmlToElements(html).forEach(function (el, i) {
+                var seg = segs[i];
+                if (el) {
+                    seg.el = el;
+                }
+            });
+            segs = filterSegsViaEls(this.context.view, segs, Boolean(mirrorInfo));
+        }
+        return segs;
+    };
+    // Generic utility for generating the HTML classNames for an event segment's element
+    FgEventRenderer.prototype.getSegClasses = function (seg, isDraggable, isResizable, mirrorInfo) {
+        var classes = [
+            'fc-event',
+            seg.isStart ? 'fc-start' : 'fc-not-start',
+            seg.isEnd ? 'fc-end' : 'fc-not-end'
+        ].concat(seg.eventRange.ui.classNames);
+        if (isDraggable) {
+            classes.push('fc-draggable');
+        }
+        if (isResizable) {
+            classes.push('fc-resizable');
+        }
+        if (mirrorInfo) {
+            classes.push('fc-mirror');
+            if (mirrorInfo.isDragging) {
+                classes.push('fc-dragging');
+            }
+            if (mirrorInfo.isResizing) {
+                classes.push('fc-resizing');
+            }
+        }
+        return classes;
+    };
+    // Compute the text that should be displayed on an event's element.
+    // `range` can be the Event object itself, or something range-like, with at least a `start`.
+    // If event times are disabled, or the event has no time, will return a blank string.
+    // If not specified, formatter will default to the eventTimeFormat setting,
+    // and displayEnd will default to the displayEventEnd setting.
+    FgEventRenderer.prototype.getTimeText = function (eventRange, formatter, displayEnd) {
+        var def = eventRange.def, instance = eventRange.instance;
+        return this._getTimeText(instance.range.start, def.hasEnd ? instance.range.end : null, def.allDay, formatter, displayEnd, instance.forcedStartTzo, instance.forcedEndTzo);
+    };
+    FgEventRenderer.prototype._getTimeText = function (start, end, allDay, formatter, displayEnd, forcedStartTzo, forcedEndTzo) {
+        var dateEnv = this.context.dateEnv;
+        if (formatter == null) {
+            formatter = this.eventTimeFormat;
+        }
+        if (displayEnd == null) {
+            displayEnd = this.displayEventEnd;
+        }
+        if (this.displayEventTime && !allDay) {
+            if (displayEnd && end) {
+                return dateEnv.formatRange(start, end, formatter, {
+                    forcedStartTzo: forcedStartTzo,
+                    forcedEndTzo: forcedEndTzo
+                });
+            }
+            else {
+                return dateEnv.format(start, formatter, {
+                    forcedTzo: forcedStartTzo
+                });
+            }
+        }
+        return '';
+    };
+    FgEventRenderer.prototype.computeEventTimeFormat = function () {
+        return {
+            hour: 'numeric',
+            minute: '2-digit',
+            omitZeroMinute: true
+        };
+    };
+    FgEventRenderer.prototype.computeDisplayEventTime = function () {
+        return true;
+    };
+    FgEventRenderer.prototype.computeDisplayEventEnd = function () {
+        return true;
+    };
+    // Utility for generating event skin-related CSS properties
+    FgEventRenderer.prototype.getSkinCss = function (ui) {
+        return {
+            'background-color': ui.backgroundColor,
+            'border-color': ui.borderColor,
+            color: ui.textColor
+        };
+    };
+    FgEventRenderer.prototype.sortEventSegs = function (segs) {
+        var specs = this.context.view.eventOrderSpecs;
+        var objs = segs.map(buildSegCompareObj);
+        objs.sort(function (obj0, obj1) {
+            return compareByFieldSpecs(obj0, obj1, specs);
+        });
+        return objs.map(function (c) {
+            return c._seg;
+        });
+    };
+    FgEventRenderer.prototype.computeSizes = function (force) {
+        if (force || this.isSizeDirty) {
+            this.computeSegSizes(this.segs);
+        }
+    };
+    FgEventRenderer.prototype.assignSizes = function (force) {
+        if (force || this.isSizeDirty) {
+            this.assignSegSizes(this.segs);
+            this.isSizeDirty = false;
+        }
+    };
+    FgEventRenderer.prototype.computeSegSizes = function (segs) {
+    };
+    FgEventRenderer.prototype.assignSegSizes = function (segs) {
+    };
+    // Manipulation on rendered segs
+    FgEventRenderer.prototype.hideByHash = function (hash) {
+        if (hash) {
+            for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                var seg = _a[_i];
+                if (hash[seg.eventRange.instance.instanceId]) {
+                    seg.el.style.visibility = 'hidden';
+                }
+            }
+        }
+    };
+    FgEventRenderer.prototype.showByHash = function (hash) {
+        if (hash) {
+            for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                var seg = _a[_i];
+                if (hash[seg.eventRange.instance.instanceId]) {
+                    seg.el.style.visibility = '';
+                }
+            }
+        }
+    };
+    FgEventRenderer.prototype.selectByInstanceId = function (instanceId) {
+        if (instanceId) {
+            for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                var seg = _a[_i];
+                var eventInstance = seg.eventRange.instance;
+                if (eventInstance && eventInstance.instanceId === instanceId &&
+                    seg.el // necessary?
+                ) {
+                    seg.el.classList.add('fc-selected');
+                }
+            }
+        }
+    };
+    FgEventRenderer.prototype.unselectByInstanceId = function (instanceId) {
+        if (instanceId) {
+            for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                var seg = _a[_i];
+                if (seg.el) { // necessary?
+                    seg.el.classList.remove('fc-selected');
+                }
+            }
+        }
+    };
+    return FgEventRenderer;
+}());
+// returns a object with all primitive props that can be compared
+function buildSegCompareObj(seg) {
+    var eventDef = seg.eventRange.def;
+    var range = seg.eventRange.instance.range;
+    var start = range.start ? range.start.valueOf() : 0; // TODO: better support for open-range events
+    var end = range.end ? range.end.valueOf() : 0; // "
+    return __assign({}, eventDef.extendedProps, eventDef, { id: eventDef.publicId, start: start,
+        end: end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg // for later retrieval
+     });
+}
+
+var FillRenderer = /** @class */ (function () {
+    function FillRenderer(context) {
+        this.fillSegTag = 'div';
+        this.dirtySizeFlags = {};
+        this.context = context;
+        this.containerElsByType = {};
+        this.segsByType = {};
+    }
+    FillRenderer.prototype.getSegsByType = function (type) {
+        return this.segsByType[type] || [];
+    };
+    FillRenderer.prototype.renderSegs = function (type, segs) {
+        var _a;
+        var renderedSegs = this.renderSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs
+        var containerEls = this.attachSegs(type, renderedSegs);
+        if (containerEls) {
+            (_a = (this.containerElsByType[type] || (this.containerElsByType[type] = []))).push.apply(_a, containerEls);
+        }
+        this.segsByType[type] = renderedSegs;
+        if (type === 'bgEvent') {
+            this.context.view.triggerRenderedSegs(renderedSegs, false); // isMirror=false
+        }
+        this.dirtySizeFlags[type] = true;
+    };
+    // Unrenders a specific type of fill that is currently rendered on the grid
+    FillRenderer.prototype.unrender = function (type) {
+        var segs = this.segsByType[type];
+        if (segs) {
+            if (type === 'bgEvent') {
+                this.context.view.triggerWillRemoveSegs(segs, false); // isMirror=false
+            }
+            this.detachSegs(type, segs);
+        }
+    };
+    // Renders and assigns an `el` property for each fill segment. Generic enough to work with different types.
+    // Only returns segments that successfully rendered.
+    FillRenderer.prototype.renderSegEls = function (type, segs) {
+        var _this = this;
+        var html = '';
+        var i;
+        if (segs.length) {
+            // build a large concatenation of segment HTML
+            for (i = 0; i < segs.length; i++) {
+                html += this.renderSegHtml(type, segs[i]);
+            }
+            // Grab individual elements from the combined HTML string. Use each as the default rendering.
+            // Then, compute the 'el' for each segment.
+            htmlToElements(html).forEach(function (el, i) {
+                var seg = segs[i];
+                if (el) {
+                    seg.el = el;
+                }
+            });
+            if (type === 'bgEvent') {
+                segs = filterSegsViaEls(this.context.view, segs, false // isMirror. background events can never be mirror elements
+                );
+            }
+            // correct element type? (would be bad if a non-TD were inserted into a table for example)
+            segs = segs.filter(function (seg) {
+                return elementMatches(seg.el, _this.fillSegTag);
+            });
+        }
+        return segs;
+    };
+    // Builds the HTML needed for one fill segment. Generic enough to work with different types.
+    FillRenderer.prototype.renderSegHtml = function (type, seg) {
+        var css = null;
+        var classNames = [];
+        if (type !== 'highlight' && type !== 'businessHours') {
+            css = {
+                'background-color': seg.eventRange.ui.backgroundColor
+            };
+        }
+        if (type !== 'highlight') {
+            classNames = classNames.concat(seg.eventRange.ui.classNames);
+        }
+        if (type === 'businessHours') {
+            classNames.push('fc-bgevent');
+        }
+        else {
+            classNames.push('fc-' + type.toLowerCase());
+        }
+        return '<' + this.fillSegTag +
+            (classNames.length ? ' class="' + classNames.join(' ') + '"' : '') +
+            (css ? ' style="' + cssToStr(css) + '"' : '') +
+            '></' + this.fillSegTag + '>';
+    };
+    FillRenderer.prototype.detachSegs = function (type, segs) {
+        var containerEls = this.containerElsByType[type];
+        if (containerEls) {
+            containerEls.forEach(removeElement);
+            delete this.containerElsByType[type];
+        }
+    };
+    FillRenderer.prototype.computeSizes = function (force) {
+        for (var type in this.segsByType) {
+            if (force || this.dirtySizeFlags[type]) {
+                this.computeSegSizes(this.segsByType[type]);
+            }
+        }
+    };
+    FillRenderer.prototype.assignSizes = function (force) {
+        for (var type in this.segsByType) {
+            if (force || this.dirtySizeFlags[type]) {
+                this.assignSegSizes(this.segsByType[type]);
+            }
+        }
+        this.dirtySizeFlags = {};
+    };
+    FillRenderer.prototype.computeSegSizes = function (segs) {
+    };
+    FillRenderer.prototype.assignSegSizes = function (segs) {
+    };
+    return FillRenderer;
+}());
+
+var NamedTimeZoneImpl = /** @class */ (function () {
+    function NamedTimeZoneImpl(timeZoneName) {
+        this.timeZoneName = timeZoneName;
+    }
+    return NamedTimeZoneImpl;
+}());
+
+/*
+An abstraction for a dragging interaction originating on an event.
+Does higher-level things than PointerDragger, such as possibly:
+- a "mirror" that moves with the pointer
+- a minimum number of pixels or other criteria for a true drag to begin
+
+subclasses must emit:
+- pointerdown
+- dragstart
+- dragmove
+- pointerup
+- dragend
+*/
+var ElementDragging = /** @class */ (function () {
+    function ElementDragging(el) {
+        this.emitter = new EmitterMixin();
+    }
+    ElementDragging.prototype.destroy = function () {
+    };
+    ElementDragging.prototype.setMirrorIsVisible = function (bool) {
+        // optional if subclass doesn't want to support a mirror
+    };
+    ElementDragging.prototype.setMirrorNeedsRevert = function (bool) {
+        // optional if subclass doesn't want to support a mirror
+    };
+    ElementDragging.prototype.setAutoScrollEnabled = function (bool) {
+        // optional
+    };
+    return ElementDragging;
+}());
+
+function formatDate(dateInput, settings) {
+    if (settings === void 0) { settings = {}; }
+    var dateEnv = buildDateEnv$1(settings);
+    var formatter = createFormatter(settings);
+    var dateMeta = dateEnv.createMarkerMeta(dateInput);
+    if (!dateMeta) { // TODO: warning?
+        return '';
+    }
+    return dateEnv.format(dateMeta.marker, formatter, {
+        forcedTzo: dateMeta.forcedTzo
+    });
+}
+function formatRange(startInput, endInput, settings // mixture of env and formatter settings
+) {
+    var dateEnv = buildDateEnv$1(typeof settings === 'object' && settings ? settings : {}); // pass in if non-null object
+    var formatter = createFormatter(settings, globalDefaults.defaultRangeSeparator);
+    var startMeta = dateEnv.createMarkerMeta(startInput);
+    var endMeta = dateEnv.createMarkerMeta(endInput);
+    if (!startMeta || !endMeta) { // TODO: warning?
+        return '';
+    }
+    return dateEnv.formatRange(startMeta.marker, endMeta.marker, formatter, {
+        forcedStartTzo: startMeta.forcedTzo,
+        forcedEndTzo: endMeta.forcedTzo,
+        isEndExclusive: settings.isEndExclusive
+    });
+}
+// TODO: more DRY and optimized
+function buildDateEnv$1(settings) {
+    var locale = buildLocale(settings.locale || 'en', parseRawLocales([]).map); // TODO: don't hardcode 'en' everywhere
+    // ensure required settings
+    settings = __assign({ timeZone: globalDefaults.timeZone, calendarSystem: 'gregory' }, settings, { locale: locale });
+    return new DateEnv(settings);
+}
+
+var DRAG_META_PROPS = {
+    startTime: createDuration,
+    duration: createDuration,
+    create: Boolean,
+    sourceId: String
+};
+var DRAG_META_DEFAULTS = {
+    create: true
+};
+function parseDragMeta(raw) {
+    var leftoverProps = {};
+    var refined = refineProps(raw, DRAG_META_PROPS, DRAG_META_DEFAULTS, leftoverProps);
+    refined.leftoverProps = leftoverProps;
+    return refined;
+}
+
+// Computes a default column header formatting string if `colFormat` is not explicitly defined
+function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
+    // if more than one week row, or if there are a lot of columns with not much space,
+    // put just the day numbers will be in each cell
+    if (!datesRepDistinctDays || dayCnt > 10) {
+        return { weekday: 'short' }; // "Sat"
+    }
+    else if (dayCnt > 1) {
+        return { weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }; // "Sat 11/12"
+    }
+    else {
+        return { weekday: 'long' }; // "Saturday"
+    }
+}
+function renderDateCell(dateMarker, dateProfile, datesRepDistinctDays, colCnt, colHeadFormat, context, colspan, otherAttrs) {
+    var view = context.view, dateEnv = context.dateEnv, theme = context.theme, options = context.options;
+    var isDateValid = rangeContainsMarker(dateProfile.activeRange, dateMarker); // TODO: called too frequently. cache somehow.
+    var classNames = [
+        'fc-day-header',
+        theme.getClass('widgetHeader')
+    ];
+    var innerHtml;
+    if (typeof options.columnHeaderHtml === 'function') {
+        innerHtml = options.columnHeaderHtml(dateEnv.toDate(dateMarker));
+    }
+    else if (typeof options.columnHeaderText === 'function') {
+        innerHtml = htmlEscape(options.columnHeaderText(dateEnv.toDate(dateMarker)));
+    }
+    else {
+        innerHtml = htmlEscape(dateEnv.format(dateMarker, colHeadFormat));
+    }
+    // if only one row of days, the classNames on the header can represent the specific days beneath
+    if (datesRepDistinctDays) {
+        classNames = classNames.concat(
+        // includes the day-of-week class
+        // noThemeHighlight=true (don't highlight the header)
+        getDayClasses(dateMarker, dateProfile, context, true));
+    }
+    else {
+        classNames.push('fc-' + DAY_IDS[dateMarker.getUTCDay()]); // only add the day-of-week class
+    }
+    return '' +
+        '<th class="' + classNames.join(' ') + '"' +
+        ((isDateValid && datesRepDistinctDays) ?
+            ' data-date="' + dateEnv.formatIso(dateMarker, { omitTime: true }) + '"' :
+            '') +
+        (colspan > 1 ?
+            ' colspan="' + colspan + '"' :
+            '') +
+        (otherAttrs ?
+            ' ' + otherAttrs :
+            '') +
+        '>' +
+        (isDateValid ?
+            // don't make a link if the heading could represent multiple days, or if there's only one day (forceOff)
+            buildGotoAnchorHtml(view, { date: dateMarker, forceOff: !datesRepDistinctDays || colCnt === 1 }, innerHtml) :
+            // if not valid, display text, but no link
+            innerHtml) +
+        '</th>';
+}
+
+var DayHeader = /** @class */ (function (_super) {
+    __extends(DayHeader, _super);
+    function DayHeader(context, parentEl) {
+        var _this = _super.call(this, context) || this;
+        parentEl.innerHTML = ''; // because might be nbsp
+        parentEl.appendChild(_this.el = htmlToElement('<div class="fc-row ' + _this.theme.getClass('headerRow') + '">' +
+            '<table class="' + _this.theme.getClass('tableGrid') + '">' +
+            '<thead></thead>' +
+            '</table>' +
+            '</div>'));
+        _this.thead = _this.el.querySelector('thead');
+        return _this;
+    }
+    DayHeader.prototype.destroy = function () {
+        removeElement(this.el);
+    };
+    DayHeader.prototype.render = function (props) {
+        var dates = props.dates, datesRepDistinctDays = props.datesRepDistinctDays;
+        var parts = [];
+        if (props.renderIntroHtml) {
+            parts.push(props.renderIntroHtml());
+        }
+        var colHeadFormat = createFormatter(this.opt('columnHeaderFormat') ||
+            computeFallbackHeaderFormat(datesRepDistinctDays, dates.length));
+        for (var _i = 0, dates_1 = dates; _i < dates_1.length; _i++) {
+            var date = dates_1[_i];
+            parts.push(renderDateCell(date, props.dateProfile, datesRepDistinctDays, dates.length, colHeadFormat, this.context));
+        }
+        if (this.isRtl) {
+            parts.reverse();
+        }
+        this.thead.innerHTML = '<tr>' + parts.join('') + '</tr>';
+    };
+    return DayHeader;
+}(Component));
+
+var DaySeries = /** @class */ (function () {
+    function DaySeries(range, dateProfileGenerator) {
+        var date = range.start;
+        var end = range.end;
+        var indices = [];
+        var dates = [];
+        var dayIndex = -1;
+        while (date < end) { // loop each day from start to end
+            if (dateProfileGenerator.isHiddenDay(date)) {
+                indices.push(dayIndex + 0.5); // mark that it's between indices
+            }
+            else {
+                dayIndex++;
+                indices.push(dayIndex);
+                dates.push(date);
+            }
+            date = addDays(date, 1);
+        }
+        this.dates = dates;
+        this.indices = indices;
+        this.cnt = dates.length;
+    }
+    DaySeries.prototype.sliceRange = function (range) {
+        var firstIndex = this.getDateDayIndex(range.start); // inclusive first index
+        var lastIndex = this.getDateDayIndex(addDays(range.end, -1)); // inclusive last index
+        var clippedFirstIndex = Math.max(0, firstIndex);
+        var clippedLastIndex = Math.min(this.cnt - 1, lastIndex);
+        // deal with in-between indices
+        clippedFirstIndex = Math.ceil(clippedFirstIndex); // in-between starts round to next cell
+        clippedLastIndex = Math.floor(clippedLastIndex); // in-between ends round to prev cell
+        if (clippedFirstIndex <= clippedLastIndex) {
+            return {
+                firstIndex: clippedFirstIndex,
+                lastIndex: clippedLastIndex,
+                isStart: firstIndex === clippedFirstIndex,
+                isEnd: lastIndex === clippedLastIndex
+            };
+        }
+        else {
+            return null;
+        }
+    };
+    // Given a date, returns its chronolocial cell-index from the first cell of the grid.
+    // If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
+    // If before the first offset, returns a negative number.
+    // If after the last offset, returns an offset past the last cell offset.
+    // Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
+    DaySeries.prototype.getDateDayIndex = function (date) {
+        var indices = this.indices;
+        var dayOffset = Math.floor(diffDays(this.dates[0], date));
+        if (dayOffset < 0) {
+            return indices[0] - 1;
+        }
+        else if (dayOffset >= indices.length) {
+            return indices[indices.length - 1] + 1;
+        }
+        else {
+            return indices[dayOffset];
+        }
+    };
+    return DaySeries;
+}());
+
+var DayTable = /** @class */ (function () {
+    function DayTable(daySeries, breakOnWeeks) {
+        var dates = daySeries.dates;
+        var daysPerRow;
+        var firstDay;
+        var rowCnt;
+        if (breakOnWeeks) {
+            // count columns until the day-of-week repeats
+            firstDay = dates[0].getUTCDay();
+            for (daysPerRow = 1; daysPerRow < dates.length; daysPerRow++) {
+                if (dates[daysPerRow].getUTCDay() === firstDay) {
+                    break;
+                }
+            }
+            rowCnt = Math.ceil(dates.length / daysPerRow);
+        }
+        else {
+            rowCnt = 1;
+            daysPerRow = dates.length;
+        }
+        this.rowCnt = rowCnt;
+        this.colCnt = daysPerRow;
+        this.daySeries = daySeries;
+        this.cells = this.buildCells();
+        this.headerDates = this.buildHeaderDates();
+    }
+    DayTable.prototype.buildCells = function () {
+        var rows = [];
+        for (var row = 0; row < this.rowCnt; row++) {
+            var cells = [];
+            for (var col = 0; col < this.colCnt; col++) {
+                cells.push(this.buildCell(row, col));
+            }
+            rows.push(cells);
+        }
+        return rows;
+    };
+    DayTable.prototype.buildCell = function (row, col) {
+        return {
+            date: this.daySeries.dates[row * this.colCnt + col]
+        };
+    };
+    DayTable.prototype.buildHeaderDates = function () {
+        var dates = [];
+        for (var col = 0; col < this.colCnt; col++) {
+            dates.push(this.cells[0][col].date);
+        }
+        return dates;
+    };
+    DayTable.prototype.sliceRange = function (range) {
+        var colCnt = this.colCnt;
+        var seriesSeg = this.daySeries.sliceRange(range);
+        var segs = [];
+        if (seriesSeg) {
+            var firstIndex = seriesSeg.firstIndex, lastIndex = seriesSeg.lastIndex;
+            var index = firstIndex;
+            while (index <= lastIndex) {
+                var row = Math.floor(index / colCnt);
+                var nextIndex = Math.min((row + 1) * colCnt, lastIndex + 1);
+                segs.push({
+                    row: row,
+                    firstCol: index % colCnt,
+                    lastCol: (nextIndex - 1) % colCnt,
+                    isStart: seriesSeg.isStart && index === firstIndex,
+                    isEnd: seriesSeg.isEnd && (nextIndex - 1) === lastIndex
+                });
+                index = nextIndex;
+            }
+        }
+        return segs;
+    };
+    return DayTable;
+}());
+
+var Slicer = /** @class */ (function () {
+    function Slicer() {
+        this.sliceBusinessHours = memoize(this._sliceBusinessHours);
+        this.sliceDateSelection = memoize(this._sliceDateSpan);
+        this.sliceEventStore = memoize(this._sliceEventStore);
+        this.sliceEventDrag = memoize(this._sliceInteraction);
+        this.sliceEventResize = memoize(this._sliceInteraction);
+    }
+    Slicer.prototype.sliceProps = function (props, dateProfile, nextDayThreshold, component) {
+        var extraArgs = [];
+        for (var _i = 4; _i < arguments.length; _i++) {
+            extraArgs[_i - 4] = arguments[_i];
+        }
+        var eventUiBases = props.eventUiBases;
+        var eventSegs = this.sliceEventStore.apply(this, [props.eventStore, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs));
+        return {
+            dateSelectionSegs: this.sliceDateSelection.apply(this, [props.dateSelection, eventUiBases, component].concat(extraArgs)),
+            businessHourSegs: this.sliceBusinessHours.apply(this, [props.businessHours, dateProfile, nextDayThreshold, component].concat(extraArgs)),
+            fgEventSegs: eventSegs.fg,
+            bgEventSegs: eventSegs.bg,
+            eventDrag: this.sliceEventDrag.apply(this, [props.eventDrag, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)),
+            eventResize: this.sliceEventResize.apply(this, [props.eventResize, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)),
+            eventSelection: props.eventSelection
+        }; // TODO: give interactionSegs?
+    };
+    Slicer.prototype.sliceNowDate = function (// does not memoize
+    date, component) {
+        var extraArgs = [];
+        for (var _i = 2; _i < arguments.length; _i++) {
+            extraArgs[_i - 2] = arguments[_i];
+        }
+        return this._sliceDateSpan.apply(this, [{ range: { start: date, end: addMs(date, 1) }, allDay: false },
+            {},
+            component].concat(extraArgs));
+    };
+    Slicer.prototype._sliceBusinessHours = function (businessHours, dateProfile, nextDayThreshold, component) {
+        var extraArgs = [];
+        for (var _i = 4; _i < arguments.length; _i++) {
+            extraArgs[_i - 4] = arguments[_i];
+        }
+        if (!businessHours) {
+            return [];
+        }
+        return this._sliceEventStore.apply(this, [expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), component.calendar),
+            {},
+            dateProfile,
+            nextDayThreshold,
+            component].concat(extraArgs)).bg;
+    };
+    Slicer.prototype._sliceEventStore = function (eventStore, eventUiBases, dateProfile, nextDayThreshold, component) {
+        var extraArgs = [];
+        for (var _i = 5; _i < arguments.length; _i++) {
+            extraArgs[_i - 5] = arguments[_i];
+        }
+        if (eventStore) {
+            var rangeRes = sliceEventStore(eventStore, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold);
+            return {
+                bg: this.sliceEventRanges(rangeRes.bg, component, extraArgs),
+                fg: this.sliceEventRanges(rangeRes.fg, component, extraArgs)
+            };
+        }
+        else {
+            return { bg: [], fg: [] };
+        }
+    };
+    Slicer.prototype._sliceInteraction = function (interaction, eventUiBases, dateProfile, nextDayThreshold, component) {
+        var extraArgs = [];
+        for (var _i = 5; _i < arguments.length; _i++) {
+            extraArgs[_i - 5] = arguments[_i];
+        }
+        if (!interaction) {
+            return null;
+        }
+        var rangeRes = sliceEventStore(interaction.mutatedEvents, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold);
+        return {
+            segs: this.sliceEventRanges(rangeRes.fg, component, extraArgs),
+            affectedInstances: interaction.affectedEvents.instances,
+            isEvent: interaction.isEvent,
+            sourceSeg: interaction.origSeg
+        };
+    };
+    Slicer.prototype._sliceDateSpan = function (dateSpan, eventUiBases, component) {
+        var extraArgs = [];
+        for (var _i = 3; _i < arguments.length; _i++) {
+            extraArgs[_i - 3] = arguments[_i];
+        }
+        if (!dateSpan) {
+            return [];
+        }
+        var eventRange = fabricateEventRange(dateSpan, eventUiBases, component.calendar);
+        var segs = this.sliceRange.apply(this, [dateSpan.range].concat(extraArgs));
+        for (var _a = 0, segs_1 = segs; _a < segs_1.length; _a++) {
+            var seg = segs_1[_a];
+            seg.component = component;
+            seg.eventRange = eventRange;
+        }
+        return segs;
+    };
+    /*
+    "complete" seg means it has component and eventRange
+    */
+    Slicer.prototype.sliceEventRanges = function (eventRanges, component, // TODO: kill
+    extraArgs) {
+        var segs = [];
+        for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
+            var eventRange = eventRanges_1[_i];
+            segs.push.apply(segs, this.sliceEventRange(eventRange, component, extraArgs));
+        }
+        return segs;
+    };
+    /*
+    "complete" seg means it has component and eventRange
+    */
+    Slicer.prototype.sliceEventRange = function (eventRange, component, // TODO: kill
+    extraArgs) {
+        var segs = this.sliceRange.apply(this, [eventRange.range].concat(extraArgs));
+        for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+            var seg = segs_2[_i];
+            seg.component = component;
+            seg.eventRange = eventRange;
+            seg.isStart = eventRange.isStart && seg.isStart;
+            seg.isEnd = eventRange.isEnd && seg.isEnd;
+        }
+        return segs;
+    };
+    return Slicer;
+}());
+/*
+for incorporating minTime/maxTime if appropriate
+TODO: should be part of DateProfile!
+TimelineDateProfile already does this btw
+*/
+function computeActiveRange(dateProfile, isComponentAllDay) {
+    var range = dateProfile.activeRange;
+    if (isComponentAllDay) {
+        return range;
+    }
+    return {
+        start: addMs(range.start, dateProfile.minTime.milliseconds),
+        end: addMs(range.end, dateProfile.maxTime.milliseconds - 864e5) // 864e5 = ms in a day
+    };
+}
+
+// exports
+// --------------------------------------------------------------------------------------------------
+var version = '4.3.1';
+
+export { Calendar, Component, DateComponent, DateEnv, DateProfileGenerator, DayHeader, DaySeries, DayTable, ElementDragging, ElementScrollController, EmitterMixin, EventApi, FgEventRenderer, FillRenderer, Interaction, Mixin, NamedTimeZoneImpl, PositionCache, ScrollComponent, ScrollController, Slicer, Splitter, Theme, View, WindowScrollController, addDays, addDurations, addMs, addWeeks, allowContextMenu, allowSelection, appendToElement, applyAll, applyMutationToEventStore, applyStyle, applyStyleProp, asRoughMinutes, asRoughMs, asRoughSeconds, buildGotoAnchorHtml, buildSegCompareObj, capitaliseFirstLetter, combineEventUis, compareByFieldSpec, compareByFieldSpecs, compareNumbers, compensateScroll, computeClippingRect, computeEdges, computeFallbackHeaderFormat, computeHeightAndMargins, computeInnerRect, computeRect, computeVisibleDayRange, config, constrainPoint, createDuration, createElement, createEmptyEventStore, createEventInstance, createFormatter, createPlugin, cssToStr, debounce, diffDates, diffDayAndTime, diffDays, diffPoints, diffWeeks, diffWholeDays, diffWholeWeeks, disableCursor, distributeHeight, elementClosest, elementMatches, enableCursor, eventTupleToStore, filterEventStoreDefs, filterHash, findChildren, findElements, flexibleCompare, forceClassName, formatDate, formatIsoTimeString, formatRange, getAllDayHtml, getClippingParents, getDayClasses, getElSeg, getRectCenter, getRelevantEvents, globalDefaults, greatestDurationDenominator, hasBgRendering, htmlEscape, htmlToElement, insertAfterElement, interactionSettingsStore, interactionSettingsToStore, intersectRanges, intersectRects, isArraysEqual, isDateSpansEqual, isInt, isInteractionValid, isMultiDayRange, isPropsEqual, isPropsValid, isSingleDay, isValidDate, listenBySelector, mapHash, matchCellWidths, memoize, memoizeOutput, memoizeRendering, mergeEventStores, multiplyDuration, padStart, parseBusinessHours, parseDragMeta, parseEventDef, parseFieldSpecs, parse as parseMarker, pointInsideRect, prependToElement, preventContextMenu, preventDefault, preventSelection, processScopedUiProps, rangeContainsMarker, rangeContainsRange, rangesEqual, rangesIntersect, refineProps, removeElement, removeExact, renderDateCell, requestJson, sliceEventStore, startOfDay, subtractInnerElHeight, translateRect, uncompensateScroll, undistributeHeight, unpromisify, version, whenTransitionDone, wholeDivideDurations };
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/main.js b/AKPlan/static/AKPlan/fullcalendar/core/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..ec0808d0dd6c77b2a18d0e5bfe595306e044e5fa
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/main.js
@@ -0,0 +1,8717 @@
+/*!
+FullCalendar Core Package v4.3.1
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+    typeof define === 'function' && define.amd ? define(['exports'], factory) :
+    (global = global || self, factory(global.FullCalendar = {}));
+}(this, function (exports) { 'use strict';
+
+    // Creating
+    // ----------------------------------------------------------------------------------------------------------------
+    var elementPropHash = {
+        className: true,
+        colSpan: true,
+        rowSpan: true
+    };
+    var containerTagHash = {
+        '<tr': 'tbody',
+        '<td': 'tr'
+    };
+    function createElement(tagName, attrs, content) {
+        var el = document.createElement(tagName);
+        if (attrs) {
+            for (var attrName in attrs) {
+                if (attrName === 'style') {
+                    applyStyle(el, attrs[attrName]);
+                }
+                else if (elementPropHash[attrName]) {
+                    el[attrName] = attrs[attrName];
+                }
+                else {
+                    el.setAttribute(attrName, attrs[attrName]);
+                }
+            }
+        }
+        if (typeof content === 'string') {
+            el.innerHTML = content; // shortcut. no need to process HTML in any way
+        }
+        else if (content != null) {
+            appendToElement(el, content);
+        }
+        return el;
+    }
+    function htmlToElement(html) {
+        html = html.trim();
+        var container = document.createElement(computeContainerTag(html));
+        container.innerHTML = html;
+        return container.firstChild;
+    }
+    function htmlToElements(html) {
+        return Array.prototype.slice.call(htmlToNodeList(html));
+    }
+    function htmlToNodeList(html) {
+        html = html.trim();
+        var container = document.createElement(computeContainerTag(html));
+        container.innerHTML = html;
+        return container.childNodes;
+    }
+    // assumes html already trimmed and tag names are lowercase
+    function computeContainerTag(html) {
+        return containerTagHash[html.substr(0, 3) // faster than using regex
+        ] || 'div';
+    }
+    function appendToElement(el, content) {
+        var childNodes = normalizeContent(content);
+        for (var i = 0; i < childNodes.length; i++) {
+            el.appendChild(childNodes[i]);
+        }
+    }
+    function prependToElement(parent, content) {
+        var newEls = normalizeContent(content);
+        var afterEl = parent.firstChild || null; // if no firstChild, will append to end, but that's okay, b/c there were no children
+        for (var i = 0; i < newEls.length; i++) {
+            parent.insertBefore(newEls[i], afterEl);
+        }
+    }
+    function insertAfterElement(refEl, content) {
+        var newEls = normalizeContent(content);
+        var afterEl = refEl.nextSibling || null;
+        for (var i = 0; i < newEls.length; i++) {
+            refEl.parentNode.insertBefore(newEls[i], afterEl);
+        }
+    }
+    function normalizeContent(content) {
+        var els;
+        if (typeof content === 'string') {
+            els = htmlToElements(content);
+        }
+        else if (content instanceof Node) {
+            els = [content];
+        }
+        else { // Node[] or NodeList
+            els = Array.prototype.slice.call(content);
+        }
+        return els;
+    }
+    function removeElement(el) {
+        if (el.parentNode) {
+            el.parentNode.removeChild(el);
+        }
+    }
+    // Querying
+    // ----------------------------------------------------------------------------------------------------------------
+    // from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
+    var matchesMethod = Element.prototype.matches ||
+        Element.prototype.matchesSelector ||
+        Element.prototype.msMatchesSelector;
+    var closestMethod = Element.prototype.closest || function (selector) {
+        // polyfill
+        var el = this;
+        if (!document.documentElement.contains(el)) {
+            return null;
+        }
+        do {
+            if (elementMatches(el, selector)) {
+                return el;
+            }
+            el = el.parentElement || el.parentNode;
+        } while (el !== null && el.nodeType === 1);
+        return null;
+    };
+    function elementClosest(el, selector) {
+        return closestMethod.call(el, selector);
+    }
+    function elementMatches(el, selector) {
+        return matchesMethod.call(el, selector);
+    }
+    // accepts multiple subject els
+    // returns a real array. good for methods like forEach
+    function findElements(container, selector) {
+        var containers = container instanceof HTMLElement ? [container] : container;
+        var allMatches = [];
+        for (var i = 0; i < containers.length; i++) {
+            var matches = containers[i].querySelectorAll(selector);
+            for (var j = 0; j < matches.length; j++) {
+                allMatches.push(matches[j]);
+            }
+        }
+        return allMatches;
+    }
+    // accepts multiple subject els
+    // only queries direct child elements
+    function findChildren(parent, selector) {
+        var parents = parent instanceof HTMLElement ? [parent] : parent;
+        var allMatches = [];
+        for (var i = 0; i < parents.length; i++) {
+            var childNodes = parents[i].children; // only ever elements
+            for (var j = 0; j < childNodes.length; j++) {
+                var childNode = childNodes[j];
+                if (!selector || elementMatches(childNode, selector)) {
+                    allMatches.push(childNode);
+                }
+            }
+        }
+        return allMatches;
+    }
+    // Attributes
+    // ----------------------------------------------------------------------------------------------------------------
+    function forceClassName(el, className, bool) {
+        if (bool) {
+            el.classList.add(className);
+        }
+        else {
+            el.classList.remove(className);
+        }
+    }
+    // Style
+    // ----------------------------------------------------------------------------------------------------------------
+    var PIXEL_PROP_RE = /(top|left|right|bottom|width|height)$/i;
+    function applyStyle(el, props) {
+        for (var propName in props) {
+            applyStyleProp(el, propName, props[propName]);
+        }
+    }
+    function applyStyleProp(el, name, val) {
+        if (val == null) {
+            el.style[name] = '';
+        }
+        else if (typeof val === 'number' && PIXEL_PROP_RE.test(name)) {
+            el.style[name] = val + 'px';
+        }
+        else {
+            el.style[name] = val;
+        }
+    }
+
+    function pointInsideRect(point, rect) {
+        return point.left >= rect.left &&
+            point.left < rect.right &&
+            point.top >= rect.top &&
+            point.top < rect.bottom;
+    }
+    // Returns a new rectangle that is the intersection of the two rectangles. If they don't intersect, returns false
+    function intersectRects(rect1, rect2) {
+        var res = {
+            left: Math.max(rect1.left, rect2.left),
+            right: Math.min(rect1.right, rect2.right),
+            top: Math.max(rect1.top, rect2.top),
+            bottom: Math.min(rect1.bottom, rect2.bottom)
+        };
+        if (res.left < res.right && res.top < res.bottom) {
+            return res;
+        }
+        return false;
+    }
+    function translateRect(rect, deltaX, deltaY) {
+        return {
+            left: rect.left + deltaX,
+            right: rect.right + deltaX,
+            top: rect.top + deltaY,
+            bottom: rect.bottom + deltaY
+        };
+    }
+    // Returns a new point that will have been moved to reside within the given rectangle
+    function constrainPoint(point, rect) {
+        return {
+            left: Math.min(Math.max(point.left, rect.left), rect.right),
+            top: Math.min(Math.max(point.top, rect.top), rect.bottom)
+        };
+    }
+    // Returns a point that is the center of the given rectangle
+    function getRectCenter(rect) {
+        return {
+            left: (rect.left + rect.right) / 2,
+            top: (rect.top + rect.bottom) / 2
+        };
+    }
+    // Subtracts point2's coordinates from point1's coordinates, returning a delta
+    function diffPoints(point1, point2) {
+        return {
+            left: point1.left - point2.left,
+            top: point1.top - point2.top
+        };
+    }
+
+    // Logic for determining if, when the element is right-to-left, the scrollbar appears on the left side
+    var isRtlScrollbarOnLeft = null;
+    function getIsRtlScrollbarOnLeft() {
+        if (isRtlScrollbarOnLeft === null) {
+            isRtlScrollbarOnLeft = computeIsRtlScrollbarOnLeft();
+        }
+        return isRtlScrollbarOnLeft;
+    }
+    function computeIsRtlScrollbarOnLeft() {
+        var outerEl = createElement('div', {
+            style: {
+                position: 'absolute',
+                top: -1000,
+                left: 0,
+                border: 0,
+                padding: 0,
+                overflow: 'scroll',
+                direction: 'rtl'
+            }
+        }, '<div></div>');
+        document.body.appendChild(outerEl);
+        var innerEl = outerEl.firstChild;
+        var res = innerEl.getBoundingClientRect().left > outerEl.getBoundingClientRect().left;
+        removeElement(outerEl);
+        return res;
+    }
+    // The scrollbar width computations in computeEdges are sometimes flawed when it comes to
+    // retina displays, rounding, and IE11. Massage them into a usable value.
+    function sanitizeScrollbarWidth(width) {
+        width = Math.max(0, width); // no negatives
+        width = Math.round(width);
+        return width;
+    }
+
+    function computeEdges(el, getPadding) {
+        if (getPadding === void 0) { getPadding = false; }
+        var computedStyle = window.getComputedStyle(el);
+        var borderLeft = parseInt(computedStyle.borderLeftWidth, 10) || 0;
+        var borderRight = parseInt(computedStyle.borderRightWidth, 10) || 0;
+        var borderTop = parseInt(computedStyle.borderTopWidth, 10) || 0;
+        var borderBottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
+        // must use offset(Width|Height) because compatible with client(Width|Height)
+        var scrollbarLeftRight = sanitizeScrollbarWidth(el.offsetWidth - el.clientWidth - borderLeft - borderRight);
+        var scrollbarBottom = sanitizeScrollbarWidth(el.offsetHeight - el.clientHeight - borderTop - borderBottom);
+        var res = {
+            borderLeft: borderLeft,
+            borderRight: borderRight,
+            borderTop: borderTop,
+            borderBottom: borderBottom,
+            scrollbarBottom: scrollbarBottom,
+            scrollbarLeft: 0,
+            scrollbarRight: 0
+        };
+        if (getIsRtlScrollbarOnLeft() && computedStyle.direction === 'rtl') { // is the scrollbar on the left side?
+            res.scrollbarLeft = scrollbarLeftRight;
+        }
+        else {
+            res.scrollbarRight = scrollbarLeftRight;
+        }
+        if (getPadding) {
+            res.paddingLeft = parseInt(computedStyle.paddingLeft, 10) || 0;
+            res.paddingRight = parseInt(computedStyle.paddingRight, 10) || 0;
+            res.paddingTop = parseInt(computedStyle.paddingTop, 10) || 0;
+            res.paddingBottom = parseInt(computedStyle.paddingBottom, 10) || 0;
+        }
+        return res;
+    }
+    function computeInnerRect(el, goWithinPadding) {
+        if (goWithinPadding === void 0) { goWithinPadding = false; }
+        var outerRect = computeRect(el);
+        var edges = computeEdges(el, goWithinPadding);
+        var res = {
+            left: outerRect.left + edges.borderLeft + edges.scrollbarLeft,
+            right: outerRect.right - edges.borderRight - edges.scrollbarRight,
+            top: outerRect.top + edges.borderTop,
+            bottom: outerRect.bottom - edges.borderBottom - edges.scrollbarBottom
+        };
+        if (goWithinPadding) {
+            res.left += edges.paddingLeft;
+            res.right -= edges.paddingRight;
+            res.top += edges.paddingTop;
+            res.bottom -= edges.paddingBottom;
+        }
+        return res;
+    }
+    function computeRect(el) {
+        var rect = el.getBoundingClientRect();
+        return {
+            left: rect.left + window.pageXOffset,
+            top: rect.top + window.pageYOffset,
+            right: rect.right + window.pageXOffset,
+            bottom: rect.bottom + window.pageYOffset
+        };
+    }
+    function computeViewportRect() {
+        return {
+            left: window.pageXOffset,
+            right: window.pageXOffset + document.documentElement.clientWidth,
+            top: window.pageYOffset,
+            bottom: window.pageYOffset + document.documentElement.clientHeight
+        };
+    }
+    function computeHeightAndMargins(el) {
+        return el.getBoundingClientRect().height + computeVMargins(el);
+    }
+    function computeVMargins(el) {
+        var computed = window.getComputedStyle(el);
+        return parseInt(computed.marginTop, 10) +
+            parseInt(computed.marginBottom, 10);
+    }
+    // does not return window
+    function getClippingParents(el) {
+        var parents = [];
+        while (el instanceof HTMLElement) { // will stop when gets to document or null
+            var computedStyle = window.getComputedStyle(el);
+            if (computedStyle.position === 'fixed') {
+                break;
+            }
+            if ((/(auto|scroll)/).test(computedStyle.overflow + computedStyle.overflowY + computedStyle.overflowX)) {
+                parents.push(el);
+            }
+            el = el.parentNode;
+        }
+        return parents;
+    }
+    function computeClippingRect(el) {
+        return getClippingParents(el)
+            .map(function (el) {
+            return computeInnerRect(el);
+        })
+            .concat(computeViewportRect())
+            .reduce(function (rect0, rect1) {
+            return intersectRects(rect0, rect1) || rect1; // should always intersect
+        });
+    }
+
+    // Stops a mouse/touch event from doing it's native browser action
+    function preventDefault(ev) {
+        ev.preventDefault();
+    }
+    // Event Delegation
+    // ----------------------------------------------------------------------------------------------------------------
+    function listenBySelector(container, eventType, selector, handler) {
+        function realHandler(ev) {
+            var matchedChild = elementClosest(ev.target, selector);
+            if (matchedChild) {
+                handler.call(matchedChild, ev, matchedChild);
+            }
+        }
+        container.addEventListener(eventType, realHandler);
+        return function () {
+            container.removeEventListener(eventType, realHandler);
+        };
+    }
+    function listenToHoverBySelector(container, selector, onMouseEnter, onMouseLeave) {
+        var currentMatchedChild;
+        return listenBySelector(container, 'mouseover', selector, function (ev, matchedChild) {
+            if (matchedChild !== currentMatchedChild) {
+                currentMatchedChild = matchedChild;
+                onMouseEnter(ev, matchedChild);
+                var realOnMouseLeave_1 = function (ev) {
+                    currentMatchedChild = null;
+                    onMouseLeave(ev, matchedChild);
+                    matchedChild.removeEventListener('mouseleave', realOnMouseLeave_1);
+                };
+                // listen to the next mouseleave, and then unattach
+                matchedChild.addEventListener('mouseleave', realOnMouseLeave_1);
+            }
+        });
+    }
+    // Animation
+    // ----------------------------------------------------------------------------------------------------------------
+    var transitionEventNames = [
+        'webkitTransitionEnd',
+        'otransitionend',
+        'oTransitionEnd',
+        'msTransitionEnd',
+        'transitionend'
+    ];
+    // triggered only when the next single subsequent transition finishes
+    function whenTransitionDone(el, callback) {
+        var realCallback = function (ev) {
+            callback(ev);
+            transitionEventNames.forEach(function (eventName) {
+                el.removeEventListener(eventName, realCallback);
+            });
+        };
+        transitionEventNames.forEach(function (eventName) {
+            el.addEventListener(eventName, realCallback); // cross-browser way to determine when the transition finishes
+        });
+    }
+
+    var DAY_IDS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
+    // Adding
+    function addWeeks(m, n) {
+        var a = dateToUtcArray(m);
+        a[2] += n * 7;
+        return arrayToUtcDate(a);
+    }
+    function addDays(m, n) {
+        var a = dateToUtcArray(m);
+        a[2] += n;
+        return arrayToUtcDate(a);
+    }
+    function addMs(m, n) {
+        var a = dateToUtcArray(m);
+        a[6] += n;
+        return arrayToUtcDate(a);
+    }
+    // Diffing (all return floats)
+    function diffWeeks(m0, m1) {
+        return diffDays(m0, m1) / 7;
+    }
+    function diffDays(m0, m1) {
+        return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60 * 24);
+    }
+    function diffHours(m0, m1) {
+        return (m1.valueOf() - m0.valueOf()) / (1000 * 60 * 60);
+    }
+    function diffMinutes(m0, m1) {
+        return (m1.valueOf() - m0.valueOf()) / (1000 * 60);
+    }
+    function diffSeconds(m0, m1) {
+        return (m1.valueOf() - m0.valueOf()) / 1000;
+    }
+    function diffDayAndTime(m0, m1) {
+        var m0day = startOfDay(m0);
+        var m1day = startOfDay(m1);
+        return {
+            years: 0,
+            months: 0,
+            days: Math.round(diffDays(m0day, m1day)),
+            milliseconds: (m1.valueOf() - m1day.valueOf()) - (m0.valueOf() - m0day.valueOf())
+        };
+    }
+    // Diffing Whole Units
+    function diffWholeWeeks(m0, m1) {
+        var d = diffWholeDays(m0, m1);
+        if (d !== null && d % 7 === 0) {
+            return d / 7;
+        }
+        return null;
+    }
+    function diffWholeDays(m0, m1) {
+        if (timeAsMs(m0) === timeAsMs(m1)) {
+            return Math.round(diffDays(m0, m1));
+        }
+        return null;
+    }
+    // Start-Of
+    function startOfDay(m) {
+        return arrayToUtcDate([
+            m.getUTCFullYear(),
+            m.getUTCMonth(),
+            m.getUTCDate()
+        ]);
+    }
+    function startOfHour(m) {
+        return arrayToUtcDate([
+            m.getUTCFullYear(),
+            m.getUTCMonth(),
+            m.getUTCDate(),
+            m.getUTCHours()
+        ]);
+    }
+    function startOfMinute(m) {
+        return arrayToUtcDate([
+            m.getUTCFullYear(),
+            m.getUTCMonth(),
+            m.getUTCDate(),
+            m.getUTCHours(),
+            m.getUTCMinutes()
+        ]);
+    }
+    function startOfSecond(m) {
+        return arrayToUtcDate([
+            m.getUTCFullYear(),
+            m.getUTCMonth(),
+            m.getUTCDate(),
+            m.getUTCHours(),
+            m.getUTCMinutes(),
+            m.getUTCSeconds()
+        ]);
+    }
+    // Week Computation
+    function weekOfYear(marker, dow, doy) {
+        var y = marker.getUTCFullYear();
+        var w = weekOfGivenYear(marker, y, dow, doy);
+        if (w < 1) {
+            return weekOfGivenYear(marker, y - 1, dow, doy);
+        }
+        var nextW = weekOfGivenYear(marker, y + 1, dow, doy);
+        if (nextW >= 1) {
+            return Math.min(w, nextW);
+        }
+        return w;
+    }
+    function weekOfGivenYear(marker, year, dow, doy) {
+        var firstWeekStart = arrayToUtcDate([year, 0, 1 + firstWeekOffset(year, dow, doy)]);
+        var dayStart = startOfDay(marker);
+        var days = Math.round(diffDays(firstWeekStart, dayStart));
+        return Math.floor(days / 7) + 1; // zero-indexed
+    }
+    // start-of-first-week - start-of-year
+    function firstWeekOffset(year, dow, doy) {
+        // first-week day -- which january is always in the first week (4 for iso, 1 for other)
+        var fwd = 7 + dow - doy;
+        // first-week day local weekday -- which local weekday is fwd
+        var fwdlw = (7 + arrayToUtcDate([year, 0, fwd]).getUTCDay() - dow) % 7;
+        return -fwdlw + fwd - 1;
+    }
+    // Array Conversion
+    function dateToLocalArray(date) {
+        return [
+            date.getFullYear(),
+            date.getMonth(),
+            date.getDate(),
+            date.getHours(),
+            date.getMinutes(),
+            date.getSeconds(),
+            date.getMilliseconds()
+        ];
+    }
+    function arrayToLocalDate(a) {
+        return new Date(a[0], a[1] || 0, a[2] == null ? 1 : a[2], // day of month
+        a[3] || 0, a[4] || 0, a[5] || 0);
+    }
+    function dateToUtcArray(date) {
+        return [
+            date.getUTCFullYear(),
+            date.getUTCMonth(),
+            date.getUTCDate(),
+            date.getUTCHours(),
+            date.getUTCMinutes(),
+            date.getUTCSeconds(),
+            date.getUTCMilliseconds()
+        ];
+    }
+    function arrayToUtcDate(a) {
+        // according to web standards (and Safari), a month index is required.
+        // massage if only given a year.
+        if (a.length === 1) {
+            a = a.concat([0]);
+        }
+        return new Date(Date.UTC.apply(Date, a));
+    }
+    // Other Utils
+    function isValidDate(m) {
+        return !isNaN(m.valueOf());
+    }
+    function timeAsMs(m) {
+        return m.getUTCHours() * 1000 * 60 * 60 +
+            m.getUTCMinutes() * 1000 * 60 +
+            m.getUTCSeconds() * 1000 +
+            m.getUTCMilliseconds();
+    }
+
+    var INTERNAL_UNITS = ['years', 'months', 'days', 'milliseconds'];
+    var PARSE_RE = /^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;
+    // Parsing and Creation
+    function createDuration(input, unit) {
+        var _a;
+        if (typeof input === 'string') {
+            return parseString(input);
+        }
+        else if (typeof input === 'object' && input) { // non-null object
+            return normalizeObject(input);
+        }
+        else if (typeof input === 'number') {
+            return normalizeObject((_a = {}, _a[unit || 'milliseconds'] = input, _a));
+        }
+        else {
+            return null;
+        }
+    }
+    function parseString(s) {
+        var m = PARSE_RE.exec(s);
+        if (m) {
+            var sign = m[1] ? -1 : 1;
+            return {
+                years: 0,
+                months: 0,
+                days: sign * (m[2] ? parseInt(m[2], 10) : 0),
+                milliseconds: sign * ((m[3] ? parseInt(m[3], 10) : 0) * 60 * 60 * 1000 + // hours
+                    (m[4] ? parseInt(m[4], 10) : 0) * 60 * 1000 + // minutes
+                    (m[5] ? parseInt(m[5], 10) : 0) * 1000 + // seconds
+                    (m[6] ? parseInt(m[6], 10) : 0) // ms
+                )
+            };
+        }
+        return null;
+    }
+    function normalizeObject(obj) {
+        return {
+            years: obj.years || obj.year || 0,
+            months: obj.months || obj.month || 0,
+            days: (obj.days || obj.day || 0) +
+                getWeeksFromInput(obj) * 7,
+            milliseconds: (obj.hours || obj.hour || 0) * 60 * 60 * 1000 + // hours
+                (obj.minutes || obj.minute || 0) * 60 * 1000 + // minutes
+                (obj.seconds || obj.second || 0) * 1000 + // seconds
+                (obj.milliseconds || obj.millisecond || obj.ms || 0) // ms
+        };
+    }
+    function getWeeksFromInput(obj) {
+        return obj.weeks || obj.week || 0;
+    }
+    // Equality
+    function durationsEqual(d0, d1) {
+        return d0.years === d1.years &&
+            d0.months === d1.months &&
+            d0.days === d1.days &&
+            d0.milliseconds === d1.milliseconds;
+    }
+    function isSingleDay(dur) {
+        return dur.years === 0 && dur.months === 0 && dur.days === 1 && dur.milliseconds === 0;
+    }
+    // Simple Math
+    function addDurations(d0, d1) {
+        return {
+            years: d0.years + d1.years,
+            months: d0.months + d1.months,
+            days: d0.days + d1.days,
+            milliseconds: d0.milliseconds + d1.milliseconds
+        };
+    }
+    function subtractDurations(d1, d0) {
+        return {
+            years: d1.years - d0.years,
+            months: d1.months - d0.months,
+            days: d1.days - d0.days,
+            milliseconds: d1.milliseconds - d0.milliseconds
+        };
+    }
+    function multiplyDuration(d, n) {
+        return {
+            years: d.years * n,
+            months: d.months * n,
+            days: d.days * n,
+            milliseconds: d.milliseconds * n
+        };
+    }
+    // Conversions
+    // "Rough" because they are based on average-case Gregorian months/years
+    function asRoughYears(dur) {
+        return asRoughDays(dur) / 365;
+    }
+    function asRoughMonths(dur) {
+        return asRoughDays(dur) / 30;
+    }
+    function asRoughDays(dur) {
+        return asRoughMs(dur) / 864e5;
+    }
+    function asRoughMinutes(dur) {
+        return asRoughMs(dur) / (1000 * 60);
+    }
+    function asRoughSeconds(dur) {
+        return asRoughMs(dur) / 1000;
+    }
+    function asRoughMs(dur) {
+        return dur.years * (365 * 864e5) +
+            dur.months * (30 * 864e5) +
+            dur.days * 864e5 +
+            dur.milliseconds;
+    }
+    // Advanced Math
+    function wholeDivideDurations(numerator, denominator) {
+        var res = null;
+        for (var i = 0; i < INTERNAL_UNITS.length; i++) {
+            var unit = INTERNAL_UNITS[i];
+            if (denominator[unit]) {
+                var localRes = numerator[unit] / denominator[unit];
+                if (!isInt(localRes) || (res !== null && res !== localRes)) {
+                    return null;
+                }
+                res = localRes;
+            }
+            else if (numerator[unit]) {
+                // needs to divide by something but can't!
+                return null;
+            }
+        }
+        return res;
+    }
+    function greatestDurationDenominator(dur, dontReturnWeeks) {
+        var ms = dur.milliseconds;
+        if (ms) {
+            if (ms % 1000 !== 0) {
+                return { unit: 'millisecond', value: ms };
+            }
+            if (ms % (1000 * 60) !== 0) {
+                return { unit: 'second', value: ms / 1000 };
+            }
+            if (ms % (1000 * 60 * 60) !== 0) {
+                return { unit: 'minute', value: ms / (1000 * 60) };
+            }
+            if (ms) {
+                return { unit: 'hour', value: ms / (1000 * 60 * 60) };
+            }
+        }
+        if (dur.days) {
+            if (!dontReturnWeeks && dur.days % 7 === 0) {
+                return { unit: 'week', value: dur.days / 7 };
+            }
+            return { unit: 'day', value: dur.days };
+        }
+        if (dur.months) {
+            return { unit: 'month', value: dur.months };
+        }
+        if (dur.years) {
+            return { unit: 'year', value: dur.years };
+        }
+        return { unit: 'millisecond', value: 0 };
+    }
+
+    /* FullCalendar-specific DOM Utilities
+    ----------------------------------------------------------------------------------------------------------------------*/
+    // Given the scrollbar widths of some other container, create borders/margins on rowEls in order to match the left
+    // and right space that was offset by the scrollbars. A 1-pixel border first, then margin beyond that.
+    function compensateScroll(rowEl, scrollbarWidths) {
+        if (scrollbarWidths.left) {
+            applyStyle(rowEl, {
+                borderLeftWidth: 1,
+                marginLeft: scrollbarWidths.left - 1
+            });
+        }
+        if (scrollbarWidths.right) {
+            applyStyle(rowEl, {
+                borderRightWidth: 1,
+                marginRight: scrollbarWidths.right - 1
+            });
+        }
+    }
+    // Undoes compensateScroll and restores all borders/margins
+    function uncompensateScroll(rowEl) {
+        applyStyle(rowEl, {
+            marginLeft: '',
+            marginRight: '',
+            borderLeftWidth: '',
+            borderRightWidth: ''
+        });
+    }
+    // Make the mouse cursor express that an event is not allowed in the current area
+    function disableCursor() {
+        document.body.classList.add('fc-not-allowed');
+    }
+    // Returns the mouse cursor to its original look
+    function enableCursor() {
+        document.body.classList.remove('fc-not-allowed');
+    }
+    // Given a total available height to fill, have `els` (essentially child rows) expand to accomodate.
+    // By default, all elements that are shorter than the recommended height are expanded uniformly, not considering
+    // any other els that are already too tall. if `shouldRedistribute` is on, it considers these tall rows and
+    // reduces the available height.
+    function distributeHeight(els, availableHeight, shouldRedistribute) {
+        // *FLOORING NOTE*: we floor in certain places because zoom can give inaccurate floating-point dimensions,
+        // and it is better to be shorter than taller, to avoid creating unnecessary scrollbars.
+        var minOffset1 = Math.floor(availableHeight / els.length); // for non-last element
+        var minOffset2 = Math.floor(availableHeight - minOffset1 * (els.length - 1)); // for last element *FLOORING NOTE*
+        var flexEls = []; // elements that are allowed to expand. array of DOM nodes
+        var flexOffsets = []; // amount of vertical space it takes up
+        var flexHeights = []; // actual css height
+        var usedHeight = 0;
+        undistributeHeight(els); // give all elements their natural height
+        // find elements that are below the recommended height (expandable).
+        // important to query for heights in a single first pass (to avoid reflow oscillation).
+        els.forEach(function (el, i) {
+            var minOffset = i === els.length - 1 ? minOffset2 : minOffset1;
+            var naturalHeight = el.getBoundingClientRect().height;
+            var naturalOffset = naturalHeight + computeVMargins(el);
+            if (naturalOffset < minOffset) {
+                flexEls.push(el);
+                flexOffsets.push(naturalOffset);
+                flexHeights.push(naturalHeight);
+            }
+            else {
+                // this element stretches past recommended height (non-expandable). mark the space as occupied.
+                usedHeight += naturalOffset;
+            }
+        });
+        // readjust the recommended height to only consider the height available to non-maxed-out rows.
+        if (shouldRedistribute) {
+            availableHeight -= usedHeight;
+            minOffset1 = Math.floor(availableHeight / flexEls.length);
+            minOffset2 = Math.floor(availableHeight - minOffset1 * (flexEls.length - 1)); // *FLOORING NOTE*
+        }
+        // assign heights to all expandable elements
+        flexEls.forEach(function (el, i) {
+            var minOffset = i === flexEls.length - 1 ? minOffset2 : minOffset1;
+            var naturalOffset = flexOffsets[i];
+            var naturalHeight = flexHeights[i];
+            var newHeight = minOffset - (naturalOffset - naturalHeight); // subtract the margin/padding
+            if (naturalOffset < minOffset) { // we check this again because redistribution might have changed things
+                el.style.height = newHeight + 'px';
+            }
+        });
+    }
+    // Undoes distrubuteHeight, restoring all els to their natural height
+    function undistributeHeight(els) {
+        els.forEach(function (el) {
+            el.style.height = '';
+        });
+    }
+    // Given `els`, a set of <td> cells, find the cell with the largest natural width and set the widths of all the
+    // cells to be that width.
+    // PREREQUISITE: if you want a cell to take up width, it needs to have a single inner element w/ display:inline
+    function matchCellWidths(els) {
+        var maxInnerWidth = 0;
+        els.forEach(function (el) {
+            var innerEl = el.firstChild; // hopefully an element
+            if (innerEl instanceof HTMLElement) {
+                var innerWidth_1 = innerEl.getBoundingClientRect().width;
+                if (innerWidth_1 > maxInnerWidth) {
+                    maxInnerWidth = innerWidth_1;
+                }
+            }
+        });
+        maxInnerWidth++; // sometimes not accurate of width the text needs to stay on one line. insurance
+        els.forEach(function (el) {
+            el.style.width = maxInnerWidth + 'px';
+        });
+        return maxInnerWidth;
+    }
+    // Given one element that resides inside another,
+    // Subtracts the height of the inner element from the outer element.
+    function subtractInnerElHeight(outerEl, innerEl) {
+        // effin' IE8/9/10/11 sometimes returns 0 for dimensions. this weird hack was the only thing that worked
+        var reflowStyleProps = {
+            position: 'relative',
+            left: -1 // ensure reflow in case the el was already relative. negative is less likely to cause new scroll
+        };
+        applyStyle(outerEl, reflowStyleProps);
+        applyStyle(innerEl, reflowStyleProps);
+        var diff = // grab the dimensions
+         outerEl.getBoundingClientRect().height -
+            innerEl.getBoundingClientRect().height;
+        // undo hack
+        var resetStyleProps = { position: '', left: '' };
+        applyStyle(outerEl, resetStyleProps);
+        applyStyle(innerEl, resetStyleProps);
+        return diff;
+    }
+    /* Selection
+    ----------------------------------------------------------------------------------------------------------------------*/
+    function preventSelection(el) {
+        el.classList.add('fc-unselectable');
+        el.addEventListener('selectstart', preventDefault);
+    }
+    function allowSelection(el) {
+        el.classList.remove('fc-unselectable');
+        el.removeEventListener('selectstart', preventDefault);
+    }
+    /* Context Menu
+    ----------------------------------------------------------------------------------------------------------------------*/
+    function preventContextMenu(el) {
+        el.addEventListener('contextmenu', preventDefault);
+    }
+    function allowContextMenu(el) {
+        el.removeEventListener('contextmenu', preventDefault);
+    }
+    /* Object Ordering by Field
+    ----------------------------------------------------------------------------------------------------------------------*/
+    function parseFieldSpecs(input) {
+        var specs = [];
+        var tokens = [];
+        var i;
+        var token;
+        if (typeof input === 'string') {
+            tokens = input.split(/\s*,\s*/);
+        }
+        else if (typeof input === 'function') {
+            tokens = [input];
+        }
+        else if (Array.isArray(input)) {
+            tokens = input;
+        }
+        for (i = 0; i < tokens.length; i++) {
+            token = tokens[i];
+            if (typeof token === 'string') {
+                specs.push(token.charAt(0) === '-' ?
+                    { field: token.substring(1), order: -1 } :
+                    { field: token, order: 1 });
+            }
+            else if (typeof token === 'function') {
+                specs.push({ func: token });
+            }
+        }
+        return specs;
+    }
+    function compareByFieldSpecs(obj0, obj1, fieldSpecs) {
+        var i;
+        var cmp;
+        for (i = 0; i < fieldSpecs.length; i++) {
+            cmp = compareByFieldSpec(obj0, obj1, fieldSpecs[i]);
+            if (cmp) {
+                return cmp;
+            }
+        }
+        return 0;
+    }
+    function compareByFieldSpec(obj0, obj1, fieldSpec) {
+        if (fieldSpec.func) {
+            return fieldSpec.func(obj0, obj1);
+        }
+        return flexibleCompare(obj0[fieldSpec.field], obj1[fieldSpec.field])
+            * (fieldSpec.order || 1);
+    }
+    function flexibleCompare(a, b) {
+        if (!a && !b) {
+            return 0;
+        }
+        if (b == null) {
+            return -1;
+        }
+        if (a == null) {
+            return 1;
+        }
+        if (typeof a === 'string' || typeof b === 'string') {
+            return String(a).localeCompare(String(b));
+        }
+        return a - b;
+    }
+    /* String Utilities
+    ----------------------------------------------------------------------------------------------------------------------*/
+    function capitaliseFirstLetter(str) {
+        return str.charAt(0).toUpperCase() + str.slice(1);
+    }
+    function padStart(val, len) {
+        var s = String(val);
+        return '000'.substr(0, len - s.length) + s;
+    }
+    /* Number Utilities
+    ----------------------------------------------------------------------------------------------------------------------*/
+    function compareNumbers(a, b) {
+        return a - b;
+    }
+    function isInt(n) {
+        return n % 1 === 0;
+    }
+    /* Weird Utilities
+    ----------------------------------------------------------------------------------------------------------------------*/
+    function applyAll(functions, thisObj, args) {
+        if (typeof functions === 'function') { // supplied a single function
+            functions = [functions];
+        }
+        if (functions) {
+            var i = void 0;
+            var ret = void 0;
+            for (i = 0; i < functions.length; i++) {
+                ret = functions[i].apply(thisObj, args) || ret;
+            }
+            return ret;
+        }
+    }
+    function firstDefined() {
+        var args = [];
+        for (var _i = 0; _i < arguments.length; _i++) {
+            args[_i] = arguments[_i];
+        }
+        for (var i = 0; i < args.length; i++) {
+            if (args[i] !== undefined) {
+                return args[i];
+            }
+        }
+    }
+    // Returns a function, that, as long as it continues to be invoked, will not
+    // be triggered. The function will be called after it stops being called for
+    // N milliseconds. If `immediate` is passed, trigger the function on the
+    // leading edge, instead of the trailing.
+    // https://github.com/jashkenas/underscore/blob/1.6.0/underscore.js#L714
+    function debounce(func, wait) {
+        var timeout;
+        var args;
+        var context;
+        var timestamp;
+        var result;
+        var later = function () {
+            var last = new Date().valueOf() - timestamp;
+            if (last < wait) {
+                timeout = setTimeout(later, wait - last);
+            }
+            else {
+                timeout = null;
+                result = func.apply(context, args);
+                context = args = null;
+            }
+        };
+        return function () {
+            context = this;
+            args = arguments;
+            timestamp = new Date().valueOf();
+            if (!timeout) {
+                timeout = setTimeout(later, wait);
+            }
+            return result;
+        };
+    }
+    // Number and Boolean are only types that defaults or not computed for
+    // TODO: write more comments
+    function refineProps(rawProps, processors, defaults, leftoverProps) {
+        if (defaults === void 0) { defaults = {}; }
+        var refined = {};
+        for (var key in processors) {
+            var processor = processors[key];
+            if (rawProps[key] !== undefined) {
+                // found
+                if (processor === Function) {
+                    refined[key] = typeof rawProps[key] === 'function' ? rawProps[key] : null;
+                }
+                else if (processor) { // a refining function?
+                    refined[key] = processor(rawProps[key]);
+                }
+                else {
+                    refined[key] = rawProps[key];
+                }
+            }
+            else if (defaults[key] !== undefined) {
+                // there's an explicit default
+                refined[key] = defaults[key];
+            }
+            else {
+                // must compute a default
+                if (processor === String) {
+                    refined[key] = ''; // empty string is default for String
+                }
+                else if (!processor || processor === Number || processor === Boolean || processor === Function) {
+                    refined[key] = null; // assign null for other non-custom processor funcs
+                }
+                else {
+                    refined[key] = processor(null); // run the custom processor func
+                }
+            }
+        }
+        if (leftoverProps) {
+            for (var key in rawProps) {
+                if (processors[key] === undefined) {
+                    leftoverProps[key] = rawProps[key];
+                }
+            }
+        }
+        return refined;
+    }
+    /* Date stuff that doesn't belong in datelib core
+    ----------------------------------------------------------------------------------------------------------------------*/
+    // given a timed range, computes an all-day range that has the same exact duration,
+    // but whose start time is aligned with the start of the day.
+    function computeAlignedDayRange(timedRange) {
+        var dayCnt = Math.floor(diffDays(timedRange.start, timedRange.end)) || 1;
+        var start = startOfDay(timedRange.start);
+        var end = addDays(start, dayCnt);
+        return { start: start, end: end };
+    }
+    // given a timed range, computes an all-day range based on how for the end date bleeds into the next day
+    // TODO: give nextDayThreshold a default arg
+    function computeVisibleDayRange(timedRange, nextDayThreshold) {
+        if (nextDayThreshold === void 0) { nextDayThreshold = createDuration(0); }
+        var startDay = null;
+        var endDay = null;
+        if (timedRange.end) {
+            endDay = startOfDay(timedRange.end);
+            var endTimeMS = timedRange.end.valueOf() - endDay.valueOf(); // # of milliseconds into `endDay`
+            // If the end time is actually inclusively part of the next day and is equal to or
+            // beyond the next day threshold, adjust the end to be the exclusive end of `endDay`.
+            // Otherwise, leaving it as inclusive will cause it to exclude `endDay`.
+            if (endTimeMS && endTimeMS >= asRoughMs(nextDayThreshold)) {
+                endDay = addDays(endDay, 1);
+            }
+        }
+        if (timedRange.start) {
+            startDay = startOfDay(timedRange.start); // the beginning of the day the range starts
+            // If end is within `startDay` but not past nextDayThreshold, assign the default duration of one day.
+            if (endDay && endDay <= startDay) {
+                endDay = addDays(startDay, 1);
+            }
+        }
+        return { start: startDay, end: endDay };
+    }
+    // spans from one day into another?
+    function isMultiDayRange(range) {
+        var visibleRange = computeVisibleDayRange(range);
+        return diffDays(visibleRange.start, visibleRange.end) > 1;
+    }
+    function diffDates(date0, date1, dateEnv, largeUnit) {
+        if (largeUnit === 'year') {
+            return createDuration(dateEnv.diffWholeYears(date0, date1), 'year');
+        }
+        else if (largeUnit === 'month') {
+            return createDuration(dateEnv.diffWholeMonths(date0, date1), 'month');
+        }
+        else {
+            return diffDayAndTime(date0, date1); // returns a duration
+        }
+    }
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    function parseRecurring(eventInput, allDayDefault, dateEnv, recurringTypes, leftovers) {
+        for (var i = 0; i < recurringTypes.length; i++) {
+            var localLeftovers = {};
+            var parsed = recurringTypes[i].parse(eventInput, localLeftovers, dateEnv);
+            if (parsed) {
+                var allDay = localLeftovers.allDay;
+                delete localLeftovers.allDay; // remove from leftovers
+                if (allDay == null) {
+                    allDay = allDayDefault;
+                    if (allDay == null) {
+                        allDay = parsed.allDayGuess;
+                        if (allDay == null) {
+                            allDay = false;
+                        }
+                    }
+                }
+                __assign(leftovers, localLeftovers);
+                return {
+                    allDay: allDay,
+                    duration: parsed.duration,
+                    typeData: parsed.typeData,
+                    typeId: i
+                };
+            }
+        }
+        return null;
+    }
+    /*
+    Event MUST have a recurringDef
+    */
+    function expandRecurringRanges(eventDef, duration, framingRange, dateEnv, recurringTypes) {
+        var typeDef = recurringTypes[eventDef.recurringDef.typeId];
+        var markers = typeDef.expand(eventDef.recurringDef.typeData, {
+            start: dateEnv.subtract(framingRange.start, duration),
+            end: framingRange.end
+        }, dateEnv);
+        // the recurrence plugins don't guarantee that all-day events are start-of-day, so we have to
+        if (eventDef.allDay) {
+            markers = markers.map(startOfDay);
+        }
+        return markers;
+    }
+
+    var hasOwnProperty = Object.prototype.hasOwnProperty;
+    // Merges an array of objects into a single object.
+    // The second argument allows for an array of property names who's object values will be merged together.
+    function mergeProps(propObjs, complexProps) {
+        var dest = {};
+        var i;
+        var name;
+        var complexObjs;
+        var j;
+        var val;
+        var props;
+        if (complexProps) {
+            for (i = 0; i < complexProps.length; i++) {
+                name = complexProps[i];
+                complexObjs = [];
+                // collect the trailing object values, stopping when a non-object is discovered
+                for (j = propObjs.length - 1; j >= 0; j--) {
+                    val = propObjs[j][name];
+                    if (typeof val === 'object' && val) { // non-null object
+                        complexObjs.unshift(val);
+                    }
+                    else if (val !== undefined) {
+                        dest[name] = val; // if there were no objects, this value will be used
+                        break;
+                    }
+                }
+                // if the trailing values were objects, use the merged value
+                if (complexObjs.length) {
+                    dest[name] = mergeProps(complexObjs);
+                }
+            }
+        }
+        // copy values into the destination, going from last to first
+        for (i = propObjs.length - 1; i >= 0; i--) {
+            props = propObjs[i];
+            for (name in props) {
+                if (!(name in dest)) { // if already assigned by previous props or complex props, don't reassign
+                    dest[name] = props[name];
+                }
+            }
+        }
+        return dest;
+    }
+    function filterHash(hash, func) {
+        var filtered = {};
+        for (var key in hash) {
+            if (func(hash[key], key)) {
+                filtered[key] = hash[key];
+            }
+        }
+        return filtered;
+    }
+    function mapHash(hash, func) {
+        var newHash = {};
+        for (var key in hash) {
+            newHash[key] = func(hash[key], key);
+        }
+        return newHash;
+    }
+    function arrayToHash(a) {
+        var hash = {};
+        for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
+            var item = a_1[_i];
+            hash[item] = true;
+        }
+        return hash;
+    }
+    function hashValuesToArray(obj) {
+        var a = [];
+        for (var key in obj) {
+            a.push(obj[key]);
+        }
+        return a;
+    }
+    function isPropsEqual(obj0, obj1) {
+        for (var key in obj0) {
+            if (hasOwnProperty.call(obj0, key)) {
+                if (!(key in obj1)) {
+                    return false;
+                }
+            }
+        }
+        for (var key in obj1) {
+            if (hasOwnProperty.call(obj1, key)) {
+                if (obj0[key] !== obj1[key]) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    function parseEvents(rawEvents, sourceId, calendar, allowOpenRange) {
+        var eventStore = createEmptyEventStore();
+        for (var _i = 0, rawEvents_1 = rawEvents; _i < rawEvents_1.length; _i++) {
+            var rawEvent = rawEvents_1[_i];
+            var tuple = parseEvent(rawEvent, sourceId, calendar, allowOpenRange);
+            if (tuple) {
+                eventTupleToStore(tuple, eventStore);
+            }
+        }
+        return eventStore;
+    }
+    function eventTupleToStore(tuple, eventStore) {
+        if (eventStore === void 0) { eventStore = createEmptyEventStore(); }
+        eventStore.defs[tuple.def.defId] = tuple.def;
+        if (tuple.instance) {
+            eventStore.instances[tuple.instance.instanceId] = tuple.instance;
+        }
+        return eventStore;
+    }
+    function expandRecurring(eventStore, framingRange, calendar) {
+        var dateEnv = calendar.dateEnv;
+        var defs = eventStore.defs, instances = eventStore.instances;
+        // remove existing recurring instances
+        instances = filterHash(instances, function (instance) {
+            return !defs[instance.defId].recurringDef;
+        });
+        for (var defId in defs) {
+            var def = defs[defId];
+            if (def.recurringDef) {
+                var duration = def.recurringDef.duration;
+                if (!duration) {
+                    duration = def.allDay ?
+                        calendar.defaultAllDayEventDuration :
+                        calendar.defaultTimedEventDuration;
+                }
+                var starts = expandRecurringRanges(def, duration, framingRange, calendar.dateEnv, calendar.pluginSystem.hooks.recurringTypes);
+                for (var _i = 0, starts_1 = starts; _i < starts_1.length; _i++) {
+                    var start = starts_1[_i];
+                    var instance = createEventInstance(defId, {
+                        start: start,
+                        end: dateEnv.add(start, duration)
+                    });
+                    instances[instance.instanceId] = instance;
+                }
+            }
+        }
+        return { defs: defs, instances: instances };
+    }
+    // retrieves events that have the same groupId as the instance specified by `instanceId`
+    // or they are the same as the instance.
+    // why might instanceId not be in the store? an event from another calendar?
+    function getRelevantEvents(eventStore, instanceId) {
+        var instance = eventStore.instances[instanceId];
+        if (instance) {
+            var def_1 = eventStore.defs[instance.defId];
+            // get events/instances with same group
+            var newStore = filterEventStoreDefs(eventStore, function (lookDef) {
+                return isEventDefsGrouped(def_1, lookDef);
+            });
+            // add the original
+            // TODO: wish we could use eventTupleToStore or something like it
+            newStore.defs[def_1.defId] = def_1;
+            newStore.instances[instance.instanceId] = instance;
+            return newStore;
+        }
+        return createEmptyEventStore();
+    }
+    function isEventDefsGrouped(def0, def1) {
+        return Boolean(def0.groupId && def0.groupId === def1.groupId);
+    }
+    function transformRawEvents(rawEvents, eventSource, calendar) {
+        var calEachTransform = calendar.opt('eventDataTransform');
+        var sourceEachTransform = eventSource ? eventSource.eventDataTransform : null;
+        if (sourceEachTransform) {
+            rawEvents = transformEachRawEvent(rawEvents, sourceEachTransform);
+        }
+        if (calEachTransform) {
+            rawEvents = transformEachRawEvent(rawEvents, calEachTransform);
+        }
+        return rawEvents;
+    }
+    function transformEachRawEvent(rawEvents, func) {
+        var refinedEvents;
+        if (!func) {
+            refinedEvents = rawEvents;
+        }
+        else {
+            refinedEvents = [];
+            for (var _i = 0, rawEvents_2 = rawEvents; _i < rawEvents_2.length; _i++) {
+                var rawEvent = rawEvents_2[_i];
+                var refinedEvent = func(rawEvent);
+                if (refinedEvent) {
+                    refinedEvents.push(refinedEvent);
+                }
+                else if (refinedEvent == null) {
+                    refinedEvents.push(rawEvent);
+                } // if a different falsy value, do nothing
+            }
+        }
+        return refinedEvents;
+    }
+    function createEmptyEventStore() {
+        return { defs: {}, instances: {} };
+    }
+    function mergeEventStores(store0, store1) {
+        return {
+            defs: __assign({}, store0.defs, store1.defs),
+            instances: __assign({}, store0.instances, store1.instances)
+        };
+    }
+    function filterEventStoreDefs(eventStore, filterFunc) {
+        var defs = filterHash(eventStore.defs, filterFunc);
+        var instances = filterHash(eventStore.instances, function (instance) {
+            return defs[instance.defId]; // still exists?
+        });
+        return { defs: defs, instances: instances };
+    }
+
+    function parseRange(input, dateEnv) {
+        var start = null;
+        var end = null;
+        if (input.start) {
+            start = dateEnv.createMarker(input.start);
+        }
+        if (input.end) {
+            end = dateEnv.createMarker(input.end);
+        }
+        if (!start && !end) {
+            return null;
+        }
+        if (start && end && end < start) {
+            return null;
+        }
+        return { start: start, end: end };
+    }
+    // SIDE-EFFECT: will mutate ranges.
+    // Will return a new array result.
+    function invertRanges(ranges, constraintRange) {
+        var invertedRanges = [];
+        var start = constraintRange.start; // the end of the previous range. the start of the new range
+        var i;
+        var dateRange;
+        // ranges need to be in order. required for our date-walking algorithm
+        ranges.sort(compareRanges);
+        for (i = 0; i < ranges.length; i++) {
+            dateRange = ranges[i];
+            // add the span of time before the event (if there is any)
+            if (dateRange.start > start) { // compare millisecond time (skip any ambig logic)
+                invertedRanges.push({ start: start, end: dateRange.start });
+            }
+            if (dateRange.end > start) {
+                start = dateRange.end;
+            }
+        }
+        // add the span of time after the last event (if there is any)
+        if (start < constraintRange.end) { // compare millisecond time (skip any ambig logic)
+            invertedRanges.push({ start: start, end: constraintRange.end });
+        }
+        return invertedRanges;
+    }
+    function compareRanges(range0, range1) {
+        return range0.start.valueOf() - range1.start.valueOf(); // earlier ranges go first
+    }
+    function intersectRanges(range0, range1) {
+        var start = range0.start;
+        var end = range0.end;
+        var newRange = null;
+        if (range1.start !== null) {
+            if (start === null) {
+                start = range1.start;
+            }
+            else {
+                start = new Date(Math.max(start.valueOf(), range1.start.valueOf()));
+            }
+        }
+        if (range1.end != null) {
+            if (end === null) {
+                end = range1.end;
+            }
+            else {
+                end = new Date(Math.min(end.valueOf(), range1.end.valueOf()));
+            }
+        }
+        if (start === null || end === null || start < end) {
+            newRange = { start: start, end: end };
+        }
+        return newRange;
+    }
+    function rangesEqual(range0, range1) {
+        return (range0.start === null ? null : range0.start.valueOf()) === (range1.start === null ? null : range1.start.valueOf()) &&
+            (range0.end === null ? null : range0.end.valueOf()) === (range1.end === null ? null : range1.end.valueOf());
+    }
+    function rangesIntersect(range0, range1) {
+        return (range0.end === null || range1.start === null || range0.end > range1.start) &&
+            (range0.start === null || range1.end === null || range0.start < range1.end);
+    }
+    function rangeContainsRange(outerRange, innerRange) {
+        return (outerRange.start === null || (innerRange.start !== null && innerRange.start >= outerRange.start)) &&
+            (outerRange.end === null || (innerRange.end !== null && innerRange.end <= outerRange.end));
+    }
+    function rangeContainsMarker(range, date) {
+        return (range.start === null || date >= range.start) &&
+            (range.end === null || date < range.end);
+    }
+    // If the given date is not within the given range, move it inside.
+    // (If it's past the end, make it one millisecond before the end).
+    function constrainMarkerToRange(date, range) {
+        if (range.start != null && date < range.start) {
+            return range.start;
+        }
+        if (range.end != null && date >= range.end) {
+            return new Date(range.end.valueOf() - 1);
+        }
+        return date;
+    }
+
+    function removeExact(array, exactVal) {
+        var removeCnt = 0;
+        var i = 0;
+        while (i < array.length) {
+            if (array[i] === exactVal) {
+                array.splice(i, 1);
+                removeCnt++;
+            }
+            else {
+                i++;
+            }
+        }
+        return removeCnt;
+    }
+    function isArraysEqual(a0, a1) {
+        var len = a0.length;
+        var i;
+        if (len !== a1.length) { // not array? or not same length?
+            return false;
+        }
+        for (i = 0; i < len; i++) {
+            if (a0[i] !== a1[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function memoize(workerFunc) {
+        var args;
+        var res;
+        return function () {
+            if (!args || !isArraysEqual(args, arguments)) {
+                args = arguments;
+                res = workerFunc.apply(this, arguments);
+            }
+            return res;
+        };
+    }
+    /*
+    always executes the workerFunc, but if the result is equal to the previous result,
+    return the previous result instead.
+    */
+    function memoizeOutput(workerFunc, equalityFunc) {
+        var cachedRes = null;
+        return function () {
+            var newRes = workerFunc.apply(this, arguments);
+            if (cachedRes === null || !(cachedRes === newRes || equalityFunc(cachedRes, newRes))) {
+                cachedRes = newRes;
+            }
+            return cachedRes;
+        };
+    }
+
+    var EXTENDED_SETTINGS_AND_SEVERITIES = {
+        week: 3,
+        separator: 0,
+        omitZeroMinute: 0,
+        meridiem: 0,
+        omitCommas: 0
+    };
+    var STANDARD_DATE_PROP_SEVERITIES = {
+        timeZoneName: 7,
+        era: 6,
+        year: 5,
+        month: 4,
+        day: 2,
+        weekday: 2,
+        hour: 1,
+        minute: 1,
+        second: 1
+    };
+    var MERIDIEM_RE = /\s*([ap])\.?m\.?/i; // eats up leading spaces too
+    var COMMA_RE = /,/g; // we need re for globalness
+    var MULTI_SPACE_RE = /\s+/g;
+    var LTR_RE = /\u200e/g; // control character
+    var UTC_RE = /UTC|GMT/;
+    var NativeFormatter = /** @class */ (function () {
+        function NativeFormatter(formatSettings) {
+            var standardDateProps = {};
+            var extendedSettings = {};
+            var severity = 0;
+            for (var name_1 in formatSettings) {
+                if (name_1 in EXTENDED_SETTINGS_AND_SEVERITIES) {
+                    extendedSettings[name_1] = formatSettings[name_1];
+                    severity = Math.max(EXTENDED_SETTINGS_AND_SEVERITIES[name_1], severity);
+                }
+                else {
+                    standardDateProps[name_1] = formatSettings[name_1];
+                    if (name_1 in STANDARD_DATE_PROP_SEVERITIES) {
+                        severity = Math.max(STANDARD_DATE_PROP_SEVERITIES[name_1], severity);
+                    }
+                }
+            }
+            this.standardDateProps = standardDateProps;
+            this.extendedSettings = extendedSettings;
+            this.severity = severity;
+            this.buildFormattingFunc = memoize(buildFormattingFunc);
+        }
+        NativeFormatter.prototype.format = function (date, context) {
+            return this.buildFormattingFunc(this.standardDateProps, this.extendedSettings, context)(date);
+        };
+        NativeFormatter.prototype.formatRange = function (start, end, context) {
+            var _a = this, standardDateProps = _a.standardDateProps, extendedSettings = _a.extendedSettings;
+            var diffSeverity = computeMarkerDiffSeverity(start.marker, end.marker, context.calendarSystem);
+            if (!diffSeverity) {
+                return this.format(start, context);
+            }
+            var biggestUnitForPartial = diffSeverity;
+            if (biggestUnitForPartial > 1 && // the two dates are different in a way that's larger scale than time
+                (standardDateProps.year === 'numeric' || standardDateProps.year === '2-digit') &&
+                (standardDateProps.month === 'numeric' || standardDateProps.month === '2-digit') &&
+                (standardDateProps.day === 'numeric' || standardDateProps.day === '2-digit')) {
+                biggestUnitForPartial = 1; // make it look like the dates are only different in terms of time
+            }
+            var full0 = this.format(start, context);
+            var full1 = this.format(end, context);
+            if (full0 === full1) {
+                return full0;
+            }
+            var partialDateProps = computePartialFormattingOptions(standardDateProps, biggestUnitForPartial);
+            var partialFormattingFunc = buildFormattingFunc(partialDateProps, extendedSettings, context);
+            var partial0 = partialFormattingFunc(start);
+            var partial1 = partialFormattingFunc(end);
+            var insertion = findCommonInsertion(full0, partial0, full1, partial1);
+            var separator = extendedSettings.separator || '';
+            if (insertion) {
+                return insertion.before + partial0 + separator + partial1 + insertion.after;
+            }
+            return full0 + separator + full1;
+        };
+        NativeFormatter.prototype.getLargestUnit = function () {
+            switch (this.severity) {
+                case 7:
+                case 6:
+                case 5:
+                    return 'year';
+                case 4:
+                    return 'month';
+                case 3:
+                    return 'week';
+                default:
+                    return 'day';
+            }
+        };
+        return NativeFormatter;
+    }());
+    function buildFormattingFunc(standardDateProps, extendedSettings, context) {
+        var standardDatePropCnt = Object.keys(standardDateProps).length;
+        if (standardDatePropCnt === 1 && standardDateProps.timeZoneName === 'short') {
+            return function (date) {
+                return formatTimeZoneOffset(date.timeZoneOffset);
+            };
+        }
+        if (standardDatePropCnt === 0 && extendedSettings.week) {
+            return function (date) {
+                return formatWeekNumber(context.computeWeekNumber(date.marker), context.weekLabel, context.locale, extendedSettings.week);
+            };
+        }
+        return buildNativeFormattingFunc(standardDateProps, extendedSettings, context);
+    }
+    function buildNativeFormattingFunc(standardDateProps, extendedSettings, context) {
+        standardDateProps = __assign({}, standardDateProps); // copy
+        extendedSettings = __assign({}, extendedSettings); // copy
+        sanitizeSettings(standardDateProps, extendedSettings);
+        standardDateProps.timeZone = 'UTC'; // we leverage the only guaranteed timeZone for our UTC markers
+        var normalFormat = new Intl.DateTimeFormat(context.locale.codes, standardDateProps);
+        var zeroFormat; // needed?
+        if (extendedSettings.omitZeroMinute) {
+            var zeroProps = __assign({}, standardDateProps);
+            delete zeroProps.minute; // seconds and ms were already considered in sanitizeSettings
+            zeroFormat = new Intl.DateTimeFormat(context.locale.codes, zeroProps);
+        }
+        return function (date) {
+            var marker = date.marker;
+            var format;
+            if (zeroFormat && !marker.getUTCMinutes()) {
+                format = zeroFormat;
+            }
+            else {
+                format = normalFormat;
+            }
+            var s = format.format(marker);
+            return postProcess(s, date, standardDateProps, extendedSettings, context);
+        };
+    }
+    function sanitizeSettings(standardDateProps, extendedSettings) {
+        // deal with a browser inconsistency where formatting the timezone
+        // requires that the hour/minute be present.
+        if (standardDateProps.timeZoneName) {
+            if (!standardDateProps.hour) {
+                standardDateProps.hour = '2-digit';
+            }
+            if (!standardDateProps.minute) {
+                standardDateProps.minute = '2-digit';
+            }
+        }
+        // only support short timezone names
+        if (standardDateProps.timeZoneName === 'long') {
+            standardDateProps.timeZoneName = 'short';
+        }
+        // if requesting to display seconds, MUST display minutes
+        if (extendedSettings.omitZeroMinute && (standardDateProps.second || standardDateProps.millisecond)) {
+            delete extendedSettings.omitZeroMinute;
+        }
+    }
+    function postProcess(s, date, standardDateProps, extendedSettings, context) {
+        s = s.replace(LTR_RE, ''); // remove left-to-right control chars. do first. good for other regexes
+        if (standardDateProps.timeZoneName === 'short') {
+            s = injectTzoStr(s, (context.timeZone === 'UTC' || date.timeZoneOffset == null) ?
+                'UTC' : // important to normalize for IE, which does "GMT"
+                formatTimeZoneOffset(date.timeZoneOffset));
+        }
+        if (extendedSettings.omitCommas) {
+            s = s.replace(COMMA_RE, '').trim();
+        }
+        if (extendedSettings.omitZeroMinute) {
+            s = s.replace(':00', ''); // zeroFormat doesn't always achieve this
+        }
+        // ^ do anything that might create adjacent spaces before this point,
+        // because MERIDIEM_RE likes to eat up loading spaces
+        if (extendedSettings.meridiem === false) {
+            s = s.replace(MERIDIEM_RE, '').trim();
+        }
+        else if (extendedSettings.meridiem === 'narrow') { // a/p
+            s = s.replace(MERIDIEM_RE, function (m0, m1) {
+                return m1.toLocaleLowerCase();
+            });
+        }
+        else if (extendedSettings.meridiem === 'short') { // am/pm
+            s = s.replace(MERIDIEM_RE, function (m0, m1) {
+                return m1.toLocaleLowerCase() + 'm';
+            });
+        }
+        else if (extendedSettings.meridiem === 'lowercase') { // other meridiem transformers already converted to lowercase
+            s = s.replace(MERIDIEM_RE, function (m0) {
+                return m0.toLocaleLowerCase();
+            });
+        }
+        s = s.replace(MULTI_SPACE_RE, ' ');
+        s = s.trim();
+        return s;
+    }
+    function injectTzoStr(s, tzoStr) {
+        var replaced = false;
+        s = s.replace(UTC_RE, function () {
+            replaced = true;
+            return tzoStr;
+        });
+        // IE11 doesn't include UTC/GMT in the original string, so append to end
+        if (!replaced) {
+            s += ' ' + tzoStr;
+        }
+        return s;
+    }
+    function formatWeekNumber(num, weekLabel, locale, display) {
+        var parts = [];
+        if (display === 'narrow') {
+            parts.push(weekLabel);
+        }
+        else if (display === 'short') {
+            parts.push(weekLabel, ' ');
+        }
+        // otherwise, considered 'numeric'
+        parts.push(locale.simpleNumberFormat.format(num));
+        if (locale.options.isRtl) { // TODO: use control characters instead?
+            parts.reverse();
+        }
+        return parts.join('');
+    }
+    // Range Formatting Utils
+    // 0 = exactly the same
+    // 1 = different by time
+    // and bigger
+    function computeMarkerDiffSeverity(d0, d1, ca) {
+        if (ca.getMarkerYear(d0) !== ca.getMarkerYear(d1)) {
+            return 5;
+        }
+        if (ca.getMarkerMonth(d0) !== ca.getMarkerMonth(d1)) {
+            return 4;
+        }
+        if (ca.getMarkerDay(d0) !== ca.getMarkerDay(d1)) {
+            return 2;
+        }
+        if (timeAsMs(d0) !== timeAsMs(d1)) {
+            return 1;
+        }
+        return 0;
+    }
+    function computePartialFormattingOptions(options, biggestUnit) {
+        var partialOptions = {};
+        for (var name_2 in options) {
+            if (!(name_2 in STANDARD_DATE_PROP_SEVERITIES) || // not a date part prop (like timeZone)
+                STANDARD_DATE_PROP_SEVERITIES[name_2] <= biggestUnit) {
+                partialOptions[name_2] = options[name_2];
+            }
+        }
+        return partialOptions;
+    }
+    function findCommonInsertion(full0, partial0, full1, partial1) {
+        var i0 = 0;
+        while (i0 < full0.length) {
+            var found0 = full0.indexOf(partial0, i0);
+            if (found0 === -1) {
+                break;
+            }
+            var before0 = full0.substr(0, found0);
+            i0 = found0 + partial0.length;
+            var after0 = full0.substr(i0);
+            var i1 = 0;
+            while (i1 < full1.length) {
+                var found1 = full1.indexOf(partial1, i1);
+                if (found1 === -1) {
+                    break;
+                }
+                var before1 = full1.substr(0, found1);
+                i1 = found1 + partial1.length;
+                var after1 = full1.substr(i1);
+                if (before0 === before1 && after0 === after1) {
+                    return {
+                        before: before0,
+                        after: after0
+                    };
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+    TODO: fix the terminology of "formatter" vs "formatting func"
+    */
+    /*
+    At the time of instantiation, this object does not know which cmd-formatting system it will use.
+    It receives this at the time of formatting, as a setting.
+    */
+    var CmdFormatter = /** @class */ (function () {
+        function CmdFormatter(cmdStr, separator) {
+            this.cmdStr = cmdStr;
+            this.separator = separator;
+        }
+        CmdFormatter.prototype.format = function (date, context) {
+            return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(date, null, context, this.separator));
+        };
+        CmdFormatter.prototype.formatRange = function (start, end, context) {
+            return context.cmdFormatter(this.cmdStr, createVerboseFormattingArg(start, end, context, this.separator));
+        };
+        return CmdFormatter;
+    }());
+
+    var FuncFormatter = /** @class */ (function () {
+        function FuncFormatter(func) {
+            this.func = func;
+        }
+        FuncFormatter.prototype.format = function (date, context) {
+            return this.func(createVerboseFormattingArg(date, null, context));
+        };
+        FuncFormatter.prototype.formatRange = function (start, end, context) {
+            return this.func(createVerboseFormattingArg(start, end, context));
+        };
+        return FuncFormatter;
+    }());
+
+    // Formatter Object Creation
+    function createFormatter(input, defaultSeparator) {
+        if (typeof input === 'object' && input) { // non-null object
+            if (typeof defaultSeparator === 'string') {
+                input = __assign({ separator: defaultSeparator }, input);
+            }
+            return new NativeFormatter(input);
+        }
+        else if (typeof input === 'string') {
+            return new CmdFormatter(input, defaultSeparator);
+        }
+        else if (typeof input === 'function') {
+            return new FuncFormatter(input);
+        }
+    }
+    // String Utils
+    // timeZoneOffset is in minutes
+    function buildIsoString(marker, timeZoneOffset, stripZeroTime) {
+        if (stripZeroTime === void 0) { stripZeroTime = false; }
+        var s = marker.toISOString();
+        s = s.replace('.000', '');
+        if (stripZeroTime) {
+            s = s.replace('T00:00:00Z', '');
+        }
+        if (s.length > 10) { // time part wasn't stripped, can add timezone info
+            if (timeZoneOffset == null) {
+                s = s.replace('Z', '');
+            }
+            else if (timeZoneOffset !== 0) {
+                s = s.replace('Z', formatTimeZoneOffset(timeZoneOffset, true));
+            }
+            // otherwise, its UTC-0 and we want to keep the Z
+        }
+        return s;
+    }
+    function formatIsoTimeString(marker) {
+        return padStart(marker.getUTCHours(), 2) + ':' +
+            padStart(marker.getUTCMinutes(), 2) + ':' +
+            padStart(marker.getUTCSeconds(), 2);
+    }
+    function formatTimeZoneOffset(minutes, doIso) {
+        if (doIso === void 0) { doIso = false; }
+        var sign = minutes < 0 ? '-' : '+';
+        var abs = Math.abs(minutes);
+        var hours = Math.floor(abs / 60);
+        var mins = Math.round(abs % 60);
+        if (doIso) {
+            return sign + padStart(hours, 2) + ':' + padStart(mins, 2);
+        }
+        else {
+            return 'GMT' + sign + hours + (mins ? ':' + padStart(mins, 2) : '');
+        }
+    }
+    // Arg Utils
+    function createVerboseFormattingArg(start, end, context, separator) {
+        var startInfo = expandZonedMarker(start, context.calendarSystem);
+        var endInfo = end ? expandZonedMarker(end, context.calendarSystem) : null;
+        return {
+            date: startInfo,
+            start: startInfo,
+            end: endInfo,
+            timeZone: context.timeZone,
+            localeCodes: context.locale.codes,
+            separator: separator
+        };
+    }
+    function expandZonedMarker(dateInfo, calendarSystem) {
+        var a = calendarSystem.markerToArray(dateInfo.marker);
+        return {
+            marker: dateInfo.marker,
+            timeZoneOffset: dateInfo.timeZoneOffset,
+            array: a,
+            year: a[0],
+            month: a[1],
+            day: a[2],
+            hour: a[3],
+            minute: a[4],
+            second: a[5],
+            millisecond: a[6]
+        };
+    }
+
+    var EventSourceApi = /** @class */ (function () {
+        function EventSourceApi(calendar, internalEventSource) {
+            this.calendar = calendar;
+            this.internalEventSource = internalEventSource;
+        }
+        EventSourceApi.prototype.remove = function () {
+            this.calendar.dispatch({
+                type: 'REMOVE_EVENT_SOURCE',
+                sourceId: this.internalEventSource.sourceId
+            });
+        };
+        EventSourceApi.prototype.refetch = function () {
+            this.calendar.dispatch({
+                type: 'FETCH_EVENT_SOURCES',
+                sourceIds: [this.internalEventSource.sourceId]
+            });
+        };
+        Object.defineProperty(EventSourceApi.prototype, "id", {
+            get: function () {
+                return this.internalEventSource.publicId;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventSourceApi.prototype, "url", {
+            // only relevant to json-feed event sources
+            get: function () {
+                return this.internalEventSource.meta.url;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        return EventSourceApi;
+    }());
+
+    var EventApi = /** @class */ (function () {
+        function EventApi(calendar, def, instance) {
+            this._calendar = calendar;
+            this._def = def;
+            this._instance = instance || null;
+        }
+        /*
+        TODO: make event struct more responsible for this
+        */
+        EventApi.prototype.setProp = function (name, val) {
+            var _a, _b;
+            if (name in DATE_PROPS) ;
+            else if (name in NON_DATE_PROPS) {
+                if (typeof NON_DATE_PROPS[name] === 'function') {
+                    val = NON_DATE_PROPS[name](val);
+                }
+                this.mutate({
+                    standardProps: (_a = {}, _a[name] = val, _a)
+                });
+            }
+            else if (name in UNSCOPED_EVENT_UI_PROPS) {
+                var ui = void 0;
+                if (typeof UNSCOPED_EVENT_UI_PROPS[name] === 'function') {
+                    val = UNSCOPED_EVENT_UI_PROPS[name](val);
+                }
+                if (name === 'color') {
+                    ui = { backgroundColor: val, borderColor: val };
+                }
+                else if (name === 'editable') {
+                    ui = { startEditable: val, durationEditable: val };
+                }
+                else {
+                    ui = (_b = {}, _b[name] = val, _b);
+                }
+                this.mutate({
+                    standardProps: { ui: ui }
+                });
+            }
+        };
+        EventApi.prototype.setExtendedProp = function (name, val) {
+            var _a;
+            this.mutate({
+                extendedProps: (_a = {}, _a[name] = val, _a)
+            });
+        };
+        EventApi.prototype.setStart = function (startInput, options) {
+            if (options === void 0) { options = {}; }
+            var dateEnv = this._calendar.dateEnv;
+            var start = dateEnv.createMarker(startInput);
+            if (start && this._instance) { // TODO: warning if parsed bad
+                var instanceRange = this._instance.range;
+                var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity); // what if parsed bad!?
+                if (options.maintainDuration) {
+                    this.mutate({ datesDelta: startDelta });
+                }
+                else {
+                    this.mutate({ startDelta: startDelta });
+                }
+            }
+        };
+        EventApi.prototype.setEnd = function (endInput, options) {
+            if (options === void 0) { options = {}; }
+            var dateEnv = this._calendar.dateEnv;
+            var end;
+            if (endInput != null) {
+                end = dateEnv.createMarker(endInput);
+                if (!end) {
+                    return; // TODO: warning if parsed bad
+                }
+            }
+            if (this._instance) {
+                if (end) {
+                    var endDelta = diffDates(this._instance.range.end, end, dateEnv, options.granularity);
+                    this.mutate({ endDelta: endDelta });
+                }
+                else {
+                    this.mutate({ standardProps: { hasEnd: false } });
+                }
+            }
+        };
+        EventApi.prototype.setDates = function (startInput, endInput, options) {
+            if (options === void 0) { options = {}; }
+            var dateEnv = this._calendar.dateEnv;
+            var standardProps = { allDay: options.allDay };
+            var start = dateEnv.createMarker(startInput);
+            var end;
+            if (!start) {
+                return; // TODO: warning if parsed bad
+            }
+            if (endInput != null) {
+                end = dateEnv.createMarker(endInput);
+                if (!end) { // TODO: warning if parsed bad
+                    return;
+                }
+            }
+            if (this._instance) {
+                var instanceRange = this._instance.range;
+                // when computing the diff for an event being converted to all-day,
+                // compute diff off of the all-day values the way event-mutation does.
+                if (options.allDay === true) {
+                    instanceRange = computeAlignedDayRange(instanceRange);
+                }
+                var startDelta = diffDates(instanceRange.start, start, dateEnv, options.granularity);
+                if (end) {
+                    var endDelta = diffDates(instanceRange.end, end, dateEnv, options.granularity);
+                    if (durationsEqual(startDelta, endDelta)) {
+                        this.mutate({ datesDelta: startDelta, standardProps: standardProps });
+                    }
+                    else {
+                        this.mutate({ startDelta: startDelta, endDelta: endDelta, standardProps: standardProps });
+                    }
+                }
+                else { // means "clear the end"
+                    standardProps.hasEnd = false;
+                    this.mutate({ datesDelta: startDelta, standardProps: standardProps });
+                }
+            }
+        };
+        EventApi.prototype.moveStart = function (deltaInput) {
+            var delta = createDuration(deltaInput);
+            if (delta) { // TODO: warning if parsed bad
+                this.mutate({ startDelta: delta });
+            }
+        };
+        EventApi.prototype.moveEnd = function (deltaInput) {
+            var delta = createDuration(deltaInput);
+            if (delta) { // TODO: warning if parsed bad
+                this.mutate({ endDelta: delta });
+            }
+        };
+        EventApi.prototype.moveDates = function (deltaInput) {
+            var delta = createDuration(deltaInput);
+            if (delta) { // TODO: warning if parsed bad
+                this.mutate({ datesDelta: delta });
+            }
+        };
+        EventApi.prototype.setAllDay = function (allDay, options) {
+            if (options === void 0) { options = {}; }
+            var standardProps = { allDay: allDay };
+            var maintainDuration = options.maintainDuration;
+            if (maintainDuration == null) {
+                maintainDuration = this._calendar.opt('allDayMaintainDuration');
+            }
+            if (this._def.allDay !== allDay) {
+                standardProps.hasEnd = maintainDuration;
+            }
+            this.mutate({ standardProps: standardProps });
+        };
+        EventApi.prototype.formatRange = function (formatInput) {
+            var dateEnv = this._calendar.dateEnv;
+            var instance = this._instance;
+            var formatter = createFormatter(formatInput, this._calendar.opt('defaultRangeSeparator'));
+            if (this._def.hasEnd) {
+                return dateEnv.formatRange(instance.range.start, instance.range.end, formatter, {
+                    forcedStartTzo: instance.forcedStartTzo,
+                    forcedEndTzo: instance.forcedEndTzo
+                });
+            }
+            else {
+                return dateEnv.format(instance.range.start, formatter, {
+                    forcedTzo: instance.forcedStartTzo
+                });
+            }
+        };
+        EventApi.prototype.mutate = function (mutation) {
+            var def = this._def;
+            var instance = this._instance;
+            if (instance) {
+                this._calendar.dispatch({
+                    type: 'MUTATE_EVENTS',
+                    instanceId: instance.instanceId,
+                    mutation: mutation,
+                    fromApi: true
+                });
+                var eventStore = this._calendar.state.eventStore;
+                this._def = eventStore.defs[def.defId];
+                this._instance = eventStore.instances[instance.instanceId];
+            }
+        };
+        EventApi.prototype.remove = function () {
+            this._calendar.dispatch({
+                type: 'REMOVE_EVENT_DEF',
+                defId: this._def.defId
+            });
+        };
+        Object.defineProperty(EventApi.prototype, "source", {
+            get: function () {
+                var sourceId = this._def.sourceId;
+                if (sourceId) {
+                    return new EventSourceApi(this._calendar, this._calendar.state.eventSources[sourceId]);
+                }
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "start", {
+            get: function () {
+                return this._instance ?
+                    this._calendar.dateEnv.toDate(this._instance.range.start) :
+                    null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "end", {
+            get: function () {
+                return (this._instance && this._def.hasEnd) ?
+                    this._calendar.dateEnv.toDate(this._instance.range.end) :
+                    null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "id", {
+            // computable props that all access the def
+            // TODO: find a TypeScript-compatible way to do this at scale
+            get: function () { return this._def.publicId; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "groupId", {
+            get: function () { return this._def.groupId; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "allDay", {
+            get: function () { return this._def.allDay; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "title", {
+            get: function () { return this._def.title; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "url", {
+            get: function () { return this._def.url; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "rendering", {
+            get: function () { return this._def.rendering; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "startEditable", {
+            get: function () { return this._def.ui.startEditable; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "durationEditable", {
+            get: function () { return this._def.ui.durationEditable; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "constraint", {
+            get: function () { return this._def.ui.constraints[0] || null; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "overlap", {
+            get: function () { return this._def.ui.overlap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "allow", {
+            get: function () { return this._def.ui.allows[0] || null; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "backgroundColor", {
+            get: function () { return this._def.ui.backgroundColor; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "borderColor", {
+            get: function () { return this._def.ui.borderColor; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "textColor", {
+            get: function () { return this._def.ui.textColor; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "classNames", {
+            // NOTE: user can't modify these because Object.freeze was called in event-def parsing
+            get: function () { return this._def.ui.classNames; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(EventApi.prototype, "extendedProps", {
+            get: function () { return this._def.extendedProps; },
+            enumerable: true,
+            configurable: true
+        });
+        return EventApi;
+    }());
+
+    /*
+    Specifying nextDayThreshold signals that all-day ranges should be sliced.
+    */
+    function sliceEventStore(eventStore, eventUiBases, framingRange, nextDayThreshold) {
+        var inverseBgByGroupId = {};
+        var inverseBgByDefId = {};
+        var defByGroupId = {};
+        var bgRanges = [];
+        var fgRanges = [];
+        var eventUis = compileEventUis(eventStore.defs, eventUiBases);
+        for (var defId in eventStore.defs) {
+            var def = eventStore.defs[defId];
+            if (def.rendering === 'inverse-background') {
+                if (def.groupId) {
+                    inverseBgByGroupId[def.groupId] = [];
+                    if (!defByGroupId[def.groupId]) {
+                        defByGroupId[def.groupId] = def;
+                    }
+                }
+                else {
+                    inverseBgByDefId[defId] = [];
+                }
+            }
+        }
+        for (var instanceId in eventStore.instances) {
+            var instance = eventStore.instances[instanceId];
+            var def = eventStore.defs[instance.defId];
+            var ui = eventUis[def.defId];
+            var origRange = instance.range;
+            var normalRange = (!def.allDay && nextDayThreshold) ?
+                computeVisibleDayRange(origRange, nextDayThreshold) :
+                origRange;
+            var slicedRange = intersectRanges(normalRange, framingRange);
+            if (slicedRange) {
+                if (def.rendering === 'inverse-background') {
+                    if (def.groupId) {
+                        inverseBgByGroupId[def.groupId].push(slicedRange);
+                    }
+                    else {
+                        inverseBgByDefId[instance.defId].push(slicedRange);
+                    }
+                }
+                else {
+                    (def.rendering === 'background' ? bgRanges : fgRanges).push({
+                        def: def,
+                        ui: ui,
+                        instance: instance,
+                        range: slicedRange,
+                        isStart: normalRange.start && normalRange.start.valueOf() === slicedRange.start.valueOf(),
+                        isEnd: normalRange.end && normalRange.end.valueOf() === slicedRange.end.valueOf()
+                    });
+                }
+            }
+        }
+        for (var groupId in inverseBgByGroupId) { // BY GROUP
+            var ranges = inverseBgByGroupId[groupId];
+            var invertedRanges = invertRanges(ranges, framingRange);
+            for (var _i = 0, invertedRanges_1 = invertedRanges; _i < invertedRanges_1.length; _i++) {
+                var invertedRange = invertedRanges_1[_i];
+                var def = defByGroupId[groupId];
+                var ui = eventUis[def.defId];
+                bgRanges.push({
+                    def: def,
+                    ui: ui,
+                    instance: null,
+                    range: invertedRange,
+                    isStart: false,
+                    isEnd: false
+                });
+            }
+        }
+        for (var defId in inverseBgByDefId) {
+            var ranges = inverseBgByDefId[defId];
+            var invertedRanges = invertRanges(ranges, framingRange);
+            for (var _a = 0, invertedRanges_2 = invertedRanges; _a < invertedRanges_2.length; _a++) {
+                var invertedRange = invertedRanges_2[_a];
+                bgRanges.push({
+                    def: eventStore.defs[defId],
+                    ui: eventUis[defId],
+                    instance: null,
+                    range: invertedRange,
+                    isStart: false,
+                    isEnd: false
+                });
+            }
+        }
+        return { bg: bgRanges, fg: fgRanges };
+    }
+    function hasBgRendering(def) {
+        return def.rendering === 'background' || def.rendering === 'inverse-background';
+    }
+    function filterSegsViaEls(view, segs, isMirror) {
+        if (view.hasPublicHandlers('eventRender')) {
+            segs = segs.filter(function (seg) {
+                var custom = view.publiclyTrigger('eventRender', [
+                    {
+                        event: new EventApi(view.calendar, seg.eventRange.def, seg.eventRange.instance),
+                        isMirror: isMirror,
+                        isStart: seg.isStart,
+                        isEnd: seg.isEnd,
+                        // TODO: include seg.range once all components consistently generate it
+                        el: seg.el,
+                        view: view
+                    }
+                ]);
+                if (custom === false) { // means don't render at all
+                    return false;
+                }
+                else if (custom && custom !== true) {
+                    seg.el = custom;
+                }
+                return true;
+            });
+        }
+        for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+            var seg = segs_1[_i];
+            setElSeg(seg.el, seg);
+        }
+        return segs;
+    }
+    function setElSeg(el, seg) {
+        el.fcSeg = seg;
+    }
+    function getElSeg(el) {
+        return el.fcSeg || null;
+    }
+    // event ui computation
+    function compileEventUis(eventDefs, eventUiBases) {
+        return mapHash(eventDefs, function (eventDef) {
+            return compileEventUi(eventDef, eventUiBases);
+        });
+    }
+    function compileEventUi(eventDef, eventUiBases) {
+        var uis = [];
+        if (eventUiBases['']) {
+            uis.push(eventUiBases['']);
+        }
+        if (eventUiBases[eventDef.defId]) {
+            uis.push(eventUiBases[eventDef.defId]);
+        }
+        uis.push(eventDef.ui);
+        return combineEventUis(uis);
+    }
+
+    // applies the mutation to ALL defs/instances within the event store
+    function applyMutationToEventStore(eventStore, eventConfigBase, mutation, calendar) {
+        var eventConfigs = compileEventUis(eventStore.defs, eventConfigBase);
+        var dest = createEmptyEventStore();
+        for (var defId in eventStore.defs) {
+            var def = eventStore.defs[defId];
+            dest.defs[defId] = applyMutationToEventDef(def, eventConfigs[defId], mutation, calendar.pluginSystem.hooks.eventDefMutationAppliers, calendar);
+        }
+        for (var instanceId in eventStore.instances) {
+            var instance = eventStore.instances[instanceId];
+            var def = dest.defs[instance.defId]; // important to grab the newly modified def
+            dest.instances[instanceId] = applyMutationToEventInstance(instance, def, eventConfigs[instance.defId], mutation, calendar);
+        }
+        return dest;
+    }
+    function applyMutationToEventDef(eventDef, eventConfig, mutation, appliers, calendar) {
+        var standardProps = mutation.standardProps || {};
+        // if hasEnd has not been specified, guess a good value based on deltas.
+        // if duration will change, there's no way the default duration will persist,
+        // and thus, we need to mark the event as having a real end
+        if (standardProps.hasEnd == null &&
+            eventConfig.durationEditable &&
+            (mutation.startDelta || mutation.endDelta)) {
+            standardProps.hasEnd = true; // TODO: is this mutation okay?
+        }
+        var copy = __assign({}, eventDef, standardProps, { ui: __assign({}, eventDef.ui, standardProps.ui) });
+        if (mutation.extendedProps) {
+            copy.extendedProps = __assign({}, copy.extendedProps, mutation.extendedProps);
+        }
+        for (var _i = 0, appliers_1 = appliers; _i < appliers_1.length; _i++) {
+            var applier = appliers_1[_i];
+            applier(copy, mutation, calendar);
+        }
+        if (!copy.hasEnd && calendar.opt('forceEventDuration')) {
+            copy.hasEnd = true;
+        }
+        return copy;
+    }
+    function applyMutationToEventInstance(eventInstance, eventDef, // must first be modified by applyMutationToEventDef
+    eventConfig, mutation, calendar) {
+        var dateEnv = calendar.dateEnv;
+        var forceAllDay = mutation.standardProps && mutation.standardProps.allDay === true;
+        var clearEnd = mutation.standardProps && mutation.standardProps.hasEnd === false;
+        var copy = __assign({}, eventInstance);
+        if (forceAllDay) {
+            copy.range = computeAlignedDayRange(copy.range);
+        }
+        if (mutation.datesDelta && eventConfig.startEditable) {
+            copy.range = {
+                start: dateEnv.add(copy.range.start, mutation.datesDelta),
+                end: dateEnv.add(copy.range.end, mutation.datesDelta)
+            };
+        }
+        if (mutation.startDelta && eventConfig.durationEditable) {
+            copy.range = {
+                start: dateEnv.add(copy.range.start, mutation.startDelta),
+                end: copy.range.end
+            };
+        }
+        if (mutation.endDelta && eventConfig.durationEditable) {
+            copy.range = {
+                start: copy.range.start,
+                end: dateEnv.add(copy.range.end, mutation.endDelta)
+            };
+        }
+        if (clearEnd) {
+            copy.range = {
+                start: copy.range.start,
+                end: calendar.getDefaultEventEnd(eventDef.allDay, copy.range.start)
+            };
+        }
+        // in case event was all-day but the supplied deltas were not
+        // better util for this?
+        if (eventDef.allDay) {
+            copy.range = {
+                start: startOfDay(copy.range.start),
+                end: startOfDay(copy.range.end)
+            };
+        }
+        // handle invalid durations
+        if (copy.range.end < copy.range.start) {
+            copy.range.end = calendar.getDefaultEventEnd(eventDef.allDay, copy.range.start);
+        }
+        return copy;
+    }
+
+    function reduceEventStore (eventStore, action, eventSources, dateProfile, calendar) {
+        switch (action.type) {
+            case 'RECEIVE_EVENTS': // raw
+                return receiveRawEvents(eventStore, eventSources[action.sourceId], action.fetchId, action.fetchRange, action.rawEvents, calendar);
+            case 'ADD_EVENTS': // already parsed, but not expanded
+                return addEvent(eventStore, action.eventStore, // new ones
+                dateProfile ? dateProfile.activeRange : null, calendar);
+            case 'MERGE_EVENTS': // already parsed and expanded
+                return mergeEventStores(eventStore, action.eventStore);
+            case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
+            case 'NEXT':
+            case 'SET_DATE':
+            case 'SET_VIEW_TYPE':
+                if (dateProfile) {
+                    return expandRecurring(eventStore, dateProfile.activeRange, calendar);
+                }
+                else {
+                    return eventStore;
+                }
+            case 'CHANGE_TIMEZONE':
+                return rezoneDates(eventStore, action.oldDateEnv, calendar.dateEnv);
+            case 'MUTATE_EVENTS':
+                return applyMutationToRelated(eventStore, action.instanceId, action.mutation, action.fromApi, calendar);
+            case 'REMOVE_EVENT_INSTANCES':
+                return excludeInstances(eventStore, action.instances);
+            case 'REMOVE_EVENT_DEF':
+                return filterEventStoreDefs(eventStore, function (eventDef) {
+                    return eventDef.defId !== action.defId;
+                });
+            case 'REMOVE_EVENT_SOURCE':
+                return excludeEventsBySourceId(eventStore, action.sourceId);
+            case 'REMOVE_ALL_EVENT_SOURCES':
+                return filterEventStoreDefs(eventStore, function (eventDef) {
+                    return !eventDef.sourceId; // only keep events with no source id
+                });
+            case 'REMOVE_ALL_EVENTS':
+                return createEmptyEventStore();
+            case 'RESET_EVENTS':
+                return {
+                    defs: eventStore.defs,
+                    instances: eventStore.instances
+                };
+            default:
+                return eventStore;
+        }
+    }
+    function receiveRawEvents(eventStore, eventSource, fetchId, fetchRange, rawEvents, calendar) {
+        if (eventSource && // not already removed
+            fetchId === eventSource.latestFetchId // TODO: wish this logic was always in event-sources
+        ) {
+            var subset = parseEvents(transformRawEvents(rawEvents, eventSource, calendar), eventSource.sourceId, calendar);
+            if (fetchRange) {
+                subset = expandRecurring(subset, fetchRange, calendar);
+            }
+            return mergeEventStores(excludeEventsBySourceId(eventStore, eventSource.sourceId), subset);
+        }
+        return eventStore;
+    }
+    function addEvent(eventStore, subset, expandRange, calendar) {
+        if (expandRange) {
+            subset = expandRecurring(subset, expandRange, calendar);
+        }
+        return mergeEventStores(eventStore, subset);
+    }
+    function rezoneDates(eventStore, oldDateEnv, newDateEnv) {
+        var defs = eventStore.defs;
+        var instances = mapHash(eventStore.instances, function (instance) {
+            var def = defs[instance.defId];
+            if (def.allDay || def.recurringDef) {
+                return instance; // isn't dependent on timezone
+            }
+            else {
+                return __assign({}, instance, { range: {
+                        start: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.start, instance.forcedStartTzo)),
+                        end: newDateEnv.createMarker(oldDateEnv.toDate(instance.range.end, instance.forcedEndTzo))
+                    }, forcedStartTzo: newDateEnv.canComputeOffset ? null : instance.forcedStartTzo, forcedEndTzo: newDateEnv.canComputeOffset ? null : instance.forcedEndTzo });
+            }
+        });
+        return { defs: defs, instances: instances };
+    }
+    function applyMutationToRelated(eventStore, instanceId, mutation, fromApi, calendar) {
+        var relevant = getRelevantEvents(eventStore, instanceId);
+        var eventConfigBase = fromApi ?
+            { '': {
+                    startEditable: true,
+                    durationEditable: true,
+                    constraints: [],
+                    overlap: null,
+                    allows: [],
+                    backgroundColor: '',
+                    borderColor: '',
+                    textColor: '',
+                    classNames: []
+                } } :
+            calendar.eventUiBases;
+        relevant = applyMutationToEventStore(relevant, eventConfigBase, mutation, calendar);
+        return mergeEventStores(eventStore, relevant);
+    }
+    function excludeEventsBySourceId(eventStore, sourceId) {
+        return filterEventStoreDefs(eventStore, function (eventDef) {
+            return eventDef.sourceId !== sourceId;
+        });
+    }
+    // QUESTION: why not just return instances? do a general object-property-exclusion util
+    function excludeInstances(eventStore, removals) {
+        return {
+            defs: eventStore.defs,
+            instances: filterHash(eventStore.instances, function (instance) {
+                return !removals[instance.instanceId];
+            })
+        };
+    }
+
+    // high-level segmenting-aware tester functions
+    // ------------------------------------------------------------------------------------------------------------------------
+    function isInteractionValid(interaction, calendar) {
+        return isNewPropsValid({ eventDrag: interaction }, calendar); // HACK: the eventDrag props is used for ALL interactions
+    }
+    function isDateSelectionValid(dateSelection, calendar) {
+        return isNewPropsValid({ dateSelection: dateSelection }, calendar);
+    }
+    function isNewPropsValid(newProps, calendar) {
+        var view = calendar.view;
+        var props = __assign({ businessHours: view ? view.props.businessHours : createEmptyEventStore(), dateSelection: '', eventStore: calendar.state.eventStore, eventUiBases: calendar.eventUiBases, eventSelection: '', eventDrag: null, eventResize: null }, newProps);
+        return (calendar.pluginSystem.hooks.isPropsValid || isPropsValid)(props, calendar);
+    }
+    function isPropsValid(state, calendar, dateSpanMeta, filterConfig) {
+        if (dateSpanMeta === void 0) { dateSpanMeta = {}; }
+        if (state.eventDrag && !isInteractionPropsValid(state, calendar, dateSpanMeta, filterConfig)) {
+            return false;
+        }
+        if (state.dateSelection && !isDateSelectionPropsValid(state, calendar, dateSpanMeta, filterConfig)) {
+            return false;
+        }
+        return true;
+    }
+    // Moving Event Validation
+    // ------------------------------------------------------------------------------------------------------------------------
+    function isInteractionPropsValid(state, calendar, dateSpanMeta, filterConfig) {
+        var interaction = state.eventDrag; // HACK: the eventDrag props is used for ALL interactions
+        var subjectEventStore = interaction.mutatedEvents;
+        var subjectDefs = subjectEventStore.defs;
+        var subjectInstances = subjectEventStore.instances;
+        var subjectConfigs = compileEventUis(subjectDefs, interaction.isEvent ?
+            state.eventUiBases :
+            { '': calendar.selectionConfig } // if not a real event, validate as a selection
+        );
+        if (filterConfig) {
+            subjectConfigs = mapHash(subjectConfigs, filterConfig);
+        }
+        var otherEventStore = excludeInstances(state.eventStore, interaction.affectedEvents.instances); // exclude the subject events. TODO: exclude defs too?
+        var otherDefs = otherEventStore.defs;
+        var otherInstances = otherEventStore.instances;
+        var otherConfigs = compileEventUis(otherDefs, state.eventUiBases);
+        for (var subjectInstanceId in subjectInstances) {
+            var subjectInstance = subjectInstances[subjectInstanceId];
+            var subjectRange = subjectInstance.range;
+            var subjectConfig = subjectConfigs[subjectInstance.defId];
+            var subjectDef = subjectDefs[subjectInstance.defId];
+            // constraint
+            if (!allConstraintsPass(subjectConfig.constraints, subjectRange, otherEventStore, state.businessHours, calendar)) {
+                return false;
+            }
+            // overlap
+            var overlapFunc = calendar.opt('eventOverlap');
+            if (typeof overlapFunc !== 'function') {
+                overlapFunc = null;
+            }
+            for (var otherInstanceId in otherInstances) {
+                var otherInstance = otherInstances[otherInstanceId];
+                // intersect! evaluate
+                if (rangesIntersect(subjectRange, otherInstance.range)) {
+                    var otherOverlap = otherConfigs[otherInstance.defId].overlap;
+                    // consider the other event's overlap. only do this if the subject event is a "real" event
+                    if (otherOverlap === false && interaction.isEvent) {
+                        return false;
+                    }
+                    if (subjectConfig.overlap === false) {
+                        return false;
+                    }
+                    if (overlapFunc && !overlapFunc(new EventApi(calendar, otherDefs[otherInstance.defId], otherInstance), // still event
+                    new EventApi(calendar, subjectDef, subjectInstance) // moving event
+                    )) {
+                        return false;
+                    }
+                }
+            }
+            // allow (a function)
+            var calendarEventStore = calendar.state.eventStore; // need global-to-calendar, not local to component (splittable)state
+            for (var _i = 0, _a = subjectConfig.allows; _i < _a.length; _i++) {
+                var subjectAllow = _a[_i];
+                var subjectDateSpan = __assign({}, dateSpanMeta, { range: subjectInstance.range, allDay: subjectDef.allDay });
+                var origDef = calendarEventStore.defs[subjectDef.defId];
+                var origInstance = calendarEventStore.instances[subjectInstanceId];
+                var eventApi = void 0;
+                if (origDef) { // was previously in the calendar
+                    eventApi = new EventApi(calendar, origDef, origInstance);
+                }
+                else { // was an external event
+                    eventApi = new EventApi(calendar, subjectDef); // no instance, because had no dates
+                }
+                if (!subjectAllow(calendar.buildDateSpanApi(subjectDateSpan), eventApi)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    // Date Selection Validation
+    // ------------------------------------------------------------------------------------------------------------------------
+    function isDateSelectionPropsValid(state, calendar, dateSpanMeta, filterConfig) {
+        var relevantEventStore = state.eventStore;
+        var relevantDefs = relevantEventStore.defs;
+        var relevantInstances = relevantEventStore.instances;
+        var selection = state.dateSelection;
+        var selectionRange = selection.range;
+        var selectionConfig = calendar.selectionConfig;
+        if (filterConfig) {
+            selectionConfig = filterConfig(selectionConfig);
+        }
+        // constraint
+        if (!allConstraintsPass(selectionConfig.constraints, selectionRange, relevantEventStore, state.businessHours, calendar)) {
+            return false;
+        }
+        // overlap
+        var overlapFunc = calendar.opt('selectOverlap');
+        if (typeof overlapFunc !== 'function') {
+            overlapFunc = null;
+        }
+        for (var relevantInstanceId in relevantInstances) {
+            var relevantInstance = relevantInstances[relevantInstanceId];
+            // intersect! evaluate
+            if (rangesIntersect(selectionRange, relevantInstance.range)) {
+                if (selectionConfig.overlap === false) {
+                    return false;
+                }
+                if (overlapFunc && !overlapFunc(new EventApi(calendar, relevantDefs[relevantInstance.defId], relevantInstance))) {
+                    return false;
+                }
+            }
+        }
+        // allow (a function)
+        for (var _i = 0, _a = selectionConfig.allows; _i < _a.length; _i++) {
+            var selectionAllow = _a[_i];
+            var fullDateSpan = __assign({}, dateSpanMeta, selection);
+            if (!selectionAllow(calendar.buildDateSpanApi(fullDateSpan), null)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    // Constraint Utils
+    // ------------------------------------------------------------------------------------------------------------------------
+    function allConstraintsPass(constraints, subjectRange, otherEventStore, businessHoursUnexpanded, calendar) {
+        for (var _i = 0, constraints_1 = constraints; _i < constraints_1.length; _i++) {
+            var constraint = constraints_1[_i];
+            if (!anyRangesContainRange(constraintToRanges(constraint, subjectRange, otherEventStore, businessHoursUnexpanded, calendar), subjectRange)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    function constraintToRanges(constraint, subjectRange, // for expanding a recurring constraint, or expanding business hours
+    otherEventStore, // for if constraint is an even group ID
+    businessHoursUnexpanded, // for if constraint is 'businessHours'
+    calendar // for expanding businesshours
+    ) {
+        if (constraint === 'businessHours') {
+            return eventStoreToRanges(expandRecurring(businessHoursUnexpanded, subjectRange, calendar));
+        }
+        else if (typeof constraint === 'string') { // an group ID
+            return eventStoreToRanges(filterEventStoreDefs(otherEventStore, function (eventDef) {
+                return eventDef.groupId === constraint;
+            }));
+        }
+        else if (typeof constraint === 'object' && constraint) { // non-null object
+            return eventStoreToRanges(expandRecurring(constraint, subjectRange, calendar));
+        }
+        return []; // if it's false
+    }
+    // TODO: move to event-store file?
+    function eventStoreToRanges(eventStore) {
+        var instances = eventStore.instances;
+        var ranges = [];
+        for (var instanceId in instances) {
+            ranges.push(instances[instanceId].range);
+        }
+        return ranges;
+    }
+    // TODO: move to geom file?
+    function anyRangesContainRange(outerRanges, innerRange) {
+        for (var _i = 0, outerRanges_1 = outerRanges; _i < outerRanges_1.length; _i++) {
+            var outerRange = outerRanges_1[_i];
+            if (rangeContainsRange(outerRange, innerRange)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    // Parsing
+    // ------------------------------------------------------------------------------------------------------------------------
+    function normalizeConstraint(input, calendar) {
+        if (Array.isArray(input)) {
+            return parseEvents(input, '', calendar, true); // allowOpenRange=true
+        }
+        else if (typeof input === 'object' && input) { // non-null object
+            return parseEvents([input], '', calendar, true); // allowOpenRange=true
+        }
+        else if (input != null) {
+            return String(input);
+        }
+        else {
+            return null;
+        }
+    }
+
+    function htmlEscape(s) {
+        return (s + '').replace(/&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/'/g, '&#039;')
+            .replace(/"/g, '&quot;')
+            .replace(/\n/g, '<br />');
+    }
+    // Given a hash of CSS properties, returns a string of CSS.
+    // Uses property names as-is (no camel-case conversion). Will not make statements for null/undefined values.
+    function cssToStr(cssProps) {
+        var statements = [];
+        for (var name_1 in cssProps) {
+            var val = cssProps[name_1];
+            if (val != null && val !== '') {
+                statements.push(name_1 + ':' + val);
+            }
+        }
+        return statements.join(';');
+    }
+    // Given an object hash of HTML attribute names to values,
+    // generates a string that can be injected between < > in HTML
+    function attrsToStr(attrs) {
+        var parts = [];
+        for (var name_2 in attrs) {
+            var val = attrs[name_2];
+            if (val != null) {
+                parts.push(name_2 + '="' + htmlEscape(val) + '"');
+            }
+        }
+        return parts.join(' ');
+    }
+    function parseClassName(raw) {
+        if (Array.isArray(raw)) {
+            return raw;
+        }
+        else if (typeof raw === 'string') {
+            return raw.split(/\s+/);
+        }
+        else {
+            return [];
+        }
+    }
+
+    var UNSCOPED_EVENT_UI_PROPS = {
+        editable: Boolean,
+        startEditable: Boolean,
+        durationEditable: Boolean,
+        constraint: null,
+        overlap: null,
+        allow: null,
+        className: parseClassName,
+        classNames: parseClassName,
+        color: String,
+        backgroundColor: String,
+        borderColor: String,
+        textColor: String
+    };
+    function processUnscopedUiProps(rawProps, calendar, leftovers) {
+        var props = refineProps(rawProps, UNSCOPED_EVENT_UI_PROPS, {}, leftovers);
+        var constraint = normalizeConstraint(props.constraint, calendar);
+        return {
+            startEditable: props.startEditable != null ? props.startEditable : props.editable,
+            durationEditable: props.durationEditable != null ? props.durationEditable : props.editable,
+            constraints: constraint != null ? [constraint] : [],
+            overlap: props.overlap,
+            allows: props.allow != null ? [props.allow] : [],
+            backgroundColor: props.backgroundColor || props.color,
+            borderColor: props.borderColor || props.color,
+            textColor: props.textColor,
+            classNames: props.classNames.concat(props.className)
+        };
+    }
+    function processScopedUiProps(prefix, rawScoped, calendar, leftovers) {
+        var rawUnscoped = {};
+        var wasFound = {};
+        for (var key in UNSCOPED_EVENT_UI_PROPS) {
+            var scopedKey = prefix + capitaliseFirstLetter(key);
+            rawUnscoped[key] = rawScoped[scopedKey];
+            wasFound[scopedKey] = true;
+        }
+        if (prefix === 'event') {
+            rawUnscoped.editable = rawScoped.editable; // special case. there is no 'eventEditable', just 'editable'
+        }
+        if (leftovers) {
+            for (var key in rawScoped) {
+                if (!wasFound[key]) {
+                    leftovers[key] = rawScoped[key];
+                }
+            }
+        }
+        return processUnscopedUiProps(rawUnscoped, calendar);
+    }
+    var EMPTY_EVENT_UI = {
+        startEditable: null,
+        durationEditable: null,
+        constraints: [],
+        overlap: null,
+        allows: [],
+        backgroundColor: '',
+        borderColor: '',
+        textColor: '',
+        classNames: []
+    };
+    // prevent against problems with <2 args!
+    function combineEventUis(uis) {
+        return uis.reduce(combineTwoEventUis, EMPTY_EVENT_UI);
+    }
+    function combineTwoEventUis(item0, item1) {
+        return {
+            startEditable: item1.startEditable != null ? item1.startEditable : item0.startEditable,
+            durationEditable: item1.durationEditable != null ? item1.durationEditable : item0.durationEditable,
+            constraints: item0.constraints.concat(item1.constraints),
+            overlap: typeof item1.overlap === 'boolean' ? item1.overlap : item0.overlap,
+            allows: item0.allows.concat(item1.allows),
+            backgroundColor: item1.backgroundColor || item0.backgroundColor,
+            borderColor: item1.borderColor || item0.borderColor,
+            textColor: item1.textColor || item0.textColor,
+            classNames: item0.classNames.concat(item1.classNames)
+        };
+    }
+
+    var NON_DATE_PROPS = {
+        id: String,
+        groupId: String,
+        title: String,
+        url: String,
+        rendering: String,
+        extendedProps: null
+    };
+    var DATE_PROPS = {
+        start: null,
+        date: null,
+        end: null,
+        allDay: null
+    };
+    var uid = 0;
+    function parseEvent(raw, sourceId, calendar, allowOpenRange) {
+        var allDayDefault = computeIsAllDayDefault(sourceId, calendar);
+        var leftovers0 = {};
+        var recurringRes = parseRecurring(raw, // raw, but with single-event stuff stripped out
+        allDayDefault, calendar.dateEnv, calendar.pluginSystem.hooks.recurringTypes, leftovers0 // will populate with non-recurring props
+        );
+        if (recurringRes) {
+            var def = parseEventDef(leftovers0, sourceId, recurringRes.allDay, Boolean(recurringRes.duration), calendar);
+            def.recurringDef = {
+                typeId: recurringRes.typeId,
+                typeData: recurringRes.typeData,
+                duration: recurringRes.duration
+            };
+            return { def: def, instance: null };
+        }
+        else {
+            var leftovers1 = {};
+            var singleRes = parseSingle(raw, allDayDefault, calendar, leftovers1, allowOpenRange);
+            if (singleRes) {
+                var def = parseEventDef(leftovers1, sourceId, singleRes.allDay, singleRes.hasEnd, calendar);
+                var instance = createEventInstance(def.defId, singleRes.range, singleRes.forcedStartTzo, singleRes.forcedEndTzo);
+                return { def: def, instance: instance };
+            }
+        }
+        return null;
+    }
+    /*
+    Will NOT populate extendedProps with the leftover properties.
+    Will NOT populate date-related props.
+    The EventNonDateInput has been normalized (id => publicId, etc).
+    */
+    function parseEventDef(raw, sourceId, allDay, hasEnd, calendar) {
+        var leftovers = {};
+        var def = pluckNonDateProps(raw, calendar, leftovers);
+        def.defId = String(uid++);
+        def.sourceId = sourceId;
+        def.allDay = allDay;
+        def.hasEnd = hasEnd;
+        for (var _i = 0, _a = calendar.pluginSystem.hooks.eventDefParsers; _i < _a.length; _i++) {
+            var eventDefParser = _a[_i];
+            var newLeftovers = {};
+            eventDefParser(def, leftovers, newLeftovers);
+            leftovers = newLeftovers;
+        }
+        def.extendedProps = __assign(leftovers, def.extendedProps || {});
+        // help out EventApi from having user modify props
+        Object.freeze(def.ui.classNames);
+        Object.freeze(def.extendedProps);
+        return def;
+    }
+    function createEventInstance(defId, range, forcedStartTzo, forcedEndTzo) {
+        return {
+            instanceId: String(uid++),
+            defId: defId,
+            range: range,
+            forcedStartTzo: forcedStartTzo == null ? null : forcedStartTzo,
+            forcedEndTzo: forcedEndTzo == null ? null : forcedEndTzo
+        };
+    }
+    function parseSingle(raw, allDayDefault, calendar, leftovers, allowOpenRange) {
+        var props = pluckDateProps(raw, leftovers);
+        var allDay = props.allDay;
+        var startMeta;
+        var startMarker = null;
+        var hasEnd = false;
+        var endMeta;
+        var endMarker = null;
+        startMeta = calendar.dateEnv.createMarkerMeta(props.start);
+        if (startMeta) {
+            startMarker = startMeta.marker;
+        }
+        else if (!allowOpenRange) {
+            return null;
+        }
+        if (props.end != null) {
+            endMeta = calendar.dateEnv.createMarkerMeta(props.end);
+        }
+        if (allDay == null) {
+            if (allDayDefault != null) {
+                allDay = allDayDefault;
+            }
+            else {
+                // fall back to the date props LAST
+                allDay = (!startMeta || startMeta.isTimeUnspecified) &&
+                    (!endMeta || endMeta.isTimeUnspecified);
+            }
+        }
+        if (allDay && startMarker) {
+            startMarker = startOfDay(startMarker);
+        }
+        if (endMeta) {
+            endMarker = endMeta.marker;
+            if (allDay) {
+                endMarker = startOfDay(endMarker);
+            }
+            if (startMarker && endMarker <= startMarker) {
+                endMarker = null;
+            }
+        }
+        if (endMarker) {
+            hasEnd = true;
+        }
+        else if (!allowOpenRange) {
+            hasEnd = calendar.opt('forceEventDuration') || false;
+            endMarker = calendar.dateEnv.add(startMarker, allDay ?
+                calendar.defaultAllDayEventDuration :
+                calendar.defaultTimedEventDuration);
+        }
+        return {
+            allDay: allDay,
+            hasEnd: hasEnd,
+            range: { start: startMarker, end: endMarker },
+            forcedStartTzo: startMeta ? startMeta.forcedTzo : null,
+            forcedEndTzo: endMeta ? endMeta.forcedTzo : null
+        };
+    }
+    function pluckDateProps(raw, leftovers) {
+        var props = refineProps(raw, DATE_PROPS, {}, leftovers);
+        props.start = (props.start !== null) ? props.start : props.date;
+        delete props.date;
+        return props;
+    }
+    function pluckNonDateProps(raw, calendar, leftovers) {
+        var preLeftovers = {};
+        var props = refineProps(raw, NON_DATE_PROPS, {}, preLeftovers);
+        var ui = processUnscopedUiProps(preLeftovers, calendar, leftovers);
+        props.publicId = props.id;
+        delete props.id;
+        props.ui = ui;
+        return props;
+    }
+    function computeIsAllDayDefault(sourceId, calendar) {
+        var res = null;
+        if (sourceId) {
+            var source = calendar.state.eventSources[sourceId];
+            res = source.allDayDefault;
+        }
+        if (res == null) {
+            res = calendar.opt('allDayDefault');
+        }
+        return res;
+    }
+
+    var DEF_DEFAULTS = {
+        startTime: '09:00',
+        endTime: '17:00',
+        daysOfWeek: [1, 2, 3, 4, 5],
+        rendering: 'inverse-background',
+        classNames: 'fc-nonbusiness',
+        groupId: '_businessHours' // so multiple defs get grouped
+    };
+    /*
+    TODO: pass around as EventDefHash!!!
+    */
+    function parseBusinessHours(input, calendar) {
+        return parseEvents(refineInputs(input), '', calendar);
+    }
+    function refineInputs(input) {
+        var rawDefs;
+        if (input === true) {
+            rawDefs = [{}]; // will get DEF_DEFAULTS verbatim
+        }
+        else if (Array.isArray(input)) {
+            // if specifying an array, every sub-definition NEEDS a day-of-week
+            rawDefs = input.filter(function (rawDef) {
+                return rawDef.daysOfWeek;
+            });
+        }
+        else if (typeof input === 'object' && input) { // non-null object
+            rawDefs = [input];
+        }
+        else { // is probably false
+            rawDefs = [];
+        }
+        rawDefs = rawDefs.map(function (rawDef) {
+            return __assign({}, DEF_DEFAULTS, rawDef);
+        });
+        return rawDefs;
+    }
+
+    function memoizeRendering(renderFunc, unrenderFunc, dependencies) {
+        if (dependencies === void 0) { dependencies = []; }
+        var dependents = [];
+        var thisContext;
+        var prevArgs;
+        function unrender() {
+            if (prevArgs) {
+                for (var _i = 0, dependents_1 = dependents; _i < dependents_1.length; _i++) {
+                    var dependent = dependents_1[_i];
+                    dependent.unrender();
+                }
+                if (unrenderFunc) {
+                    unrenderFunc.apply(thisContext, prevArgs);
+                }
+                prevArgs = null;
+            }
+        }
+        function res() {
+            if (!prevArgs || !isArraysEqual(prevArgs, arguments)) {
+                unrender();
+                thisContext = this;
+                prevArgs = arguments;
+                renderFunc.apply(this, arguments);
+            }
+        }
+        res.dependents = dependents;
+        res.unrender = unrender;
+        for (var _i = 0, dependencies_1 = dependencies; _i < dependencies_1.length; _i++) {
+            var dependency = dependencies_1[_i];
+            dependency.dependents.push(res);
+        }
+        return res;
+    }
+
+    var EMPTY_EVENT_STORE = createEmptyEventStore(); // for purecomponents. TODO: keep elsewhere
+    var Splitter = /** @class */ (function () {
+        function Splitter() {
+            this.getKeysForEventDefs = memoize(this._getKeysForEventDefs);
+            this.splitDateSelection = memoize(this._splitDateSpan);
+            this.splitEventStore = memoize(this._splitEventStore);
+            this.splitIndividualUi = memoize(this._splitIndividualUi);
+            this.splitEventDrag = memoize(this._splitInteraction);
+            this.splitEventResize = memoize(this._splitInteraction);
+            this.eventUiBuilders = {}; // TODO: typescript protection
+        }
+        Splitter.prototype.splitProps = function (props) {
+            var _this = this;
+            var keyInfos = this.getKeyInfo(props);
+            var defKeys = this.getKeysForEventDefs(props.eventStore);
+            var dateSelections = this.splitDateSelection(props.dateSelection);
+            var individualUi = this.splitIndividualUi(props.eventUiBases, defKeys); // the individual *bases*
+            var eventStores = this.splitEventStore(props.eventStore, defKeys);
+            var eventDrags = this.splitEventDrag(props.eventDrag);
+            var eventResizes = this.splitEventResize(props.eventResize);
+            var splitProps = {};
+            this.eventUiBuilders = mapHash(keyInfos, function (info, key) {
+                return _this.eventUiBuilders[key] || memoize(buildEventUiForKey);
+            });
+            for (var key in keyInfos) {
+                var keyInfo = keyInfos[key];
+                var eventStore = eventStores[key] || EMPTY_EVENT_STORE;
+                var buildEventUi = this.eventUiBuilders[key];
+                splitProps[key] = {
+                    businessHours: keyInfo.businessHours || props.businessHours,
+                    dateSelection: dateSelections[key] || null,
+                    eventStore: eventStore,
+                    eventUiBases: buildEventUi(props.eventUiBases[''], keyInfo.ui, individualUi[key]),
+                    eventSelection: eventStore.instances[props.eventSelection] ? props.eventSelection : '',
+                    eventDrag: eventDrags[key] || null,
+                    eventResize: eventResizes[key] || null
+                };
+            }
+            return splitProps;
+        };
+        Splitter.prototype._splitDateSpan = function (dateSpan) {
+            var dateSpans = {};
+            if (dateSpan) {
+                var keys = this.getKeysForDateSpan(dateSpan);
+                for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
+                    var key = keys_1[_i];
+                    dateSpans[key] = dateSpan;
+                }
+            }
+            return dateSpans;
+        };
+        Splitter.prototype._getKeysForEventDefs = function (eventStore) {
+            var _this = this;
+            return mapHash(eventStore.defs, function (eventDef) {
+                return _this.getKeysForEventDef(eventDef);
+            });
+        };
+        Splitter.prototype._splitEventStore = function (eventStore, defKeys) {
+            var defs = eventStore.defs, instances = eventStore.instances;
+            var splitStores = {};
+            for (var defId in defs) {
+                for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) {
+                    var key = _a[_i];
+                    if (!splitStores[key]) {
+                        splitStores[key] = createEmptyEventStore();
+                    }
+                    splitStores[key].defs[defId] = defs[defId];
+                }
+            }
+            for (var instanceId in instances) {
+                var instance = instances[instanceId];
+                for (var _b = 0, _c = defKeys[instance.defId]; _b < _c.length; _b++) {
+                    var key = _c[_b];
+                    if (splitStores[key]) { // must have already been created
+                        splitStores[key].instances[instanceId] = instance;
+                    }
+                }
+            }
+            return splitStores;
+        };
+        Splitter.prototype._splitIndividualUi = function (eventUiBases, defKeys) {
+            var splitHashes = {};
+            for (var defId in eventUiBases) {
+                if (defId) { // not the '' key
+                    for (var _i = 0, _a = defKeys[defId]; _i < _a.length; _i++) {
+                        var key = _a[_i];
+                        if (!splitHashes[key]) {
+                            splitHashes[key] = {};
+                        }
+                        splitHashes[key][defId] = eventUiBases[defId];
+                    }
+                }
+            }
+            return splitHashes;
+        };
+        Splitter.prototype._splitInteraction = function (interaction) {
+            var splitStates = {};
+            if (interaction) {
+                var affectedStores_1 = this._splitEventStore(interaction.affectedEvents, this._getKeysForEventDefs(interaction.affectedEvents) // can't use cached. might be events from other calendar
+                );
+                // can't rely on defKeys because event data is mutated
+                var mutatedKeysByDefId = this._getKeysForEventDefs(interaction.mutatedEvents);
+                var mutatedStores_1 = this._splitEventStore(interaction.mutatedEvents, mutatedKeysByDefId);
+                var populate = function (key) {
+                    if (!splitStates[key]) {
+                        splitStates[key] = {
+                            affectedEvents: affectedStores_1[key] || EMPTY_EVENT_STORE,
+                            mutatedEvents: mutatedStores_1[key] || EMPTY_EVENT_STORE,
+                            isEvent: interaction.isEvent,
+                            origSeg: interaction.origSeg
+                        };
+                    }
+                };
+                for (var key in affectedStores_1) {
+                    populate(key);
+                }
+                for (var key in mutatedStores_1) {
+                    populate(key);
+                }
+            }
+            return splitStates;
+        };
+        return Splitter;
+    }());
+    function buildEventUiForKey(allUi, eventUiForKey, individualUi) {
+        var baseParts = [];
+        if (allUi) {
+            baseParts.push(allUi);
+        }
+        if (eventUiForKey) {
+            baseParts.push(eventUiForKey);
+        }
+        var stuff = {
+            '': combineEventUis(baseParts)
+        };
+        if (individualUi) {
+            __assign(stuff, individualUi);
+        }
+        return stuff;
+    }
+
+    // Generates HTML for an anchor to another view into the calendar.
+    // Will either generate an <a> tag or a non-clickable <span> tag, depending on enabled settings.
+    // `gotoOptions` can either be a DateMarker, or an object with the form:
+    // { date, type, forceOff }
+    // `type` is a view-type like "day" or "week". default value is "day".
+    // `attrs` and `innerHtml` are use to generate the rest of the HTML tag.
+    function buildGotoAnchorHtml(component, gotoOptions, attrs, innerHtml) {
+        var dateEnv = component.dateEnv;
+        var date;
+        var type;
+        var forceOff;
+        var finalOptions;
+        if (gotoOptions instanceof Date) {
+            date = gotoOptions; // a single date-like input
+        }
+        else {
+            date = gotoOptions.date;
+            type = gotoOptions.type;
+            forceOff = gotoOptions.forceOff;
+        }
+        finalOptions = {
+            date: dateEnv.formatIso(date, { omitTime: true }),
+            type: type || 'day'
+        };
+        if (typeof attrs === 'string') {
+            innerHtml = attrs;
+            attrs = null;
+        }
+        attrs = attrs ? ' ' + attrsToStr(attrs) : ''; // will have a leading space
+        innerHtml = innerHtml || '';
+        if (!forceOff && component.opt('navLinks')) {
+            return '<a' + attrs +
+                ' data-goto="' + htmlEscape(JSON.stringify(finalOptions)) + '">' +
+                innerHtml +
+                '</a>';
+        }
+        else {
+            return '<span' + attrs + '>' +
+                innerHtml +
+                '</span>';
+        }
+    }
+    function getAllDayHtml(component) {
+        return component.opt('allDayHtml') || htmlEscape(component.opt('allDayText'));
+    }
+    // Computes HTML classNames for a single-day element
+    function getDayClasses(date, dateProfile, context, noThemeHighlight) {
+        var calendar = context.calendar, view = context.view, theme = context.theme, dateEnv = context.dateEnv;
+        var classes = [];
+        var todayStart;
+        var todayEnd;
+        if (!rangeContainsMarker(dateProfile.activeRange, date)) {
+            classes.push('fc-disabled-day');
+        }
+        else {
+            classes.push('fc-' + DAY_IDS[date.getUTCDay()]);
+            if (view.opt('monthMode') &&
+                dateEnv.getMonth(date) !== dateEnv.getMonth(dateProfile.currentRange.start)) {
+                classes.push('fc-other-month');
+            }
+            todayStart = startOfDay(calendar.getNow());
+            todayEnd = addDays(todayStart, 1);
+            if (date < todayStart) {
+                classes.push('fc-past');
+            }
+            else if (date >= todayEnd) {
+                classes.push('fc-future');
+            }
+            else {
+                classes.push('fc-today');
+                if (noThemeHighlight !== true) {
+                    classes.push(theme.getClass('today'));
+                }
+            }
+        }
+        return classes;
+    }
+
+    // given a function that resolves a result asynchronously.
+    // the function can either call passed-in success and failure callbacks,
+    // or it can return a promise.
+    // if you need to pass additional params to func, bind them first.
+    function unpromisify(func, success, failure) {
+        // guard against success/failure callbacks being called more than once
+        // and guard against a promise AND callback being used together.
+        var isResolved = false;
+        var wrappedSuccess = function () {
+            if (!isResolved) {
+                isResolved = true;
+                success.apply(this, arguments);
+            }
+        };
+        var wrappedFailure = function () {
+            if (!isResolved) {
+                isResolved = true;
+                if (failure) {
+                    failure.apply(this, arguments);
+                }
+            }
+        };
+        var res = func(wrappedSuccess, wrappedFailure);
+        if (res && typeof res.then === 'function') {
+            res.then(wrappedSuccess, wrappedFailure);
+        }
+    }
+
+    var Mixin = /** @class */ (function () {
+        function Mixin() {
+        }
+        // mix into a CLASS
+        Mixin.mixInto = function (destClass) {
+            this.mixIntoObj(destClass.prototype);
+        };
+        // mix into ANY object
+        Mixin.mixIntoObj = function (destObj) {
+            var _this = this;
+            Object.getOwnPropertyNames(this.prototype).forEach(function (name) {
+                if (!destObj[name]) { // if destination doesn't already define it
+                    destObj[name] = _this.prototype[name];
+                }
+            });
+        };
+        /*
+        will override existing methods
+        TODO: remove! not used anymore
+        */
+        Mixin.mixOver = function (destClass) {
+            var _this = this;
+            Object.getOwnPropertyNames(this.prototype).forEach(function (name) {
+                destClass.prototype[name] = _this.prototype[name];
+            });
+        };
+        return Mixin;
+    }());
+
+    /*
+    USAGE:
+      import { default as EmitterMixin, EmitterInterface } from './EmitterMixin'
+    in class:
+      on: EmitterInterface['on']
+      one: EmitterInterface['one']
+      off: EmitterInterface['off']
+      trigger: EmitterInterface['trigger']
+      triggerWith: EmitterInterface['triggerWith']
+      hasHandlers: EmitterInterface['hasHandlers']
+    after class:
+      EmitterMixin.mixInto(TheClass)
+    */
+    var EmitterMixin = /** @class */ (function (_super) {
+        __extends(EmitterMixin, _super);
+        function EmitterMixin() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        EmitterMixin.prototype.on = function (type, handler) {
+            addToHash(this._handlers || (this._handlers = {}), type, handler);
+            return this; // for chaining
+        };
+        // todo: add comments
+        EmitterMixin.prototype.one = function (type, handler) {
+            addToHash(this._oneHandlers || (this._oneHandlers = {}), type, handler);
+            return this; // for chaining
+        };
+        EmitterMixin.prototype.off = function (type, handler) {
+            if (this._handlers) {
+                removeFromHash(this._handlers, type, handler);
+            }
+            if (this._oneHandlers) {
+                removeFromHash(this._oneHandlers, type, handler);
+            }
+            return this; // for chaining
+        };
+        EmitterMixin.prototype.trigger = function (type) {
+            var args = [];
+            for (var _i = 1; _i < arguments.length; _i++) {
+                args[_i - 1] = arguments[_i];
+            }
+            this.triggerWith(type, this, args);
+            return this; // for chaining
+        };
+        EmitterMixin.prototype.triggerWith = function (type, context, args) {
+            if (this._handlers) {
+                applyAll(this._handlers[type], context, args);
+            }
+            if (this._oneHandlers) {
+                applyAll(this._oneHandlers[type], context, args);
+                delete this._oneHandlers[type]; // will never fire again
+            }
+            return this; // for chaining
+        };
+        EmitterMixin.prototype.hasHandlers = function (type) {
+            return (this._handlers && this._handlers[type] && this._handlers[type].length) ||
+                (this._oneHandlers && this._oneHandlers[type] && this._oneHandlers[type].length);
+        };
+        return EmitterMixin;
+    }(Mixin));
+    function addToHash(hash, type, handler) {
+        (hash[type] || (hash[type] = []))
+            .push(handler);
+    }
+    function removeFromHash(hash, type, handler) {
+        if (handler) {
+            if (hash[type]) {
+                hash[type] = hash[type].filter(function (func) {
+                    return func !== handler;
+                });
+            }
+        }
+        else {
+            delete hash[type]; // remove all handler funcs for this type
+        }
+    }
+
+    /*
+    Records offset information for a set of elements, relative to an origin element.
+    Can record the left/right OR the top/bottom OR both.
+    Provides methods for querying the cache by position.
+    */
+    var PositionCache = /** @class */ (function () {
+        function PositionCache(originEl, els, isHorizontal, isVertical) {
+            this.originEl = originEl;
+            this.els = els;
+            this.isHorizontal = isHorizontal;
+            this.isVertical = isVertical;
+        }
+        // Queries the els for coordinates and stores them.
+        // Call this method before using and of the get* methods below.
+        PositionCache.prototype.build = function () {
+            var originEl = this.originEl;
+            var originClientRect = this.originClientRect =
+                originEl.getBoundingClientRect(); // relative to viewport top-left
+            if (this.isHorizontal) {
+                this.buildElHorizontals(originClientRect.left);
+            }
+            if (this.isVertical) {
+                this.buildElVerticals(originClientRect.top);
+            }
+        };
+        // Populates the left/right internal coordinate arrays
+        PositionCache.prototype.buildElHorizontals = function (originClientLeft) {
+            var lefts = [];
+            var rights = [];
+            for (var _i = 0, _a = this.els; _i < _a.length; _i++) {
+                var el = _a[_i];
+                var rect = el.getBoundingClientRect();
+                lefts.push(rect.left - originClientLeft);
+                rights.push(rect.right - originClientLeft);
+            }
+            this.lefts = lefts;
+            this.rights = rights;
+        };
+        // Populates the top/bottom internal coordinate arrays
+        PositionCache.prototype.buildElVerticals = function (originClientTop) {
+            var tops = [];
+            var bottoms = [];
+            for (var _i = 0, _a = this.els; _i < _a.length; _i++) {
+                var el = _a[_i];
+                var rect = el.getBoundingClientRect();
+                tops.push(rect.top - originClientTop);
+                bottoms.push(rect.bottom - originClientTop);
+            }
+            this.tops = tops;
+            this.bottoms = bottoms;
+        };
+        // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
+        // If no intersection is made, returns undefined.
+        PositionCache.prototype.leftToIndex = function (leftPosition) {
+            var lefts = this.lefts;
+            var rights = this.rights;
+            var len = lefts.length;
+            var i;
+            for (i = 0; i < len; i++) {
+                if (leftPosition >= lefts[i] && leftPosition < rights[i]) {
+                    return i;
+                }
+            }
+        };
+        // Given a top offset (from document top), returns the index of the el that it vertically intersects.
+        // If no intersection is made, returns undefined.
+        PositionCache.prototype.topToIndex = function (topPosition) {
+            var tops = this.tops;
+            var bottoms = this.bottoms;
+            var len = tops.length;
+            var i;
+            for (i = 0; i < len; i++) {
+                if (topPosition >= tops[i] && topPosition < bottoms[i]) {
+                    return i;
+                }
+            }
+        };
+        // Gets the width of the element at the given index
+        PositionCache.prototype.getWidth = function (leftIndex) {
+            return this.rights[leftIndex] - this.lefts[leftIndex];
+        };
+        // Gets the height of the element at the given index
+        PositionCache.prototype.getHeight = function (topIndex) {
+            return this.bottoms[topIndex] - this.tops[topIndex];
+        };
+        return PositionCache;
+    }());
+
+    /*
+    An object for getting/setting scroll-related information for an element.
+    Internally, this is done very differently for window versus DOM element,
+    so this object serves as a common interface.
+    */
+    var ScrollController = /** @class */ (function () {
+        function ScrollController() {
+        }
+        ScrollController.prototype.getMaxScrollTop = function () {
+            return this.getScrollHeight() - this.getClientHeight();
+        };
+        ScrollController.prototype.getMaxScrollLeft = function () {
+            return this.getScrollWidth() - this.getClientWidth();
+        };
+        ScrollController.prototype.canScrollVertically = function () {
+            return this.getMaxScrollTop() > 0;
+        };
+        ScrollController.prototype.canScrollHorizontally = function () {
+            return this.getMaxScrollLeft() > 0;
+        };
+        ScrollController.prototype.canScrollUp = function () {
+            return this.getScrollTop() > 0;
+        };
+        ScrollController.prototype.canScrollDown = function () {
+            return this.getScrollTop() < this.getMaxScrollTop();
+        };
+        ScrollController.prototype.canScrollLeft = function () {
+            return this.getScrollLeft() > 0;
+        };
+        ScrollController.prototype.canScrollRight = function () {
+            return this.getScrollLeft() < this.getMaxScrollLeft();
+        };
+        return ScrollController;
+    }());
+    var ElementScrollController = /** @class */ (function (_super) {
+        __extends(ElementScrollController, _super);
+        function ElementScrollController(el) {
+            var _this = _super.call(this) || this;
+            _this.el = el;
+            return _this;
+        }
+        ElementScrollController.prototype.getScrollTop = function () {
+            return this.el.scrollTop;
+        };
+        ElementScrollController.prototype.getScrollLeft = function () {
+            return this.el.scrollLeft;
+        };
+        ElementScrollController.prototype.setScrollTop = function (top) {
+            this.el.scrollTop = top;
+        };
+        ElementScrollController.prototype.setScrollLeft = function (left) {
+            this.el.scrollLeft = left;
+        };
+        ElementScrollController.prototype.getScrollWidth = function () {
+            return this.el.scrollWidth;
+        };
+        ElementScrollController.prototype.getScrollHeight = function () {
+            return this.el.scrollHeight;
+        };
+        ElementScrollController.prototype.getClientHeight = function () {
+            return this.el.clientHeight;
+        };
+        ElementScrollController.prototype.getClientWidth = function () {
+            return this.el.clientWidth;
+        };
+        return ElementScrollController;
+    }(ScrollController));
+    var WindowScrollController = /** @class */ (function (_super) {
+        __extends(WindowScrollController, _super);
+        function WindowScrollController() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        WindowScrollController.prototype.getScrollTop = function () {
+            return window.pageYOffset;
+        };
+        WindowScrollController.prototype.getScrollLeft = function () {
+            return window.pageXOffset;
+        };
+        WindowScrollController.prototype.setScrollTop = function (n) {
+            window.scroll(window.pageXOffset, n);
+        };
+        WindowScrollController.prototype.setScrollLeft = function (n) {
+            window.scroll(n, window.pageYOffset);
+        };
+        WindowScrollController.prototype.getScrollWidth = function () {
+            return document.documentElement.scrollWidth;
+        };
+        WindowScrollController.prototype.getScrollHeight = function () {
+            return document.documentElement.scrollHeight;
+        };
+        WindowScrollController.prototype.getClientHeight = function () {
+            return document.documentElement.clientHeight;
+        };
+        WindowScrollController.prototype.getClientWidth = function () {
+            return document.documentElement.clientWidth;
+        };
+        return WindowScrollController;
+    }(ScrollController));
+
+    /*
+    Embodies a div that has potential scrollbars
+    */
+    var ScrollComponent = /** @class */ (function (_super) {
+        __extends(ScrollComponent, _super);
+        function ScrollComponent(overflowX, overflowY) {
+            var _this = _super.call(this, createElement('div', {
+                className: 'fc-scroller'
+            })) || this;
+            _this.overflowX = overflowX;
+            _this.overflowY = overflowY;
+            _this.applyOverflow();
+            return _this;
+        }
+        // sets to natural height, unlocks overflow
+        ScrollComponent.prototype.clear = function () {
+            this.setHeight('auto');
+            this.applyOverflow();
+        };
+        ScrollComponent.prototype.destroy = function () {
+            removeElement(this.el);
+        };
+        // Overflow
+        // -----------------------------------------------------------------------------------------------------------------
+        ScrollComponent.prototype.applyOverflow = function () {
+            applyStyle(this.el, {
+                overflowX: this.overflowX,
+                overflowY: this.overflowY
+            });
+        };
+        // Causes any 'auto' overflow values to resolves to 'scroll' or 'hidden'.
+        // Useful for preserving scrollbar widths regardless of future resizes.
+        // Can pass in scrollbarWidths for optimization.
+        ScrollComponent.prototype.lockOverflow = function (scrollbarWidths) {
+            var overflowX = this.overflowX;
+            var overflowY = this.overflowY;
+            scrollbarWidths = scrollbarWidths || this.getScrollbarWidths();
+            if (overflowX === 'auto') {
+                overflowX = (scrollbarWidths.bottom || // horizontal scrollbars?
+                    this.canScrollHorizontally() // OR scrolling pane with massless scrollbars?
+                ) ? 'scroll' : 'hidden';
+            }
+            if (overflowY === 'auto') {
+                overflowY = (scrollbarWidths.left || scrollbarWidths.right || // horizontal scrollbars?
+                    this.canScrollVertically() // OR scrolling pane with massless scrollbars?
+                ) ? 'scroll' : 'hidden';
+            }
+            applyStyle(this.el, { overflowX: overflowX, overflowY: overflowY });
+        };
+        ScrollComponent.prototype.setHeight = function (height) {
+            applyStyleProp(this.el, 'height', height);
+        };
+        ScrollComponent.prototype.getScrollbarWidths = function () {
+            var edges = computeEdges(this.el);
+            return {
+                left: edges.scrollbarLeft,
+                right: edges.scrollbarRight,
+                bottom: edges.scrollbarBottom
+            };
+        };
+        return ScrollComponent;
+    }(ElementScrollController));
+
+    var Theme = /** @class */ (function () {
+        function Theme(calendarOptions) {
+            this.calendarOptions = calendarOptions;
+            this.processIconOverride();
+        }
+        Theme.prototype.processIconOverride = function () {
+            if (this.iconOverrideOption) {
+                this.setIconOverride(this.calendarOptions[this.iconOverrideOption]);
+            }
+        };
+        Theme.prototype.setIconOverride = function (iconOverrideHash) {
+            var iconClassesCopy;
+            var buttonName;
+            if (typeof iconOverrideHash === 'object' && iconOverrideHash) { // non-null object
+                iconClassesCopy = __assign({}, this.iconClasses);
+                for (buttonName in iconOverrideHash) {
+                    iconClassesCopy[buttonName] = this.applyIconOverridePrefix(iconOverrideHash[buttonName]);
+                }
+                this.iconClasses = iconClassesCopy;
+            }
+            else if (iconOverrideHash === false) {
+                this.iconClasses = {};
+            }
+        };
+        Theme.prototype.applyIconOverridePrefix = function (className) {
+            var prefix = this.iconOverridePrefix;
+            if (prefix && className.indexOf(prefix) !== 0) { // if not already present
+                className = prefix + className;
+            }
+            return className;
+        };
+        Theme.prototype.getClass = function (key) {
+            return this.classes[key] || '';
+        };
+        Theme.prototype.getIconClass = function (buttonName) {
+            var className = this.iconClasses[buttonName];
+            if (className) {
+                return this.baseIconClass + ' ' + className;
+            }
+            return '';
+        };
+        Theme.prototype.getCustomButtonIconClass = function (customButtonProps) {
+            var className;
+            if (this.iconOverrideCustomButtonOption) {
+                className = customButtonProps[this.iconOverrideCustomButtonOption];
+                if (className) {
+                    return this.baseIconClass + ' ' + this.applyIconOverridePrefix(className);
+                }
+            }
+            return '';
+        };
+        return Theme;
+    }());
+    Theme.prototype.classes = {};
+    Theme.prototype.iconClasses = {};
+    Theme.prototype.baseIconClass = '';
+    Theme.prototype.iconOverridePrefix = '';
+
+    var guid = 0;
+    var Component = /** @class */ (function () {
+        function Component(context, isView) {
+            // HACK to populate view at top of component instantiation call chain
+            if (isView) {
+                context.view = this;
+            }
+            this.uid = String(guid++);
+            this.context = context;
+            this.dateEnv = context.dateEnv;
+            this.theme = context.theme;
+            this.view = context.view;
+            this.calendar = context.calendar;
+            this.isRtl = this.opt('dir') === 'rtl';
+        }
+        Component.addEqualityFuncs = function (newFuncs) {
+            this.prototype.equalityFuncs = __assign({}, this.prototype.equalityFuncs, newFuncs);
+        };
+        Component.prototype.opt = function (name) {
+            return this.context.options[name];
+        };
+        Component.prototype.receiveProps = function (props) {
+            var _a = recycleProps(this.props || {}, props, this.equalityFuncs), anyChanges = _a.anyChanges, comboProps = _a.comboProps;
+            this.props = comboProps;
+            if (anyChanges) {
+                this.render(comboProps);
+            }
+        };
+        Component.prototype.render = function (props) {
+        };
+        // after destroy is called, this component won't ever be used again
+        Component.prototype.destroy = function () {
+        };
+        return Component;
+    }());
+    Component.prototype.equalityFuncs = {};
+    /*
+    Reuses old values when equal. If anything is unequal, returns newProps as-is.
+    Great for PureComponent, but won't be feasible with React, so just eliminate and use React's DOM diffing.
+    */
+    function recycleProps(oldProps, newProps, equalityFuncs) {
+        var comboProps = {}; // some old, some new
+        var anyChanges = false;
+        for (var key in newProps) {
+            if (key in oldProps && (oldProps[key] === newProps[key] ||
+                (equalityFuncs[key] && equalityFuncs[key](oldProps[key], newProps[key])))) {
+                // equal to old? use old prop
+                comboProps[key] = oldProps[key];
+            }
+            else {
+                comboProps[key] = newProps[key];
+                anyChanges = true;
+            }
+        }
+        for (var key in oldProps) {
+            if (!(key in newProps)) {
+                anyChanges = true;
+                break;
+            }
+        }
+        return { anyChanges: anyChanges, comboProps: comboProps };
+    }
+
+    /*
+    PURPOSES:
+    - hook up to fg, fill, and mirror renderers
+    - interface for dragging and hits
+    */
+    var DateComponent = /** @class */ (function (_super) {
+        __extends(DateComponent, _super);
+        function DateComponent(context, el, isView) {
+            var _this = _super.call(this, context, isView) || this;
+            _this.el = el;
+            return _this;
+        }
+        DateComponent.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            removeElement(this.el);
+        };
+        // TODO: WHAT ABOUT (sourceSeg && sourceSeg.component.doesDragMirror)
+        //
+        // Event Drag-n-Drop Rendering (for both events and external elements)
+        // ---------------------------------------------------------------------------------------------------------------
+        /*
+        renderEventDragSegs(state: EventSegUiInteractionState) {
+          if (state) {
+            let { isEvent, segs, sourceSeg } = state
+      
+            if (this.eventRenderer) {
+              this.eventRenderer.hideByHash(state.affectedInstances)
+            }
+      
+            // if the user is dragging something that is considered an event with real event data,
+            // and this component likes to do drag mirrors OR the component where the seg came from
+            // likes to do drag mirrors, then render a drag mirror.
+            if (isEvent && (this.doesDragMirror || sourceSeg && sourceSeg.component.doesDragMirror)) {
+              if (this.mirrorRenderer) {
+                this.mirrorRenderer.renderSegs(segs, { isDragging: true, sourceSeg })
+              }
+            }
+      
+            // if it would be impossible to render a drag mirror OR this component likes to render
+            // highlights, then render a highlight.
+            if (!isEvent || this.doesDragHighlight) {
+              if (this.fillRenderer) {
+                this.fillRenderer.renderSegs('highlight', segs)
+              }
+            }
+          }
+        }
+        */
+        // Hit System
+        // -----------------------------------------------------------------------------------------------------------------
+        DateComponent.prototype.buildPositionCaches = function () {
+        };
+        DateComponent.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+            return null; // this should be abstract
+        };
+        // Validation
+        // -----------------------------------------------------------------------------------------------------------------
+        DateComponent.prototype.isInteractionValid = function (interaction) {
+            var calendar = this.calendar;
+            var dateProfile = this.props.dateProfile; // HACK
+            var instances = interaction.mutatedEvents.instances;
+            if (dateProfile) { // HACK for DayTile
+                for (var instanceId in instances) {
+                    if (!rangeContainsRange(dateProfile.validRange, instances[instanceId].range)) {
+                        return false;
+                    }
+                }
+            }
+            return isInteractionValid(interaction, calendar);
+        };
+        DateComponent.prototype.isDateSelectionValid = function (selection) {
+            var dateProfile = this.props.dateProfile; // HACK
+            if (dateProfile && // HACK for DayTile
+                !rangeContainsRange(dateProfile.validRange, selection.range)) {
+                return false;
+            }
+            return isDateSelectionValid(selection, this.calendar);
+        };
+        // Triggering
+        // -----------------------------------------------------------------------------------------------------------------
+        // TODO: move to Calendar
+        DateComponent.prototype.publiclyTrigger = function (name, args) {
+            var calendar = this.calendar;
+            return calendar.publiclyTrigger(name, args);
+        };
+        DateComponent.prototype.publiclyTriggerAfterSizing = function (name, args) {
+            var calendar = this.calendar;
+            return calendar.publiclyTriggerAfterSizing(name, args);
+        };
+        DateComponent.prototype.hasPublicHandlers = function (name) {
+            var calendar = this.calendar;
+            return calendar.hasPublicHandlers(name);
+        };
+        DateComponent.prototype.triggerRenderedSegs = function (segs, isMirrors) {
+            var calendar = this.calendar;
+            if (this.hasPublicHandlers('eventPositioned')) {
+                for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                    var seg = segs_1[_i];
+                    this.publiclyTriggerAfterSizing('eventPositioned', [
+                        {
+                            event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance),
+                            isMirror: isMirrors,
+                            isStart: seg.isStart,
+                            isEnd: seg.isEnd,
+                            el: seg.el,
+                            view: this // safe to cast because this method is only called on context.view
+                        }
+                    ]);
+                }
+            }
+            if (!calendar.state.loadingLevel) { // avoid initial empty state while pending
+                calendar.afterSizingTriggers._eventsPositioned = [null]; // fire once
+            }
+        };
+        DateComponent.prototype.triggerWillRemoveSegs = function (segs, isMirrors) {
+            var calendar = this.calendar;
+            for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+                var seg = segs_2[_i];
+                calendar.trigger('eventElRemove', seg.el);
+            }
+            if (this.hasPublicHandlers('eventDestroy')) {
+                for (var _a = 0, segs_3 = segs; _a < segs_3.length; _a++) {
+                    var seg = segs_3[_a];
+                    this.publiclyTrigger('eventDestroy', [
+                        {
+                            event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance),
+                            isMirror: isMirrors,
+                            el: seg.el,
+                            view: this // safe to cast because this method is only called on context.view
+                        }
+                    ]);
+                }
+            }
+        };
+        // Pointer Interaction Utils
+        // -----------------------------------------------------------------------------------------------------------------
+        DateComponent.prototype.isValidSegDownEl = function (el) {
+            return !this.props.eventDrag && // HACK
+                !this.props.eventResize && // HACK
+                !elementClosest(el, '.fc-mirror') &&
+                (this.isPopover() || !this.isInPopover(el));
+            // ^above line ensures we don't detect a seg interaction within a nested component.
+            // it's a HACK because it only supports a popover as the nested component.
+        };
+        DateComponent.prototype.isValidDateDownEl = function (el) {
+            var segEl = elementClosest(el, this.fgSegSelector);
+            return (!segEl || segEl.classList.contains('fc-mirror')) &&
+                !elementClosest(el, '.fc-more') && // a "more.." link
+                !elementClosest(el, 'a[data-goto]') && // a clickable nav link
+                !this.isInPopover(el);
+        };
+        DateComponent.prototype.isPopover = function () {
+            return this.el.classList.contains('fc-popover');
+        };
+        DateComponent.prototype.isInPopover = function (el) {
+            return Boolean(elementClosest(el, '.fc-popover'));
+        };
+        return DateComponent;
+    }(Component));
+    DateComponent.prototype.fgSegSelector = '.fc-event-container > *';
+    DateComponent.prototype.bgSegSelector = '.fc-bgevent:not(.fc-nonbusiness)';
+
+    var uid$1 = 0;
+    function createPlugin(input) {
+        return {
+            id: String(uid$1++),
+            deps: input.deps || [],
+            reducers: input.reducers || [],
+            eventDefParsers: input.eventDefParsers || [],
+            isDraggableTransformers: input.isDraggableTransformers || [],
+            eventDragMutationMassagers: input.eventDragMutationMassagers || [],
+            eventDefMutationAppliers: input.eventDefMutationAppliers || [],
+            dateSelectionTransformers: input.dateSelectionTransformers || [],
+            datePointTransforms: input.datePointTransforms || [],
+            dateSpanTransforms: input.dateSpanTransforms || [],
+            views: input.views || {},
+            viewPropsTransformers: input.viewPropsTransformers || [],
+            isPropsValid: input.isPropsValid || null,
+            externalDefTransforms: input.externalDefTransforms || [],
+            eventResizeJoinTransforms: input.eventResizeJoinTransforms || [],
+            viewContainerModifiers: input.viewContainerModifiers || [],
+            eventDropTransformers: input.eventDropTransformers || [],
+            componentInteractions: input.componentInteractions || [],
+            calendarInteractions: input.calendarInteractions || [],
+            themeClasses: input.themeClasses || {},
+            eventSourceDefs: input.eventSourceDefs || [],
+            cmdFormatter: input.cmdFormatter,
+            recurringTypes: input.recurringTypes || [],
+            namedTimeZonedImpl: input.namedTimeZonedImpl,
+            defaultView: input.defaultView || '',
+            elementDraggingImpl: input.elementDraggingImpl,
+            optionChangeHandlers: input.optionChangeHandlers || {}
+        };
+    }
+    var PluginSystem = /** @class */ (function () {
+        function PluginSystem() {
+            this.hooks = {
+                reducers: [],
+                eventDefParsers: [],
+                isDraggableTransformers: [],
+                eventDragMutationMassagers: [],
+                eventDefMutationAppliers: [],
+                dateSelectionTransformers: [],
+                datePointTransforms: [],
+                dateSpanTransforms: [],
+                views: {},
+                viewPropsTransformers: [],
+                isPropsValid: null,
+                externalDefTransforms: [],
+                eventResizeJoinTransforms: [],
+                viewContainerModifiers: [],
+                eventDropTransformers: [],
+                componentInteractions: [],
+                calendarInteractions: [],
+                themeClasses: {},
+                eventSourceDefs: [],
+                cmdFormatter: null,
+                recurringTypes: [],
+                namedTimeZonedImpl: null,
+                defaultView: '',
+                elementDraggingImpl: null,
+                optionChangeHandlers: {}
+            };
+            this.addedHash = {};
+        }
+        PluginSystem.prototype.add = function (plugin) {
+            if (!this.addedHash[plugin.id]) {
+                this.addedHash[plugin.id] = true;
+                for (var _i = 0, _a = plugin.deps; _i < _a.length; _i++) {
+                    var dep = _a[_i];
+                    this.add(dep);
+                }
+                this.hooks = combineHooks(this.hooks, plugin);
+            }
+        };
+        return PluginSystem;
+    }());
+    function combineHooks(hooks0, hooks1) {
+        return {
+            reducers: hooks0.reducers.concat(hooks1.reducers),
+            eventDefParsers: hooks0.eventDefParsers.concat(hooks1.eventDefParsers),
+            isDraggableTransformers: hooks0.isDraggableTransformers.concat(hooks1.isDraggableTransformers),
+            eventDragMutationMassagers: hooks0.eventDragMutationMassagers.concat(hooks1.eventDragMutationMassagers),
+            eventDefMutationAppliers: hooks0.eventDefMutationAppliers.concat(hooks1.eventDefMutationAppliers),
+            dateSelectionTransformers: hooks0.dateSelectionTransformers.concat(hooks1.dateSelectionTransformers),
+            datePointTransforms: hooks0.datePointTransforms.concat(hooks1.datePointTransforms),
+            dateSpanTransforms: hooks0.dateSpanTransforms.concat(hooks1.dateSpanTransforms),
+            views: __assign({}, hooks0.views, hooks1.views),
+            viewPropsTransformers: hooks0.viewPropsTransformers.concat(hooks1.viewPropsTransformers),
+            isPropsValid: hooks1.isPropsValid || hooks0.isPropsValid,
+            externalDefTransforms: hooks0.externalDefTransforms.concat(hooks1.externalDefTransforms),
+            eventResizeJoinTransforms: hooks0.eventResizeJoinTransforms.concat(hooks1.eventResizeJoinTransforms),
+            viewContainerModifiers: hooks0.viewContainerModifiers.concat(hooks1.viewContainerModifiers),
+            eventDropTransformers: hooks0.eventDropTransformers.concat(hooks1.eventDropTransformers),
+            calendarInteractions: hooks0.calendarInteractions.concat(hooks1.calendarInteractions),
+            componentInteractions: hooks0.componentInteractions.concat(hooks1.componentInteractions),
+            themeClasses: __assign({}, hooks0.themeClasses, hooks1.themeClasses),
+            eventSourceDefs: hooks0.eventSourceDefs.concat(hooks1.eventSourceDefs),
+            cmdFormatter: hooks1.cmdFormatter || hooks0.cmdFormatter,
+            recurringTypes: hooks0.recurringTypes.concat(hooks1.recurringTypes),
+            namedTimeZonedImpl: hooks1.namedTimeZonedImpl || hooks0.namedTimeZonedImpl,
+            defaultView: hooks0.defaultView || hooks1.defaultView,
+            elementDraggingImpl: hooks0.elementDraggingImpl || hooks1.elementDraggingImpl,
+            optionChangeHandlers: __assign({}, hooks0.optionChangeHandlers, hooks1.optionChangeHandlers)
+        };
+    }
+
+    var eventSourceDef = {
+        ignoreRange: true,
+        parseMeta: function (raw) {
+            if (Array.isArray(raw)) { // short form
+                return raw;
+            }
+            else if (Array.isArray(raw.events)) {
+                return raw.events;
+            }
+            return null;
+        },
+        fetch: function (arg, success) {
+            success({
+                rawEvents: arg.eventSource.meta
+            });
+        }
+    };
+    var ArrayEventSourcePlugin = createPlugin({
+        eventSourceDefs: [eventSourceDef]
+    });
+
+    var eventSourceDef$1 = {
+        parseMeta: function (raw) {
+            if (typeof raw === 'function') { // short form
+                return raw;
+            }
+            else if (typeof raw.events === 'function') {
+                return raw.events;
+            }
+            return null;
+        },
+        fetch: function (arg, success, failure) {
+            var dateEnv = arg.calendar.dateEnv;
+            var func = arg.eventSource.meta;
+            unpromisify(func.bind(null, {
+                start: dateEnv.toDate(arg.range.start),
+                end: dateEnv.toDate(arg.range.end),
+                startStr: dateEnv.formatIso(arg.range.start),
+                endStr: dateEnv.formatIso(arg.range.end),
+                timeZone: dateEnv.timeZone
+            }), function (rawEvents) {
+                success({ rawEvents: rawEvents }); // needs an object response
+            }, failure // send errorObj directly to failure callback
+            );
+        }
+    };
+    var FuncEventSourcePlugin = createPlugin({
+        eventSourceDefs: [eventSourceDef$1]
+    });
+
+    function requestJson(method, url, params, successCallback, failureCallback) {
+        method = method.toUpperCase();
+        var body = null;
+        if (method === 'GET') {
+            url = injectQueryStringParams(url, params);
+        }
+        else {
+            body = encodeParams(params);
+        }
+        var xhr = new XMLHttpRequest();
+        xhr.open(method, url, true);
+        if (method !== 'GET') {
+            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+        }
+        xhr.onload = function () {
+            if (xhr.status >= 200 && xhr.status < 400) {
+                try {
+                    var res = JSON.parse(xhr.responseText);
+                    successCallback(res, xhr);
+                }
+                catch (err) {
+                    failureCallback('Failure parsing JSON', xhr);
+                }
+            }
+            else {
+                failureCallback('Request failed', xhr);
+            }
+        };
+        xhr.onerror = function () {
+            failureCallback('Request failed', xhr);
+        };
+        xhr.send(body);
+    }
+    function injectQueryStringParams(url, params) {
+        return url +
+            (url.indexOf('?') === -1 ? '?' : '&') +
+            encodeParams(params);
+    }
+    function encodeParams(params) {
+        var parts = [];
+        for (var key in params) {
+            parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
+        }
+        return parts.join('&');
+    }
+
+    var eventSourceDef$2 = {
+        parseMeta: function (raw) {
+            if (typeof raw === 'string') { // short form
+                raw = { url: raw };
+            }
+            else if (!raw || typeof raw !== 'object' || !raw.url) {
+                return null;
+            }
+            return {
+                url: raw.url,
+                method: (raw.method || 'GET').toUpperCase(),
+                extraParams: raw.extraParams,
+                startParam: raw.startParam,
+                endParam: raw.endParam,
+                timeZoneParam: raw.timeZoneParam
+            };
+        },
+        fetch: function (arg, success, failure) {
+            var meta = arg.eventSource.meta;
+            var requestParams = buildRequestParams(meta, arg.range, arg.calendar);
+            requestJson(meta.method, meta.url, requestParams, function (rawEvents, xhr) {
+                success({ rawEvents: rawEvents, xhr: xhr });
+            }, function (errorMessage, xhr) {
+                failure({ message: errorMessage, xhr: xhr });
+            });
+        }
+    };
+    var JsonFeedEventSourcePlugin = createPlugin({
+        eventSourceDefs: [eventSourceDef$2]
+    });
+    function buildRequestParams(meta, range, calendar) {
+        var dateEnv = calendar.dateEnv;
+        var startParam;
+        var endParam;
+        var timeZoneParam;
+        var customRequestParams;
+        var params = {};
+        startParam = meta.startParam;
+        if (startParam == null) {
+            startParam = calendar.opt('startParam');
+        }
+        endParam = meta.endParam;
+        if (endParam == null) {
+            endParam = calendar.opt('endParam');
+        }
+        timeZoneParam = meta.timeZoneParam;
+        if (timeZoneParam == null) {
+            timeZoneParam = calendar.opt('timeZoneParam');
+        }
+        // retrieve any outbound GET/POST data from the options
+        if (typeof meta.extraParams === 'function') {
+            // supplied as a function that returns a key/value object
+            customRequestParams = meta.extraParams();
+        }
+        else {
+            // probably supplied as a straight key/value object
+            customRequestParams = meta.extraParams || {};
+        }
+        __assign(params, customRequestParams);
+        params[startParam] = dateEnv.formatIso(range.start);
+        params[endParam] = dateEnv.formatIso(range.end);
+        if (dateEnv.timeZone !== 'local') {
+            params[timeZoneParam] = dateEnv.timeZone;
+        }
+        return params;
+    }
+
+    var recurring = {
+        parse: function (rawEvent, leftoverProps, dateEnv) {
+            var createMarker = dateEnv.createMarker.bind(dateEnv);
+            var processors = {
+                daysOfWeek: null,
+                startTime: createDuration,
+                endTime: createDuration,
+                startRecur: createMarker,
+                endRecur: createMarker
+            };
+            var props = refineProps(rawEvent, processors, {}, leftoverProps);
+            var anyValid = false;
+            for (var propName in props) {
+                if (props[propName] != null) {
+                    anyValid = true;
+                    break;
+                }
+            }
+            if (anyValid) {
+                var duration = null;
+                if ('duration' in leftoverProps) {
+                    duration = createDuration(leftoverProps.duration);
+                    delete leftoverProps.duration;
+                }
+                if (!duration && props.startTime && props.endTime) {
+                    duration = subtractDurations(props.endTime, props.startTime);
+                }
+                return {
+                    allDayGuess: Boolean(!props.startTime && !props.endTime),
+                    duration: duration,
+                    typeData: props // doesn't need endTime anymore but oh well
+                };
+            }
+            return null;
+        },
+        expand: function (typeData, framingRange, dateEnv) {
+            var clippedFramingRange = intersectRanges(framingRange, { start: typeData.startRecur, end: typeData.endRecur });
+            if (clippedFramingRange) {
+                return expandRanges(typeData.daysOfWeek, typeData.startTime, clippedFramingRange, dateEnv);
+            }
+            else {
+                return [];
+            }
+        }
+    };
+    var SimpleRecurrencePlugin = createPlugin({
+        recurringTypes: [recurring]
+    });
+    function expandRanges(daysOfWeek, startTime, framingRange, dateEnv) {
+        var dowHash = daysOfWeek ? arrayToHash(daysOfWeek) : null;
+        var dayMarker = startOfDay(framingRange.start);
+        var endMarker = framingRange.end;
+        var instanceStarts = [];
+        while (dayMarker < endMarker) {
+            var instanceStart 
+            // if everyday, or this particular day-of-week
+            = void 0;
+            // if everyday, or this particular day-of-week
+            if (!dowHash || dowHash[dayMarker.getUTCDay()]) {
+                if (startTime) {
+                    instanceStart = dateEnv.add(dayMarker, startTime);
+                }
+                else {
+                    instanceStart = dayMarker;
+                }
+                instanceStarts.push(instanceStart);
+            }
+            dayMarker = addDays(dayMarker, 1);
+        }
+        return instanceStarts;
+    }
+
+    var DefaultOptionChangeHandlers = createPlugin({
+        optionChangeHandlers: {
+            events: function (events, calendar, deepEqual) {
+                handleEventSources([events], calendar, deepEqual);
+            },
+            eventSources: handleEventSources,
+            plugins: handlePlugins
+        }
+    });
+    function handleEventSources(inputs, calendar, deepEqual) {
+        var unfoundSources = hashValuesToArray(calendar.state.eventSources);
+        var newInputs = [];
+        for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) {
+            var input = inputs_1[_i];
+            var inputFound = false;
+            for (var i = 0; i < unfoundSources.length; i++) {
+                if (deepEqual(unfoundSources[i]._raw, input)) {
+                    unfoundSources.splice(i, 1); // delete
+                    inputFound = true;
+                    break;
+                }
+            }
+            if (!inputFound) {
+                newInputs.push(input);
+            }
+        }
+        for (var _a = 0, unfoundSources_1 = unfoundSources; _a < unfoundSources_1.length; _a++) {
+            var unfoundSource = unfoundSources_1[_a];
+            calendar.dispatch({
+                type: 'REMOVE_EVENT_SOURCE',
+                sourceId: unfoundSource.sourceId
+            });
+        }
+        for (var _b = 0, newInputs_1 = newInputs; _b < newInputs_1.length; _b++) {
+            var newInput = newInputs_1[_b];
+            calendar.addEventSource(newInput);
+        }
+    }
+    // shortcoming: won't remove plugins
+    function handlePlugins(inputs, calendar) {
+        calendar.addPluginInputs(inputs); // will gracefully handle duplicates
+    }
+
+    var config = {}; // TODO: make these options
+    var globalDefaults = {
+        defaultRangeSeparator: ' - ',
+        titleRangeSeparator: ' \u2013 ',
+        defaultTimedEventDuration: '01:00:00',
+        defaultAllDayEventDuration: { day: 1 },
+        forceEventDuration: false,
+        nextDayThreshold: '00:00:00',
+        // display
+        columnHeader: true,
+        defaultView: '',
+        aspectRatio: 1.35,
+        header: {
+            left: 'title',
+            center: '',
+            right: 'today prev,next'
+        },
+        weekends: true,
+        weekNumbers: false,
+        weekNumberCalculation: 'local',
+        editable: false,
+        // nowIndicator: false,
+        scrollTime: '06:00:00',
+        minTime: '00:00:00',
+        maxTime: '24:00:00',
+        showNonCurrentDates: true,
+        // event ajax
+        lazyFetching: true,
+        startParam: 'start',
+        endParam: 'end',
+        timeZoneParam: 'timeZone',
+        timeZone: 'local',
+        // allDayDefault: undefined,
+        // locale
+        locales: [],
+        locale: '',
+        // dir: will get this from the default locale
+        // buttonIcons: null,
+        // allows setting a min-height to the event segment to prevent short events overlapping each other
+        timeGridEventMinHeight: 0,
+        themeSystem: 'standard',
+        // eventResizableFromStart: false,
+        dragRevertDuration: 500,
+        dragScroll: true,
+        allDayMaintainDuration: false,
+        // selectable: false,
+        unselectAuto: true,
+        // selectMinDistance: 0,
+        dropAccept: '*',
+        eventOrder: 'start,-duration,allDay,title',
+        // ^ if start tie, longer events go before shorter. final tie-breaker is title text
+        // rerenderDelay: null,
+        eventLimit: false,
+        eventLimitClick: 'popover',
+        dayPopoverFormat: { month: 'long', day: 'numeric', year: 'numeric' },
+        handleWindowResize: true,
+        windowResizeDelay: 100,
+        longPressDelay: 1000,
+        eventDragMinDistance: 5 // only applies to mouse
+    };
+    var rtlDefaults = {
+        header: {
+            left: 'next,prev today',
+            center: '',
+            right: 'title'
+        },
+        buttonIcons: {
+            // TODO: make RTL support the responibility of the theme
+            prev: 'fc-icon-chevron-right',
+            next: 'fc-icon-chevron-left',
+            prevYear: 'fc-icon-chevrons-right',
+            nextYear: 'fc-icon-chevrons-left'
+        }
+    };
+    var complexOptions = [
+        'header',
+        'footer',
+        'buttonText',
+        'buttonIcons'
+    ];
+    // Merges an array of option objects into a single object
+    function mergeOptions(optionObjs) {
+        return mergeProps(optionObjs, complexOptions);
+    }
+    // TODO: move this stuff to a "plugin"-related file...
+    var INTERNAL_PLUGINS = [
+        ArrayEventSourcePlugin,
+        FuncEventSourcePlugin,
+        JsonFeedEventSourcePlugin,
+        SimpleRecurrencePlugin,
+        DefaultOptionChangeHandlers
+    ];
+    function refinePluginDefs(pluginInputs) {
+        var plugins = [];
+        for (var _i = 0, pluginInputs_1 = pluginInputs; _i < pluginInputs_1.length; _i++) {
+            var pluginInput = pluginInputs_1[_i];
+            if (typeof pluginInput === 'string') {
+                var globalName = 'FullCalendar' + capitaliseFirstLetter(pluginInput);
+                if (!window[globalName]) {
+                    console.warn('Plugin file not loaded for ' + pluginInput);
+                }
+                else {
+                    plugins.push(window[globalName].default); // is an ES6 module
+                }
+            }
+            else {
+                plugins.push(pluginInput);
+            }
+        }
+        return INTERNAL_PLUGINS.concat(plugins);
+    }
+
+    var RAW_EN_LOCALE = {
+        code: 'en',
+        week: {
+            dow: 0,
+            doy: 4 // 4 days need to be within the year to be considered the first week
+        },
+        dir: 'ltr',
+        buttonText: {
+            prev: 'prev',
+            next: 'next',
+            prevYear: 'prev year',
+            nextYear: 'next year',
+            year: 'year',
+            today: 'today',
+            month: 'month',
+            week: 'week',
+            day: 'day',
+            list: 'list'
+        },
+        weekLabel: 'W',
+        allDayText: 'all-day',
+        eventLimitText: 'more',
+        noEventsMessage: 'No events to display'
+    };
+    function parseRawLocales(explicitRawLocales) {
+        var defaultCode = explicitRawLocales.length > 0 ? explicitRawLocales[0].code : 'en';
+        var globalArray = window['FullCalendarLocalesAll'] || []; // from locales-all.js
+        var globalObject = window['FullCalendarLocales'] || {}; // from locales/*.js. keys are meaningless
+        var allRawLocales = globalArray.concat(// globalArray is low prio
+        hashValuesToArray(globalObject), // medium prio
+        explicitRawLocales // highest prio
+        );
+        var rawLocaleMap = {
+            en: RAW_EN_LOCALE // necessary?
+        };
+        for (var _i = 0, allRawLocales_1 = allRawLocales; _i < allRawLocales_1.length; _i++) {
+            var rawLocale = allRawLocales_1[_i];
+            rawLocaleMap[rawLocale.code] = rawLocale;
+        }
+        return {
+            map: rawLocaleMap,
+            defaultCode: defaultCode
+        };
+    }
+    function buildLocale(inputSingular, available) {
+        if (typeof inputSingular === 'object' && !Array.isArray(inputSingular)) {
+            return parseLocale(inputSingular.code, [inputSingular.code], inputSingular);
+        }
+        else {
+            return queryLocale(inputSingular, available);
+        }
+    }
+    function queryLocale(codeArg, available) {
+        var codes = [].concat(codeArg || []); // will convert to array
+        var raw = queryRawLocale(codes, available) || RAW_EN_LOCALE;
+        return parseLocale(codeArg, codes, raw);
+    }
+    function queryRawLocale(codes, available) {
+        for (var i = 0; i < codes.length; i++) {
+            var parts = codes[i].toLocaleLowerCase().split('-');
+            for (var j = parts.length; j > 0; j--) {
+                var simpleId = parts.slice(0, j).join('-');
+                if (available[simpleId]) {
+                    return available[simpleId];
+                }
+            }
+        }
+        return null;
+    }
+    function parseLocale(codeArg, codes, raw) {
+        var merged = mergeProps([RAW_EN_LOCALE, raw], ['buttonText']);
+        delete merged.code; // don't want this part of the options
+        var week = merged.week;
+        delete merged.week;
+        return {
+            codeArg: codeArg,
+            codes: codes,
+            week: week,
+            simpleNumberFormat: new Intl.NumberFormat(codeArg),
+            options: merged
+        };
+    }
+
+    var OptionsManager = /** @class */ (function () {
+        function OptionsManager(overrides) {
+            this.overrides = __assign({}, overrides); // make a copy
+            this.dynamicOverrides = {};
+            this.compute();
+        }
+        OptionsManager.prototype.mutate = function (updates, removals, isDynamic) {
+            var overrideHash = isDynamic ? this.dynamicOverrides : this.overrides;
+            __assign(overrideHash, updates);
+            for (var _i = 0, removals_1 = removals; _i < removals_1.length; _i++) {
+                var propName = removals_1[_i];
+                delete overrideHash[propName];
+            }
+            this.compute();
+        };
+        // Computes the flattened options hash for the calendar and assigns to `this.options`.
+        // Assumes this.overrides and this.dynamicOverrides have already been initialized.
+        OptionsManager.prototype.compute = function () {
+            // TODO: not a very efficient system
+            var locales = firstDefined(// explicit locale option given?
+            this.dynamicOverrides.locales, this.overrides.locales, globalDefaults.locales);
+            var locale = firstDefined(// explicit locales option given?
+            this.dynamicOverrides.locale, this.overrides.locale, globalDefaults.locale);
+            var available = parseRawLocales(locales);
+            var localeDefaults = buildLocale(locale || available.defaultCode, available.map).options;
+            var dir = firstDefined(// based on options computed so far, is direction RTL?
+            this.dynamicOverrides.dir, this.overrides.dir, localeDefaults.dir);
+            var dirDefaults = dir === 'rtl' ? rtlDefaults : {};
+            this.dirDefaults = dirDefaults;
+            this.localeDefaults = localeDefaults;
+            this.computed = mergeOptions([
+                globalDefaults,
+                dirDefaults,
+                localeDefaults,
+                this.overrides,
+                this.dynamicOverrides
+            ]);
+        };
+        return OptionsManager;
+    }());
+
+    var calendarSystemClassMap = {};
+    function registerCalendarSystem(name, theClass) {
+        calendarSystemClassMap[name] = theClass;
+    }
+    function createCalendarSystem(name) {
+        return new calendarSystemClassMap[name]();
+    }
+    var GregorianCalendarSystem = /** @class */ (function () {
+        function GregorianCalendarSystem() {
+        }
+        GregorianCalendarSystem.prototype.getMarkerYear = function (d) {
+            return d.getUTCFullYear();
+        };
+        GregorianCalendarSystem.prototype.getMarkerMonth = function (d) {
+            return d.getUTCMonth();
+        };
+        GregorianCalendarSystem.prototype.getMarkerDay = function (d) {
+            return d.getUTCDate();
+        };
+        GregorianCalendarSystem.prototype.arrayToMarker = function (arr) {
+            return arrayToUtcDate(arr);
+        };
+        GregorianCalendarSystem.prototype.markerToArray = function (marker) {
+            return dateToUtcArray(marker);
+        };
+        return GregorianCalendarSystem;
+    }());
+    registerCalendarSystem('gregory', GregorianCalendarSystem);
+
+    var ISO_RE = /^\s*(\d{4})(-(\d{2})(-(\d{2})([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/;
+    function parse(str) {
+        var m = ISO_RE.exec(str);
+        if (m) {
+            var marker = new Date(Date.UTC(Number(m[1]), m[3] ? Number(m[3]) - 1 : 0, Number(m[5] || 1), Number(m[7] || 0), Number(m[8] || 0), Number(m[10] || 0), m[12] ? Number('0.' + m[12]) * 1000 : 0));
+            if (isValidDate(marker)) {
+                var timeZoneOffset = null;
+                if (m[13]) {
+                    timeZoneOffset = (m[15] === '-' ? -1 : 1) * (Number(m[16] || 0) * 60 +
+                        Number(m[18] || 0));
+                }
+                return {
+                    marker: marker,
+                    isTimeUnspecified: !m[6],
+                    timeZoneOffset: timeZoneOffset
+                };
+            }
+        }
+        return null;
+    }
+
+    var DateEnv = /** @class */ (function () {
+        function DateEnv(settings) {
+            var timeZone = this.timeZone = settings.timeZone;
+            var isNamedTimeZone = timeZone !== 'local' && timeZone !== 'UTC';
+            if (settings.namedTimeZoneImpl && isNamedTimeZone) {
+                this.namedTimeZoneImpl = new settings.namedTimeZoneImpl(timeZone);
+            }
+            this.canComputeOffset = Boolean(!isNamedTimeZone || this.namedTimeZoneImpl);
+            this.calendarSystem = createCalendarSystem(settings.calendarSystem);
+            this.locale = settings.locale;
+            this.weekDow = settings.locale.week.dow;
+            this.weekDoy = settings.locale.week.doy;
+            if (settings.weekNumberCalculation === 'ISO') {
+                this.weekDow = 1;
+                this.weekDoy = 4;
+            }
+            if (typeof settings.firstDay === 'number') {
+                this.weekDow = settings.firstDay;
+            }
+            if (typeof settings.weekNumberCalculation === 'function') {
+                this.weekNumberFunc = settings.weekNumberCalculation;
+            }
+            this.weekLabel = settings.weekLabel != null ? settings.weekLabel : settings.locale.options.weekLabel;
+            this.cmdFormatter = settings.cmdFormatter;
+        }
+        // Creating / Parsing
+        DateEnv.prototype.createMarker = function (input) {
+            var meta = this.createMarkerMeta(input);
+            if (meta === null) {
+                return null;
+            }
+            return meta.marker;
+        };
+        DateEnv.prototype.createNowMarker = function () {
+            if (this.canComputeOffset) {
+                return this.timestampToMarker(new Date().valueOf());
+            }
+            else {
+                // if we can't compute the current date val for a timezone,
+                // better to give the current local date vals than UTC
+                return arrayToUtcDate(dateToLocalArray(new Date()));
+            }
+        };
+        DateEnv.prototype.createMarkerMeta = function (input) {
+            if (typeof input === 'string') {
+                return this.parse(input);
+            }
+            var marker = null;
+            if (typeof input === 'number') {
+                marker = this.timestampToMarker(input);
+            }
+            else if (input instanceof Date) {
+                input = input.valueOf();
+                if (!isNaN(input)) {
+                    marker = this.timestampToMarker(input);
+                }
+            }
+            else if (Array.isArray(input)) {
+                marker = arrayToUtcDate(input);
+            }
+            if (marker === null || !isValidDate(marker)) {
+                return null;
+            }
+            return { marker: marker, isTimeUnspecified: false, forcedTzo: null };
+        };
+        DateEnv.prototype.parse = function (s) {
+            var parts = parse(s);
+            if (parts === null) {
+                return null;
+            }
+            var marker = parts.marker;
+            var forcedTzo = null;
+            if (parts.timeZoneOffset !== null) {
+                if (this.canComputeOffset) {
+                    marker = this.timestampToMarker(marker.valueOf() - parts.timeZoneOffset * 60 * 1000);
+                }
+                else {
+                    forcedTzo = parts.timeZoneOffset;
+                }
+            }
+            return { marker: marker, isTimeUnspecified: parts.isTimeUnspecified, forcedTzo: forcedTzo };
+        };
+        // Accessors
+        DateEnv.prototype.getYear = function (marker) {
+            return this.calendarSystem.getMarkerYear(marker);
+        };
+        DateEnv.prototype.getMonth = function (marker) {
+            return this.calendarSystem.getMarkerMonth(marker);
+        };
+        // Adding / Subtracting
+        DateEnv.prototype.add = function (marker, dur) {
+            var a = this.calendarSystem.markerToArray(marker);
+            a[0] += dur.years;
+            a[1] += dur.months;
+            a[2] += dur.days;
+            a[6] += dur.milliseconds;
+            return this.calendarSystem.arrayToMarker(a);
+        };
+        DateEnv.prototype.subtract = function (marker, dur) {
+            var a = this.calendarSystem.markerToArray(marker);
+            a[0] -= dur.years;
+            a[1] -= dur.months;
+            a[2] -= dur.days;
+            a[6] -= dur.milliseconds;
+            return this.calendarSystem.arrayToMarker(a);
+        };
+        DateEnv.prototype.addYears = function (marker, n) {
+            var a = this.calendarSystem.markerToArray(marker);
+            a[0] += n;
+            return this.calendarSystem.arrayToMarker(a);
+        };
+        DateEnv.prototype.addMonths = function (marker, n) {
+            var a = this.calendarSystem.markerToArray(marker);
+            a[1] += n;
+            return this.calendarSystem.arrayToMarker(a);
+        };
+        // Diffing Whole Units
+        DateEnv.prototype.diffWholeYears = function (m0, m1) {
+            var calendarSystem = this.calendarSystem;
+            if (timeAsMs(m0) === timeAsMs(m1) &&
+                calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1) &&
+                calendarSystem.getMarkerMonth(m0) === calendarSystem.getMarkerMonth(m1)) {
+                return calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0);
+            }
+            return null;
+        };
+        DateEnv.prototype.diffWholeMonths = function (m0, m1) {
+            var calendarSystem = this.calendarSystem;
+            if (timeAsMs(m0) === timeAsMs(m1) &&
+                calendarSystem.getMarkerDay(m0) === calendarSystem.getMarkerDay(m1)) {
+                return (calendarSystem.getMarkerMonth(m1) - calendarSystem.getMarkerMonth(m0)) +
+                    (calendarSystem.getMarkerYear(m1) - calendarSystem.getMarkerYear(m0)) * 12;
+            }
+            return null;
+        };
+        // Range / Duration
+        DateEnv.prototype.greatestWholeUnit = function (m0, m1) {
+            var n = this.diffWholeYears(m0, m1);
+            if (n !== null) {
+                return { unit: 'year', value: n };
+            }
+            n = this.diffWholeMonths(m0, m1);
+            if (n !== null) {
+                return { unit: 'month', value: n };
+            }
+            n = diffWholeWeeks(m0, m1);
+            if (n !== null) {
+                return { unit: 'week', value: n };
+            }
+            n = diffWholeDays(m0, m1);
+            if (n !== null) {
+                return { unit: 'day', value: n };
+            }
+            n = diffHours(m0, m1);
+            if (isInt(n)) {
+                return { unit: 'hour', value: n };
+            }
+            n = diffMinutes(m0, m1);
+            if (isInt(n)) {
+                return { unit: 'minute', value: n };
+            }
+            n = diffSeconds(m0, m1);
+            if (isInt(n)) {
+                return { unit: 'second', value: n };
+            }
+            return { unit: 'millisecond', value: m1.valueOf() - m0.valueOf() };
+        };
+        DateEnv.prototype.countDurationsBetween = function (m0, m1, d) {
+            // TODO: can use greatestWholeUnit
+            var diff;
+            if (d.years) {
+                diff = this.diffWholeYears(m0, m1);
+                if (diff !== null) {
+                    return diff / asRoughYears(d);
+                }
+            }
+            if (d.months) {
+                diff = this.diffWholeMonths(m0, m1);
+                if (diff !== null) {
+                    return diff / asRoughMonths(d);
+                }
+            }
+            if (d.days) {
+                diff = diffWholeDays(m0, m1);
+                if (diff !== null) {
+                    return diff / asRoughDays(d);
+                }
+            }
+            return (m1.valueOf() - m0.valueOf()) / asRoughMs(d);
+        };
+        // Start-Of
+        DateEnv.prototype.startOf = function (m, unit) {
+            if (unit === 'year') {
+                return this.startOfYear(m);
+            }
+            else if (unit === 'month') {
+                return this.startOfMonth(m);
+            }
+            else if (unit === 'week') {
+                return this.startOfWeek(m);
+            }
+            else if (unit === 'day') {
+                return startOfDay(m);
+            }
+            else if (unit === 'hour') {
+                return startOfHour(m);
+            }
+            else if (unit === 'minute') {
+                return startOfMinute(m);
+            }
+            else if (unit === 'second') {
+                return startOfSecond(m);
+            }
+        };
+        DateEnv.prototype.startOfYear = function (m) {
+            return this.calendarSystem.arrayToMarker([
+                this.calendarSystem.getMarkerYear(m)
+            ]);
+        };
+        DateEnv.prototype.startOfMonth = function (m) {
+            return this.calendarSystem.arrayToMarker([
+                this.calendarSystem.getMarkerYear(m),
+                this.calendarSystem.getMarkerMonth(m)
+            ]);
+        };
+        DateEnv.prototype.startOfWeek = function (m) {
+            return this.calendarSystem.arrayToMarker([
+                this.calendarSystem.getMarkerYear(m),
+                this.calendarSystem.getMarkerMonth(m),
+                m.getUTCDate() - ((m.getUTCDay() - this.weekDow + 7) % 7)
+            ]);
+        };
+        // Week Number
+        DateEnv.prototype.computeWeekNumber = function (marker) {
+            if (this.weekNumberFunc) {
+                return this.weekNumberFunc(this.toDate(marker));
+            }
+            else {
+                return weekOfYear(marker, this.weekDow, this.weekDoy);
+            }
+        };
+        // TODO: choke on timeZoneName: long
+        DateEnv.prototype.format = function (marker, formatter, dateOptions) {
+            if (dateOptions === void 0) { dateOptions = {}; }
+            return formatter.format({
+                marker: marker,
+                timeZoneOffset: dateOptions.forcedTzo != null ?
+                    dateOptions.forcedTzo :
+                    this.offsetForMarker(marker)
+            }, this);
+        };
+        DateEnv.prototype.formatRange = function (start, end, formatter, dateOptions) {
+            if (dateOptions === void 0) { dateOptions = {}; }
+            if (dateOptions.isEndExclusive) {
+                end = addMs(end, -1);
+            }
+            return formatter.formatRange({
+                marker: start,
+                timeZoneOffset: dateOptions.forcedStartTzo != null ?
+                    dateOptions.forcedStartTzo :
+                    this.offsetForMarker(start)
+            }, {
+                marker: end,
+                timeZoneOffset: dateOptions.forcedEndTzo != null ?
+                    dateOptions.forcedEndTzo :
+                    this.offsetForMarker(end)
+            }, this);
+        };
+        DateEnv.prototype.formatIso = function (marker, extraOptions) {
+            if (extraOptions === void 0) { extraOptions = {}; }
+            var timeZoneOffset = null;
+            if (!extraOptions.omitTimeZoneOffset) {
+                if (extraOptions.forcedTzo != null) {
+                    timeZoneOffset = extraOptions.forcedTzo;
+                }
+                else {
+                    timeZoneOffset = this.offsetForMarker(marker);
+                }
+            }
+            return buildIsoString(marker, timeZoneOffset, extraOptions.omitTime);
+        };
+        // TimeZone
+        DateEnv.prototype.timestampToMarker = function (ms) {
+            if (this.timeZone === 'local') {
+                return arrayToUtcDate(dateToLocalArray(new Date(ms)));
+            }
+            else if (this.timeZone === 'UTC' || !this.namedTimeZoneImpl) {
+                return new Date(ms);
+            }
+            else {
+                return arrayToUtcDate(this.namedTimeZoneImpl.timestampToArray(ms));
+            }
+        };
+        DateEnv.prototype.offsetForMarker = function (m) {
+            if (this.timeZone === 'local') {
+                return -arrayToLocalDate(dateToUtcArray(m)).getTimezoneOffset(); // convert "inverse" offset to "normal" offset
+            }
+            else if (this.timeZone === 'UTC') {
+                return 0;
+            }
+            else if (this.namedTimeZoneImpl) {
+                return this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m));
+            }
+            return null;
+        };
+        // Conversion
+        DateEnv.prototype.toDate = function (m, forcedTzo) {
+            if (this.timeZone === 'local') {
+                return arrayToLocalDate(dateToUtcArray(m));
+            }
+            else if (this.timeZone === 'UTC') {
+                return new Date(m.valueOf()); // make sure it's a copy
+            }
+            else if (!this.namedTimeZoneImpl) {
+                return new Date(m.valueOf() - (forcedTzo || 0));
+            }
+            else {
+                return new Date(m.valueOf() -
+                    this.namedTimeZoneImpl.offsetForArray(dateToUtcArray(m)) * 1000 * 60 // convert minutes -> ms
+                );
+            }
+        };
+        return DateEnv;
+    }());
+
+    var SIMPLE_SOURCE_PROPS = {
+        id: String,
+        allDayDefault: Boolean,
+        eventDataTransform: Function,
+        success: Function,
+        failure: Function
+    };
+    var uid$2 = 0;
+    function doesSourceNeedRange(eventSource, calendar) {
+        var defs = calendar.pluginSystem.hooks.eventSourceDefs;
+        return !defs[eventSource.sourceDefId].ignoreRange;
+    }
+    function parseEventSource(raw, calendar) {
+        var defs = calendar.pluginSystem.hooks.eventSourceDefs;
+        for (var i = defs.length - 1; i >= 0; i--) { // later-added plugins take precedence
+            var def = defs[i];
+            var meta = def.parseMeta(raw);
+            if (meta) {
+                var res = parseEventSourceProps(typeof raw === 'object' ? raw : {}, meta, i, calendar);
+                res._raw = raw;
+                return res;
+            }
+        }
+        return null;
+    }
+    function parseEventSourceProps(raw, meta, sourceDefId, calendar) {
+        var leftovers0 = {};
+        var props = refineProps(raw, SIMPLE_SOURCE_PROPS, {}, leftovers0);
+        var leftovers1 = {};
+        var ui = processUnscopedUiProps(leftovers0, calendar, leftovers1);
+        props.isFetching = false;
+        props.latestFetchId = '';
+        props.fetchRange = null;
+        props.publicId = String(raw.id || '');
+        props.sourceId = String(uid$2++);
+        props.sourceDefId = sourceDefId;
+        props.meta = meta;
+        props.ui = ui;
+        props.extendedProps = leftovers1;
+        return props;
+    }
+
+    function reduceEventSources (eventSources, action, dateProfile, calendar) {
+        switch (action.type) {
+            case 'ADD_EVENT_SOURCES': // already parsed
+                return addSources(eventSources, action.sources, dateProfile ? dateProfile.activeRange : null, calendar);
+            case 'REMOVE_EVENT_SOURCE':
+                return removeSource(eventSources, action.sourceId);
+            case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
+            case 'NEXT':
+            case 'SET_DATE':
+            case 'SET_VIEW_TYPE':
+                if (dateProfile) {
+                    return fetchDirtySources(eventSources, dateProfile.activeRange, calendar);
+                }
+                else {
+                    return eventSources;
+                }
+            case 'FETCH_EVENT_SOURCES':
+            case 'CHANGE_TIMEZONE':
+                return fetchSourcesByIds(eventSources, action.sourceIds ?
+                    arrayToHash(action.sourceIds) :
+                    excludeStaticSources(eventSources, calendar), dateProfile ? dateProfile.activeRange : null, calendar);
+            case 'RECEIVE_EVENTS':
+            case 'RECEIVE_EVENT_ERROR':
+                return receiveResponse(eventSources, action.sourceId, action.fetchId, action.fetchRange);
+            case 'REMOVE_ALL_EVENT_SOURCES':
+                return {};
+            default:
+                return eventSources;
+        }
+    }
+    var uid$3 = 0;
+    function addSources(eventSourceHash, sources, fetchRange, calendar) {
+        var hash = {};
+        for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) {
+            var source = sources_1[_i];
+            hash[source.sourceId] = source;
+        }
+        if (fetchRange) {
+            hash = fetchDirtySources(hash, fetchRange, calendar);
+        }
+        return __assign({}, eventSourceHash, hash);
+    }
+    function removeSource(eventSourceHash, sourceId) {
+        return filterHash(eventSourceHash, function (eventSource) {
+            return eventSource.sourceId !== sourceId;
+        });
+    }
+    function fetchDirtySources(sourceHash, fetchRange, calendar) {
+        return fetchSourcesByIds(sourceHash, filterHash(sourceHash, function (eventSource) {
+            return isSourceDirty(eventSource, fetchRange, calendar);
+        }), fetchRange, calendar);
+    }
+    function isSourceDirty(eventSource, fetchRange, calendar) {
+        if (!doesSourceNeedRange(eventSource, calendar)) {
+            return !eventSource.latestFetchId;
+        }
+        else {
+            return !calendar.opt('lazyFetching') ||
+                !eventSource.fetchRange ||
+                fetchRange.start < eventSource.fetchRange.start ||
+                fetchRange.end > eventSource.fetchRange.end;
+        }
+    }
+    function fetchSourcesByIds(prevSources, sourceIdHash, fetchRange, calendar) {
+        var nextSources = {};
+        for (var sourceId in prevSources) {
+            var source = prevSources[sourceId];
+            if (sourceIdHash[sourceId]) {
+                nextSources[sourceId] = fetchSource(source, fetchRange, calendar);
+            }
+            else {
+                nextSources[sourceId] = source;
+            }
+        }
+        return nextSources;
+    }
+    function fetchSource(eventSource, fetchRange, calendar) {
+        var sourceDef = calendar.pluginSystem.hooks.eventSourceDefs[eventSource.sourceDefId];
+        var fetchId = String(uid$3++);
+        sourceDef.fetch({
+            eventSource: eventSource,
+            calendar: calendar,
+            range: fetchRange
+        }, function (res) {
+            var rawEvents = res.rawEvents;
+            var calSuccess = calendar.opt('eventSourceSuccess');
+            var calSuccessRes;
+            var sourceSuccessRes;
+            if (eventSource.success) {
+                sourceSuccessRes = eventSource.success(rawEvents, res.xhr);
+            }
+            if (calSuccess) {
+                calSuccessRes = calSuccess(rawEvents, res.xhr);
+            }
+            rawEvents = sourceSuccessRes || calSuccessRes || rawEvents;
+            calendar.dispatch({
+                type: 'RECEIVE_EVENTS',
+                sourceId: eventSource.sourceId,
+                fetchId: fetchId,
+                fetchRange: fetchRange,
+                rawEvents: rawEvents
+            });
+        }, function (error) {
+            var callFailure = calendar.opt('eventSourceFailure');
+            console.warn(error.message, error);
+            if (eventSource.failure) {
+                eventSource.failure(error);
+            }
+            if (callFailure) {
+                callFailure(error);
+            }
+            calendar.dispatch({
+                type: 'RECEIVE_EVENT_ERROR',
+                sourceId: eventSource.sourceId,
+                fetchId: fetchId,
+                fetchRange: fetchRange,
+                error: error
+            });
+        });
+        return __assign({}, eventSource, { isFetching: true, latestFetchId: fetchId });
+    }
+    function receiveResponse(sourceHash, sourceId, fetchId, fetchRange) {
+        var _a;
+        var eventSource = sourceHash[sourceId];
+        if (eventSource && // not already removed
+            fetchId === eventSource.latestFetchId) {
+            return __assign({}, sourceHash, (_a = {}, _a[sourceId] = __assign({}, eventSource, { isFetching: false, fetchRange: fetchRange }), _a));
+        }
+        return sourceHash;
+    }
+    function excludeStaticSources(eventSources, calendar) {
+        return filterHash(eventSources, function (eventSource) {
+            return doesSourceNeedRange(eventSource, calendar);
+        });
+    }
+
+    var DateProfileGenerator = /** @class */ (function () {
+        function DateProfileGenerator(viewSpec, calendar) {
+            this.viewSpec = viewSpec;
+            this.options = viewSpec.options;
+            this.dateEnv = calendar.dateEnv;
+            this.calendar = calendar;
+            this.initHiddenDays();
+        }
+        /* Date Range Computation
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Builds a structure with info about what the dates/ranges will be for the "prev" view.
+        DateProfileGenerator.prototype.buildPrev = function (currentDateProfile, currentDate) {
+            var dateEnv = this.dateEnv;
+            var prevDate = dateEnv.subtract(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
+            currentDateProfile.dateIncrement);
+            return this.build(prevDate, -1);
+        };
+        // Builds a structure with info about what the dates/ranges will be for the "next" view.
+        DateProfileGenerator.prototype.buildNext = function (currentDateProfile, currentDate) {
+            var dateEnv = this.dateEnv;
+            var nextDate = dateEnv.add(dateEnv.startOf(currentDate, currentDateProfile.currentRangeUnit), // important for start-of-month
+            currentDateProfile.dateIncrement);
+            return this.build(nextDate, 1);
+        };
+        // Builds a structure holding dates/ranges for rendering around the given date.
+        // Optional direction param indicates whether the date is being incremented/decremented
+        // from its previous value. decremented = -1, incremented = 1 (default).
+        DateProfileGenerator.prototype.build = function (currentDate, direction, forceToValid) {
+            if (forceToValid === void 0) { forceToValid = false; }
+            var validRange;
+            var minTime = null;
+            var maxTime = null;
+            var currentInfo;
+            var isRangeAllDay;
+            var renderRange;
+            var activeRange;
+            var isValid;
+            validRange = this.buildValidRange();
+            validRange = this.trimHiddenDays(validRange);
+            if (forceToValid) {
+                currentDate = constrainMarkerToRange(currentDate, validRange);
+            }
+            currentInfo = this.buildCurrentRangeInfo(currentDate, direction);
+            isRangeAllDay = /^(year|month|week|day)$/.test(currentInfo.unit);
+            renderRange = this.buildRenderRange(this.trimHiddenDays(currentInfo.range), currentInfo.unit, isRangeAllDay);
+            renderRange = this.trimHiddenDays(renderRange);
+            activeRange = renderRange;
+            if (!this.options.showNonCurrentDates) {
+                activeRange = intersectRanges(activeRange, currentInfo.range);
+            }
+            minTime = createDuration(this.options.minTime);
+            maxTime = createDuration(this.options.maxTime);
+            activeRange = this.adjustActiveRange(activeRange, minTime, maxTime);
+            activeRange = intersectRanges(activeRange, validRange); // might return null
+            // it's invalid if the originally requested date is not contained,
+            // or if the range is completely outside of the valid range.
+            isValid = rangesIntersect(currentInfo.range, validRange);
+            return {
+                // constraint for where prev/next operations can go and where events can be dragged/resized to.
+                // an object with optional start and end properties.
+                validRange: validRange,
+                // range the view is formally responsible for.
+                // for example, a month view might have 1st-31st, excluding padded dates
+                currentRange: currentInfo.range,
+                // name of largest unit being displayed, like "month" or "week"
+                currentRangeUnit: currentInfo.unit,
+                isRangeAllDay: isRangeAllDay,
+                // dates that display events and accept drag-n-drop
+                // will be `null` if no dates accept events
+                activeRange: activeRange,
+                // date range with a rendered skeleton
+                // includes not-active days that need some sort of DOM
+                renderRange: renderRange,
+                // Duration object that denotes the first visible time of any given day
+                minTime: minTime,
+                // Duration object that denotes the exclusive visible end time of any given day
+                maxTime: maxTime,
+                isValid: isValid,
+                // how far the current date will move for a prev/next operation
+                dateIncrement: this.buildDateIncrement(currentInfo.duration)
+                // pass a fallback (might be null) ^
+            };
+        };
+        // Builds an object with optional start/end properties.
+        // Indicates the minimum/maximum dates to display.
+        // not responsible for trimming hidden days.
+        DateProfileGenerator.prototype.buildValidRange = function () {
+            return this.getRangeOption('validRange', this.calendar.getNow()) ||
+                { start: null, end: null }; // completely open-ended
+        };
+        // Builds a structure with info about the "current" range, the range that is
+        // highlighted as being the current month for example.
+        // See build() for a description of `direction`.
+        // Guaranteed to have `range` and `unit` properties. `duration` is optional.
+        DateProfileGenerator.prototype.buildCurrentRangeInfo = function (date, direction) {
+            var _a = this, viewSpec = _a.viewSpec, dateEnv = _a.dateEnv;
+            var duration = null;
+            var unit = null;
+            var range = null;
+            var dayCount;
+            if (viewSpec.duration) {
+                duration = viewSpec.duration;
+                unit = viewSpec.durationUnit;
+                range = this.buildRangeFromDuration(date, direction, duration, unit);
+            }
+            else if ((dayCount = this.options.dayCount)) {
+                unit = 'day';
+                range = this.buildRangeFromDayCount(date, direction, dayCount);
+            }
+            else if ((range = this.buildCustomVisibleRange(date))) {
+                unit = dateEnv.greatestWholeUnit(range.start, range.end).unit;
+            }
+            else {
+                duration = this.getFallbackDuration();
+                unit = greatestDurationDenominator(duration).unit;
+                range = this.buildRangeFromDuration(date, direction, duration, unit);
+            }
+            return { duration: duration, unit: unit, range: range };
+        };
+        DateProfileGenerator.prototype.getFallbackDuration = function () {
+            return createDuration({ day: 1 });
+        };
+        // Returns a new activeRange to have time values (un-ambiguate)
+        // minTime or maxTime causes the range to expand.
+        DateProfileGenerator.prototype.adjustActiveRange = function (range, minTime, maxTime) {
+            var dateEnv = this.dateEnv;
+            var start = range.start;
+            var end = range.end;
+            if (this.viewSpec.class.prototype.usesMinMaxTime) {
+                // expand active range if minTime is negative (why not when positive?)
+                if (asRoughDays(minTime) < 0) {
+                    start = startOfDay(start); // necessary?
+                    start = dateEnv.add(start, minTime);
+                }
+                // expand active range if maxTime is beyond one day (why not when positive?)
+                if (asRoughDays(maxTime) > 1) {
+                    end = startOfDay(end); // necessary?
+                    end = addDays(end, -1);
+                    end = dateEnv.add(end, maxTime);
+                }
+            }
+            return { start: start, end: end };
+        };
+        // Builds the "current" range when it is specified as an explicit duration.
+        // `unit` is the already-computed greatestDurationDenominator unit of duration.
+        DateProfileGenerator.prototype.buildRangeFromDuration = function (date, direction, duration, unit) {
+            var dateEnv = this.dateEnv;
+            var alignment = this.options.dateAlignment;
+            var dateIncrementInput;
+            var dateIncrementDuration;
+            var start;
+            var end;
+            var res;
+            // compute what the alignment should be
+            if (!alignment) {
+                dateIncrementInput = this.options.dateIncrement;
+                if (dateIncrementInput) {
+                    dateIncrementDuration = createDuration(dateIncrementInput);
+                    // use the smaller of the two units
+                    if (asRoughMs(dateIncrementDuration) < asRoughMs(duration)) {
+                        alignment = greatestDurationDenominator(dateIncrementDuration, !getWeeksFromInput(dateIncrementInput)).unit;
+                    }
+                    else {
+                        alignment = unit;
+                    }
+                }
+                else {
+                    alignment = unit;
+                }
+            }
+            // if the view displays a single day or smaller
+            if (asRoughDays(duration) <= 1) {
+                if (this.isHiddenDay(start)) {
+                    start = this.skipHiddenDays(start, direction);
+                    start = startOfDay(start);
+                }
+            }
+            function computeRes() {
+                start = dateEnv.startOf(date, alignment);
+                end = dateEnv.add(start, duration);
+                res = { start: start, end: end };
+            }
+            computeRes();
+            // if range is completely enveloped by hidden days, go past the hidden days
+            if (!this.trimHiddenDays(res)) {
+                date = this.skipHiddenDays(date, direction);
+                computeRes();
+            }
+            return res;
+        };
+        // Builds the "current" range when a dayCount is specified.
+        DateProfileGenerator.prototype.buildRangeFromDayCount = function (date, direction, dayCount) {
+            var dateEnv = this.dateEnv;
+            var customAlignment = this.options.dateAlignment;
+            var runningCount = 0;
+            var start = date;
+            var end;
+            if (customAlignment) {
+                start = dateEnv.startOf(start, customAlignment);
+            }
+            start = startOfDay(start);
+            start = this.skipHiddenDays(start, direction);
+            end = start;
+            do {
+                end = addDays(end, 1);
+                if (!this.isHiddenDay(end)) {
+                    runningCount++;
+                }
+            } while (runningCount < dayCount);
+            return { start: start, end: end };
+        };
+        // Builds a normalized range object for the "visible" range,
+        // which is a way to define the currentRange and activeRange at the same time.
+        DateProfileGenerator.prototype.buildCustomVisibleRange = function (date) {
+            var dateEnv = this.dateEnv;
+            var visibleRange = this.getRangeOption('visibleRange', dateEnv.toDate(date));
+            if (visibleRange && (visibleRange.start == null || visibleRange.end == null)) {
+                return null;
+            }
+            return visibleRange;
+        };
+        // Computes the range that will represent the element/cells for *rendering*,
+        // but which may have voided days/times.
+        // not responsible for trimming hidden days.
+        DateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) {
+            return currentRange;
+        };
+        // Compute the duration value that should be added/substracted to the current date
+        // when a prev/next operation happens.
+        DateProfileGenerator.prototype.buildDateIncrement = function (fallback) {
+            var dateIncrementInput = this.options.dateIncrement;
+            var customAlignment;
+            if (dateIncrementInput) {
+                return createDuration(dateIncrementInput);
+            }
+            else if ((customAlignment = this.options.dateAlignment)) {
+                return createDuration(1, customAlignment);
+            }
+            else if (fallback) {
+                return fallback;
+            }
+            else {
+                return createDuration({ days: 1 });
+            }
+        };
+        // Arguments after name will be forwarded to a hypothetical function value
+        // WARNING: passed-in arguments will be given to generator functions as-is and can cause side-effects.
+        // Always clone your objects if you fear mutation.
+        DateProfileGenerator.prototype.getRangeOption = function (name) {
+            var otherArgs = [];
+            for (var _i = 1; _i < arguments.length; _i++) {
+                otherArgs[_i - 1] = arguments[_i];
+            }
+            var val = this.options[name];
+            if (typeof val === 'function') {
+                val = val.apply(null, otherArgs);
+            }
+            if (val) {
+                val = parseRange(val, this.dateEnv);
+            }
+            if (val) {
+                val = computeVisibleDayRange(val);
+            }
+            return val;
+        };
+        /* Hidden Days
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Initializes internal variables related to calculating hidden days-of-week
+        DateProfileGenerator.prototype.initHiddenDays = function () {
+            var hiddenDays = this.options.hiddenDays || []; // array of day-of-week indices that are hidden
+            var isHiddenDayHash = []; // is the day-of-week hidden? (hash with day-of-week-index -> bool)
+            var dayCnt = 0;
+            var i;
+            if (this.options.weekends === false) {
+                hiddenDays.push(0, 6); // 0=sunday, 6=saturday
+            }
+            for (i = 0; i < 7; i++) {
+                if (!(isHiddenDayHash[i] = hiddenDays.indexOf(i) !== -1)) {
+                    dayCnt++;
+                }
+            }
+            if (!dayCnt) {
+                throw new Error('invalid hiddenDays'); // all days were hidden? bad.
+            }
+            this.isHiddenDayHash = isHiddenDayHash;
+        };
+        // Remove days from the beginning and end of the range that are computed as hidden.
+        // If the whole range is trimmed off, returns null
+        DateProfileGenerator.prototype.trimHiddenDays = function (range) {
+            var start = range.start;
+            var end = range.end;
+            if (start) {
+                start = this.skipHiddenDays(start);
+            }
+            if (end) {
+                end = this.skipHiddenDays(end, -1, true);
+            }
+            if (start == null || end == null || start < end) {
+                return { start: start, end: end };
+            }
+            return null;
+        };
+        // Is the current day hidden?
+        // `day` is a day-of-week index (0-6), or a Date (used for UTC)
+        DateProfileGenerator.prototype.isHiddenDay = function (day) {
+            if (day instanceof Date) {
+                day = day.getUTCDay();
+            }
+            return this.isHiddenDayHash[day];
+        };
+        // Incrementing the current day until it is no longer a hidden day, returning a copy.
+        // DOES NOT CONSIDER validRange!
+        // If the initial value of `date` is not a hidden day, don't do anything.
+        // Pass `isExclusive` as `true` if you are dealing with an end date.
+        // `inc` defaults to `1` (increment one day forward each time)
+        DateProfileGenerator.prototype.skipHiddenDays = function (date, inc, isExclusive) {
+            if (inc === void 0) { inc = 1; }
+            if (isExclusive === void 0) { isExclusive = false; }
+            while (this.isHiddenDayHash[(date.getUTCDay() + (isExclusive ? inc : 0) + 7) % 7]) {
+                date = addDays(date, inc);
+            }
+            return date;
+        };
+        return DateProfileGenerator;
+    }());
+    // TODO: find a way to avoid comparing DateProfiles. it's tedious
+    function isDateProfilesEqual(p0, p1) {
+        return rangesEqual(p0.validRange, p1.validRange) &&
+            rangesEqual(p0.activeRange, p1.activeRange) &&
+            rangesEqual(p0.renderRange, p1.renderRange) &&
+            durationsEqual(p0.minTime, p1.minTime) &&
+            durationsEqual(p0.maxTime, p1.maxTime);
+        /*
+        TODO: compare more?
+          currentRange: DateRange
+          currentRangeUnit: string
+          isRangeAllDay: boolean
+          isValid: boolean
+          dateIncrement: Duration
+        */
+    }
+
+    function reduce (state, action, calendar) {
+        var viewType = reduceViewType(state.viewType, action);
+        var dateProfile = reduceDateProfile(state.dateProfile, action, state.currentDate, viewType, calendar);
+        var eventSources = reduceEventSources(state.eventSources, action, dateProfile, calendar);
+        var nextState = __assign({}, state, { viewType: viewType,
+            dateProfile: dateProfile, currentDate: reduceCurrentDate(state.currentDate, action, dateProfile), eventSources: eventSources, eventStore: reduceEventStore(state.eventStore, action, eventSources, dateProfile, calendar), dateSelection: reduceDateSelection(state.dateSelection, action, calendar), eventSelection: reduceSelectedEvent(state.eventSelection, action), eventDrag: reduceEventDrag(state.eventDrag, action, eventSources, calendar), eventResize: reduceEventResize(state.eventResize, action, eventSources, calendar), eventSourceLoadingLevel: computeLoadingLevel(eventSources), loadingLevel: computeLoadingLevel(eventSources) });
+        for (var _i = 0, _a = calendar.pluginSystem.hooks.reducers; _i < _a.length; _i++) {
+            var reducerFunc = _a[_i];
+            nextState = reducerFunc(nextState, action, calendar);
+        }
+        // console.log(action.type, nextState)
+        return nextState;
+    }
+    function reduceViewType(currentViewType, action) {
+        switch (action.type) {
+            case 'SET_VIEW_TYPE':
+                return action.viewType;
+            default:
+                return currentViewType;
+        }
+    }
+    function reduceDateProfile(currentDateProfile, action, currentDate, viewType, calendar) {
+        var newDateProfile;
+        switch (action.type) {
+            case 'PREV':
+                newDateProfile = calendar.dateProfileGenerators[viewType].buildPrev(currentDateProfile, currentDate);
+                break;
+            case 'NEXT':
+                newDateProfile = calendar.dateProfileGenerators[viewType].buildNext(currentDateProfile, currentDate);
+                break;
+            case 'SET_DATE':
+                if (!currentDateProfile.activeRange ||
+                    !rangeContainsMarker(currentDateProfile.currentRange, action.dateMarker)) {
+                    newDateProfile = calendar.dateProfileGenerators[viewType].build(action.dateMarker, undefined, true // forceToValid
+                    );
+                }
+                break;
+            case 'SET_VIEW_TYPE':
+                var generator = calendar.dateProfileGenerators[viewType];
+                if (!generator) {
+                    throw new Error(viewType ?
+                        'The FullCalendar view "' + viewType + '" does not exist. Make sure your plugins are loaded correctly.' :
+                        'No available FullCalendar view plugins.');
+                }
+                newDateProfile = generator.build(action.dateMarker || currentDate, undefined, true // forceToValid
+                );
+                break;
+        }
+        if (newDateProfile &&
+            newDateProfile.isValid &&
+            !(currentDateProfile && isDateProfilesEqual(currentDateProfile, newDateProfile))) {
+            return newDateProfile;
+        }
+        else {
+            return currentDateProfile;
+        }
+    }
+    function reduceCurrentDate(currentDate, action, dateProfile) {
+        switch (action.type) {
+            case 'PREV':
+            case 'NEXT':
+                if (!rangeContainsMarker(dateProfile.currentRange, currentDate)) {
+                    return dateProfile.currentRange.start;
+                }
+                else {
+                    return currentDate;
+                }
+            case 'SET_DATE':
+            case 'SET_VIEW_TYPE':
+                var newDate = action.dateMarker || currentDate;
+                if (dateProfile.activeRange && !rangeContainsMarker(dateProfile.activeRange, newDate)) {
+                    return dateProfile.currentRange.start;
+                }
+                else {
+                    return newDate;
+                }
+            default:
+                return currentDate;
+        }
+    }
+    function reduceDateSelection(currentSelection, action, calendar) {
+        switch (action.type) {
+            case 'SELECT_DATES':
+                return action.selection;
+            case 'UNSELECT_DATES':
+                return null;
+            default:
+                return currentSelection;
+        }
+    }
+    function reduceSelectedEvent(currentInstanceId, action) {
+        switch (action.type) {
+            case 'SELECT_EVENT':
+                return action.eventInstanceId;
+            case 'UNSELECT_EVENT':
+                return '';
+            default:
+                return currentInstanceId;
+        }
+    }
+    function reduceEventDrag(currentDrag, action, sources, calendar) {
+        switch (action.type) {
+            case 'SET_EVENT_DRAG':
+                var newDrag = action.state;
+                return {
+                    affectedEvents: newDrag.affectedEvents,
+                    mutatedEvents: newDrag.mutatedEvents,
+                    isEvent: newDrag.isEvent,
+                    origSeg: newDrag.origSeg
+                };
+            case 'UNSET_EVENT_DRAG':
+                return null;
+            default:
+                return currentDrag;
+        }
+    }
+    function reduceEventResize(currentResize, action, sources, calendar) {
+        switch (action.type) {
+            case 'SET_EVENT_RESIZE':
+                var newResize = action.state;
+                return {
+                    affectedEvents: newResize.affectedEvents,
+                    mutatedEvents: newResize.mutatedEvents,
+                    isEvent: newResize.isEvent,
+                    origSeg: newResize.origSeg
+                };
+            case 'UNSET_EVENT_RESIZE':
+                return null;
+            default:
+                return currentResize;
+        }
+    }
+    function computeLoadingLevel(eventSources) {
+        var cnt = 0;
+        for (var sourceId in eventSources) {
+            if (eventSources[sourceId].isFetching) {
+                cnt++;
+            }
+        }
+        return cnt;
+    }
+
+    var STANDARD_PROPS = {
+        start: null,
+        end: null,
+        allDay: Boolean
+    };
+    function parseDateSpan(raw, dateEnv, defaultDuration) {
+        var span = parseOpenDateSpan(raw, dateEnv);
+        var range = span.range;
+        if (!range.start) {
+            return null;
+        }
+        if (!range.end) {
+            if (defaultDuration == null) {
+                return null;
+            }
+            else {
+                range.end = dateEnv.add(range.start, defaultDuration);
+            }
+        }
+        return span;
+    }
+    /*
+    TODO: somehow combine with parseRange?
+    Will return null if the start/end props were present but parsed invalidly.
+    */
+    function parseOpenDateSpan(raw, dateEnv) {
+        var leftovers = {};
+        var standardProps = refineProps(raw, STANDARD_PROPS, {}, leftovers);
+        var startMeta = standardProps.start ? dateEnv.createMarkerMeta(standardProps.start) : null;
+        var endMeta = standardProps.end ? dateEnv.createMarkerMeta(standardProps.end) : null;
+        var allDay = standardProps.allDay;
+        if (allDay == null) {
+            allDay = (startMeta && startMeta.isTimeUnspecified) &&
+                (!endMeta || endMeta.isTimeUnspecified);
+        }
+        // use this leftover object as the selection object
+        leftovers.range = {
+            start: startMeta ? startMeta.marker : null,
+            end: endMeta ? endMeta.marker : null
+        };
+        leftovers.allDay = allDay;
+        return leftovers;
+    }
+    function isDateSpansEqual(span0, span1) {
+        return rangesEqual(span0.range, span1.range) &&
+            span0.allDay === span1.allDay &&
+            isSpanPropsEqual(span0, span1);
+    }
+    // the NON-DATE-RELATED props
+    function isSpanPropsEqual(span0, span1) {
+        for (var propName in span1) {
+            if (propName !== 'range' && propName !== 'allDay') {
+                if (span0[propName] !== span1[propName]) {
+                    return false;
+                }
+            }
+        }
+        // are there any props that span0 has that span1 DOESN'T have?
+        // both have range/allDay, so no need to special-case.
+        for (var propName in span0) {
+            if (!(propName in span1)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    function buildDateSpanApi(span, dateEnv) {
+        return {
+            start: dateEnv.toDate(span.range.start),
+            end: dateEnv.toDate(span.range.end),
+            startStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
+            endStr: dateEnv.formatIso(span.range.end, { omitTime: span.allDay }),
+            allDay: span.allDay
+        };
+    }
+    function buildDatePointApi(span, dateEnv) {
+        return {
+            date: dateEnv.toDate(span.range.start),
+            dateStr: dateEnv.formatIso(span.range.start, { omitTime: span.allDay }),
+            allDay: span.allDay
+        };
+    }
+    function fabricateEventRange(dateSpan, eventUiBases, calendar) {
+        var def = parseEventDef({ editable: false }, '', // sourceId
+        dateSpan.allDay, true, // hasEnd
+        calendar);
+        return {
+            def: def,
+            ui: compileEventUi(def, eventUiBases),
+            instance: createEventInstance(def.defId, dateSpan.range),
+            range: dateSpan.range,
+            isStart: true,
+            isEnd: true
+        };
+    }
+
+    function compileViewDefs(defaultConfigs, overrideConfigs) {
+        var hash = {};
+        var viewType;
+        for (viewType in defaultConfigs) {
+            ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs);
+        }
+        for (viewType in overrideConfigs) {
+            ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs);
+        }
+        return hash;
+    }
+    function ensureViewDef(viewType, hash, defaultConfigs, overrideConfigs) {
+        if (hash[viewType]) {
+            return hash[viewType];
+        }
+        var viewDef = buildViewDef(viewType, hash, defaultConfigs, overrideConfigs);
+        if (viewDef) {
+            hash[viewType] = viewDef;
+        }
+        return viewDef;
+    }
+    function buildViewDef(viewType, hash, defaultConfigs, overrideConfigs) {
+        var defaultConfig = defaultConfigs[viewType];
+        var overrideConfig = overrideConfigs[viewType];
+        var queryProp = function (name) {
+            return (defaultConfig && defaultConfig[name] !== null) ? defaultConfig[name] :
+                ((overrideConfig && overrideConfig[name] !== null) ? overrideConfig[name] : null);
+        };
+        var theClass = queryProp('class');
+        var superType = queryProp('superType');
+        if (!superType && theClass) {
+            superType =
+                findViewNameBySubclass(theClass, overrideConfigs) ||
+                    findViewNameBySubclass(theClass, defaultConfigs);
+        }
+        var superDef = null;
+        if (superType) {
+            if (superType === viewType) {
+                throw new Error('Can\'t have a custom view type that references itself');
+            }
+            superDef = ensureViewDef(superType, hash, defaultConfigs, overrideConfigs);
+        }
+        if (!theClass && superDef) {
+            theClass = superDef.class;
+        }
+        if (!theClass) {
+            return null; // don't throw a warning, might be settings for a single-unit view
+        }
+        return {
+            type: viewType,
+            class: theClass,
+            defaults: __assign({}, (superDef ? superDef.defaults : {}), (defaultConfig ? defaultConfig.options : {})),
+            overrides: __assign({}, (superDef ? superDef.overrides : {}), (overrideConfig ? overrideConfig.options : {}))
+        };
+    }
+    function findViewNameBySubclass(viewSubclass, configs) {
+        var superProto = Object.getPrototypeOf(viewSubclass.prototype);
+        for (var viewType in configs) {
+            var parsed = configs[viewType];
+            // need DIRECT subclass, so instanceof won't do it
+            if (parsed.class && parsed.class.prototype === superProto) {
+                return viewType;
+            }
+        }
+        return '';
+    }
+
+    function parseViewConfigs(inputs) {
+        return mapHash(inputs, parseViewConfig);
+    }
+    var VIEW_DEF_PROPS = {
+        type: String,
+        class: null
+    };
+    function parseViewConfig(input) {
+        if (typeof input === 'function') {
+            input = { class: input };
+        }
+        var options = {};
+        var props = refineProps(input, VIEW_DEF_PROPS, {}, options);
+        return {
+            superType: props.type,
+            class: props.class,
+            options: options
+        };
+    }
+
+    function buildViewSpecs(defaultInputs, optionsManager) {
+        var defaultConfigs = parseViewConfigs(defaultInputs);
+        var overrideConfigs = parseViewConfigs(optionsManager.overrides.views);
+        var viewDefs = compileViewDefs(defaultConfigs, overrideConfigs);
+        return mapHash(viewDefs, function (viewDef) {
+            return buildViewSpec(viewDef, overrideConfigs, optionsManager);
+        });
+    }
+    function buildViewSpec(viewDef, overrideConfigs, optionsManager) {
+        var durationInput = viewDef.overrides.duration ||
+            viewDef.defaults.duration ||
+            optionsManager.dynamicOverrides.duration ||
+            optionsManager.overrides.duration;
+        var duration = null;
+        var durationUnit = '';
+        var singleUnit = '';
+        var singleUnitOverrides = {};
+        if (durationInput) {
+            duration = createDuration(durationInput);
+            if (duration) { // valid?
+                var denom = greatestDurationDenominator(duration, !getWeeksFromInput(durationInput));
+                durationUnit = denom.unit;
+                if (denom.value === 1) {
+                    singleUnit = durationUnit;
+                    singleUnitOverrides = overrideConfigs[durationUnit] ? overrideConfigs[durationUnit].options : {};
+                }
+            }
+        }
+        var queryButtonText = function (options) {
+            var buttonTextMap = options.buttonText || {};
+            var buttonTextKey = viewDef.defaults.buttonTextKey;
+            if (buttonTextKey != null && buttonTextMap[buttonTextKey] != null) {
+                return buttonTextMap[buttonTextKey];
+            }
+            if (buttonTextMap[viewDef.type] != null) {
+                return buttonTextMap[viewDef.type];
+            }
+            if (buttonTextMap[singleUnit] != null) {
+                return buttonTextMap[singleUnit];
+            }
+        };
+        return {
+            type: viewDef.type,
+            class: viewDef.class,
+            duration: duration,
+            durationUnit: durationUnit,
+            singleUnit: singleUnit,
+            options: __assign({}, globalDefaults, viewDef.defaults, optionsManager.dirDefaults, optionsManager.localeDefaults, optionsManager.overrides, singleUnitOverrides, viewDef.overrides, optionsManager.dynamicOverrides),
+            buttonTextOverride: queryButtonText(optionsManager.dynamicOverrides) ||
+                queryButtonText(optionsManager.overrides) || // constructor-specified buttonText lookup hash takes precedence
+                viewDef.overrides.buttonText,
+            buttonTextDefault: queryButtonText(optionsManager.localeDefaults) ||
+                queryButtonText(optionsManager.dirDefaults) ||
+                viewDef.defaults.buttonText ||
+                queryButtonText(globalDefaults) ||
+                viewDef.type // fall back to given view name
+        };
+    }
+
+    var Toolbar = /** @class */ (function (_super) {
+        __extends(Toolbar, _super);
+        function Toolbar(context, extraClassName) {
+            var _this = _super.call(this, context) || this;
+            _this._renderLayout = memoizeRendering(_this.renderLayout, _this.unrenderLayout);
+            _this._updateTitle = memoizeRendering(_this.updateTitle, null, [_this._renderLayout]);
+            _this._updateActiveButton = memoizeRendering(_this.updateActiveButton, null, [_this._renderLayout]);
+            _this._updateToday = memoizeRendering(_this.updateToday, null, [_this._renderLayout]);
+            _this._updatePrev = memoizeRendering(_this.updatePrev, null, [_this._renderLayout]);
+            _this._updateNext = memoizeRendering(_this.updateNext, null, [_this._renderLayout]);
+            _this.el = createElement('div', { className: 'fc-toolbar ' + extraClassName });
+            return _this;
+        }
+        Toolbar.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this._renderLayout.unrender(); // should unrender everything else
+            removeElement(this.el);
+        };
+        Toolbar.prototype.render = function (props) {
+            this._renderLayout(props.layout);
+            this._updateTitle(props.title);
+            this._updateActiveButton(props.activeButton);
+            this._updateToday(props.isTodayEnabled);
+            this._updatePrev(props.isPrevEnabled);
+            this._updateNext(props.isNextEnabled);
+        };
+        Toolbar.prototype.renderLayout = function (layout) {
+            var el = this.el;
+            this.viewsWithButtons = [];
+            appendToElement(el, this.renderSection('left', layout.left));
+            appendToElement(el, this.renderSection('center', layout.center));
+            appendToElement(el, this.renderSection('right', layout.right));
+        };
+        Toolbar.prototype.unrenderLayout = function () {
+            this.el.innerHTML = '';
+        };
+        Toolbar.prototype.renderSection = function (position, buttonStr) {
+            var _this = this;
+            var _a = this, theme = _a.theme, calendar = _a.calendar;
+            var optionsManager = calendar.optionsManager;
+            var viewSpecs = calendar.viewSpecs;
+            var sectionEl = createElement('div', { className: 'fc-' + position });
+            var calendarCustomButtons = optionsManager.computed.customButtons || {};
+            var calendarButtonTextOverrides = optionsManager.overrides.buttonText || {};
+            var calendarButtonText = optionsManager.computed.buttonText || {};
+            if (buttonStr) {
+                buttonStr.split(' ').forEach(function (buttonGroupStr, i) {
+                    var groupChildren = [];
+                    var isOnlyButtons = true;
+                    var groupEl;
+                    buttonGroupStr.split(',').forEach(function (buttonName, j) {
+                        var customButtonProps;
+                        var viewSpec;
+                        var buttonClick;
+                        var buttonIcon; // only one of these will be set
+                        var buttonText; // "
+                        var buttonInnerHtml;
+                        var buttonClasses;
+                        var buttonEl;
+                        var buttonAriaAttr;
+                        if (buttonName === 'title') {
+                            groupChildren.push(htmlToElement('<h2>&nbsp;</h2>')); // we always want it to take up height
+                            isOnlyButtons = false;
+                        }
+                        else {
+                            if ((customButtonProps = calendarCustomButtons[buttonName])) {
+                                buttonClick = function (ev) {
+                                    if (customButtonProps.click) {
+                                        customButtonProps.click.call(buttonEl, ev);
+                                    }
+                                };
+                                (buttonIcon = theme.getCustomButtonIconClass(customButtonProps)) ||
+                                    (buttonIcon = theme.getIconClass(buttonName)) ||
+                                    (buttonText = customButtonProps.text);
+                            }
+                            else if ((viewSpec = viewSpecs[buttonName])) {
+                                _this.viewsWithButtons.push(buttonName);
+                                buttonClick = function () {
+                                    calendar.changeView(buttonName);
+                                };
+                                (buttonText = viewSpec.buttonTextOverride) ||
+                                    (buttonIcon = theme.getIconClass(buttonName)) ||
+                                    (buttonText = viewSpec.buttonTextDefault);
+                            }
+                            else if (calendar[buttonName]) { // a calendar method
+                                buttonClick = function () {
+                                    calendar[buttonName]();
+                                };
+                                (buttonText = calendarButtonTextOverrides[buttonName]) ||
+                                    (buttonIcon = theme.getIconClass(buttonName)) ||
+                                    (buttonText = calendarButtonText[buttonName]);
+                                //            ^ everything else is considered default
+                            }
+                            if (buttonClick) {
+                                buttonClasses = [
+                                    'fc-' + buttonName + '-button',
+                                    theme.getClass('button')
+                                ];
+                                if (buttonText) {
+                                    buttonInnerHtml = htmlEscape(buttonText);
+                                    buttonAriaAttr = '';
+                                }
+                                else if (buttonIcon) {
+                                    buttonInnerHtml = "<span class='" + buttonIcon + "'></span>";
+                                    buttonAriaAttr = ' aria-label="' + buttonName + '"';
+                                }
+                                buttonEl = htmlToElement(// type="button" so that it doesn't submit a form
+                                '<button type="button" class="' + buttonClasses.join(' ') + '"' +
+                                    buttonAriaAttr +
+                                    '>' + buttonInnerHtml + '</button>');
+                                buttonEl.addEventListener('click', buttonClick);
+                                groupChildren.push(buttonEl);
+                            }
+                        }
+                    });
+                    if (groupChildren.length > 1) {
+                        groupEl = document.createElement('div');
+                        var buttonGroupClassName = theme.getClass('buttonGroup');
+                        if (isOnlyButtons && buttonGroupClassName) {
+                            groupEl.classList.add(buttonGroupClassName);
+                        }
+                        appendToElement(groupEl, groupChildren);
+                        sectionEl.appendChild(groupEl);
+                    }
+                    else {
+                        appendToElement(sectionEl, groupChildren); // 1 or 0 children
+                    }
+                });
+            }
+            return sectionEl;
+        };
+        Toolbar.prototype.updateToday = function (isTodayEnabled) {
+            this.toggleButtonEnabled('today', isTodayEnabled);
+        };
+        Toolbar.prototype.updatePrev = function (isPrevEnabled) {
+            this.toggleButtonEnabled('prev', isPrevEnabled);
+        };
+        Toolbar.prototype.updateNext = function (isNextEnabled) {
+            this.toggleButtonEnabled('next', isNextEnabled);
+        };
+        Toolbar.prototype.updateTitle = function (text) {
+            findElements(this.el, 'h2').forEach(function (titleEl) {
+                titleEl.innerText = text;
+            });
+        };
+        Toolbar.prototype.updateActiveButton = function (buttonName) {
+            var className = this.theme.getClass('buttonActive');
+            findElements(this.el, 'button').forEach(function (buttonEl) {
+                if (buttonName && buttonEl.classList.contains('fc-' + buttonName + '-button')) {
+                    buttonEl.classList.add(className);
+                }
+                else {
+                    buttonEl.classList.remove(className);
+                }
+            });
+        };
+        Toolbar.prototype.toggleButtonEnabled = function (buttonName, bool) {
+            findElements(this.el, '.fc-' + buttonName + '-button').forEach(function (buttonEl) {
+                buttonEl.disabled = !bool;
+            });
+        };
+        return Toolbar;
+    }(Component));
+
+    var CalendarComponent = /** @class */ (function (_super) {
+        __extends(CalendarComponent, _super);
+        function CalendarComponent(context, el) {
+            var _this = _super.call(this, context) || this;
+            _this._renderToolbars = memoizeRendering(_this.renderToolbars);
+            _this.buildViewPropTransformers = memoize(buildViewPropTransformers);
+            _this.el = el;
+            prependToElement(el, _this.contentEl = createElement('div', { className: 'fc-view-container' }));
+            var calendar = _this.calendar;
+            for (var _i = 0, _a = calendar.pluginSystem.hooks.viewContainerModifiers; _i < _a.length; _i++) {
+                var modifyViewContainer = _a[_i];
+                modifyViewContainer(_this.contentEl, calendar);
+            }
+            _this.toggleElClassNames(true);
+            _this.computeTitle = memoize(computeTitle);
+            _this.parseBusinessHours = memoize(function (input) {
+                return parseBusinessHours(input, _this.calendar);
+            });
+            return _this;
+        }
+        CalendarComponent.prototype.destroy = function () {
+            if (this.header) {
+                this.header.destroy();
+            }
+            if (this.footer) {
+                this.footer.destroy();
+            }
+            if (this.view) {
+                this.view.destroy();
+            }
+            removeElement(this.contentEl);
+            this.toggleElClassNames(false);
+            _super.prototype.destroy.call(this);
+        };
+        CalendarComponent.prototype.toggleElClassNames = function (bool) {
+            var classList = this.el.classList;
+            var dirClassName = 'fc-' + this.opt('dir');
+            var themeClassName = this.theme.getClass('widget');
+            if (bool) {
+                classList.add('fc');
+                classList.add(dirClassName);
+                classList.add(themeClassName);
+            }
+            else {
+                classList.remove('fc');
+                classList.remove(dirClassName);
+                classList.remove(themeClassName);
+            }
+        };
+        CalendarComponent.prototype.render = function (props) {
+            this.freezeHeight();
+            var title = this.computeTitle(props.dateProfile, props.viewSpec.options);
+            this._renderToolbars(props.viewSpec, props.dateProfile, props.currentDate, props.dateProfileGenerator, title);
+            this.renderView(props, title);
+            this.updateSize();
+            this.thawHeight();
+        };
+        CalendarComponent.prototype.renderToolbars = function (viewSpec, dateProfile, currentDate, dateProfileGenerator, title) {
+            var headerLayout = this.opt('header');
+            var footerLayout = this.opt('footer');
+            var now = this.calendar.getNow();
+            var todayInfo = dateProfileGenerator.build(now);
+            var prevInfo = dateProfileGenerator.buildPrev(dateProfile, currentDate);
+            var nextInfo = dateProfileGenerator.buildNext(dateProfile, currentDate);
+            var toolbarProps = {
+                title: title,
+                activeButton: viewSpec.type,
+                isTodayEnabled: todayInfo.isValid && !rangeContainsMarker(dateProfile.currentRange, now),
+                isPrevEnabled: prevInfo.isValid,
+                isNextEnabled: nextInfo.isValid
+            };
+            if (headerLayout) {
+                if (!this.header) {
+                    this.header = new Toolbar(this.context, 'fc-header-toolbar');
+                    prependToElement(this.el, this.header.el);
+                }
+                this.header.receiveProps(__assign({ layout: headerLayout }, toolbarProps));
+            }
+            else if (this.header) {
+                this.header.destroy();
+                this.header = null;
+            }
+            if (footerLayout) {
+                if (!this.footer) {
+                    this.footer = new Toolbar(this.context, 'fc-footer-toolbar');
+                    appendToElement(this.el, this.footer.el);
+                }
+                this.footer.receiveProps(__assign({ layout: footerLayout }, toolbarProps));
+            }
+            else if (this.footer) {
+                this.footer.destroy();
+                this.footer = null;
+            }
+        };
+        CalendarComponent.prototype.renderView = function (props, title) {
+            var view = this.view;
+            var viewSpec = props.viewSpec, dateProfileGenerator = props.dateProfileGenerator;
+            if (!view || view.viewSpec !== viewSpec) {
+                if (view) {
+                    view.destroy();
+                }
+                view = this.view = new viewSpec['class']({
+                    calendar: this.calendar,
+                    view: null,
+                    dateEnv: this.dateEnv,
+                    theme: this.theme,
+                    options: viewSpec.options
+                }, viewSpec, dateProfileGenerator, this.contentEl);
+            }
+            else {
+                view.addScroll(view.queryScroll());
+            }
+            view.title = title; // for the API
+            var viewProps = {
+                dateProfile: props.dateProfile,
+                businessHours: this.parseBusinessHours(viewSpec.options.businessHours),
+                eventStore: props.eventStore,
+                eventUiBases: props.eventUiBases,
+                dateSelection: props.dateSelection,
+                eventSelection: props.eventSelection,
+                eventDrag: props.eventDrag,
+                eventResize: props.eventResize
+            };
+            var transformers = this.buildViewPropTransformers(this.calendar.pluginSystem.hooks.viewPropsTransformers);
+            for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) {
+                var transformer = transformers_1[_i];
+                __assign(viewProps, transformer.transform(viewProps, viewSpec, props, view));
+            }
+            view.receiveProps(viewProps);
+        };
+        // Sizing
+        // -----------------------------------------------------------------------------------------------------------------
+        CalendarComponent.prototype.updateSize = function (isResize) {
+            if (isResize === void 0) { isResize = false; }
+            var view = this.view;
+            if (isResize) {
+                view.addScroll(view.queryScroll());
+            }
+            if (isResize || this.isHeightAuto == null) {
+                this.computeHeightVars();
+            }
+            view.updateSize(isResize, this.viewHeight, this.isHeightAuto);
+            view.updateNowIndicator(); // we need to guarantee this will run after updateSize
+            view.popScroll(isResize);
+        };
+        CalendarComponent.prototype.computeHeightVars = function () {
+            var calendar = this.calendar; // yuck. need to handle dynamic options
+            var heightInput = calendar.opt('height');
+            var contentHeightInput = calendar.opt('contentHeight');
+            this.isHeightAuto = heightInput === 'auto' || contentHeightInput === 'auto';
+            if (typeof contentHeightInput === 'number') { // exists and not 'auto'
+                this.viewHeight = contentHeightInput;
+            }
+            else if (typeof contentHeightInput === 'function') { // exists and is a function
+                this.viewHeight = contentHeightInput();
+            }
+            else if (typeof heightInput === 'number') { // exists and not 'auto'
+                this.viewHeight = heightInput - this.queryToolbarsHeight();
+            }
+            else if (typeof heightInput === 'function') { // exists and is a function
+                this.viewHeight = heightInput() - this.queryToolbarsHeight();
+            }
+            else if (heightInput === 'parent') { // set to height of parent element
+                var parentEl = this.el.parentNode;
+                this.viewHeight = parentEl.getBoundingClientRect().height - this.queryToolbarsHeight();
+            }
+            else {
+                this.viewHeight = Math.round(this.contentEl.getBoundingClientRect().width /
+                    Math.max(calendar.opt('aspectRatio'), .5));
+            }
+        };
+        CalendarComponent.prototype.queryToolbarsHeight = function () {
+            var height = 0;
+            if (this.header) {
+                height += computeHeightAndMargins(this.header.el);
+            }
+            if (this.footer) {
+                height += computeHeightAndMargins(this.footer.el);
+            }
+            return height;
+        };
+        // Height "Freezing"
+        // -----------------------------------------------------------------------------------------------------------------
+        CalendarComponent.prototype.freezeHeight = function () {
+            applyStyle(this.el, {
+                height: this.el.getBoundingClientRect().height,
+                overflow: 'hidden'
+            });
+        };
+        CalendarComponent.prototype.thawHeight = function () {
+            applyStyle(this.el, {
+                height: '',
+                overflow: ''
+            });
+        };
+        return CalendarComponent;
+    }(Component));
+    // Title and Date Formatting
+    // -----------------------------------------------------------------------------------------------------------------
+    // Computes what the title at the top of the calendar should be for this view
+    function computeTitle(dateProfile, viewOptions) {
+        var range;
+        // for views that span a large unit of time, show the proper interval, ignoring stray days before and after
+        if (/^(year|month)$/.test(dateProfile.currentRangeUnit)) {
+            range = dateProfile.currentRange;
+        }
+        else { // for day units or smaller, use the actual day range
+            range = dateProfile.activeRange;
+        }
+        return this.dateEnv.formatRange(range.start, range.end, createFormatter(viewOptions.titleFormat || computeTitleFormat(dateProfile), viewOptions.titleRangeSeparator), { isEndExclusive: dateProfile.isRangeAllDay });
+    }
+    // Generates the format string that should be used to generate the title for the current date range.
+    // Attempts to compute the most appropriate format if not explicitly specified with `titleFormat`.
+    function computeTitleFormat(dateProfile) {
+        var currentRangeUnit = dateProfile.currentRangeUnit;
+        if (currentRangeUnit === 'year') {
+            return { year: 'numeric' };
+        }
+        else if (currentRangeUnit === 'month') {
+            return { year: 'numeric', month: 'long' }; // like "September 2014"
+        }
+        else {
+            var days = diffWholeDays(dateProfile.currentRange.start, dateProfile.currentRange.end);
+            if (days !== null && days > 1) {
+                // multi-day range. shorter, like "Sep 9 - 10 2014"
+                return { year: 'numeric', month: 'short', day: 'numeric' };
+            }
+            else {
+                // one day. longer, like "September 9 2014"
+                return { year: 'numeric', month: 'long', day: 'numeric' };
+            }
+        }
+    }
+    // Plugin
+    // -----------------------------------------------------------------------------------------------------------------
+    function buildViewPropTransformers(theClasses) {
+        return theClasses.map(function (theClass) {
+            return new theClass();
+        });
+    }
+
+    var Interaction = /** @class */ (function () {
+        function Interaction(settings) {
+            this.component = settings.component;
+        }
+        Interaction.prototype.destroy = function () {
+        };
+        return Interaction;
+    }());
+    function parseInteractionSettings(component, input) {
+        return {
+            component: component,
+            el: input.el,
+            useEventCenter: input.useEventCenter != null ? input.useEventCenter : true
+        };
+    }
+    function interactionSettingsToStore(settings) {
+        var _a;
+        return _a = {},
+            _a[settings.component.uid] = settings,
+            _a;
+    }
+    // global state
+    var interactionSettingsStore = {};
+
+    /*
+    Detects when the user clicks on an event within a DateComponent
+    */
+    var EventClicking = /** @class */ (function (_super) {
+        __extends(EventClicking, _super);
+        function EventClicking(settings) {
+            var _this = _super.call(this, settings) || this;
+            _this.handleSegClick = function (ev, segEl) {
+                var component = _this.component;
+                var seg = getElSeg(segEl);
+                if (seg && // might be the <div> surrounding the more link
+                    component.isValidSegDownEl(ev.target)) {
+                    // our way to simulate a link click for elements that can't be <a> tags
+                    // grab before trigger fired in case trigger trashes DOM thru rerendering
+                    var hasUrlContainer = elementClosest(ev.target, '.fc-has-url');
+                    var url = hasUrlContainer ? hasUrlContainer.querySelector('a[href]').href : '';
+                    component.publiclyTrigger('eventClick', [
+                        {
+                            el: segEl,
+                            event: new EventApi(component.calendar, seg.eventRange.def, seg.eventRange.instance),
+                            jsEvent: ev,
+                            view: component.view
+                        }
+                    ]);
+                    if (url && !ev.defaultPrevented) {
+                        window.location.href = url;
+                    }
+                }
+            };
+            var component = settings.component;
+            _this.destroy = listenBySelector(component.el, 'click', component.fgSegSelector + ',' + component.bgSegSelector, _this.handleSegClick);
+            return _this;
+        }
+        return EventClicking;
+    }(Interaction));
+
+    /*
+    Triggers events and adds/removes core classNames when the user's pointer
+    enters/leaves event-elements of a component.
+    */
+    var EventHovering = /** @class */ (function (_super) {
+        __extends(EventHovering, _super);
+        function EventHovering(settings) {
+            var _this = _super.call(this, settings) || this;
+            // for simulating an eventMouseLeave when the event el is destroyed while mouse is over it
+            _this.handleEventElRemove = function (el) {
+                if (el === _this.currentSegEl) {
+                    _this.handleSegLeave(null, _this.currentSegEl);
+                }
+            };
+            _this.handleSegEnter = function (ev, segEl) {
+                if (getElSeg(segEl)) { // TODO: better way to make sure not hovering over more+ link or its wrapper
+                    segEl.classList.add('fc-allow-mouse-resize');
+                    _this.currentSegEl = segEl;
+                    _this.triggerEvent('eventMouseEnter', ev, segEl);
+                }
+            };
+            _this.handleSegLeave = function (ev, segEl) {
+                if (_this.currentSegEl) {
+                    segEl.classList.remove('fc-allow-mouse-resize');
+                    _this.currentSegEl = null;
+                    _this.triggerEvent('eventMouseLeave', ev, segEl);
+                }
+            };
+            var component = settings.component;
+            _this.removeHoverListeners = listenToHoverBySelector(component.el, component.fgSegSelector + ',' + component.bgSegSelector, _this.handleSegEnter, _this.handleSegLeave);
+            component.calendar.on('eventElRemove', _this.handleEventElRemove);
+            return _this;
+        }
+        EventHovering.prototype.destroy = function () {
+            this.removeHoverListeners();
+            this.component.calendar.off('eventElRemove', this.handleEventElRemove);
+        };
+        EventHovering.prototype.triggerEvent = function (publicEvName, ev, segEl) {
+            var component = this.component;
+            var seg = getElSeg(segEl);
+            if (!ev || component.isValidSegDownEl(ev.target)) {
+                component.publiclyTrigger(publicEvName, [
+                    {
+                        el: segEl,
+                        event: new EventApi(this.component.calendar, seg.eventRange.def, seg.eventRange.instance),
+                        jsEvent: ev,
+                        view: component.view
+                    }
+                ]);
+            }
+        };
+        return EventHovering;
+    }(Interaction));
+
+    var StandardTheme = /** @class */ (function (_super) {
+        __extends(StandardTheme, _super);
+        function StandardTheme() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        return StandardTheme;
+    }(Theme));
+    StandardTheme.prototype.classes = {
+        widget: 'fc-unthemed',
+        widgetHeader: 'fc-widget-header',
+        widgetContent: 'fc-widget-content',
+        buttonGroup: 'fc-button-group',
+        button: 'fc-button fc-button-primary',
+        buttonActive: 'fc-button-active',
+        popoverHeader: 'fc-widget-header',
+        popoverContent: 'fc-widget-content',
+        // day grid
+        headerRow: 'fc-widget-header',
+        dayRow: 'fc-widget-content',
+        // list view
+        listView: 'fc-widget-content'
+    };
+    StandardTheme.prototype.baseIconClass = 'fc-icon';
+    StandardTheme.prototype.iconClasses = {
+        close: 'fc-icon-x',
+        prev: 'fc-icon-chevron-left',
+        next: 'fc-icon-chevron-right',
+        prevYear: 'fc-icon-chevrons-left',
+        nextYear: 'fc-icon-chevrons-right'
+    };
+    StandardTheme.prototype.iconOverrideOption = 'buttonIcons';
+    StandardTheme.prototype.iconOverrideCustomButtonOption = 'icon';
+    StandardTheme.prototype.iconOverridePrefix = 'fc-icon-';
+
+    var Calendar = /** @class */ (function () {
+        function Calendar(el, overrides) {
+            var _this = this;
+            this.parseRawLocales = memoize(parseRawLocales);
+            this.buildLocale = memoize(buildLocale);
+            this.buildDateEnv = memoize(buildDateEnv);
+            this.buildTheme = memoize(buildTheme);
+            this.buildEventUiSingleBase = memoize(this._buildEventUiSingleBase);
+            this.buildSelectionConfig = memoize(this._buildSelectionConfig);
+            this.buildEventUiBySource = memoizeOutput(buildEventUiBySource, isPropsEqual);
+            this.buildEventUiBases = memoize(buildEventUiBases);
+            this.interactionsStore = {};
+            this.actionQueue = [];
+            this.isReducing = false;
+            // isDisplaying: boolean = false // installed in DOM? accepting renders?
+            this.needsRerender = false; // needs a render?
+            this.needsFullRerender = false;
+            this.isRendering = false; // currently in the executeRender function?
+            this.renderingPauseDepth = 0;
+            this.buildDelayedRerender = memoize(buildDelayedRerender);
+            this.afterSizingTriggers = {};
+            this.isViewUpdated = false;
+            this.isDatesUpdated = false;
+            this.isEventsUpdated = false;
+            this.el = el;
+            this.optionsManager = new OptionsManager(overrides || {});
+            this.pluginSystem = new PluginSystem();
+            // only do once. don't do in handleOptions. because can't remove plugins
+            this.addPluginInputs(this.optionsManager.computed.plugins || []);
+            this.handleOptions(this.optionsManager.computed);
+            this.publiclyTrigger('_init'); // for tests
+            this.hydrate();
+            this.calendarInteractions = this.pluginSystem.hooks.calendarInteractions
+                .map(function (calendarInteractionClass) {
+                return new calendarInteractionClass(_this);
+            });
+        }
+        Calendar.prototype.addPluginInputs = function (pluginInputs) {
+            var pluginDefs = refinePluginDefs(pluginInputs);
+            for (var _i = 0, pluginDefs_1 = pluginDefs; _i < pluginDefs_1.length; _i++) {
+                var pluginDef = pluginDefs_1[_i];
+                this.pluginSystem.add(pluginDef);
+            }
+        };
+        Object.defineProperty(Calendar.prototype, "view", {
+            // public API
+            get: function () {
+                return this.component ? this.component.view : null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        // Public API for rendering
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.render = function () {
+            if (!this.component) {
+                this.renderableEventStore = createEmptyEventStore();
+                this.bindHandlers();
+                this.executeRender();
+            }
+            else {
+                this.requestRerender(true);
+            }
+        };
+        Calendar.prototype.destroy = function () {
+            if (this.component) {
+                this.unbindHandlers();
+                this.component.destroy(); // don't null-out. in case API needs access
+                this.component = null; // umm ???
+                for (var _i = 0, _a = this.calendarInteractions; _i < _a.length; _i++) {
+                    var interaction = _a[_i];
+                    interaction.destroy();
+                }
+                this.publiclyTrigger('_destroyed');
+            }
+        };
+        // Handlers
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.bindHandlers = function () {
+            var _this = this;
+            // event delegation for nav links
+            this.removeNavLinkListener = listenBySelector(this.el, 'click', 'a[data-goto]', function (ev, anchorEl) {
+                var gotoOptions = anchorEl.getAttribute('data-goto');
+                gotoOptions = gotoOptions ? JSON.parse(gotoOptions) : {};
+                var dateEnv = _this.dateEnv;
+                var dateMarker = dateEnv.createMarker(gotoOptions.date);
+                var viewType = gotoOptions.type;
+                // property like "navLinkDayClick". might be a string or a function
+                var customAction = _this.viewOpt('navLink' + capitaliseFirstLetter(viewType) + 'Click');
+                if (typeof customAction === 'function') {
+                    customAction(dateEnv.toDate(dateMarker), ev);
+                }
+                else {
+                    if (typeof customAction === 'string') {
+                        viewType = customAction;
+                    }
+                    _this.zoomTo(dateMarker, viewType);
+                }
+            });
+            if (this.opt('handleWindowResize')) {
+                window.addEventListener('resize', this.windowResizeProxy = debounce(// prevents rapid calls
+                this.windowResize.bind(this), this.opt('windowResizeDelay')));
+            }
+        };
+        Calendar.prototype.unbindHandlers = function () {
+            this.removeNavLinkListener();
+            if (this.windowResizeProxy) {
+                window.removeEventListener('resize', this.windowResizeProxy);
+                this.windowResizeProxy = null;
+            }
+        };
+        // Dispatcher
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.hydrate = function () {
+            var _this = this;
+            this.state = this.buildInitialState();
+            var rawSources = this.opt('eventSources') || [];
+            var singleRawSource = this.opt('events');
+            var sources = []; // parsed
+            if (singleRawSource) {
+                rawSources.unshift(singleRawSource);
+            }
+            for (var _i = 0, rawSources_1 = rawSources; _i < rawSources_1.length; _i++) {
+                var rawSource = rawSources_1[_i];
+                var source = parseEventSource(rawSource, this);
+                if (source) {
+                    sources.push(source);
+                }
+            }
+            this.batchRendering(function () {
+                _this.dispatch({ type: 'INIT' }); // pass in sources here?
+                _this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: sources });
+                _this.dispatch({
+                    type: 'SET_VIEW_TYPE',
+                    viewType: _this.opt('defaultView') || _this.pluginSystem.hooks.defaultView
+                });
+            });
+        };
+        Calendar.prototype.buildInitialState = function () {
+            return {
+                viewType: null,
+                loadingLevel: 0,
+                eventSourceLoadingLevel: 0,
+                currentDate: this.getInitialDate(),
+                dateProfile: null,
+                eventSources: {},
+                eventStore: createEmptyEventStore(),
+                dateSelection: null,
+                eventSelection: '',
+                eventDrag: null,
+                eventResize: null
+            };
+        };
+        Calendar.prototype.dispatch = function (action) {
+            this.actionQueue.push(action);
+            if (!this.isReducing) {
+                this.isReducing = true;
+                var oldState = this.state;
+                while (this.actionQueue.length) {
+                    this.state = this.reduce(this.state, this.actionQueue.shift(), this);
+                }
+                var newState = this.state;
+                this.isReducing = false;
+                if (!oldState.loadingLevel && newState.loadingLevel) {
+                    this.publiclyTrigger('loading', [true]);
+                }
+                else if (oldState.loadingLevel && !newState.loadingLevel) {
+                    this.publiclyTrigger('loading', [false]);
+                }
+                var view = this.component && this.component.view;
+                if (oldState.eventStore !== newState.eventStore || this.needsFullRerender) {
+                    if (oldState.eventStore) {
+                        this.isEventsUpdated = true;
+                    }
+                }
+                if (oldState.dateProfile !== newState.dateProfile || this.needsFullRerender) {
+                    if (oldState.dateProfile && view) { // why would view be null!?
+                        this.publiclyTrigger('datesDestroy', [
+                            {
+                                view: view,
+                                el: view.el
+                            }
+                        ]);
+                    }
+                    this.isDatesUpdated = true;
+                }
+                if (oldState.viewType !== newState.viewType || this.needsFullRerender) {
+                    if (oldState.viewType && view) { // why would view be null!?
+                        this.publiclyTrigger('viewSkeletonDestroy', [
+                            {
+                                view: view,
+                                el: view.el
+                            }
+                        ]);
+                    }
+                    this.isViewUpdated = true;
+                }
+                this.requestRerender();
+            }
+        };
+        Calendar.prototype.reduce = function (state, action, calendar) {
+            return reduce(state, action, calendar);
+        };
+        // Render Queue
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.requestRerender = function (needsFull) {
+            if (needsFull === void 0) { needsFull = false; }
+            this.needsRerender = true;
+            this.needsFullRerender = this.needsFullRerender || needsFull;
+            this.delayedRerender(); // will call a debounced-version of tryRerender
+        };
+        Calendar.prototype.tryRerender = function () {
+            if (this.component && // must be accepting renders
+                this.needsRerender && // indicates that a rerender was requested
+                !this.renderingPauseDepth && // not paused
+                !this.isRendering // not currently in the render loop
+            ) {
+                this.executeRender();
+            }
+        };
+        Calendar.prototype.batchRendering = function (func) {
+            this.renderingPauseDepth++;
+            func();
+            this.renderingPauseDepth--;
+            if (this.needsRerender) {
+                this.requestRerender();
+            }
+        };
+        // Rendering
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.executeRender = function () {
+            var needsFullRerender = this.needsFullRerender; // save before clearing
+            // clear these BEFORE the render so that new values will accumulate during render
+            this.needsRerender = false;
+            this.needsFullRerender = false;
+            this.isRendering = true;
+            this.renderComponent(needsFullRerender);
+            this.isRendering = false;
+            // received a rerender request while rendering
+            if (this.needsRerender) {
+                this.delayedRerender();
+            }
+        };
+        /*
+        don't call this directly. use executeRender instead
+        */
+        Calendar.prototype.renderComponent = function (needsFull) {
+            var _a = this, state = _a.state, component = _a.component;
+            var viewType = state.viewType;
+            var viewSpec = this.viewSpecs[viewType];
+            var savedScroll = (needsFull && component) ? component.view.queryScroll() : null;
+            if (!viewSpec) {
+                throw new Error("View type \"" + viewType + "\" is not valid");
+            }
+            // if event sources are still loading and progressive rendering hasn't been enabled,
+            // keep rendering the last fully loaded set of events
+            var renderableEventStore = this.renderableEventStore =
+                (state.eventSourceLoadingLevel && !this.opt('progressiveEventRendering')) ?
+                    this.renderableEventStore :
+                    state.eventStore;
+            var eventUiSingleBase = this.buildEventUiSingleBase(viewSpec.options);
+            var eventUiBySource = this.buildEventUiBySource(state.eventSources);
+            var eventUiBases = this.eventUiBases = this.buildEventUiBases(renderableEventStore.defs, eventUiSingleBase, eventUiBySource);
+            if (needsFull || !component) {
+                if (component) {
+                    component.freezeHeight(); // next component will unfreeze it
+                    component.destroy();
+                }
+                component = this.component = new CalendarComponent({
+                    calendar: this,
+                    view: null,
+                    dateEnv: this.dateEnv,
+                    theme: this.theme,
+                    options: this.optionsManager.computed
+                }, this.el);
+                this.isViewUpdated = true;
+                this.isDatesUpdated = true;
+                this.isEventsUpdated = true;
+            }
+            component.receiveProps(__assign({}, state, { viewSpec: viewSpec, dateProfile: state.dateProfile, dateProfileGenerator: this.dateProfileGenerators[viewType], eventStore: renderableEventStore, eventUiBases: eventUiBases, dateSelection: state.dateSelection, eventSelection: state.eventSelection, eventDrag: state.eventDrag, eventResize: state.eventResize }));
+            if (savedScroll) {
+                component.view.applyScroll(savedScroll, false);
+            }
+            if (this.isViewUpdated) {
+                this.isViewUpdated = false;
+                this.publiclyTrigger('viewSkeletonRender', [
+                    {
+                        view: component.view,
+                        el: component.view.el
+                    }
+                ]);
+            }
+            if (this.isDatesUpdated) {
+                this.isDatesUpdated = false;
+                this.publiclyTrigger('datesRender', [
+                    {
+                        view: component.view,
+                        el: component.view.el
+                    }
+                ]);
+            }
+            if (this.isEventsUpdated) {
+                this.isEventsUpdated = false;
+            }
+            this.releaseAfterSizingTriggers();
+        };
+        // Options
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.setOption = function (name, val) {
+            var _a;
+            this.mutateOptions((_a = {}, _a[name] = val, _a), [], true);
+        };
+        Calendar.prototype.getOption = function (name) {
+            return this.optionsManager.computed[name];
+        };
+        Calendar.prototype.opt = function (name) {
+            return this.optionsManager.computed[name];
+        };
+        Calendar.prototype.viewOpt = function (name) {
+            return this.viewOpts()[name];
+        };
+        Calendar.prototype.viewOpts = function () {
+            return this.viewSpecs[this.state.viewType].options;
+        };
+        /*
+        handles option changes (like a diff)
+        */
+        Calendar.prototype.mutateOptions = function (updates, removals, isDynamic, deepEqual) {
+            var _this = this;
+            var changeHandlers = this.pluginSystem.hooks.optionChangeHandlers;
+            var normalUpdates = {};
+            var specialUpdates = {};
+            var oldDateEnv = this.dateEnv; // do this before handleOptions
+            var isTimeZoneDirty = false;
+            var isSizeDirty = false;
+            var anyDifficultOptions = Boolean(removals.length);
+            for (var name_1 in updates) {
+                if (changeHandlers[name_1]) {
+                    specialUpdates[name_1] = updates[name_1];
+                }
+                else {
+                    normalUpdates[name_1] = updates[name_1];
+                }
+            }
+            for (var name_2 in normalUpdates) {
+                if (/^(height|contentHeight|aspectRatio)$/.test(name_2)) {
+                    isSizeDirty = true;
+                }
+                else if (/^(defaultDate|defaultView)$/.test(name_2)) ;
+                else {
+                    anyDifficultOptions = true;
+                    if (name_2 === 'timeZone') {
+                        isTimeZoneDirty = true;
+                    }
+                }
+            }
+            this.optionsManager.mutate(normalUpdates, removals, isDynamic);
+            if (anyDifficultOptions) {
+                this.handleOptions(this.optionsManager.computed);
+                this.needsFullRerender = true;
+            }
+            this.batchRendering(function () {
+                if (anyDifficultOptions) {
+                    if (isTimeZoneDirty) {
+                        _this.dispatch({
+                            type: 'CHANGE_TIMEZONE',
+                            oldDateEnv: oldDateEnv
+                        });
+                    }
+                    /* HACK
+                    has the same effect as calling this.requestRerender(true)
+                    but recomputes the state's dateProfile
+                    */
+                    _this.dispatch({
+                        type: 'SET_VIEW_TYPE',
+                        viewType: _this.state.viewType
+                    });
+                }
+                else if (isSizeDirty) {
+                    _this.updateSize();
+                }
+                // special updates
+                if (deepEqual) {
+                    for (var name_3 in specialUpdates) {
+                        changeHandlers[name_3](specialUpdates[name_3], _this, deepEqual);
+                    }
+                }
+            });
+        };
+        /*
+        rebuilds things based off of a complete set of refined options
+        */
+        Calendar.prototype.handleOptions = function (options) {
+            var _this = this;
+            var pluginHooks = this.pluginSystem.hooks;
+            this.defaultAllDayEventDuration = createDuration(options.defaultAllDayEventDuration);
+            this.defaultTimedEventDuration = createDuration(options.defaultTimedEventDuration);
+            this.delayedRerender = this.buildDelayedRerender(options.rerenderDelay);
+            this.theme = this.buildTheme(options);
+            var available = this.parseRawLocales(options.locales);
+            this.availableRawLocales = available.map;
+            var locale = this.buildLocale(options.locale || available.defaultCode, available.map);
+            this.dateEnv = this.buildDateEnv(locale, options.timeZone, pluginHooks.namedTimeZonedImpl, options.firstDay, options.weekNumberCalculation, options.weekLabel, pluginHooks.cmdFormatter);
+            this.selectionConfig = this.buildSelectionConfig(options); // needs dateEnv. do after :(
+            // ineffecient to do every time?
+            this.viewSpecs = buildViewSpecs(pluginHooks.views, this.optionsManager);
+            // ineffecient to do every time?
+            this.dateProfileGenerators = mapHash(this.viewSpecs, function (viewSpec) {
+                return new viewSpec.class.prototype.dateProfileGeneratorClass(viewSpec, _this);
+            });
+        };
+        Calendar.prototype.getAvailableLocaleCodes = function () {
+            return Object.keys(this.availableRawLocales);
+        };
+        Calendar.prototype._buildSelectionConfig = function (rawOpts) {
+            return processScopedUiProps('select', rawOpts, this);
+        };
+        Calendar.prototype._buildEventUiSingleBase = function (rawOpts) {
+            if (rawOpts.editable) { // so 'editable' affected events
+                rawOpts = __assign({}, rawOpts, { eventEditable: true });
+            }
+            return processScopedUiProps('event', rawOpts, this);
+        };
+        // Trigger
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.hasPublicHandlers = function (name) {
+            return this.hasHandlers(name) ||
+                this.opt(name); // handler specified in options
+        };
+        Calendar.prototype.publiclyTrigger = function (name, args) {
+            var optHandler = this.opt(name);
+            this.triggerWith(name, this, args);
+            if (optHandler) {
+                return optHandler.apply(this, args);
+            }
+        };
+        Calendar.prototype.publiclyTriggerAfterSizing = function (name, args) {
+            var afterSizingTriggers = this.afterSizingTriggers;
+            (afterSizingTriggers[name] || (afterSizingTriggers[name] = [])).push(args);
+        };
+        Calendar.prototype.releaseAfterSizingTriggers = function () {
+            var afterSizingTriggers = this.afterSizingTriggers;
+            for (var name_4 in afterSizingTriggers) {
+                for (var _i = 0, _a = afterSizingTriggers[name_4]; _i < _a.length; _i++) {
+                    var args = _a[_i];
+                    this.publiclyTrigger(name_4, args);
+                }
+            }
+            this.afterSizingTriggers = {};
+        };
+        // View
+        // -----------------------------------------------------------------------------------------------------------------
+        // Returns a boolean about whether the view is okay to instantiate at some point
+        Calendar.prototype.isValidViewType = function (viewType) {
+            return Boolean(this.viewSpecs[viewType]);
+        };
+        Calendar.prototype.changeView = function (viewType, dateOrRange) {
+            var dateMarker = null;
+            if (dateOrRange) {
+                if (dateOrRange.start && dateOrRange.end) { // a range
+                    this.optionsManager.mutate({ visibleRange: dateOrRange }, []); // will not rerender
+                    this.handleOptions(this.optionsManager.computed); // ...but yuck
+                }
+                else { // a date
+                    dateMarker = this.dateEnv.createMarker(dateOrRange); // just like gotoDate
+                }
+            }
+            this.unselect();
+            this.dispatch({
+                type: 'SET_VIEW_TYPE',
+                viewType: viewType,
+                dateMarker: dateMarker
+            });
+        };
+        // Forces navigation to a view for the given date.
+        // `viewType` can be a specific view name or a generic one like "week" or "day".
+        // needs to change
+        Calendar.prototype.zoomTo = function (dateMarker, viewType) {
+            var spec;
+            viewType = viewType || 'day'; // day is default zoom
+            spec = this.viewSpecs[viewType] ||
+                this.getUnitViewSpec(viewType);
+            this.unselect();
+            if (spec) {
+                this.dispatch({
+                    type: 'SET_VIEW_TYPE',
+                    viewType: spec.type,
+                    dateMarker: dateMarker
+                });
+            }
+            else {
+                this.dispatch({
+                    type: 'SET_DATE',
+                    dateMarker: dateMarker
+                });
+            }
+        };
+        // Given a duration singular unit, like "week" or "day", finds a matching view spec.
+        // Preference is given to views that have corresponding buttons.
+        Calendar.prototype.getUnitViewSpec = function (unit) {
+            var component = this.component;
+            var viewTypes = [];
+            var i;
+            var spec;
+            // put views that have buttons first. there will be duplicates, but oh
+            if (component.header) {
+                viewTypes.push.apply(viewTypes, component.header.viewsWithButtons);
+            }
+            if (component.footer) {
+                viewTypes.push.apply(viewTypes, component.footer.viewsWithButtons);
+            }
+            for (var viewType in this.viewSpecs) {
+                viewTypes.push(viewType);
+            }
+            for (i = 0; i < viewTypes.length; i++) {
+                spec = this.viewSpecs[viewTypes[i]];
+                if (spec) {
+                    if (spec.singleUnit === unit) {
+                        return spec;
+                    }
+                }
+            }
+        };
+        // Current Date
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.getInitialDate = function () {
+            var defaultDateInput = this.opt('defaultDate');
+            // compute the initial ambig-timezone date
+            if (defaultDateInput != null) {
+                return this.dateEnv.createMarker(defaultDateInput);
+            }
+            else {
+                return this.getNow(); // getNow already returns unzoned
+            }
+        };
+        Calendar.prototype.prev = function () {
+            this.unselect();
+            this.dispatch({ type: 'PREV' });
+        };
+        Calendar.prototype.next = function () {
+            this.unselect();
+            this.dispatch({ type: 'NEXT' });
+        };
+        Calendar.prototype.prevYear = function () {
+            this.unselect();
+            this.dispatch({
+                type: 'SET_DATE',
+                dateMarker: this.dateEnv.addYears(this.state.currentDate, -1)
+            });
+        };
+        Calendar.prototype.nextYear = function () {
+            this.unselect();
+            this.dispatch({
+                type: 'SET_DATE',
+                dateMarker: this.dateEnv.addYears(this.state.currentDate, 1)
+            });
+        };
+        Calendar.prototype.today = function () {
+            this.unselect();
+            this.dispatch({
+                type: 'SET_DATE',
+                dateMarker: this.getNow()
+            });
+        };
+        Calendar.prototype.gotoDate = function (zonedDateInput) {
+            this.unselect();
+            this.dispatch({
+                type: 'SET_DATE',
+                dateMarker: this.dateEnv.createMarker(zonedDateInput)
+            });
+        };
+        Calendar.prototype.incrementDate = function (deltaInput) {
+            var delta = createDuration(deltaInput);
+            if (delta) { // else, warn about invalid input?
+                this.unselect();
+                this.dispatch({
+                    type: 'SET_DATE',
+                    dateMarker: this.dateEnv.add(this.state.currentDate, delta)
+                });
+            }
+        };
+        // for external API
+        Calendar.prototype.getDate = function () {
+            return this.dateEnv.toDate(this.state.currentDate);
+        };
+        // Date Formatting Utils
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.formatDate = function (d, formatter) {
+            var dateEnv = this.dateEnv;
+            return dateEnv.format(dateEnv.createMarker(d), createFormatter(formatter));
+        };
+        // `settings` is for formatter AND isEndExclusive
+        Calendar.prototype.formatRange = function (d0, d1, settings) {
+            var dateEnv = this.dateEnv;
+            return dateEnv.formatRange(dateEnv.createMarker(d0), dateEnv.createMarker(d1), createFormatter(settings, this.opt('defaultRangeSeparator')), settings);
+        };
+        Calendar.prototype.formatIso = function (d, omitTime) {
+            var dateEnv = this.dateEnv;
+            return dateEnv.formatIso(dateEnv.createMarker(d), { omitTime: omitTime });
+        };
+        // Sizing
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.windowResize = function (ev) {
+            if (!this.isHandlingWindowResize &&
+                this.component && // why?
+                ev.target === window // not a jqui resize event
+            ) {
+                this.isHandlingWindowResize = true;
+                this.updateSize();
+                this.publiclyTrigger('windowResize', [this.view]);
+                this.isHandlingWindowResize = false;
+            }
+        };
+        Calendar.prototype.updateSize = function () {
+            if (this.component) { // when?
+                this.component.updateSize(true);
+            }
+        };
+        // Component Registration
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.registerInteractiveComponent = function (component, settingsInput) {
+            var settings = parseInteractionSettings(component, settingsInput);
+            var DEFAULT_INTERACTIONS = [
+                EventClicking,
+                EventHovering
+            ];
+            var interactionClasses = DEFAULT_INTERACTIONS.concat(this.pluginSystem.hooks.componentInteractions);
+            var interactions = interactionClasses.map(function (interactionClass) {
+                return new interactionClass(settings);
+            });
+            this.interactionsStore[component.uid] = interactions;
+            interactionSettingsStore[component.uid] = settings;
+        };
+        Calendar.prototype.unregisterInteractiveComponent = function (component) {
+            for (var _i = 0, _a = this.interactionsStore[component.uid]; _i < _a.length; _i++) {
+                var listener = _a[_i];
+                listener.destroy();
+            }
+            delete this.interactionsStore[component.uid];
+            delete interactionSettingsStore[component.uid];
+        };
+        // Date Selection / Event Selection / DayClick
+        // -----------------------------------------------------------------------------------------------------------------
+        // this public method receives start/end dates in any format, with any timezone
+        // NOTE: args were changed from v3
+        Calendar.prototype.select = function (dateOrObj, endDate) {
+            var selectionInput;
+            if (endDate == null) {
+                if (dateOrObj.start != null) {
+                    selectionInput = dateOrObj;
+                }
+                else {
+                    selectionInput = {
+                        start: dateOrObj,
+                        end: null
+                    };
+                }
+            }
+            else {
+                selectionInput = {
+                    start: dateOrObj,
+                    end: endDate
+                };
+            }
+            var selection = parseDateSpan(selectionInput, this.dateEnv, createDuration({ days: 1 }) // TODO: cache this?
+            );
+            if (selection) { // throw parse error otherwise?
+                this.dispatch({ type: 'SELECT_DATES', selection: selection });
+                this.triggerDateSelect(selection);
+            }
+        };
+        // public method
+        Calendar.prototype.unselect = function (pev) {
+            if (this.state.dateSelection) {
+                this.dispatch({ type: 'UNSELECT_DATES' });
+                this.triggerDateUnselect(pev);
+            }
+        };
+        Calendar.prototype.triggerDateSelect = function (selection, pev) {
+            var arg = __assign({}, this.buildDateSpanApi(selection), { jsEvent: pev ? pev.origEvent : null, view: this.view });
+            this.publiclyTrigger('select', [arg]);
+        };
+        Calendar.prototype.triggerDateUnselect = function (pev) {
+            this.publiclyTrigger('unselect', [
+                {
+                    jsEvent: pev ? pev.origEvent : null,
+                    view: this.view
+                }
+            ]);
+        };
+        // TODO: receive pev?
+        Calendar.prototype.triggerDateClick = function (dateSpan, dayEl, view, ev) {
+            var arg = __assign({}, this.buildDatePointApi(dateSpan), { dayEl: dayEl, jsEvent: ev, // Is this always a mouse event? See #4655
+                view: view });
+            this.publiclyTrigger('dateClick', [arg]);
+        };
+        Calendar.prototype.buildDatePointApi = function (dateSpan) {
+            var props = {};
+            for (var _i = 0, _a = this.pluginSystem.hooks.datePointTransforms; _i < _a.length; _i++) {
+                var transform = _a[_i];
+                __assign(props, transform(dateSpan, this));
+            }
+            __assign(props, buildDatePointApi(dateSpan, this.dateEnv));
+            return props;
+        };
+        Calendar.prototype.buildDateSpanApi = function (dateSpan) {
+            var props = {};
+            for (var _i = 0, _a = this.pluginSystem.hooks.dateSpanTransforms; _i < _a.length; _i++) {
+                var transform = _a[_i];
+                __assign(props, transform(dateSpan, this));
+            }
+            __assign(props, buildDateSpanApi(dateSpan, this.dateEnv));
+            return props;
+        };
+        // Date Utils
+        // -----------------------------------------------------------------------------------------------------------------
+        // Returns a DateMarker for the current date, as defined by the client's computer or from the `now` option
+        Calendar.prototype.getNow = function () {
+            var now = this.opt('now');
+            if (typeof now === 'function') {
+                now = now();
+            }
+            if (now == null) {
+                return this.dateEnv.createNowMarker();
+            }
+            return this.dateEnv.createMarker(now);
+        };
+        // Event-Date Utilities
+        // -----------------------------------------------------------------------------------------------------------------
+        // Given an event's allDay status and start date, return what its fallback end date should be.
+        // TODO: rename to computeDefaultEventEnd
+        Calendar.prototype.getDefaultEventEnd = function (allDay, marker) {
+            var end = marker;
+            if (allDay) {
+                end = startOfDay(end);
+                end = this.dateEnv.add(end, this.defaultAllDayEventDuration);
+            }
+            else {
+                end = this.dateEnv.add(end, this.defaultTimedEventDuration);
+            }
+            return end;
+        };
+        // Public Events API
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.addEvent = function (eventInput, sourceInput) {
+            if (eventInput instanceof EventApi) {
+                var def = eventInput._def;
+                var instance = eventInput._instance;
+                // not already present? don't want to add an old snapshot
+                if (!this.state.eventStore.defs[def.defId]) {
+                    this.dispatch({
+                        type: 'ADD_EVENTS',
+                        eventStore: eventTupleToStore({ def: def, instance: instance }) // TODO: better util for two args?
+                    });
+                }
+                return eventInput;
+            }
+            var sourceId;
+            if (sourceInput instanceof EventSourceApi) {
+                sourceId = sourceInput.internalEventSource.sourceId;
+            }
+            else if (sourceInput != null) {
+                var sourceApi = this.getEventSourceById(sourceInput); // TODO: use an internal function
+                if (!sourceApi) {
+                    console.warn('Could not find an event source with ID "' + sourceInput + '"'); // TODO: test
+                    return null;
+                }
+                else {
+                    sourceId = sourceApi.internalEventSource.sourceId;
+                }
+            }
+            var tuple = parseEvent(eventInput, sourceId, this);
+            if (tuple) {
+                this.dispatch({
+                    type: 'ADD_EVENTS',
+                    eventStore: eventTupleToStore(tuple)
+                });
+                return new EventApi(this, tuple.def, tuple.def.recurringDef ? null : tuple.instance);
+            }
+            return null;
+        };
+        // TODO: optimize
+        Calendar.prototype.getEventById = function (id) {
+            var _a = this.state.eventStore, defs = _a.defs, instances = _a.instances;
+            id = String(id);
+            for (var defId in defs) {
+                var def = defs[defId];
+                if (def.publicId === id) {
+                    if (def.recurringDef) {
+                        return new EventApi(this, def, null);
+                    }
+                    else {
+                        for (var instanceId in instances) {
+                            var instance = instances[instanceId];
+                            if (instance.defId === def.defId) {
+                                return new EventApi(this, def, instance);
+                            }
+                        }
+                    }
+                }
+            }
+            return null;
+        };
+        Calendar.prototype.getEvents = function () {
+            var _a = this.state.eventStore, defs = _a.defs, instances = _a.instances;
+            var eventApis = [];
+            for (var id in instances) {
+                var instance = instances[id];
+                var def = defs[instance.defId];
+                eventApis.push(new EventApi(this, def, instance));
+            }
+            return eventApis;
+        };
+        Calendar.prototype.removeAllEvents = function () {
+            this.dispatch({ type: 'REMOVE_ALL_EVENTS' });
+        };
+        Calendar.prototype.rerenderEvents = function () {
+            this.dispatch({ type: 'RESET_EVENTS' });
+        };
+        // Public Event Sources API
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.getEventSources = function () {
+            var sourceHash = this.state.eventSources;
+            var sourceApis = [];
+            for (var internalId in sourceHash) {
+                sourceApis.push(new EventSourceApi(this, sourceHash[internalId]));
+            }
+            return sourceApis;
+        };
+        Calendar.prototype.getEventSourceById = function (id) {
+            var sourceHash = this.state.eventSources;
+            id = String(id);
+            for (var sourceId in sourceHash) {
+                if (sourceHash[sourceId].publicId === id) {
+                    return new EventSourceApi(this, sourceHash[sourceId]);
+                }
+            }
+            return null;
+        };
+        Calendar.prototype.addEventSource = function (sourceInput) {
+            if (sourceInput instanceof EventSourceApi) {
+                // not already present? don't want to add an old snapshot
+                if (!this.state.eventSources[sourceInput.internalEventSource.sourceId]) {
+                    this.dispatch({
+                        type: 'ADD_EVENT_SOURCES',
+                        sources: [sourceInput.internalEventSource]
+                    });
+                }
+                return sourceInput;
+            }
+            var eventSource = parseEventSource(sourceInput, this);
+            if (eventSource) { // TODO: error otherwise?
+                this.dispatch({ type: 'ADD_EVENT_SOURCES', sources: [eventSource] });
+                return new EventSourceApi(this, eventSource);
+            }
+            return null;
+        };
+        Calendar.prototype.removeAllEventSources = function () {
+            this.dispatch({ type: 'REMOVE_ALL_EVENT_SOURCES' });
+        };
+        Calendar.prototype.refetchEvents = function () {
+            this.dispatch({ type: 'FETCH_EVENT_SOURCES' });
+        };
+        // Scroll
+        // -----------------------------------------------------------------------------------------------------------------
+        Calendar.prototype.scrollToTime = function (timeInput) {
+            var duration = createDuration(timeInput);
+            if (duration) {
+                this.component.view.scrollToDuration(duration);
+            }
+        };
+        return Calendar;
+    }());
+    EmitterMixin.mixInto(Calendar);
+    // for memoizers
+    // -----------------------------------------------------------------------------------------------------------------
+    function buildDateEnv(locale, timeZone, namedTimeZoneImpl, firstDay, weekNumberCalculation, weekLabel, cmdFormatter) {
+        return new DateEnv({
+            calendarSystem: 'gregory',
+            timeZone: timeZone,
+            namedTimeZoneImpl: namedTimeZoneImpl,
+            locale: locale,
+            weekNumberCalculation: weekNumberCalculation,
+            firstDay: firstDay,
+            weekLabel: weekLabel,
+            cmdFormatter: cmdFormatter
+        });
+    }
+    function buildTheme(calendarOptions) {
+        var themeClass = this.pluginSystem.hooks.themeClasses[calendarOptions.themeSystem] || StandardTheme;
+        return new themeClass(calendarOptions);
+    }
+    function buildDelayedRerender(wait) {
+        var func = this.tryRerender.bind(this);
+        if (wait != null) {
+            func = debounce(func, wait);
+        }
+        return func;
+    }
+    function buildEventUiBySource(eventSources) {
+        return mapHash(eventSources, function (eventSource) {
+            return eventSource.ui;
+        });
+    }
+    function buildEventUiBases(eventDefs, eventUiSingleBase, eventUiBySource) {
+        var eventUiBases = { '': eventUiSingleBase };
+        for (var defId in eventDefs) {
+            var def = eventDefs[defId];
+            if (def.sourceId && eventUiBySource[def.sourceId]) {
+                eventUiBases[defId] = eventUiBySource[def.sourceId];
+            }
+        }
+        return eventUiBases;
+    }
+
+    var View = /** @class */ (function (_super) {
+        __extends(View, _super);
+        function View(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, createElement('div', { className: 'fc-view fc-' + viewSpec.type + '-view' }), true // isView (HACK)
+            ) || this;
+            _this.renderDatesMem = memoizeRendering(_this.renderDatesWrap, _this.unrenderDatesWrap);
+            _this.renderBusinessHoursMem = memoizeRendering(_this.renderBusinessHours, _this.unrenderBusinessHours, [_this.renderDatesMem]);
+            _this.renderDateSelectionMem = memoizeRendering(_this.renderDateSelectionWrap, _this.unrenderDateSelectionWrap, [_this.renderDatesMem]);
+            _this.renderEventsMem = memoizeRendering(_this.renderEvents, _this.unrenderEvents, [_this.renderDatesMem]);
+            _this.renderEventSelectionMem = memoizeRendering(_this.renderEventSelectionWrap, _this.unrenderEventSelectionWrap, [_this.renderEventsMem]);
+            _this.renderEventDragMem = memoizeRendering(_this.renderEventDragWrap, _this.unrenderEventDragWrap, [_this.renderDatesMem]);
+            _this.renderEventResizeMem = memoizeRendering(_this.renderEventResizeWrap, _this.unrenderEventResizeWrap, [_this.renderDatesMem]);
+            _this.viewSpec = viewSpec;
+            _this.dateProfileGenerator = dateProfileGenerator;
+            _this.type = viewSpec.type;
+            _this.eventOrderSpecs = parseFieldSpecs(_this.opt('eventOrder'));
+            _this.nextDayThreshold = createDuration(_this.opt('nextDayThreshold'));
+            parentEl.appendChild(_this.el);
+            _this.initialize();
+            return _this;
+        }
+        View.prototype.initialize = function () {
+        };
+        Object.defineProperty(View.prototype, "activeStart", {
+            // Date Setting/Unsetting
+            // -----------------------------------------------------------------------------------------------------------------
+            get: function () {
+                return this.dateEnv.toDate(this.props.dateProfile.activeRange.start);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(View.prototype, "activeEnd", {
+            get: function () {
+                return this.dateEnv.toDate(this.props.dateProfile.activeRange.end);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(View.prototype, "currentStart", {
+            get: function () {
+                return this.dateEnv.toDate(this.props.dateProfile.currentRange.start);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(View.prototype, "currentEnd", {
+            get: function () {
+                return this.dateEnv.toDate(this.props.dateProfile.currentRange.end);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        // General Rendering
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.render = function (props) {
+            this.renderDatesMem(props.dateProfile);
+            this.renderBusinessHoursMem(props.businessHours);
+            this.renderDateSelectionMem(props.dateSelection);
+            this.renderEventsMem(props.eventStore);
+            this.renderEventSelectionMem(props.eventSelection);
+            this.renderEventDragMem(props.eventDrag);
+            this.renderEventResizeMem(props.eventResize);
+        };
+        View.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.renderDatesMem.unrender(); // should unrender everything else
+        };
+        // Sizing
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+            var calendar = this.calendar;
+            if (isResize || // HACKS...
+                calendar.isViewUpdated ||
+                calendar.isDatesUpdated ||
+                calendar.isEventsUpdated) {
+                // sort of the catch-all sizing
+                // anything that might cause dimension changes
+                this.updateBaseSize(isResize, viewHeight, isAuto);
+            }
+        };
+        View.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
+        };
+        // Date Rendering
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderDatesWrap = function (dateProfile) {
+            this.renderDates(dateProfile);
+            this.addScroll({
+                duration: createDuration(this.opt('scrollTime'))
+            });
+            this.startNowIndicator(dateProfile); // shouldn't render yet because updateSize will be called soon
+        };
+        View.prototype.unrenderDatesWrap = function () {
+            this.stopNowIndicator();
+            this.unrenderDates();
+        };
+        View.prototype.renderDates = function (dateProfile) { };
+        View.prototype.unrenderDates = function () { };
+        // Business Hours
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderBusinessHours = function (businessHours) { };
+        View.prototype.unrenderBusinessHours = function () { };
+        // Date Selection
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderDateSelectionWrap = function (selection) {
+            if (selection) {
+                this.renderDateSelection(selection);
+            }
+        };
+        View.prototype.unrenderDateSelectionWrap = function (selection) {
+            if (selection) {
+                this.unrenderDateSelection(selection);
+            }
+        };
+        View.prototype.renderDateSelection = function (selection) { };
+        View.prototype.unrenderDateSelection = function (selection) { };
+        // Event Rendering
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderEvents = function (eventStore) { };
+        View.prototype.unrenderEvents = function () { };
+        // util for subclasses
+        View.prototype.sliceEvents = function (eventStore, allDay) {
+            var props = this.props;
+            return sliceEventStore(eventStore, props.eventUiBases, props.dateProfile.activeRange, allDay ? this.nextDayThreshold : null).fg;
+        };
+        View.prototype.computeEventDraggable = function (eventDef, eventUi) {
+            var transformers = this.calendar.pluginSystem.hooks.isDraggableTransformers;
+            var val = eventUi.startEditable;
+            for (var _i = 0, transformers_1 = transformers; _i < transformers_1.length; _i++) {
+                var transformer = transformers_1[_i];
+                val = transformer(val, eventDef, eventUi, this);
+            }
+            return val;
+        };
+        View.prototype.computeEventStartResizable = function (eventDef, eventUi) {
+            return eventUi.durationEditable && this.opt('eventResizableFromStart');
+        };
+        View.prototype.computeEventEndResizable = function (eventDef, eventUi) {
+            return eventUi.durationEditable;
+        };
+        // Event Selection
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderEventSelectionWrap = function (instanceId) {
+            if (instanceId) {
+                this.renderEventSelection(instanceId);
+            }
+        };
+        View.prototype.unrenderEventSelectionWrap = function (instanceId) {
+            if (instanceId) {
+                this.unrenderEventSelection(instanceId);
+            }
+        };
+        View.prototype.renderEventSelection = function (instanceId) { };
+        View.prototype.unrenderEventSelection = function (instanceId) { };
+        // Event Drag
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderEventDragWrap = function (state) {
+            if (state) {
+                this.renderEventDrag(state);
+            }
+        };
+        View.prototype.unrenderEventDragWrap = function (state) {
+            if (state) {
+                this.unrenderEventDrag(state);
+            }
+        };
+        View.prototype.renderEventDrag = function (state) { };
+        View.prototype.unrenderEventDrag = function (state) { };
+        // Event Resize
+        // -----------------------------------------------------------------------------------------------------------------
+        View.prototype.renderEventResizeWrap = function (state) {
+            if (state) {
+                this.renderEventResize(state);
+            }
+        };
+        View.prototype.unrenderEventResizeWrap = function (state) {
+            if (state) {
+                this.unrenderEventResize(state);
+            }
+        };
+        View.prototype.renderEventResize = function (state) { };
+        View.prototype.unrenderEventResize = function (state) { };
+        /* Now Indicator
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Immediately render the current time indicator and begins re-rendering it at an interval,
+        // which is defined by this.getNowIndicatorUnit().
+        // TODO: somehow do this for the current whole day's background too
+        View.prototype.startNowIndicator = function (dateProfile) {
+            var _this = this;
+            var dateEnv = this.dateEnv;
+            var unit;
+            var update;
+            var delay; // ms wait value
+            if (this.opt('nowIndicator')) {
+                unit = this.getNowIndicatorUnit(dateProfile);
+                if (unit) {
+                    update = this.updateNowIndicator.bind(this);
+                    this.initialNowDate = this.calendar.getNow();
+                    this.initialNowQueriedMs = new Date().valueOf();
+                    // wait until the beginning of the next interval
+                    delay = dateEnv.add(dateEnv.startOf(this.initialNowDate, unit), createDuration(1, unit)).valueOf() - this.initialNowDate.valueOf();
+                    // TODO: maybe always use setTimeout, waiting until start of next unit
+                    this.nowIndicatorTimeoutID = setTimeout(function () {
+                        _this.nowIndicatorTimeoutID = null;
+                        update();
+                        if (unit === 'second') {
+                            delay = 1000; // every second
+                        }
+                        else {
+                            delay = 1000 * 60; // otherwise, every minute
+                        }
+                        _this.nowIndicatorIntervalID = setInterval(update, delay); // update every interval
+                    }, delay);
+                }
+                // rendering will be initiated in updateSize
+            }
+        };
+        // rerenders the now indicator, computing the new current time from the amount of time that has passed
+        // since the initial getNow call.
+        View.prototype.updateNowIndicator = function () {
+            if (this.props.dateProfile && // a way to determine if dates were rendered yet
+                this.initialNowDate // activated before?
+            ) {
+                this.unrenderNowIndicator(); // won't unrender if unnecessary
+                this.renderNowIndicator(addMs(this.initialNowDate, new Date().valueOf() - this.initialNowQueriedMs));
+                this.isNowIndicatorRendered = true;
+            }
+        };
+        // Immediately unrenders the view's current time indicator and stops any re-rendering timers.
+        // Won't cause side effects if indicator isn't rendered.
+        View.prototype.stopNowIndicator = function () {
+            if (this.isNowIndicatorRendered) {
+                if (this.nowIndicatorTimeoutID) {
+                    clearTimeout(this.nowIndicatorTimeoutID);
+                    this.nowIndicatorTimeoutID = null;
+                }
+                if (this.nowIndicatorIntervalID) {
+                    clearInterval(this.nowIndicatorIntervalID);
+                    this.nowIndicatorIntervalID = null;
+                }
+                this.unrenderNowIndicator();
+                this.isNowIndicatorRendered = false;
+            }
+        };
+        View.prototype.getNowIndicatorUnit = function (dateProfile) {
+            // subclasses should implement
+        };
+        // Renders a current time indicator at the given datetime
+        View.prototype.renderNowIndicator = function (date) {
+            // SUBCLASSES MUST PASS TO CHILDREN!
+        };
+        // Undoes the rendering actions from renderNowIndicator
+        View.prototype.unrenderNowIndicator = function () {
+            // SUBCLASSES MUST PASS TO CHILDREN!
+        };
+        /* Scroller
+        ------------------------------------------------------------------------------------------------------------------*/
+        View.prototype.addScroll = function (scroll) {
+            var queuedScroll = this.queuedScroll || (this.queuedScroll = {});
+            __assign(queuedScroll, scroll);
+        };
+        View.prototype.popScroll = function (isResize) {
+            this.applyQueuedScroll(isResize);
+            this.queuedScroll = null;
+        };
+        View.prototype.applyQueuedScroll = function (isResize) {
+            this.applyScroll(this.queuedScroll || {}, isResize);
+        };
+        View.prototype.queryScroll = function () {
+            var scroll = {};
+            if (this.props.dateProfile) { // dates rendered yet?
+                __assign(scroll, this.queryDateScroll());
+            }
+            return scroll;
+        };
+        View.prototype.applyScroll = function (scroll, isResize) {
+            var duration = scroll.duration;
+            if (duration != null) {
+                delete scroll.duration;
+                if (this.props.dateProfile) { // dates rendered yet?
+                    __assign(scroll, this.computeDateScroll(duration));
+                }
+            }
+            if (this.props.dateProfile) { // dates rendered yet?
+                this.applyDateScroll(scroll);
+            }
+        };
+        View.prototype.computeDateScroll = function (duration) {
+            return {}; // subclasses must implement
+        };
+        View.prototype.queryDateScroll = function () {
+            return {}; // subclasses must implement
+        };
+        View.prototype.applyDateScroll = function (scroll) {
+            // subclasses must implement
+        };
+        // for API
+        View.prototype.scrollToDuration = function (duration) {
+            this.applyScroll({ duration: duration }, false);
+        };
+        return View;
+    }(DateComponent));
+    EmitterMixin.mixInto(View);
+    View.prototype.usesMinMaxTime = false;
+    View.prototype.dateProfileGeneratorClass = DateProfileGenerator;
+
+    var FgEventRenderer = /** @class */ (function () {
+        function FgEventRenderer(context) {
+            this.segs = [];
+            this.isSizeDirty = false;
+            this.context = context;
+        }
+        FgEventRenderer.prototype.renderSegs = function (segs, mirrorInfo) {
+            this.rangeUpdated(); // called too frequently :(
+            // render an `.el` on each seg
+            // returns a subset of the segs. segs that were actually rendered
+            segs = this.renderSegEls(segs, mirrorInfo);
+            this.segs = segs;
+            this.attachSegs(segs, mirrorInfo);
+            this.isSizeDirty = true;
+            this.context.view.triggerRenderedSegs(this.segs, Boolean(mirrorInfo));
+        };
+        FgEventRenderer.prototype.unrender = function (_segs, mirrorInfo) {
+            this.context.view.triggerWillRemoveSegs(this.segs, Boolean(mirrorInfo));
+            this.detachSegs(this.segs);
+            this.segs = [];
+        };
+        // Updates values that rely on options and also relate to range
+        FgEventRenderer.prototype.rangeUpdated = function () {
+            var options = this.context.options;
+            var displayEventTime;
+            var displayEventEnd;
+            this.eventTimeFormat = createFormatter(options.eventTimeFormat || this.computeEventTimeFormat(), options.defaultRangeSeparator);
+            displayEventTime = options.displayEventTime;
+            if (displayEventTime == null) {
+                displayEventTime = this.computeDisplayEventTime(); // might be based off of range
+            }
+            displayEventEnd = options.displayEventEnd;
+            if (displayEventEnd == null) {
+                displayEventEnd = this.computeDisplayEventEnd(); // might be based off of range
+            }
+            this.displayEventTime = displayEventTime;
+            this.displayEventEnd = displayEventEnd;
+        };
+        // Renders and assigns an `el` property for each foreground event segment.
+        // Only returns segments that successfully rendered.
+        FgEventRenderer.prototype.renderSegEls = function (segs, mirrorInfo) {
+            var html = '';
+            var i;
+            if (segs.length) { // don't build an empty html string
+                // build a large concatenation of event segment HTML
+                for (i = 0; i < segs.length; i++) {
+                    html += this.renderSegHtml(segs[i], mirrorInfo);
+                }
+                // Grab individual elements from the combined HTML string. Use each as the default rendering.
+                // Then, compute the 'el' for each segment. An el might be null if the eventRender callback returned false.
+                htmlToElements(html).forEach(function (el, i) {
+                    var seg = segs[i];
+                    if (el) {
+                        seg.el = el;
+                    }
+                });
+                segs = filterSegsViaEls(this.context.view, segs, Boolean(mirrorInfo));
+            }
+            return segs;
+        };
+        // Generic utility for generating the HTML classNames for an event segment's element
+        FgEventRenderer.prototype.getSegClasses = function (seg, isDraggable, isResizable, mirrorInfo) {
+            var classes = [
+                'fc-event',
+                seg.isStart ? 'fc-start' : 'fc-not-start',
+                seg.isEnd ? 'fc-end' : 'fc-not-end'
+            ].concat(seg.eventRange.ui.classNames);
+            if (isDraggable) {
+                classes.push('fc-draggable');
+            }
+            if (isResizable) {
+                classes.push('fc-resizable');
+            }
+            if (mirrorInfo) {
+                classes.push('fc-mirror');
+                if (mirrorInfo.isDragging) {
+                    classes.push('fc-dragging');
+                }
+                if (mirrorInfo.isResizing) {
+                    classes.push('fc-resizing');
+                }
+            }
+            return classes;
+        };
+        // Compute the text that should be displayed on an event's element.
+        // `range` can be the Event object itself, or something range-like, with at least a `start`.
+        // If event times are disabled, or the event has no time, will return a blank string.
+        // If not specified, formatter will default to the eventTimeFormat setting,
+        // and displayEnd will default to the displayEventEnd setting.
+        FgEventRenderer.prototype.getTimeText = function (eventRange, formatter, displayEnd) {
+            var def = eventRange.def, instance = eventRange.instance;
+            return this._getTimeText(instance.range.start, def.hasEnd ? instance.range.end : null, def.allDay, formatter, displayEnd, instance.forcedStartTzo, instance.forcedEndTzo);
+        };
+        FgEventRenderer.prototype._getTimeText = function (start, end, allDay, formatter, displayEnd, forcedStartTzo, forcedEndTzo) {
+            var dateEnv = this.context.dateEnv;
+            if (formatter == null) {
+                formatter = this.eventTimeFormat;
+            }
+            if (displayEnd == null) {
+                displayEnd = this.displayEventEnd;
+            }
+            if (this.displayEventTime && !allDay) {
+                if (displayEnd && end) {
+                    return dateEnv.formatRange(start, end, formatter, {
+                        forcedStartTzo: forcedStartTzo,
+                        forcedEndTzo: forcedEndTzo
+                    });
+                }
+                else {
+                    return dateEnv.format(start, formatter, {
+                        forcedTzo: forcedStartTzo
+                    });
+                }
+            }
+            return '';
+        };
+        FgEventRenderer.prototype.computeEventTimeFormat = function () {
+            return {
+                hour: 'numeric',
+                minute: '2-digit',
+                omitZeroMinute: true
+            };
+        };
+        FgEventRenderer.prototype.computeDisplayEventTime = function () {
+            return true;
+        };
+        FgEventRenderer.prototype.computeDisplayEventEnd = function () {
+            return true;
+        };
+        // Utility for generating event skin-related CSS properties
+        FgEventRenderer.prototype.getSkinCss = function (ui) {
+            return {
+                'background-color': ui.backgroundColor,
+                'border-color': ui.borderColor,
+                color: ui.textColor
+            };
+        };
+        FgEventRenderer.prototype.sortEventSegs = function (segs) {
+            var specs = this.context.view.eventOrderSpecs;
+            var objs = segs.map(buildSegCompareObj);
+            objs.sort(function (obj0, obj1) {
+                return compareByFieldSpecs(obj0, obj1, specs);
+            });
+            return objs.map(function (c) {
+                return c._seg;
+            });
+        };
+        FgEventRenderer.prototype.computeSizes = function (force) {
+            if (force || this.isSizeDirty) {
+                this.computeSegSizes(this.segs);
+            }
+        };
+        FgEventRenderer.prototype.assignSizes = function (force) {
+            if (force || this.isSizeDirty) {
+                this.assignSegSizes(this.segs);
+                this.isSizeDirty = false;
+            }
+        };
+        FgEventRenderer.prototype.computeSegSizes = function (segs) {
+        };
+        FgEventRenderer.prototype.assignSegSizes = function (segs) {
+        };
+        // Manipulation on rendered segs
+        FgEventRenderer.prototype.hideByHash = function (hash) {
+            if (hash) {
+                for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                    var seg = _a[_i];
+                    if (hash[seg.eventRange.instance.instanceId]) {
+                        seg.el.style.visibility = 'hidden';
+                    }
+                }
+            }
+        };
+        FgEventRenderer.prototype.showByHash = function (hash) {
+            if (hash) {
+                for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                    var seg = _a[_i];
+                    if (hash[seg.eventRange.instance.instanceId]) {
+                        seg.el.style.visibility = '';
+                    }
+                }
+            }
+        };
+        FgEventRenderer.prototype.selectByInstanceId = function (instanceId) {
+            if (instanceId) {
+                for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                    var seg = _a[_i];
+                    var eventInstance = seg.eventRange.instance;
+                    if (eventInstance && eventInstance.instanceId === instanceId &&
+                        seg.el // necessary?
+                    ) {
+                        seg.el.classList.add('fc-selected');
+                    }
+                }
+            }
+        };
+        FgEventRenderer.prototype.unselectByInstanceId = function (instanceId) {
+            if (instanceId) {
+                for (var _i = 0, _a = this.segs; _i < _a.length; _i++) {
+                    var seg = _a[_i];
+                    if (seg.el) { // necessary?
+                        seg.el.classList.remove('fc-selected');
+                    }
+                }
+            }
+        };
+        return FgEventRenderer;
+    }());
+    // returns a object with all primitive props that can be compared
+    function buildSegCompareObj(seg) {
+        var eventDef = seg.eventRange.def;
+        var range = seg.eventRange.instance.range;
+        var start = range.start ? range.start.valueOf() : 0; // TODO: better support for open-range events
+        var end = range.end ? range.end.valueOf() : 0; // "
+        return __assign({}, eventDef.extendedProps, eventDef, { id: eventDef.publicId, start: start,
+            end: end, duration: end - start, allDay: Number(eventDef.allDay), _seg: seg // for later retrieval
+         });
+    }
+
+    var FillRenderer = /** @class */ (function () {
+        function FillRenderer(context) {
+            this.fillSegTag = 'div';
+            this.dirtySizeFlags = {};
+            this.context = context;
+            this.containerElsByType = {};
+            this.segsByType = {};
+        }
+        FillRenderer.prototype.getSegsByType = function (type) {
+            return this.segsByType[type] || [];
+        };
+        FillRenderer.prototype.renderSegs = function (type, segs) {
+            var _a;
+            var renderedSegs = this.renderSegEls(type, segs); // assignes `.el` to each seg. returns successfully rendered segs
+            var containerEls = this.attachSegs(type, renderedSegs);
+            if (containerEls) {
+                (_a = (this.containerElsByType[type] || (this.containerElsByType[type] = []))).push.apply(_a, containerEls);
+            }
+            this.segsByType[type] = renderedSegs;
+            if (type === 'bgEvent') {
+                this.context.view.triggerRenderedSegs(renderedSegs, false); // isMirror=false
+            }
+            this.dirtySizeFlags[type] = true;
+        };
+        // Unrenders a specific type of fill that is currently rendered on the grid
+        FillRenderer.prototype.unrender = function (type) {
+            var segs = this.segsByType[type];
+            if (segs) {
+                if (type === 'bgEvent') {
+                    this.context.view.triggerWillRemoveSegs(segs, false); // isMirror=false
+                }
+                this.detachSegs(type, segs);
+            }
+        };
+        // Renders and assigns an `el` property for each fill segment. Generic enough to work with different types.
+        // Only returns segments that successfully rendered.
+        FillRenderer.prototype.renderSegEls = function (type, segs) {
+            var _this = this;
+            var html = '';
+            var i;
+            if (segs.length) {
+                // build a large concatenation of segment HTML
+                for (i = 0; i < segs.length; i++) {
+                    html += this.renderSegHtml(type, segs[i]);
+                }
+                // Grab individual elements from the combined HTML string. Use each as the default rendering.
+                // Then, compute the 'el' for each segment.
+                htmlToElements(html).forEach(function (el, i) {
+                    var seg = segs[i];
+                    if (el) {
+                        seg.el = el;
+                    }
+                });
+                if (type === 'bgEvent') {
+                    segs = filterSegsViaEls(this.context.view, segs, false // isMirror. background events can never be mirror elements
+                    );
+                }
+                // correct element type? (would be bad if a non-TD were inserted into a table for example)
+                segs = segs.filter(function (seg) {
+                    return elementMatches(seg.el, _this.fillSegTag);
+                });
+            }
+            return segs;
+        };
+        // Builds the HTML needed for one fill segment. Generic enough to work with different types.
+        FillRenderer.prototype.renderSegHtml = function (type, seg) {
+            var css = null;
+            var classNames = [];
+            if (type !== 'highlight' && type !== 'businessHours') {
+                css = {
+                    'background-color': seg.eventRange.ui.backgroundColor
+                };
+            }
+            if (type !== 'highlight') {
+                classNames = classNames.concat(seg.eventRange.ui.classNames);
+            }
+            if (type === 'businessHours') {
+                classNames.push('fc-bgevent');
+            }
+            else {
+                classNames.push('fc-' + type.toLowerCase());
+            }
+            return '<' + this.fillSegTag +
+                (classNames.length ? ' class="' + classNames.join(' ') + '"' : '') +
+                (css ? ' style="' + cssToStr(css) + '"' : '') +
+                '></' + this.fillSegTag + '>';
+        };
+        FillRenderer.prototype.detachSegs = function (type, segs) {
+            var containerEls = this.containerElsByType[type];
+            if (containerEls) {
+                containerEls.forEach(removeElement);
+                delete this.containerElsByType[type];
+            }
+        };
+        FillRenderer.prototype.computeSizes = function (force) {
+            for (var type in this.segsByType) {
+                if (force || this.dirtySizeFlags[type]) {
+                    this.computeSegSizes(this.segsByType[type]);
+                }
+            }
+        };
+        FillRenderer.prototype.assignSizes = function (force) {
+            for (var type in this.segsByType) {
+                if (force || this.dirtySizeFlags[type]) {
+                    this.assignSegSizes(this.segsByType[type]);
+                }
+            }
+            this.dirtySizeFlags = {};
+        };
+        FillRenderer.prototype.computeSegSizes = function (segs) {
+        };
+        FillRenderer.prototype.assignSegSizes = function (segs) {
+        };
+        return FillRenderer;
+    }());
+
+    var NamedTimeZoneImpl = /** @class */ (function () {
+        function NamedTimeZoneImpl(timeZoneName) {
+            this.timeZoneName = timeZoneName;
+        }
+        return NamedTimeZoneImpl;
+    }());
+
+    /*
+    An abstraction for a dragging interaction originating on an event.
+    Does higher-level things than PointerDragger, such as possibly:
+    - a "mirror" that moves with the pointer
+    - a minimum number of pixels or other criteria for a true drag to begin
+
+    subclasses must emit:
+    - pointerdown
+    - dragstart
+    - dragmove
+    - pointerup
+    - dragend
+    */
+    var ElementDragging = /** @class */ (function () {
+        function ElementDragging(el) {
+            this.emitter = new EmitterMixin();
+        }
+        ElementDragging.prototype.destroy = function () {
+        };
+        ElementDragging.prototype.setMirrorIsVisible = function (bool) {
+            // optional if subclass doesn't want to support a mirror
+        };
+        ElementDragging.prototype.setMirrorNeedsRevert = function (bool) {
+            // optional if subclass doesn't want to support a mirror
+        };
+        ElementDragging.prototype.setAutoScrollEnabled = function (bool) {
+            // optional
+        };
+        return ElementDragging;
+    }());
+
+    function formatDate(dateInput, settings) {
+        if (settings === void 0) { settings = {}; }
+        var dateEnv = buildDateEnv$1(settings);
+        var formatter = createFormatter(settings);
+        var dateMeta = dateEnv.createMarkerMeta(dateInput);
+        if (!dateMeta) { // TODO: warning?
+            return '';
+        }
+        return dateEnv.format(dateMeta.marker, formatter, {
+            forcedTzo: dateMeta.forcedTzo
+        });
+    }
+    function formatRange(startInput, endInput, settings // mixture of env and formatter settings
+    ) {
+        var dateEnv = buildDateEnv$1(typeof settings === 'object' && settings ? settings : {}); // pass in if non-null object
+        var formatter = createFormatter(settings, globalDefaults.defaultRangeSeparator);
+        var startMeta = dateEnv.createMarkerMeta(startInput);
+        var endMeta = dateEnv.createMarkerMeta(endInput);
+        if (!startMeta || !endMeta) { // TODO: warning?
+            return '';
+        }
+        return dateEnv.formatRange(startMeta.marker, endMeta.marker, formatter, {
+            forcedStartTzo: startMeta.forcedTzo,
+            forcedEndTzo: endMeta.forcedTzo,
+            isEndExclusive: settings.isEndExclusive
+        });
+    }
+    // TODO: more DRY and optimized
+    function buildDateEnv$1(settings) {
+        var locale = buildLocale(settings.locale || 'en', parseRawLocales([]).map); // TODO: don't hardcode 'en' everywhere
+        // ensure required settings
+        settings = __assign({ timeZone: globalDefaults.timeZone, calendarSystem: 'gregory' }, settings, { locale: locale });
+        return new DateEnv(settings);
+    }
+
+    var DRAG_META_PROPS = {
+        startTime: createDuration,
+        duration: createDuration,
+        create: Boolean,
+        sourceId: String
+    };
+    var DRAG_META_DEFAULTS = {
+        create: true
+    };
+    function parseDragMeta(raw) {
+        var leftoverProps = {};
+        var refined = refineProps(raw, DRAG_META_PROPS, DRAG_META_DEFAULTS, leftoverProps);
+        refined.leftoverProps = leftoverProps;
+        return refined;
+    }
+
+    // Computes a default column header formatting string if `colFormat` is not explicitly defined
+    function computeFallbackHeaderFormat(datesRepDistinctDays, dayCnt) {
+        // if more than one week row, or if there are a lot of columns with not much space,
+        // put just the day numbers will be in each cell
+        if (!datesRepDistinctDays || dayCnt > 10) {
+            return { weekday: 'short' }; // "Sat"
+        }
+        else if (dayCnt > 1) {
+            return { weekday: 'short', month: 'numeric', day: 'numeric', omitCommas: true }; // "Sat 11/12"
+        }
+        else {
+            return { weekday: 'long' }; // "Saturday"
+        }
+    }
+    function renderDateCell(dateMarker, dateProfile, datesRepDistinctDays, colCnt, colHeadFormat, context, colspan, otherAttrs) {
+        var view = context.view, dateEnv = context.dateEnv, theme = context.theme, options = context.options;
+        var isDateValid = rangeContainsMarker(dateProfile.activeRange, dateMarker); // TODO: called too frequently. cache somehow.
+        var classNames = [
+            'fc-day-header',
+            theme.getClass('widgetHeader')
+        ];
+        var innerHtml;
+        if (typeof options.columnHeaderHtml === 'function') {
+            innerHtml = options.columnHeaderHtml(dateEnv.toDate(dateMarker));
+        }
+        else if (typeof options.columnHeaderText === 'function') {
+            innerHtml = htmlEscape(options.columnHeaderText(dateEnv.toDate(dateMarker)));
+        }
+        else {
+            innerHtml = htmlEscape(dateEnv.format(dateMarker, colHeadFormat));
+        }
+        // if only one row of days, the classNames on the header can represent the specific days beneath
+        if (datesRepDistinctDays) {
+            classNames = classNames.concat(
+            // includes the day-of-week class
+            // noThemeHighlight=true (don't highlight the header)
+            getDayClasses(dateMarker, dateProfile, context, true));
+        }
+        else {
+            classNames.push('fc-' + DAY_IDS[dateMarker.getUTCDay()]); // only add the day-of-week class
+        }
+        return '' +
+            '<th class="' + classNames.join(' ') + '"' +
+            ((isDateValid && datesRepDistinctDays) ?
+                ' data-date="' + dateEnv.formatIso(dateMarker, { omitTime: true }) + '"' :
+                '') +
+            (colspan > 1 ?
+                ' colspan="' + colspan + '"' :
+                '') +
+            (otherAttrs ?
+                ' ' + otherAttrs :
+                '') +
+            '>' +
+            (isDateValid ?
+                // don't make a link if the heading could represent multiple days, or if there's only one day (forceOff)
+                buildGotoAnchorHtml(view, { date: dateMarker, forceOff: !datesRepDistinctDays || colCnt === 1 }, innerHtml) :
+                // if not valid, display text, but no link
+                innerHtml) +
+            '</th>';
+    }
+
+    var DayHeader = /** @class */ (function (_super) {
+        __extends(DayHeader, _super);
+        function DayHeader(context, parentEl) {
+            var _this = _super.call(this, context) || this;
+            parentEl.innerHTML = ''; // because might be nbsp
+            parentEl.appendChild(_this.el = htmlToElement('<div class="fc-row ' + _this.theme.getClass('headerRow') + '">' +
+                '<table class="' + _this.theme.getClass('tableGrid') + '">' +
+                '<thead></thead>' +
+                '</table>' +
+                '</div>'));
+            _this.thead = _this.el.querySelector('thead');
+            return _this;
+        }
+        DayHeader.prototype.destroy = function () {
+            removeElement(this.el);
+        };
+        DayHeader.prototype.render = function (props) {
+            var dates = props.dates, datesRepDistinctDays = props.datesRepDistinctDays;
+            var parts = [];
+            if (props.renderIntroHtml) {
+                parts.push(props.renderIntroHtml());
+            }
+            var colHeadFormat = createFormatter(this.opt('columnHeaderFormat') ||
+                computeFallbackHeaderFormat(datesRepDistinctDays, dates.length));
+            for (var _i = 0, dates_1 = dates; _i < dates_1.length; _i++) {
+                var date = dates_1[_i];
+                parts.push(renderDateCell(date, props.dateProfile, datesRepDistinctDays, dates.length, colHeadFormat, this.context));
+            }
+            if (this.isRtl) {
+                parts.reverse();
+            }
+            this.thead.innerHTML = '<tr>' + parts.join('') + '</tr>';
+        };
+        return DayHeader;
+    }(Component));
+
+    var DaySeries = /** @class */ (function () {
+        function DaySeries(range, dateProfileGenerator) {
+            var date = range.start;
+            var end = range.end;
+            var indices = [];
+            var dates = [];
+            var dayIndex = -1;
+            while (date < end) { // loop each day from start to end
+                if (dateProfileGenerator.isHiddenDay(date)) {
+                    indices.push(dayIndex + 0.5); // mark that it's between indices
+                }
+                else {
+                    dayIndex++;
+                    indices.push(dayIndex);
+                    dates.push(date);
+                }
+                date = addDays(date, 1);
+            }
+            this.dates = dates;
+            this.indices = indices;
+            this.cnt = dates.length;
+        }
+        DaySeries.prototype.sliceRange = function (range) {
+            var firstIndex = this.getDateDayIndex(range.start); // inclusive first index
+            var lastIndex = this.getDateDayIndex(addDays(range.end, -1)); // inclusive last index
+            var clippedFirstIndex = Math.max(0, firstIndex);
+            var clippedLastIndex = Math.min(this.cnt - 1, lastIndex);
+            // deal with in-between indices
+            clippedFirstIndex = Math.ceil(clippedFirstIndex); // in-between starts round to next cell
+            clippedLastIndex = Math.floor(clippedLastIndex); // in-between ends round to prev cell
+            if (clippedFirstIndex <= clippedLastIndex) {
+                return {
+                    firstIndex: clippedFirstIndex,
+                    lastIndex: clippedLastIndex,
+                    isStart: firstIndex === clippedFirstIndex,
+                    isEnd: lastIndex === clippedLastIndex
+                };
+            }
+            else {
+                return null;
+            }
+        };
+        // Given a date, returns its chronolocial cell-index from the first cell of the grid.
+        // If the date lies between cells (because of hiddenDays), returns a floating-point value between offsets.
+        // If before the first offset, returns a negative number.
+        // If after the last offset, returns an offset past the last cell offset.
+        // Only works for *start* dates of cells. Will not work for exclusive end dates for cells.
+        DaySeries.prototype.getDateDayIndex = function (date) {
+            var indices = this.indices;
+            var dayOffset = Math.floor(diffDays(this.dates[0], date));
+            if (dayOffset < 0) {
+                return indices[0] - 1;
+            }
+            else if (dayOffset >= indices.length) {
+                return indices[indices.length - 1] + 1;
+            }
+            else {
+                return indices[dayOffset];
+            }
+        };
+        return DaySeries;
+    }());
+
+    var DayTable = /** @class */ (function () {
+        function DayTable(daySeries, breakOnWeeks) {
+            var dates = daySeries.dates;
+            var daysPerRow;
+            var firstDay;
+            var rowCnt;
+            if (breakOnWeeks) {
+                // count columns until the day-of-week repeats
+                firstDay = dates[0].getUTCDay();
+                for (daysPerRow = 1; daysPerRow < dates.length; daysPerRow++) {
+                    if (dates[daysPerRow].getUTCDay() === firstDay) {
+                        break;
+                    }
+                }
+                rowCnt = Math.ceil(dates.length / daysPerRow);
+            }
+            else {
+                rowCnt = 1;
+                daysPerRow = dates.length;
+            }
+            this.rowCnt = rowCnt;
+            this.colCnt = daysPerRow;
+            this.daySeries = daySeries;
+            this.cells = this.buildCells();
+            this.headerDates = this.buildHeaderDates();
+        }
+        DayTable.prototype.buildCells = function () {
+            var rows = [];
+            for (var row = 0; row < this.rowCnt; row++) {
+                var cells = [];
+                for (var col = 0; col < this.colCnt; col++) {
+                    cells.push(this.buildCell(row, col));
+                }
+                rows.push(cells);
+            }
+            return rows;
+        };
+        DayTable.prototype.buildCell = function (row, col) {
+            return {
+                date: this.daySeries.dates[row * this.colCnt + col]
+            };
+        };
+        DayTable.prototype.buildHeaderDates = function () {
+            var dates = [];
+            for (var col = 0; col < this.colCnt; col++) {
+                dates.push(this.cells[0][col].date);
+            }
+            return dates;
+        };
+        DayTable.prototype.sliceRange = function (range) {
+            var colCnt = this.colCnt;
+            var seriesSeg = this.daySeries.sliceRange(range);
+            var segs = [];
+            if (seriesSeg) {
+                var firstIndex = seriesSeg.firstIndex, lastIndex = seriesSeg.lastIndex;
+                var index = firstIndex;
+                while (index <= lastIndex) {
+                    var row = Math.floor(index / colCnt);
+                    var nextIndex = Math.min((row + 1) * colCnt, lastIndex + 1);
+                    segs.push({
+                        row: row,
+                        firstCol: index % colCnt,
+                        lastCol: (nextIndex - 1) % colCnt,
+                        isStart: seriesSeg.isStart && index === firstIndex,
+                        isEnd: seriesSeg.isEnd && (nextIndex - 1) === lastIndex
+                    });
+                    index = nextIndex;
+                }
+            }
+            return segs;
+        };
+        return DayTable;
+    }());
+
+    var Slicer = /** @class */ (function () {
+        function Slicer() {
+            this.sliceBusinessHours = memoize(this._sliceBusinessHours);
+            this.sliceDateSelection = memoize(this._sliceDateSpan);
+            this.sliceEventStore = memoize(this._sliceEventStore);
+            this.sliceEventDrag = memoize(this._sliceInteraction);
+            this.sliceEventResize = memoize(this._sliceInteraction);
+        }
+        Slicer.prototype.sliceProps = function (props, dateProfile, nextDayThreshold, component) {
+            var extraArgs = [];
+            for (var _i = 4; _i < arguments.length; _i++) {
+                extraArgs[_i - 4] = arguments[_i];
+            }
+            var eventUiBases = props.eventUiBases;
+            var eventSegs = this.sliceEventStore.apply(this, [props.eventStore, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs));
+            return {
+                dateSelectionSegs: this.sliceDateSelection.apply(this, [props.dateSelection, eventUiBases, component].concat(extraArgs)),
+                businessHourSegs: this.sliceBusinessHours.apply(this, [props.businessHours, dateProfile, nextDayThreshold, component].concat(extraArgs)),
+                fgEventSegs: eventSegs.fg,
+                bgEventSegs: eventSegs.bg,
+                eventDrag: this.sliceEventDrag.apply(this, [props.eventDrag, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)),
+                eventResize: this.sliceEventResize.apply(this, [props.eventResize, eventUiBases, dateProfile, nextDayThreshold, component].concat(extraArgs)),
+                eventSelection: props.eventSelection
+            }; // TODO: give interactionSegs?
+        };
+        Slicer.prototype.sliceNowDate = function (// does not memoize
+        date, component) {
+            var extraArgs = [];
+            for (var _i = 2; _i < arguments.length; _i++) {
+                extraArgs[_i - 2] = arguments[_i];
+            }
+            return this._sliceDateSpan.apply(this, [{ range: { start: date, end: addMs(date, 1) }, allDay: false },
+                {},
+                component].concat(extraArgs));
+        };
+        Slicer.prototype._sliceBusinessHours = function (businessHours, dateProfile, nextDayThreshold, component) {
+            var extraArgs = [];
+            for (var _i = 4; _i < arguments.length; _i++) {
+                extraArgs[_i - 4] = arguments[_i];
+            }
+            if (!businessHours) {
+                return [];
+            }
+            return this._sliceEventStore.apply(this, [expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), component.calendar),
+                {},
+                dateProfile,
+                nextDayThreshold,
+                component].concat(extraArgs)).bg;
+        };
+        Slicer.prototype._sliceEventStore = function (eventStore, eventUiBases, dateProfile, nextDayThreshold, component) {
+            var extraArgs = [];
+            for (var _i = 5; _i < arguments.length; _i++) {
+                extraArgs[_i - 5] = arguments[_i];
+            }
+            if (eventStore) {
+                var rangeRes = sliceEventStore(eventStore, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold);
+                return {
+                    bg: this.sliceEventRanges(rangeRes.bg, component, extraArgs),
+                    fg: this.sliceEventRanges(rangeRes.fg, component, extraArgs)
+                };
+            }
+            else {
+                return { bg: [], fg: [] };
+            }
+        };
+        Slicer.prototype._sliceInteraction = function (interaction, eventUiBases, dateProfile, nextDayThreshold, component) {
+            var extraArgs = [];
+            for (var _i = 5; _i < arguments.length; _i++) {
+                extraArgs[_i - 5] = arguments[_i];
+            }
+            if (!interaction) {
+                return null;
+            }
+            var rangeRes = sliceEventStore(interaction.mutatedEvents, eventUiBases, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), nextDayThreshold);
+            return {
+                segs: this.sliceEventRanges(rangeRes.fg, component, extraArgs),
+                affectedInstances: interaction.affectedEvents.instances,
+                isEvent: interaction.isEvent,
+                sourceSeg: interaction.origSeg
+            };
+        };
+        Slicer.prototype._sliceDateSpan = function (dateSpan, eventUiBases, component) {
+            var extraArgs = [];
+            for (var _i = 3; _i < arguments.length; _i++) {
+                extraArgs[_i - 3] = arguments[_i];
+            }
+            if (!dateSpan) {
+                return [];
+            }
+            var eventRange = fabricateEventRange(dateSpan, eventUiBases, component.calendar);
+            var segs = this.sliceRange.apply(this, [dateSpan.range].concat(extraArgs));
+            for (var _a = 0, segs_1 = segs; _a < segs_1.length; _a++) {
+                var seg = segs_1[_a];
+                seg.component = component;
+                seg.eventRange = eventRange;
+            }
+            return segs;
+        };
+        /*
+        "complete" seg means it has component and eventRange
+        */
+        Slicer.prototype.sliceEventRanges = function (eventRanges, component, // TODO: kill
+        extraArgs) {
+            var segs = [];
+            for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
+                var eventRange = eventRanges_1[_i];
+                segs.push.apply(segs, this.sliceEventRange(eventRange, component, extraArgs));
+            }
+            return segs;
+        };
+        /*
+        "complete" seg means it has component and eventRange
+        */
+        Slicer.prototype.sliceEventRange = function (eventRange, component, // TODO: kill
+        extraArgs) {
+            var segs = this.sliceRange.apply(this, [eventRange.range].concat(extraArgs));
+            for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+                var seg = segs_2[_i];
+                seg.component = component;
+                seg.eventRange = eventRange;
+                seg.isStart = eventRange.isStart && seg.isStart;
+                seg.isEnd = eventRange.isEnd && seg.isEnd;
+            }
+            return segs;
+        };
+        return Slicer;
+    }());
+    /*
+    for incorporating minTime/maxTime if appropriate
+    TODO: should be part of DateProfile!
+    TimelineDateProfile already does this btw
+    */
+    function computeActiveRange(dateProfile, isComponentAllDay) {
+        var range = dateProfile.activeRange;
+        if (isComponentAllDay) {
+            return range;
+        }
+        return {
+            start: addMs(range.start, dateProfile.minTime.milliseconds),
+            end: addMs(range.end, dateProfile.maxTime.milliseconds - 864e5) // 864e5 = ms in a day
+        };
+    }
+
+    // exports
+    // --------------------------------------------------------------------------------------------------
+    var version = '4.3.1';
+
+    exports.Calendar = Calendar;
+    exports.Component = Component;
+    exports.DateComponent = DateComponent;
+    exports.DateEnv = DateEnv;
+    exports.DateProfileGenerator = DateProfileGenerator;
+    exports.DayHeader = DayHeader;
+    exports.DaySeries = DaySeries;
+    exports.DayTable = DayTable;
+    exports.ElementDragging = ElementDragging;
+    exports.ElementScrollController = ElementScrollController;
+    exports.EmitterMixin = EmitterMixin;
+    exports.EventApi = EventApi;
+    exports.FgEventRenderer = FgEventRenderer;
+    exports.FillRenderer = FillRenderer;
+    exports.Interaction = Interaction;
+    exports.Mixin = Mixin;
+    exports.NamedTimeZoneImpl = NamedTimeZoneImpl;
+    exports.PositionCache = PositionCache;
+    exports.ScrollComponent = ScrollComponent;
+    exports.ScrollController = ScrollController;
+    exports.Slicer = Slicer;
+    exports.Splitter = Splitter;
+    exports.Theme = Theme;
+    exports.View = View;
+    exports.WindowScrollController = WindowScrollController;
+    exports.addDays = addDays;
+    exports.addDurations = addDurations;
+    exports.addMs = addMs;
+    exports.addWeeks = addWeeks;
+    exports.allowContextMenu = allowContextMenu;
+    exports.allowSelection = allowSelection;
+    exports.appendToElement = appendToElement;
+    exports.applyAll = applyAll;
+    exports.applyMutationToEventStore = applyMutationToEventStore;
+    exports.applyStyle = applyStyle;
+    exports.applyStyleProp = applyStyleProp;
+    exports.asRoughMinutes = asRoughMinutes;
+    exports.asRoughMs = asRoughMs;
+    exports.asRoughSeconds = asRoughSeconds;
+    exports.buildGotoAnchorHtml = buildGotoAnchorHtml;
+    exports.buildSegCompareObj = buildSegCompareObj;
+    exports.capitaliseFirstLetter = capitaliseFirstLetter;
+    exports.combineEventUis = combineEventUis;
+    exports.compareByFieldSpec = compareByFieldSpec;
+    exports.compareByFieldSpecs = compareByFieldSpecs;
+    exports.compareNumbers = compareNumbers;
+    exports.compensateScroll = compensateScroll;
+    exports.computeClippingRect = computeClippingRect;
+    exports.computeEdges = computeEdges;
+    exports.computeFallbackHeaderFormat = computeFallbackHeaderFormat;
+    exports.computeHeightAndMargins = computeHeightAndMargins;
+    exports.computeInnerRect = computeInnerRect;
+    exports.computeRect = computeRect;
+    exports.computeVisibleDayRange = computeVisibleDayRange;
+    exports.config = config;
+    exports.constrainPoint = constrainPoint;
+    exports.createDuration = createDuration;
+    exports.createElement = createElement;
+    exports.createEmptyEventStore = createEmptyEventStore;
+    exports.createEventInstance = createEventInstance;
+    exports.createFormatter = createFormatter;
+    exports.createPlugin = createPlugin;
+    exports.cssToStr = cssToStr;
+    exports.debounce = debounce;
+    exports.diffDates = diffDates;
+    exports.diffDayAndTime = diffDayAndTime;
+    exports.diffDays = diffDays;
+    exports.diffPoints = diffPoints;
+    exports.diffWeeks = diffWeeks;
+    exports.diffWholeDays = diffWholeDays;
+    exports.diffWholeWeeks = diffWholeWeeks;
+    exports.disableCursor = disableCursor;
+    exports.distributeHeight = distributeHeight;
+    exports.elementClosest = elementClosest;
+    exports.elementMatches = elementMatches;
+    exports.enableCursor = enableCursor;
+    exports.eventTupleToStore = eventTupleToStore;
+    exports.filterEventStoreDefs = filterEventStoreDefs;
+    exports.filterHash = filterHash;
+    exports.findChildren = findChildren;
+    exports.findElements = findElements;
+    exports.flexibleCompare = flexibleCompare;
+    exports.forceClassName = forceClassName;
+    exports.formatDate = formatDate;
+    exports.formatIsoTimeString = formatIsoTimeString;
+    exports.formatRange = formatRange;
+    exports.getAllDayHtml = getAllDayHtml;
+    exports.getClippingParents = getClippingParents;
+    exports.getDayClasses = getDayClasses;
+    exports.getElSeg = getElSeg;
+    exports.getRectCenter = getRectCenter;
+    exports.getRelevantEvents = getRelevantEvents;
+    exports.globalDefaults = globalDefaults;
+    exports.greatestDurationDenominator = greatestDurationDenominator;
+    exports.hasBgRendering = hasBgRendering;
+    exports.htmlEscape = htmlEscape;
+    exports.htmlToElement = htmlToElement;
+    exports.insertAfterElement = insertAfterElement;
+    exports.interactionSettingsStore = interactionSettingsStore;
+    exports.interactionSettingsToStore = interactionSettingsToStore;
+    exports.intersectRanges = intersectRanges;
+    exports.intersectRects = intersectRects;
+    exports.isArraysEqual = isArraysEqual;
+    exports.isDateSpansEqual = isDateSpansEqual;
+    exports.isInt = isInt;
+    exports.isInteractionValid = isInteractionValid;
+    exports.isMultiDayRange = isMultiDayRange;
+    exports.isPropsEqual = isPropsEqual;
+    exports.isPropsValid = isPropsValid;
+    exports.isSingleDay = isSingleDay;
+    exports.isValidDate = isValidDate;
+    exports.listenBySelector = listenBySelector;
+    exports.mapHash = mapHash;
+    exports.matchCellWidths = matchCellWidths;
+    exports.memoize = memoize;
+    exports.memoizeOutput = memoizeOutput;
+    exports.memoizeRendering = memoizeRendering;
+    exports.mergeEventStores = mergeEventStores;
+    exports.multiplyDuration = multiplyDuration;
+    exports.padStart = padStart;
+    exports.parseBusinessHours = parseBusinessHours;
+    exports.parseDragMeta = parseDragMeta;
+    exports.parseEventDef = parseEventDef;
+    exports.parseFieldSpecs = parseFieldSpecs;
+    exports.parseMarker = parse;
+    exports.pointInsideRect = pointInsideRect;
+    exports.prependToElement = prependToElement;
+    exports.preventContextMenu = preventContextMenu;
+    exports.preventDefault = preventDefault;
+    exports.preventSelection = preventSelection;
+    exports.processScopedUiProps = processScopedUiProps;
+    exports.rangeContainsMarker = rangeContainsMarker;
+    exports.rangeContainsRange = rangeContainsRange;
+    exports.rangesEqual = rangesEqual;
+    exports.rangesIntersect = rangesIntersect;
+    exports.refineProps = refineProps;
+    exports.removeElement = removeElement;
+    exports.removeExact = removeExact;
+    exports.renderDateCell = renderDateCell;
+    exports.requestJson = requestJson;
+    exports.sliceEventStore = sliceEventStore;
+    exports.startOfDay = startOfDay;
+    exports.subtractInnerElHeight = subtractInnerElHeight;
+    exports.translateRect = translateRect;
+    exports.uncompensateScroll = uncompensateScroll;
+    exports.undistributeHeight = undistributeHeight;
+    exports.unpromisify = unpromisify;
+    exports.version = version;
+    exports.whenTransitionDone = whenTransitionDone;
+    exports.wholeDivideDurations = wholeDivideDurations;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/main.min.css b/AKPlan/static/AKPlan/fullcalendar/core/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..8948b534b177ac86405cac6e82acf66876348b3a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/main.min.css
@@ -0,0 +1 @@
+@charset "UTF-8";.fc-button:not(:disabled),.fc-event.fc-draggable,.fc-event[href],.fc-popover .fc-header .fc-close,a.fc-more,a[data-goto]{cursor:pointer}.fc-bg,.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}body .fc{font-size:1em}.fc-highlight{background:#bce8f1;opacity:.3}.fc-bgevent{background:#8fdf82;opacity:.3}.fc-nonbusiness{background:#d7d7d7}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{display:flex;flex-direction:row;justify-content:space-between;align-items:center;padding:2px 4px}.fc-rtl .fc-popover .fc-header{flex-direction:row-reverse}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-popover .fc-header .fc-close{opacity:.65;font-size:1.1em}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-bg table,.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-bg,.fc-bgevent-skeleton,.fc-highlight-skeleton,.fc-mirror-skeleton{position:absolute;top:0;left:0;right:0}.fc table{width:100%;box-sizing:border-box;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}a[data-goto]:hover{text-decoration:underline}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-mirror-skeleton{z-index:5}.fc .fc-row .fc-content-skeleton table,.fc .fc-row .fc-content-skeleton td,.fc .fc-row .fc-mirror-skeleton td{background:0 0;border-color:transparent}.fc-row .fc-content-skeleton td,.fc-row .fc-mirror-skeleton td{border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-mirror-skeleton tbody td{border-top:0}.fc-scroller{-webkit-overflow-scrolling:touch}.fc-scroller>.fc-day-grid,.fc-scroller>.fc-time-grid{position:relative;width:100%}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.4;border-radius:3px;border:1px solid #3788d8}.fc-event,.fc-event-dot{background-color:#3788d8}.fc-event,.fc-event:hover{color:#fff;text-decoration:none}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:4;display:none}.fc-event.fc-allow-mouse-resize .fc-resizer,.fc-event.fc-selected .fc-resizer{display:block}.fc-event.fc-selected .fc-resizer:before{content:"";position:absolute;z-index:9999;top:50%;left:50%;width:40px;height:40px;margin-left:-20px;margin-top:-20px}.fc-event.fc-selected{z-index:9999!important;box-shadow:0 2px 5px rgba(0,0,0,.2)}.fc-event.fc-selected:after{content:"";position:absolute;z-index:1;top:-1px;right:-1px;bottom:-1px;left:-1px;background:#000;opacity:.25}.fc-event.fc-dragging.fc-selected{box-shadow:0 2px 7px rgba(0,0,0,.3)}.fc-event.fc-dragging:not(.fc-selected){opacity:.75}.fc-h-event.fc-selected:before{content:"";position:absolute;z-index:3;top:-10px;bottom:-10px;left:0;right:0}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-ltr .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-end-resizer{cursor:w-resize;left:-1px}.fc-ltr .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-start-resizer{cursor:e-resize;right:-1px}.fc-h-event.fc-allow-mouse-resize .fc-resizer{width:7px;top:-1px;bottom:-1px}.fc-h-event.fc-selected .fc-resizer{border-radius:4px;border-width:1px;width:6px;height:6px;border-style:solid;border-color:inherit;background:#fff;top:50%;margin-top:-4px}.fc-ltr .fc-h-event.fc-selected .fc-start-resizer,.fc-rtl .fc-h-event.fc-selected .fc-end-resizer{margin-left:-4px}.fc-ltr .fc-h-event.fc-selected .fc-end-resizer,.fc-rtl .fc-h-event.fc-selected .fc-start-resizer{margin-right:-4px}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}tr:first-child>td>.fc-day-grid-event{margin-top:2px}.fc-mirror-skeleton tr:first-child>td>.fc-day-grid-event{margin-top:0}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer{margin-left:-2px}.fc-ltr .fc-day-grid-event.fc-allow-mouse-resize .fc-end-resizer,.fc-rtl .fc-day-grid-event.fc-allow-mouse-resize .fc-start-resizer{margin-right:-2px}a.fc-more{margin:1px 3px;font-size:.85em;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-button,.fc-icon{display:inline-block;font-weight:400;text-align:center}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-now-indicator{position:absolute;border:0 solid red}.fc-unselectable{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent}.fc-unthemed .fc-content,.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-list-view,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-list-heading td,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed td.fc-today{background:#fcf8e3}.fc-unthemed .fc-disabled-day{background:#d7d7d7;opacity:.3}@font-face{font-family:fcicons;src:url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBfAAAAC8AAAAYGNtYXAXVtKNAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5ZgYydxIAAAF4AAAFNGhlYWQUJ7cIAAAGrAAAADZoaGVhB20DzAAABuQAAAAkaG10eCIABhQAAAcIAAAALGxvY2ED4AU6AAAHNAAAABhtYXhwAA8AjAAAB0wAAAAgbmFtZXsr690AAAdsAAABhnBvc3QAAwAAAAAI9AAAACAAAwPAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpBgPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Qb//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAABAWIAjQKeAskAEwAAJSc3NjQnJiIHAQYUFwEWMjc2NCcCnuLiDQ0MJAz/AA0NAQAMJAwNDcni4gwjDQwM/wANIwz/AA0NDCMNAAAAAQFiAI0CngLJABMAACUBNjQnASYiBwYUHwEHBhQXFjI3AZ4BAA0N/wAMJAwNDeLiDQ0MJAyNAQAMIw0BAAwMDSMM4uINIwwNDQAAAAIA4gC3Ax4CngATACcAACUnNzY0JyYiDwEGFB8BFjI3NjQnISc3NjQnJiIPAQYUHwEWMjc2NCcB87e3DQ0MIw3VDQ3VDSMMDQ0BK7e3DQ0MJAzVDQ3VDCQMDQ3zuLcMJAwNDdUNIwzWDAwNIwy4twwkDA0N1Q0jDNYMDA0jDAAAAgDiALcDHgKeABMAJwAAJTc2NC8BJiIHBhQfAQcGFBcWMjchNzY0LwEmIgcGFB8BBwYUFxYyNwJJ1Q0N1Q0jDA0Nt7cNDQwjDf7V1Q0N1QwkDA0Nt7cNDQwkDLfWDCMN1Q0NDCQMt7gMIw0MDNYMIw3VDQ0MJAy3uAwjDQwMAAADAFUAAAOrA1UAMwBoAHcAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMhMjY1NCYjISIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAAVYRGRkR/qoRGRkRA1UFBAUOCQkVDAsZDf2rDRkLDBUJCA4FBQUFBQUOCQgVDAsZDQJVDRkLDBUJCQ4FBAVVAgECBQMCBwQECAX9qwQJAwQHAwMFAQICAgIBBQMDBwQDCQQCVQUIBAQHAgMFAgEC/oAZEhEZGRESGQAAAAADAFUAAAOrA1UAMwBoAIkAABMiBgcOAQcOAQcOARURFBYXHgEXHgEXHgEzITI2Nz4BNz4BNz4BNRE0JicuAScuAScuASMFITIWFx4BFx4BFx4BFREUBgcOAQcOAQcOASMhIiYnLgEnLgEnLgE1ETQ2Nz4BNz4BNz4BMxMzFRQWMzI2PQEzMjY1NCYrATU0JiMiBh0BIyIGFRQWM9UNGAwLFQkJDgUFBQUFBQ4JCRULDBgNAlYNGAwLFQkJDgUFBQUFBQ4JCRULDBgN/aoCVgQIBAQHAwMFAQIBAQIBBQMDBwQECAT9qgQIBAQHAwMFAQIBAQIBBQMDBwQECASAgBkSEhmAERkZEYAZEhIZgBEZGREDVQUEBQ4JCRUMCxkN/asNGQsMFQkIDgUFBQUFBQ4JCBUMCxkNAlUNGQsMFQkJDgUEBVUCAQIFAwIHBAQIBf2rBAkDBAcDAwUBAgICAgEFAwMHBAMJBAJVBQgEBAcCAwUCAQL+gIASGRkSgBkSERmAEhkZEoAZERIZAAABAOIAjQMeAskAIAAAExcHBhQXFjI/ARcWMjc2NC8BNzY0JyYiDwEnJiIHBhQX4uLiDQ0MJAzi4gwkDA0N4uINDQwkDOLiDCQMDQ0CjeLiDSMMDQ3h4Q0NDCMN4uIMIw0MDOLiDAwNIwwAAAABAAAAAQAAa5n0y18PPPUACwQAAAAAANivOVsAAAAA2K85WwAAAAADqwNVAAAACAACAAAAAAAAAAEAAAPA/8AAAAQAAAAAAAOrAAEAAAAAAAAAAAAAAAAAAAALBAAAAAAAAAAAAAAAAgAAAAQAAWIEAAFiBAAA4gQAAOIEAABVBAAAVQQAAOIAAAAAAAoAFAAeAEQAagCqAOoBngJkApoAAQAAAAsAigADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAcAAAABAAAAAAACAAcAYAABAAAAAAADAAcANgABAAAAAAAEAAcAdQABAAAAAAAFAAsAFQABAAAAAAAGAAcASwABAAAAAAAKABoAigADAAEECQABAA4ABwADAAEECQACAA4AZwADAAEECQADAA4APQADAAEECQAEAA4AfAADAAEECQAFABYAIAADAAEECQAGAA4AUgADAAEECQAKADQApGZjaWNvbnMAZgBjAGkAYwBvAG4Ac1ZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMGZjaWNvbnMAZgBjAGkAYwBvAG4Ac2ZjaWNvbnMAZgBjAGkAYwBvAG4Ac1JlZ3VsYXIAUgBlAGcAdQBsAGEAcmZjaWNvbnMAZgBjAGkAYwBvAG4Ac0ZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype");font-weight:400;font-style:normal}.fc-icon{font-family:fcicons!important;speak:none;font-style:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:1em;height:1em}.fc-icon-chevron-left:before{content:""}.fc-icon-chevron-right:before{content:""}.fc-icon-chevrons-left:before{content:""}.fc-icon-chevrons-right:before{content:""}.fc-icon-minus-square:before{content:""}.fc-icon-plus-square:before{content:""}.fc-icon-x:before{content:""}.fc-button{overflow:visible;text-transform:none;margin:0;font-family:inherit}.fc-button::-moz-focus-inner{padding:0;border-style:none}.fc-button{-webkit-appearance:button;color:#212529;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.4em .65em;font-size:1em;line-height:1.5;border-radius:.25em}.fc-button:hover{color:#212529;text-decoration:none}.fc-button:focus{outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(44,62,80,.25);box-shadow:0 0 0 .2rem rgba(44,62,80,.25)}.fc-button:disabled{opacity:.65}.fc-button-primary{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.fc-button-primary:hover{color:#fff;background-color:#1e2b37;border-color:#1a252f}.fc-button-primary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(76,91,106,.5);box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc-button-primary:disabled{color:#fff;background-color:#2C3E50;border-color:#2C3E50}.fc-button-primary:not(:disabled).fc-button-active,.fc-button-primary:not(:disabled):active{color:#fff;background-color:#1a252f;border-color:#151e27}.fc-button-primary:not(:disabled).fc-button-active:focus,.fc-button-primary:not(:disabled):active:focus{-webkit-box-shadow:0 0 0 .2rem rgba(76,91,106,.5);box-shadow:0 0 0 .2rem rgba(76,91,106,.5)}.fc-button .fc-icon{vertical-align:middle;font-size:1.5em}.fc-button-group{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.fc-button-group>.fc-button{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.fc-button-group>.fc-button.fc-button-active,.fc-button-group>.fc-button:active,.fc-button-group>.fc-button:focus,.fc-button-group>.fc-button:hover{z-index:1}.fc-button-group>.fc-button:not(:first-child){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-button-group>.fc-button:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-list-item:hover td{background-color:#f5f5f5}.fc-toolbar{display:flex;justify-content:space-between;align-items:center}.fc-toolbar.fc-header-toolbar{margin-bottom:1.5em}.fc-toolbar.fc-footer-toolbar{margin-top:1.5em}.fc-toolbar>*>:not(:first-child){margin-left:.75em}.fc-toolbar h2{font-size:1.75em;margin:0}.fc-view-container{position:relative}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}@media print{.fc-bg,.fc-bgevent-container,.fc-bgevent-skeleton,.fc-business-container,.fc-event .fc-resizer,.fc-highlight-container,.fc-highlight-skeleton,.fc-mirror-container,.fc-mirror-skeleton{display:none}.fc tbody .fc-row,.fc-time-grid{min-height:0!important}.fc-time-grid .fc-event.fc-not-end:after,.fc-time-grid .fc-event.fc-not-start:before{content:"..."}.fc{max-width:100%!important}.fc-event{background:#fff!important;color:#000!important;page-break-inside:avoid}.fc hr,.fc tbody,.fc td,.fc th,.fc thead,.fc-row{border-color:#ccc!important;background:#fff!important}.fc tbody .fc-row{height:auto!important}.fc tbody .fc-row .fc-content-skeleton{position:static;padding-bottom:0!important}.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td{padding-bottom:1em}.fc tbody .fc-row .fc-content-skeleton table{height:1em}.fc-more,.fc-more-cell{display:none!important}.fc tr.fc-limited{display:table-row!important}.fc td.fc-limited{display:table-cell!important}.fc-popover,.fc-timeGrid-view .fc-axis{display:none}.fc-slats,.fc-time-grid hr{display:none!important}.fc button,.fc-button-group,.fc-time-grid .fc-event .fc-time span{display:none}.fc-time-grid .fc-content-skeleton{position:static}.fc-time-grid .fc-content-skeleton table{height:4em}.fc-time-grid .fc-event-container{margin:0!important}.fc-time-grid .fc-event{position:static!important;margin:3px 2px!important}.fc-time-grid .fc-event.fc-not-end{border-bottom-width:1px!important}.fc-time-grid .fc-event.fc-not-start{border-top-width:1px!important}.fc-time-grid .fc-event .fc-time{white-space:normal!important}.fc-time-grid .fc-event .fc-time:after{content:attr(data-full)}.fc-day-grid-container,.fc-scroller,.fc-time-grid-container{overflow:visible!important;height:auto!important}.fc-row{border:0!important;margin:0!important}}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/main.min.js b/AKPlan/static/AKPlan/fullcalendar/core/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..6b72fff6c1a4dc91ff572391fec23c0c36236079
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Core Package v4.3.1
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).FullCalendar={})}(this,function(e){"use strict";var t={className:!0,colSpan:!0,rowSpan:!0},n={"<tr":"tbody","<td":"tr"};function r(e,n,r){var i=document.createElement(e);if(n)for(var o in n)"style"===o?y(i,n[o]):t[o]?i[o]=n[o]:i.setAttribute(o,n[o]);return"string"==typeof r?i.innerHTML=r:null!=r&&s(i,r),i}function i(e){e=e.trim();var t=document.createElement(a(e));return t.innerHTML=e,t.firstChild}function o(e){return Array.prototype.slice.call(function(e){e=e.trim();var t=document.createElement(a(e));return t.innerHTML=e,t.childNodes}(e))}function a(e){return n[e.substr(0,3)]||"div"}function s(e,t){for(var n=l(t),r=0;r<n.length;r++)e.appendChild(n[r])}function u(e,t){for(var n=l(t),r=e.firstChild||null,i=0;i<n.length;i++)e.insertBefore(n[i],r)}function l(e){return"string"==typeof e?o(e):e instanceof Node?[e]:Array.prototype.slice.call(e)}function c(e){e.parentNode&&e.parentNode.removeChild(e)}var d=Element.prototype.matches||Element.prototype.matchesSelector||Element.prototype.msMatchesSelector,f=Element.prototype.closest||function(e){var t=this;if(!document.documentElement.contains(t))return null;do{if(h(t,e))return t;t=t.parentElement||t.parentNode}while(null!==t&&1===t.nodeType);return null};function p(e,t){return f.call(e,t)}function h(e,t){return d.call(e,t)}function v(e,t){for(var n=e instanceof HTMLElement?[e]:e,r=[],i=0;i<n.length;i++)for(var o=n[i].querySelectorAll(t),a=0;a<o.length;a++)r.push(o[a]);return r}var g=/(top|left|right|bottom|width|height)$/i;function y(e,t){for(var n in t)m(e,n,t[n])}function m(e,t,n){null==n?e.style[t]="":"number"==typeof n&&g.test(t)?e.style[t]=n+"px":e.style[t]=n}function E(e,t){var n={left:Math.max(e.left,t.left),right:Math.min(e.right,t.right),top:Math.max(e.top,t.top),bottom:Math.min(e.bottom,t.bottom)};return n.left<n.right&&n.top<n.bottom&&n}var S=null;function b(){return null===S&&(S=function(){var e=r("div",{style:{position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}},"<div></div>");document.body.appendChild(e);var t=e.firstChild.getBoundingClientRect().left>e.getBoundingClientRect().left;return c(e),t}()),S}function D(e){return e=Math.max(0,e),e=Math.round(e)}function T(e,t){void 0===t&&(t=!1);var n=window.getComputedStyle(e),r=parseInt(n.borderLeftWidth,10)||0,i=parseInt(n.borderRightWidth,10)||0,o=parseInt(n.borderTopWidth,10)||0,a=parseInt(n.borderBottomWidth,10)||0,s=D(e.offsetWidth-e.clientWidth-r-i),u={borderLeft:r,borderRight:i,borderTop:o,borderBottom:a,scrollbarBottom:D(e.offsetHeight-e.clientHeight-o-a),scrollbarLeft:0,scrollbarRight:0};return b()&&"rtl"===n.direction?u.scrollbarLeft=s:u.scrollbarRight=s,t&&(u.paddingLeft=parseInt(n.paddingLeft,10)||0,u.paddingRight=parseInt(n.paddingRight,10)||0,u.paddingTop=parseInt(n.paddingTop,10)||0,u.paddingBottom=parseInt(n.paddingBottom,10)||0),u}function w(e,t){void 0===t&&(t=!1);var n=R(e),r=T(e,t),i={left:n.left+r.borderLeft+r.scrollbarLeft,right:n.right-r.borderRight-r.scrollbarRight,top:n.top+r.borderTop,bottom:n.bottom-r.borderBottom-r.scrollbarBottom};return t&&(i.left+=r.paddingLeft,i.right-=r.paddingRight,i.top+=r.paddingTop,i.bottom-=r.paddingBottom),i}function R(e){var t=e.getBoundingClientRect();return{left:t.left+window.pageXOffset,top:t.top+window.pageYOffset,right:t.right+window.pageXOffset,bottom:t.bottom+window.pageYOffset}}function I(e){return e.getBoundingClientRect().height+C(e)}function C(e){var t=window.getComputedStyle(e);return parseInt(t.marginTop,10)+parseInt(t.marginBottom,10)}function M(e){for(var t=[];e instanceof HTMLElement;){var n=window.getComputedStyle(e);if("fixed"===n.position)break;/(auto|scroll)/.test(n.overflow+n.overflowY+n.overflowX)&&t.push(e),e=e.parentNode}return t}function k(e){e.preventDefault()}function O(e,t,n,r){function i(e){var t=p(e.target,n);t&&r.call(t,e,t)}return e.addEventListener(t,i),function(){e.removeEventListener(t,i)}}var _=["webkitTransitionEnd","otransitionend","oTransitionEnd","msTransitionEnd","transitionend"];var P=["sun","mon","tue","wed","thu","fri","sat"];function x(e,t){var n=Z(e);return n[2]+=t,j(n)}function H(e,t){var n=Z(e);return n[6]+=t,j(n)}function N(e,t){return(t.valueOf()-e.valueOf())/864e5}function z(e,t){var n=B(e),r=B(t);return{years:0,months:0,days:Math.round(N(n,r)),milliseconds:t.valueOf()-r.valueOf()-(e.valueOf()-n.valueOf())}}function U(e,t){var n=L(e,t);return null!==n&&n%7==0?n/7:null}function L(e,t){return q(e)===q(t)?Math.round(N(e,t)):null}function B(e){return j([e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()])}function V(e,t,n,r){var i=j([t,0,1+A(t,n,r)]),o=B(e),a=Math.round(N(i,o));return Math.floor(a/7)+1}function A(e,t,n){var r=7+t-n;return-((7+j([e,0,r]).getUTCDay()-t)%7)+r-1}function F(e){return[e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),e.getMilliseconds()]}function W(e){return new Date(e[0],e[1]||0,null==e[2]?1:e[2],e[3]||0,e[4]||0,e[5]||0)}function Z(e){return[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds()]}function j(e){return 1===e.length&&(e=e.concat([0])),new Date(Date.UTC.apply(Date,e))}function Y(e){return!isNaN(e.valueOf())}function q(e){return 1e3*e.getUTCHours()*60*60+1e3*e.getUTCMinutes()*60+1e3*e.getUTCSeconds()+e.getUTCMilliseconds()}var G=["years","months","days","milliseconds"],X=/^(-?)(?:(\d+)\.)?(\d+):(\d\d)(?::(\d\d)(?:\.(\d\d\d))?)?/;function J(e,t){var n;return"string"==typeof e?function(e){var t=X.exec(e);if(t){var n=t[1]?-1:1;return{years:0,months:0,days:n*(t[2]?parseInt(t[2],10):0),milliseconds:n*(60*(t[3]?parseInt(t[3],10):0)*60*1e3+60*(t[4]?parseInt(t[4],10):0)*1e3+1e3*(t[5]?parseInt(t[5],10):0)+(t[6]?parseInt(t[6],10):0))}}return null}(e):"object"==typeof e&&e?K(e):"number"==typeof e?K(((n={})[t||"milliseconds"]=e,n)):null}function K(e){return{years:e.years||e.year||0,months:e.months||e.month||0,days:(e.days||e.day||0)+7*Q(e),milliseconds:60*(e.hours||e.hour||0)*60*1e3+60*(e.minutes||e.minute||0)*1e3+1e3*(e.seconds||e.second||0)+(e.milliseconds||e.millisecond||e.ms||0)}}function Q(e){return e.weeks||e.week||0}function $(e,t){return e.years===t.years&&e.months===t.months&&e.days===t.days&&e.milliseconds===t.milliseconds}function ee(e){return te(e)/864e5}function te(e){return 31536e6*e.years+2592e6*e.months+864e5*e.days+e.milliseconds}function ne(e,t){var n=e.milliseconds;if(n){if(n%1e3!=0)return{unit:"millisecond",value:n};if(n%6e4!=0)return{unit:"second",value:n/1e3};if(n%36e5!=0)return{unit:"minute",value:n/6e4};if(n)return{unit:"hour",value:n/36e5}}return e.days?t||e.days%7!=0?{unit:"day",value:e.days}:{unit:"week",value:e.days/7}:e.months?{unit:"month",value:e.months}:e.years?{unit:"year",value:e.years}:{unit:"millisecond",value:0}}function re(e){e.forEach(function(e){e.style.height=""})}function ie(e){var t,n,r=[],i=[];for("string"==typeof e?i=e.split(/\s*,\s*/):"function"==typeof e?i=[e]:Array.isArray(e)&&(i=e),t=0;t<i.length;t++)"string"==typeof(n=i[t])?r.push("-"===n.charAt(0)?{field:n.substring(1),order:-1}:{field:n,order:1}):"function"==typeof n&&r.push({func:n});return r}function oe(e,t,n){var r,i;for(r=0;r<n.length;r++)if(i=ae(e,t,n[r]))return i;return 0}function ae(e,t,n){return n.func?n.func(e,t):se(e[n.field],t[n.field])*(n.order||1)}function se(e,t){return e||t?null==t?-1:null==e?1:"string"==typeof e||"string"==typeof t?String(e).localeCompare(String(t)):e-t:0}function ue(e){return e.charAt(0).toUpperCase()+e.slice(1)}function le(e,t){var n=String(e);return"000".substr(0,t-n.length)+n}function ce(e){return e%1==0}function de(e,t,n){if("function"==typeof e&&(e=[e]),e){var r=void 0,i=void 0;for(r=0;r<e.length;r++)i=e[r].apply(t,n)||i;return i}}function fe(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];for(var n=0;n<e.length;n++)if(void 0!==e[n])return e[n]}function pe(e,t){var n,r,i,o,a,s=function(){var u=(new Date).valueOf()-o;u<t?n=setTimeout(s,t-u):(n=null,a=e.apply(i,r),i=r=null)};return function(){return i=this,r=arguments,o=(new Date).valueOf(),n||(n=setTimeout(s,t)),a}}function he(e,t,n,r){void 0===n&&(n={});var i={};for(var o in t){var a=t[o];void 0!==e[o]?a===Function?i[o]="function"==typeof e[o]?e[o]:null:i[o]=a?a(e[o]):e[o]:void 0!==n[o]?i[o]=n[o]:a===String?i[o]="":a&&a!==Number&&a!==Boolean&&a!==Function?i[o]=a(null):i[o]=null}if(r)for(var o in e)void 0===t[o]&&(r[o]=e[o]);return i}function ve(e){var t=Math.floor(N(e.start,e.end))||1,n=B(e.start);return{start:n,end:x(n,t)}}function ge(e,t){void 0===t&&(t=J(0));var n=null,r=null;if(e.end){r=B(e.end);var i=e.end.valueOf()-r.valueOf();i&&i>=te(t)&&(r=x(r,1))}return e.start&&(n=B(e.start),r&&r<=n&&(r=x(n,1))),{start:n,end:r}}function ye(e,t,n,r){return"year"===r?J(n.diffWholeYears(e,t),"year"):"month"===r?J(n.diffWholeMonths(e,t),"month"):z(e,t)}var me=function(e,t){return(me=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function Ee(e,t){function n(){this.constructor=e}me(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var Se=function(){return(Se=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};function be(e,t,n,r,i){var o=i[e.recurringDef.typeId].expand(e.recurringDef.typeData,{start:r.subtract(n.start,t),end:n.end},r);return e.allDay&&(o=o.map(B)),o}var De=Object.prototype.hasOwnProperty;function Te(e,t){var n,r,i,o,a,s,u={};if(t)for(n=0;n<t.length;n++){for(r=t[n],i=[],o=e.length-1;o>=0;o--)if("object"==typeof(a=e[o][r])&&a)i.unshift(a);else if(void 0!==a){u[r]=a;break}i.length&&(u[r]=Te(i))}for(n=e.length-1;n>=0;n--)for(r in s=e[n])r in u||(u[r]=s[r]);return u}function we(e,t){var n={};for(var r in e)t(e[r],r)&&(n[r]=e[r]);return n}function Re(e,t){var n={};for(var r in e)n[r]=t(e[r],r);return n}function Ie(e){for(var t={},n=0,r=e;n<r.length;n++){t[r[n]]=!0}return t}function Ce(e){var t=[];for(var n in e)t.push(e[n]);return t}function Me(e,t){for(var n in e)if(De.call(e,n)&&!(n in t))return!1;for(var n in t)if(De.call(t,n)&&e[n]!==t[n])return!1;return!0}function ke(e,t,n,r){for(var i={defs:{},instances:{}},o=0,a=e;o<a.length;o++){var s=Ft(a[o],t,n,r);s&&Oe(s,i)}return i}function Oe(e,t){return void 0===t&&(t={defs:{},instances:{}}),t.defs[e.def.defId]=e.def,e.instance&&(t.instances[e.instance.instanceId]=e.instance),t}function _e(e,t,n){var r=n.dateEnv,i=e.defs,o=e.instances;for(var a in o=we(o,function(e){return!i[e.defId].recurringDef}),i){var s=i[a];if(s.recurringDef){var u=s.recurringDef.duration;u||(u=s.allDay?n.defaultAllDayEventDuration:n.defaultTimedEventDuration);for(var l=0,c=be(s,u,t,n.dateEnv,n.pluginSystem.hooks.recurringTypes);l<c.length;l++){var d=c[l],f=Zt(a,{start:d,end:r.add(d,u)});o[f.instanceId]=f}}}return{defs:i,instances:o}}function Pe(e,t){var n=e.instances[t];if(n){var r=e.defs[n.defId],i=ze(e,function(e){return t=r,n=e,Boolean(t.groupId&&t.groupId===n.groupId);var t,n});return i.defs[r.defId]=r,i.instances[n.instanceId]=n,i}return{defs:{},instances:{}}}function xe(e,t){var n;if(t){n=[];for(var r=0,i=e;r<i.length;r++){var o=i[r],a=t(o);a?n.push(a):null==a&&n.push(o)}}else n=e;return n}function He(){return{defs:{},instances:{}}}function Ne(e,t){return{defs:Se({},e.defs,t.defs),instances:Se({},e.instances,t.instances)}}function ze(e,t){var n=we(e.defs,t),r=we(e.instances,function(e){return n[e.defId]});return{defs:n,instances:r}}function Ue(e,t){var n=null,r=null;return e.start&&(n=t.createMarker(e.start)),e.end&&(r=t.createMarker(e.end)),n||r?n&&r&&r<n?null:{start:n,end:r}:null}function Le(e,t){var n,r,i=[],o=t.start;for(e.sort(Be),n=0;n<e.length;n++)(r=e[n]).start>o&&i.push({start:o,end:r.start}),r.end>o&&(o=r.end);return o<t.end&&i.push({start:o,end:t.end}),i}function Be(e,t){return e.start.valueOf()-t.start.valueOf()}function Ve(e,t){var n=e.start,r=e.end,i=null;return null!==t.start&&(n=null===n?t.start:new Date(Math.max(n.valueOf(),t.start.valueOf()))),null!=t.end&&(r=null===r?t.end:new Date(Math.min(r.valueOf(),t.end.valueOf()))),(null===n||null===r||n<r)&&(i={start:n,end:r}),i}function Ae(e,t){return(null===e.start?null:e.start.valueOf())===(null===t.start?null:t.start.valueOf())&&(null===e.end?null:e.end.valueOf())===(null===t.end?null:t.end.valueOf())}function Fe(e,t){return(null===e.end||null===t.start||e.end>t.start)&&(null===e.start||null===t.end||e.start<t.end)}function We(e,t){return(null===e.start||null!==t.start&&t.start>=e.start)&&(null===e.end||null!==t.end&&t.end<=e.end)}function Ze(e,t){return(null===e.start||t>=e.start)&&(null===e.end||t<e.end)}function je(e,t){var n,r=e.length;if(r!==t.length)return!1;for(n=0;n<r;n++)if(e[n]!==t[n])return!1;return!0}function Ye(e){var t,n;return function(){return t&&je(t,arguments)||(t=arguments,n=e.apply(this,arguments)),n}}function qe(e,t){var n=null;return function(){var r=e.apply(this,arguments);return(null===n||n!==r&&!t(n,r))&&(n=r),n}}var Ge={week:3,separator:0,omitZeroMinute:0,meridiem:0,omitCommas:0},Xe={timeZoneName:7,era:6,year:5,month:4,day:2,weekday:2,hour:1,minute:1,second:1},Je=/\s*([ap])\.?m\.?/i,Ke=/,/g,Qe=/\s+/g,$e=/\u200e/g,et=/UTC|GMT/,tt=function(){function e(e){var t={},n={},r=0;for(var i in e)i in Ge?(n[i]=e[i],r=Math.max(Ge[i],r)):(t[i]=e[i],i in Xe&&(r=Math.max(Xe[i],r)));this.standardDateProps=t,this.extendedSettings=n,this.severity=r,this.buildFormattingFunc=Ye(nt)}return e.prototype.format=function(e,t){return this.buildFormattingFunc(this.standardDateProps,this.extendedSettings,t)(e)},e.prototype.formatRange=function(e,t,n){var r=this.standardDateProps,i=this.extendedSettings,o=function(e,t,n){if(n.getMarkerYear(e)!==n.getMarkerYear(t))return 5;if(n.getMarkerMonth(e)!==n.getMarkerMonth(t))return 4;if(n.getMarkerDay(e)!==n.getMarkerDay(t))return 2;if(q(e)!==q(t))return 1;return 0}(e.marker,t.marker,n.calendarSystem);if(!o)return this.format(e,n);var a=o;!(a>1)||"numeric"!==r.year&&"2-digit"!==r.year||"numeric"!==r.month&&"2-digit"!==r.month||"numeric"!==r.day&&"2-digit"!==r.day||(a=1);var s=this.format(e,n),u=this.format(t,n);if(s===u)return s;var l=nt(function(e,t){var n={};for(var r in e)r in Xe&&!(Xe[r]<=t)||(n[r]=e[r]);return n}(r,a),i,n),c=l(e),d=l(t),f=function(e,t,n,r){var i=0;for(;i<e.length;){var o=e.indexOf(t,i);if(-1===o)break;var a=e.substr(0,o);i=o+t.length;for(var s=e.substr(i),u=0;u<n.length;){var l=n.indexOf(r,u);if(-1===l)break;var c=n.substr(0,l);u=l+r.length;var d=n.substr(u);if(a===c&&s===d)return{before:a,after:s}}}return null}(s,c,u,d),p=i.separator||"";return f?f.before+c+p+d+f.after:s+p+u},e.prototype.getLargestUnit=function(){switch(this.severity){case 7:case 6:case 5:return"year";case 4:return"month";case 3:return"week";default:return"day"}},e}();function nt(e,t,n){var r=Object.keys(e).length;return 1===r&&"short"===e.timeZoneName?function(e){return at(e.timeZoneOffset)}:0===r&&t.week?function(e){return function(e,t,n,r){var i=[];"narrow"===r?i.push(t):"short"===r&&i.push(t," ");i.push(n.simpleNumberFormat.format(e)),n.options.isRtl&&i.reverse();return i.join("")}(n.computeWeekNumber(e.marker),n.weekLabel,n.locale,t.week)}:function(e,t,n){e=Se({},e),t=Se({},t),function(e,t){e.timeZoneName&&(e.hour||(e.hour="2-digit"),e.minute||(e.minute="2-digit"));"long"===e.timeZoneName&&(e.timeZoneName="short");t.omitZeroMinute&&(e.second||e.millisecond)&&delete t.omitZeroMinute}(e,t),e.timeZone="UTC";var r,i=new Intl.DateTimeFormat(n.locale.codes,e);if(t.omitZeroMinute){var o=Se({},e);delete o.minute,r=new Intl.DateTimeFormat(n.locale.codes,o)}return function(o){var a=o.marker,s=(r&&!a.getUTCMinutes()?r:i).format(a);return function(e,t,n,r,i){e=e.replace($e,""),"short"===n.timeZoneName&&(e=function(e,t){var n=!1;e=e.replace(et,function(){return n=!0,t}),n||(e+=" "+t);return e}(e,"UTC"===i.timeZone||null==t.timeZoneOffset?"UTC":at(t.timeZoneOffset)));r.omitCommas&&(e=e.replace(Ke,"").trim());r.omitZeroMinute&&(e=e.replace(":00",""));!1===r.meridiem?e=e.replace(Je,"").trim():"narrow"===r.meridiem?e=e.replace(Je,function(e,t){return t.toLocaleLowerCase()}):"short"===r.meridiem?e=e.replace(Je,function(e,t){return t.toLocaleLowerCase()+"m"}):"lowercase"===r.meridiem&&(e=e.replace(Je,function(e){return e.toLocaleLowerCase()}));return e=(e=e.replace(Qe," ")).trim()}(s,o,e,t,n)}}(e,t,n)}var rt=function(){function e(e,t){this.cmdStr=e,this.separator=t}return e.prototype.format=function(e,t){return t.cmdFormatter(this.cmdStr,st(e,null,t,this.separator))},e.prototype.formatRange=function(e,t,n){return n.cmdFormatter(this.cmdStr,st(e,t,n,this.separator))},e}(),it=function(){function e(e){this.func=e}return e.prototype.format=function(e,t){return this.func(st(e,null,t))},e.prototype.formatRange=function(e,t,n){return this.func(st(e,t,n))},e}();function ot(e,t){return"object"==typeof e&&e?("string"==typeof t&&(e=Se({separator:t},e)),new tt(e)):"string"==typeof e?new rt(e,t):"function"==typeof e?new it(e):void 0}function at(e,t){void 0===t&&(t=!1);var n=e<0?"-":"+",r=Math.abs(e),i=Math.floor(r/60),o=Math.round(r%60);return t?n+le(i,2)+":"+le(o,2):"GMT"+n+i+(o?":"+le(o,2):"")}function st(e,t,n,r){var i=ut(e,n.calendarSystem);return{date:i,start:i,end:t?ut(t,n.calendarSystem):null,timeZone:n.timeZone,localeCodes:n.locale.codes,separator:r}}function ut(e,t){var n=t.markerToArray(e.marker);return{marker:e.marker,timeZoneOffset:e.timeZoneOffset,array:n,year:n[0],month:n[1],day:n[2],hour:n[3],minute:n[4],second:n[5],millisecond:n[6]}}var lt=function(){function e(e,t){this.calendar=e,this.internalEventSource=t}return e.prototype.remove=function(){this.calendar.dispatch({type:"REMOVE_EVENT_SOURCE",sourceId:this.internalEventSource.sourceId})},e.prototype.refetch=function(){this.calendar.dispatch({type:"FETCH_EVENT_SOURCES",sourceIds:[this.internalEventSource.sourceId]})},Object.defineProperty(e.prototype,"id",{get:function(){return this.internalEventSource.publicId},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"url",{get:function(){return this.internalEventSource.meta.url},enumerable:!0,configurable:!0}),e}(),ct=function(){function e(e,t,n){this._calendar=e,this._def=t,this._instance=n||null}return e.prototype.setProp=function(e,t){var n,r;if(e in Vt);else if(e in Bt)"function"==typeof Bt[e]&&(t=Bt[e](t)),this.mutate({standardProps:(n={},n[e]=t,n)});else if(e in xt){var i=void 0;"function"==typeof xt[e]&&(t=xt[e](t)),"color"===e?i={backgroundColor:t,borderColor:t}:"editable"===e?i={startEditable:t,durationEditable:t}:((r={})[e]=t,i=r),this.mutate({standardProps:{ui:i}})}},e.prototype.setExtendedProp=function(e,t){var n;this.mutate({extendedProps:(n={},n[e]=t,n)})},e.prototype.setStart=function(e,t){void 0===t&&(t={});var n=this._calendar.dateEnv,r=n.createMarker(e);if(r&&this._instance){var i=ye(this._instance.range.start,r,n,t.granularity);t.maintainDuration?this.mutate({datesDelta:i}):this.mutate({startDelta:i})}},e.prototype.setEnd=function(e,t){void 0===t&&(t={});var n,r=this._calendar.dateEnv;if((null==e||(n=r.createMarker(e)))&&this._instance)if(n){var i=ye(this._instance.range.end,n,r,t.granularity);this.mutate({endDelta:i})}else this.mutate({standardProps:{hasEnd:!1}})},e.prototype.setDates=function(e,t,n){void 0===n&&(n={});var r,i=this._calendar.dateEnv,o={allDay:n.allDay},a=i.createMarker(e);if(a&&(null==t||(r=i.createMarker(t)))&&this._instance){var s=this._instance.range;!0===n.allDay&&(s=ve(s));var u=ye(s.start,a,i,n.granularity);if(r){var l=ye(s.end,r,i,n.granularity);$(u,l)?this.mutate({datesDelta:u,standardProps:o}):this.mutate({startDelta:u,endDelta:l,standardProps:o})}else o.hasEnd=!1,this.mutate({datesDelta:u,standardProps:o})}},e.prototype.moveStart=function(e){var t=J(e);t&&this.mutate({startDelta:t})},e.prototype.moveEnd=function(e){var t=J(e);t&&this.mutate({endDelta:t})},e.prototype.moveDates=function(e){var t=J(e);t&&this.mutate({datesDelta:t})},e.prototype.setAllDay=function(e,t){void 0===t&&(t={});var n={allDay:e},r=t.maintainDuration;null==r&&(r=this._calendar.opt("allDayMaintainDuration")),this._def.allDay!==e&&(n.hasEnd=r),this.mutate({standardProps:n})},e.prototype.formatRange=function(e){var t=this._calendar.dateEnv,n=this._instance,r=ot(e,this._calendar.opt("defaultRangeSeparator"));return this._def.hasEnd?t.formatRange(n.range.start,n.range.end,r,{forcedStartTzo:n.forcedStartTzo,forcedEndTzo:n.forcedEndTzo}):t.format(n.range.start,r,{forcedTzo:n.forcedStartTzo})},e.prototype.mutate=function(e){var t=this._def,n=this._instance;if(n){this._calendar.dispatch({type:"MUTATE_EVENTS",instanceId:n.instanceId,mutation:e,fromApi:!0});var r=this._calendar.state.eventStore;this._def=r.defs[t.defId],this._instance=r.instances[n.instanceId]}},e.prototype.remove=function(){this._calendar.dispatch({type:"REMOVE_EVENT_DEF",defId:this._def.defId})},Object.defineProperty(e.prototype,"source",{get:function(){var e=this._def.sourceId;return e?new lt(this._calendar,this._calendar.state.eventSources[e]):null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"start",{get:function(){return this._instance?this._calendar.dateEnv.toDate(this._instance.range.start):null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"end",{get:function(){return this._instance&&this._def.hasEnd?this._calendar.dateEnv.toDate(this._instance.range.end):null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"id",{get:function(){return this._def.publicId},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"groupId",{get:function(){return this._def.groupId},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"allDay",{get:function(){return this._def.allDay},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"title",{get:function(){return this._def.title},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"url",{get:function(){return this._def.url},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"rendering",{get:function(){return this._def.rendering},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"startEditable",{get:function(){return this._def.ui.startEditable},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"durationEditable",{get:function(){return this._def.ui.durationEditable},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"constraint",{get:function(){return this._def.ui.constraints[0]||null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"overlap",{get:function(){return this._def.ui.overlap},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"allow",{get:function(){return this._def.ui.allows[0]||null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"backgroundColor",{get:function(){return this._def.ui.backgroundColor},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"borderColor",{get:function(){return this._def.ui.borderColor},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"textColor",{get:function(){return this._def.ui.textColor},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"classNames",{get:function(){return this._def.ui.classNames},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"extendedProps",{get:function(){return this._def.extendedProps},enumerable:!0,configurable:!0}),e}();function dt(e,t,n,r){var i={},o={},a={},s=[],u=[],l=vt(e.defs,t);for(var c in e.defs){"inverse-background"===(S=e.defs[c]).rendering&&(S.groupId?(i[S.groupId]=[],a[S.groupId]||(a[S.groupId]=S)):o[c]=[])}for(var d in e.instances){var f=e.instances[d],p=l[(S=e.defs[f.defId]).defId],h=f.range,v=!S.allDay&&r?ge(h,r):h,g=Ve(v,n);g&&("inverse-background"===S.rendering?S.groupId?i[S.groupId].push(g):o[f.defId].push(g):("background"===S.rendering?s:u).push({def:S,ui:p,instance:f,range:g,isStart:v.start&&v.start.valueOf()===g.start.valueOf(),isEnd:v.end&&v.end.valueOf()===g.end.valueOf()}))}for(var y in i)for(var m=0,E=Le(i[y],n);m<E.length;m++){var S,b=E[m];p=l[(S=a[y]).defId];s.push({def:S,ui:p,instance:null,range:b,isStart:!1,isEnd:!1})}for(var c in o)for(var D=0,T=Le(o[c],n);D<T.length;D++){b=T[D];s.push({def:e.defs[c],ui:l[c],instance:null,range:b,isStart:!1,isEnd:!1})}return{bg:s,fg:u}}function ft(e,t,n){e.hasPublicHandlers("eventRender")&&(t=t.filter(function(t){var r=e.publiclyTrigger("eventRender",[{event:new ct(e.calendar,t.eventRange.def,t.eventRange.instance),isMirror:n,isStart:t.isStart,isEnd:t.isEnd,el:t.el,view:e}]);return!1!==r&&(r&&!0!==r&&(t.el=r),!0)}));for(var r=0,i=t;r<i.length;r++){var o=i[r];pt(o.el,o)}return t}function pt(e,t){e.fcSeg=t}function ht(e){return e.fcSeg||null}function vt(e,t){return Re(e,function(e){return gt(e,t)})}function gt(e,t){var n=[];return t[""]&&n.push(t[""]),t[e.defId]&&n.push(t[e.defId]),n.push(e.ui),Ut(n)}function yt(e,t,n,r){var i=vt(e.defs,t),o={defs:{},instances:{}};for(var a in e.defs){var s=e.defs[a];o.defs[a]=mt(s,i[a],n,r.pluginSystem.hooks.eventDefMutationAppliers,r)}for(var u in e.instances){var l=e.instances[u];s=o.defs[l.defId];o.instances[u]=Et(l,s,i[l.defId],n,r)}return o}function mt(e,t,n,r,i){var o=n.standardProps||{};null==o.hasEnd&&t.durationEditable&&(n.startDelta||n.endDelta)&&(o.hasEnd=!0);var a=Se({},e,o,{ui:Se({},e.ui,o.ui)});n.extendedProps&&(a.extendedProps=Se({},a.extendedProps,n.extendedProps));for(var s=0,u=r;s<u.length;s++){(0,u[s])(a,n,i)}return!a.hasEnd&&i.opt("forceEventDuration")&&(a.hasEnd=!0),a}function Et(e,t,n,r,i){var o=i.dateEnv,a=r.standardProps&&!0===r.standardProps.allDay,s=r.standardProps&&!1===r.standardProps.hasEnd,u=Se({},e);return a&&(u.range=ve(u.range)),r.datesDelta&&n.startEditable&&(u.range={start:o.add(u.range.start,r.datesDelta),end:o.add(u.range.end,r.datesDelta)}),r.startDelta&&n.durationEditable&&(u.range={start:o.add(u.range.start,r.startDelta),end:u.range.end}),r.endDelta&&n.durationEditable&&(u.range={start:u.range.start,end:o.add(u.range.end,r.endDelta)}),s&&(u.range={start:u.range.start,end:i.getDefaultEventEnd(t.allDay,u.range.start)}),t.allDay&&(u.range={start:B(u.range.start),end:B(u.range.end)}),u.range.end<u.range.start&&(u.range.end=i.getDefaultEventEnd(t.allDay,u.range.start)),u}function St(e,t,n,r,i){switch(t.type){case"RECEIVE_EVENTS":return function(e,t,n,r,i,o){if(t&&n===t.latestFetchId){var a=ke(function(e,t,n){var r=n.opt("eventDataTransform"),i=t?t.eventDataTransform:null;return i&&(e=xe(e,i)),r&&(e=xe(e,r)),e}(i,t,o),t.sourceId,o);return r&&(a=_e(a,r,o)),Ne(bt(e,t.sourceId),a)}return e}(e,n[t.sourceId],t.fetchId,t.fetchRange,t.rawEvents,i);case"ADD_EVENTS":return function(e,t,n,r){n&&(t=_e(t,n,r));return Ne(e,t)}(e,t.eventStore,r?r.activeRange:null,i);case"MERGE_EVENTS":return Ne(e,t.eventStore);case"PREV":case"NEXT":case"SET_DATE":case"SET_VIEW_TYPE":return r?_e(e,r.activeRange,i):e;case"CHANGE_TIMEZONE":return function(e,t,n){var r=e.defs,i=Re(e.instances,function(e){var i=r[e.defId];return i.allDay||i.recurringDef?e:Se({},e,{range:{start:n.createMarker(t.toDate(e.range.start,e.forcedStartTzo)),end:n.createMarker(t.toDate(e.range.end,e.forcedEndTzo))},forcedStartTzo:n.canComputeOffset?null:e.forcedStartTzo,forcedEndTzo:n.canComputeOffset?null:e.forcedEndTzo})});return{defs:r,instances:i}}(e,t.oldDateEnv,i.dateEnv);case"MUTATE_EVENTS":return function(e,t,n,r,i){var o=Pe(e,t),a=r?{"":{startEditable:!0,durationEditable:!0,constraints:[],overlap:null,allows:[],backgroundColor:"",borderColor:"",textColor:"",classNames:[]}}:i.eventUiBases;return o=yt(o,a,n,i),Ne(e,o)}(e,t.instanceId,t.mutation,t.fromApi,i);case"REMOVE_EVENT_INSTANCES":return Dt(e,t.instances);case"REMOVE_EVENT_DEF":return ze(e,function(e){return e.defId!==t.defId});case"REMOVE_EVENT_SOURCE":return bt(e,t.sourceId);case"REMOVE_ALL_EVENT_SOURCES":return ze(e,function(e){return!e.sourceId});case"REMOVE_ALL_EVENTS":return{defs:{},instances:{}};case"RESET_EVENTS":return{defs:e.defs,instances:e.instances};default:return e}}function bt(e,t){return ze(e,function(e){return e.sourceId!==t})}function Dt(e,t){return{defs:e.defs,instances:we(e.instances,function(e){return!t[e.instanceId]})}}function Tt(e,t){return wt({eventDrag:e},t)}function wt(e,t){var n=t.view,r=Se({businessHours:n?n.props.businessHours:{defs:{},instances:{}},dateSelection:"",eventStore:t.state.eventStore,eventUiBases:t.eventUiBases,eventSelection:"",eventDrag:null,eventResize:null},e);return(t.pluginSystem.hooks.isPropsValid||Rt)(r,t)}function Rt(e,t,n,r){return void 0===n&&(n={}),!(e.eventDrag&&!function(e,t,n,r){var i=e.eventDrag,o=i.mutatedEvents,a=o.defs,s=o.instances,u=vt(a,i.isEvent?e.eventUiBases:{"":t.selectionConfig});r&&(u=Re(u,r));var l=Dt(e.eventStore,i.affectedEvents.instances),c=l.defs,d=l.instances,f=vt(c,e.eventUiBases);for(var p in s){var h=s[p],v=h.range,g=u[h.defId],y=a[h.defId];if(!It(g.constraints,v,l,e.businessHours,t))return!1;var m=t.opt("eventOverlap");for(var E in"function"!=typeof m&&(m=null),d){var S=d[E];if(Fe(v,S.range)){var b=f[S.defId].overlap;if(!1===b&&i.isEvent)return!1;if(!1===g.overlap)return!1;if(m&&!m(new ct(t,c[S.defId],S),new ct(t,y,h)))return!1}}for(var D=t.state.eventStore,T=0,w=g.allows;T<w.length;T++){var R=w[T],I=Se({},n,{range:h.range,allDay:y.allDay}),C=D.defs[y.defId],M=D.instances[p],k=void 0;if(k=C?new ct(t,C,M):new ct(t,y),!R(t.buildDateSpanApi(I),k))return!1}}return!0}(e,t,n,r))&&!(e.dateSelection&&!function(e,t,n,r){var i=e.eventStore,o=i.defs,a=i.instances,s=e.dateSelection,u=s.range,l=t.selectionConfig;r&&(l=r(l));if(!It(l.constraints,u,i,e.businessHours,t))return!1;var c=t.opt("selectOverlap");"function"!=typeof c&&(c=null);for(var d in a){var f=a[d];if(Fe(u,f.range)){if(!1===l.overlap)return!1;if(c&&!c(new ct(t,o[f.defId],f)))return!1}}for(var p=0,h=l.allows;p<h.length;p++){var v=h[p],g=Se({},n,s);if(!v(t.buildDateSpanApi(g),null))return!1}return!0}(e,t,n,r))}function It(e,t,n,r,i){for(var o=0,a=e;o<a.length;o++){if(!kt(Ct(a[o],t,n,r,i),t))return!1}return!0}function Ct(e,t,n,r,i){return"businessHours"===e?Mt(_e(r,t,i)):"string"==typeof e?Mt(ze(n,function(t){return t.groupId===e})):"object"==typeof e&&e?Mt(_e(e,t,i)):[]}function Mt(e){var t=e.instances,n=[];for(var r in t)n.push(t[r].range);return n}function kt(e,t){for(var n=0,r=e;n<r.length;n++){if(We(r[n],t))return!0}return!1}function Ot(e){return(e+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/'/g,"&#039;").replace(/"/g,"&quot;").replace(/\n/g,"<br />")}function _t(e){var t=[];for(var n in e){var r=e[n];null!=r&&""!==r&&t.push(n+":"+r)}return t.join(";")}function Pt(e){return Array.isArray(e)?e:"string"==typeof e?e.split(/\s+/):[]}var xt={editable:Boolean,startEditable:Boolean,durationEditable:Boolean,constraint:null,overlap:null,allow:null,className:Pt,classNames:Pt,color:String,backgroundColor:String,borderColor:String,textColor:String};function Ht(e,t,n){var r=he(e,xt,{},n),i=function(e,t){return Array.isArray(e)?ke(e,"",t,!0):"object"==typeof e&&e?ke([e],"",t,!0):null!=e?String(e):null}(r.constraint,t);return{startEditable:null!=r.startEditable?r.startEditable:r.editable,durationEditable:null!=r.durationEditable?r.durationEditable:r.editable,constraints:null!=i?[i]:[],overlap:r.overlap,allows:null!=r.allow?[r.allow]:[],backgroundColor:r.backgroundColor||r.color,borderColor:r.borderColor||r.color,textColor:r.textColor,classNames:r.classNames.concat(r.className)}}function Nt(e,t,n,r){var i={},o={};for(var a in xt){var s=e+ue(a);i[a]=t[s],o[s]=!0}if("event"===e&&(i.editable=t.editable),r)for(var a in t)o[a]||(r[a]=t[a]);return Ht(i,n)}var zt={startEditable:null,durationEditable:null,constraints:[],overlap:null,allows:[],backgroundColor:"",borderColor:"",textColor:"",classNames:[]};function Ut(e){return e.reduce(Lt,zt)}function Lt(e,t){return{startEditable:null!=t.startEditable?t.startEditable:e.startEditable,durationEditable:null!=t.durationEditable?t.durationEditable:e.durationEditable,constraints:e.constraints.concat(t.constraints),overlap:"boolean"==typeof t.overlap?t.overlap:e.overlap,allows:e.allows.concat(t.allows),backgroundColor:t.backgroundColor||e.backgroundColor,borderColor:t.borderColor||e.borderColor,textColor:t.textColor||e.textColor,classNames:e.classNames.concat(t.classNames)}}var Bt={id:String,groupId:String,title:String,url:String,rendering:String,extendedProps:null},Vt={start:null,date:null,end:null,allDay:null},At=0;function Ft(e,t,n,r){var i=function(e,t){var n=null;if(e){var r=t.state.eventSources[e];n=r.allDayDefault}null==n&&(n=t.opt("allDayDefault"));return n}(t,n),o={},a=function(e,t,n,r,i){for(var o=0;o<r.length;o++){var a={},s=r[o].parse(e,a,n);if(s){var u=a.allDay;return delete a.allDay,null==u&&null==(u=t)&&null==(u=s.allDayGuess)&&(u=!1),Se(i,a),{allDay:u,duration:s.duration,typeData:s.typeData,typeId:o}}}return null}(e,i,n.dateEnv,n.pluginSystem.hooks.recurringTypes,o);if(a)return(s=Wt(o,t,a.allDay,Boolean(a.duration),n)).recurringDef={typeId:a.typeId,typeData:a.typeData,duration:a.duration},{def:s,instance:null};var s,u={},l=function(e,t,n,r,i){var o,a,s=function(e,t){var n=he(e,Vt,{},t);return n.start=null!==n.start?n.start:n.date,delete n.date,n}(e,r),u=s.allDay,l=null,c=!1,d=null;if(o=n.dateEnv.createMarkerMeta(s.start))l=o.marker;else if(!i)return null;null!=s.end&&(a=n.dateEnv.createMarkerMeta(s.end));null==u&&(u=null!=t?t:(!o||o.isTimeUnspecified)&&(!a||a.isTimeUnspecified));u&&l&&(l=B(l));a&&(d=a.marker,u&&(d=B(d)),l&&d<=l&&(d=null));d?c=!0:i||(c=n.opt("forceEventDuration")||!1,d=n.dateEnv.add(l,u?n.defaultAllDayEventDuration:n.defaultTimedEventDuration));return{allDay:u,hasEnd:c,range:{start:l,end:d},forcedStartTzo:o?o.forcedTzo:null,forcedEndTzo:a?a.forcedTzo:null}}(e,i,n,u,r);return l?{def:s=Wt(u,t,l.allDay,l.hasEnd,n),instance:Zt(s.defId,l.range,l.forcedStartTzo,l.forcedEndTzo)}:null}function Wt(e,t,n,r,i){var o={},a=function(e,t,n){var r={},i=he(e,Bt,{},r),o=Ht(r,t,n);return i.publicId=i.id,delete i.id,i.ui=o,i}(e,i,o);a.defId=String(At++),a.sourceId=t,a.allDay=n,a.hasEnd=r;for(var s=0,u=i.pluginSystem.hooks.eventDefParsers;s<u.length;s++){var l={};(0,u[s])(a,o,l),o=l}return a.extendedProps=Se(o,a.extendedProps||{}),Object.freeze(a.ui.classNames),Object.freeze(a.extendedProps),a}function Zt(e,t,n,r){return{instanceId:String(At++),defId:e,range:t,forcedStartTzo:null==n?null:n,forcedEndTzo:null==r?null:r}}var jt={startTime:"09:00",endTime:"17:00",daysOfWeek:[1,2,3,4,5],rendering:"inverse-background",classNames:"fc-nonbusiness",groupId:"_businessHours"};function Yt(e,t){return ke(function(e){var t;t=!0===e?[{}]:Array.isArray(e)?e.filter(function(e){return e.daysOfWeek}):"object"==typeof e&&e?[e]:[];return t=t.map(function(e){return Se({},jt,e)})}(e),"",t)}function qt(e,t,n){void 0===n&&(n=[]);var r,i,o=[];function a(){if(i){for(var e=0,n=o;e<n.length;e++){n[e].unrender()}t&&t.apply(r,i),i=null}}function s(){i&&je(i,arguments)||(a(),r=this,i=arguments,e.apply(this,arguments))}s.dependents=o,s.unrender=a;for(var u=0,l=n;u<l.length;u++){l[u].dependents.push(s)}return s}var Gt={defs:{},instances:{}},Xt=function(){function e(){this.getKeysForEventDefs=Ye(this._getKeysForEventDefs),this.splitDateSelection=Ye(this._splitDateSpan),this.splitEventStore=Ye(this._splitEventStore),this.splitIndividualUi=Ye(this._splitIndividualUi),this.splitEventDrag=Ye(this._splitInteraction),this.splitEventResize=Ye(this._splitInteraction),this.eventUiBuilders={}}return e.prototype.splitProps=function(e){var t=this,n=this.getKeyInfo(e),r=this.getKeysForEventDefs(e.eventStore),i=this.splitDateSelection(e.dateSelection),o=this.splitIndividualUi(e.eventUiBases,r),a=this.splitEventStore(e.eventStore,r),s=this.splitEventDrag(e.eventDrag),u=this.splitEventResize(e.eventResize),l={};for(var c in this.eventUiBuilders=Re(n,function(e,n){return t.eventUiBuilders[n]||Ye(Jt)}),n){var d=n[c],f=a[c]||Gt,p=this.eventUiBuilders[c];l[c]={businessHours:d.businessHours||e.businessHours,dateSelection:i[c]||null,eventStore:f,eventUiBases:p(e.eventUiBases[""],d.ui,o[c]),eventSelection:f.instances[e.eventSelection]?e.eventSelection:"",eventDrag:s[c]||null,eventResize:u[c]||null}}return l},e.prototype._splitDateSpan=function(e){var t={};if(e)for(var n=0,r=this.getKeysForDateSpan(e);n<r.length;n++){t[r[n]]=e}return t},e.prototype._getKeysForEventDefs=function(e){var t=this;return Re(e.defs,function(e){return t.getKeysForEventDef(e)})},e.prototype._splitEventStore=function(e,t){var n=e.defs,r=e.instances,i={};for(var o in n)for(var a=0,s=t[o];a<s.length;a++){i[f=s[a]]||(i[f]={defs:{},instances:{}}),i[f].defs[o]=n[o]}for(var u in r)for(var l=r[u],c=0,d=t[l.defId];c<d.length;c++){var f;i[f=d[c]]&&(i[f].instances[u]=l)}return i},e.prototype._splitIndividualUi=function(e,t){var n={};for(var r in e)if(r)for(var i=0,o=t[r];i<o.length;i++){var a=o[i];n[a]||(n[a]={}),n[a][r]=e[r]}return n},e.prototype._splitInteraction=function(e){var t={};if(e){var n=this._splitEventStore(e.affectedEvents,this._getKeysForEventDefs(e.affectedEvents)),r=this._getKeysForEventDefs(e.mutatedEvents),i=this._splitEventStore(e.mutatedEvents,r),o=function(r){t[r]||(t[r]={affectedEvents:n[r]||Gt,mutatedEvents:i[r]||Gt,isEvent:e.isEvent,origSeg:e.origSeg})};for(var a in n)o(a);for(var a in i)o(a)}return t},e}();function Jt(e,t,n){var r=[];e&&r.push(e),t&&r.push(t);var i={"":Ut(r)};return n&&Se(i,n),i}function Kt(e,t,n,r){var i,o,a,s,u=e.dateEnv;return t instanceof Date?i=t:(i=t.date,o=t.type,a=t.forceOff),s={date:u.formatIso(i,{omitTime:!0}),type:o||"day"},"string"==typeof n&&(r=n,n=null),n=n?" "+function(e){var t=[];for(var n in e){var r=e[n];null!=r&&t.push(n+'="'+Ot(r)+'"')}return t.join(" ")}(n):"",r=r||"",!a&&e.opt("navLinks")?"<a"+n+' data-goto="'+Ot(JSON.stringify(s))+'">'+r+"</a>":"<span"+n+">"+r+"</span>"}function Qt(e,t,n,r){var i,o,a=n.calendar,s=n.view,u=n.theme,l=n.dateEnv,c=[];return Ze(t.activeRange,e)?(c.push("fc-"+P[e.getUTCDay()]),s.opt("monthMode")&&l.getMonth(e)!==l.getMonth(t.currentRange.start)&&c.push("fc-other-month"),o=x(i=B(a.getNow()),1),e<i?c.push("fc-past"):e>=o?c.push("fc-future"):(c.push("fc-today"),!0!==r&&c.push(u.getClass("today")))):c.push("fc-disabled-day"),c}function $t(e,t,n){var r=!1,i=function(){r||(r=!0,t.apply(this,arguments))},o=function(){r||(r=!0,n&&n.apply(this,arguments))},a=e(i,o);a&&"function"==typeof a.then&&a.then(i,o)}var en=function(){function e(){}return e.mixInto=function(e){this.mixIntoObj(e.prototype)},e.mixIntoObj=function(e){var t=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){e[n]||(e[n]=t.prototype[n])})},e.mixOver=function(e){var t=this;Object.getOwnPropertyNames(this.prototype).forEach(function(n){e.prototype[n]=t.prototype[n]})},e}(),tn=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Ee(t,e),t.prototype.on=function(e,t){return nn(this._handlers||(this._handlers={}),e,t),this},t.prototype.one=function(e,t){return nn(this._oneHandlers||(this._oneHandlers={}),e,t),this},t.prototype.off=function(e,t){return this._handlers&&rn(this._handlers,e,t),this._oneHandlers&&rn(this._oneHandlers,e,t),this},t.prototype.trigger=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];return this.triggerWith(e,this,t),this},t.prototype.triggerWith=function(e,t,n){return this._handlers&&de(this._handlers[e],t,n),this._oneHandlers&&(de(this._oneHandlers[e],t,n),delete this._oneHandlers[e]),this},t.prototype.hasHandlers=function(e){return this._handlers&&this._handlers[e]&&this._handlers[e].length||this._oneHandlers&&this._oneHandlers[e]&&this._oneHandlers[e].length},t}(en);function nn(e,t,n){(e[t]||(e[t]=[])).push(n)}function rn(e,t,n){n?e[t]&&(e[t]=e[t].filter(function(e){return e!==n})):delete e[t]}var on=function(){function e(e,t,n,r){this.originEl=e,this.els=t,this.isHorizontal=n,this.isVertical=r}return e.prototype.build=function(){var e=this.originEl,t=this.originClientRect=e.getBoundingClientRect();this.isHorizontal&&this.buildElHorizontals(t.left),this.isVertical&&this.buildElVerticals(t.top)},e.prototype.buildElHorizontals=function(e){for(var t=[],n=[],r=0,i=this.els;r<i.length;r++){var o=i[r].getBoundingClientRect();t.push(o.left-e),n.push(o.right-e)}this.lefts=t,this.rights=n},e.prototype.buildElVerticals=function(e){for(var t=[],n=[],r=0,i=this.els;r<i.length;r++){var o=i[r].getBoundingClientRect();t.push(o.top-e),n.push(o.bottom-e)}this.tops=t,this.bottoms=n},e.prototype.leftToIndex=function(e){var t,n=this.lefts,r=this.rights,i=n.length;for(t=0;t<i;t++)if(e>=n[t]&&e<r[t])return t},e.prototype.topToIndex=function(e){var t,n=this.tops,r=this.bottoms,i=n.length;for(t=0;t<i;t++)if(e>=n[t]&&e<r[t])return t},e.prototype.getWidth=function(e){return this.rights[e]-this.lefts[e]},e.prototype.getHeight=function(e){return this.bottoms[e]-this.tops[e]},e}(),an=function(){function e(){}return e.prototype.getMaxScrollTop=function(){return this.getScrollHeight()-this.getClientHeight()},e.prototype.getMaxScrollLeft=function(){return this.getScrollWidth()-this.getClientWidth()},e.prototype.canScrollVertically=function(){return this.getMaxScrollTop()>0},e.prototype.canScrollHorizontally=function(){return this.getMaxScrollLeft()>0},e.prototype.canScrollUp=function(){return this.getScrollTop()>0},e.prototype.canScrollDown=function(){return this.getScrollTop()<this.getMaxScrollTop()},e.prototype.canScrollLeft=function(){return this.getScrollLeft()>0},e.prototype.canScrollRight=function(){return this.getScrollLeft()<this.getMaxScrollLeft()},e}(),sn=function(e){function t(t){var n=e.call(this)||this;return n.el=t,n}return Ee(t,e),t.prototype.getScrollTop=function(){return this.el.scrollTop},t.prototype.getScrollLeft=function(){return this.el.scrollLeft},t.prototype.setScrollTop=function(e){this.el.scrollTop=e},t.prototype.setScrollLeft=function(e){this.el.scrollLeft=e},t.prototype.getScrollWidth=function(){return this.el.scrollWidth},t.prototype.getScrollHeight=function(){return this.el.scrollHeight},t.prototype.getClientHeight=function(){return this.el.clientHeight},t.prototype.getClientWidth=function(){return this.el.clientWidth},t}(an),un=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Ee(t,e),t.prototype.getScrollTop=function(){return window.pageYOffset},t.prototype.getScrollLeft=function(){return window.pageXOffset},t.prototype.setScrollTop=function(e){window.scroll(window.pageXOffset,e)},t.prototype.setScrollLeft=function(e){window.scroll(e,window.pageYOffset)},t.prototype.getScrollWidth=function(){return document.documentElement.scrollWidth},t.prototype.getScrollHeight=function(){return document.documentElement.scrollHeight},t.prototype.getClientHeight=function(){return document.documentElement.clientHeight},t.prototype.getClientWidth=function(){return document.documentElement.clientWidth},t}(an),ln=function(e){function t(t,n){var i=e.call(this,r("div",{className:"fc-scroller"}))||this;return i.overflowX=t,i.overflowY=n,i.applyOverflow(),i}return Ee(t,e),t.prototype.clear=function(){this.setHeight("auto"),this.applyOverflow()},t.prototype.destroy=function(){c(this.el)},t.prototype.applyOverflow=function(){y(this.el,{overflowX:this.overflowX,overflowY:this.overflowY})},t.prototype.lockOverflow=function(e){var t=this.overflowX,n=this.overflowY;e=e||this.getScrollbarWidths(),"auto"===t&&(t=e.bottom||this.canScrollHorizontally()?"scroll":"hidden"),"auto"===n&&(n=e.left||e.right||this.canScrollVertically()?"scroll":"hidden"),y(this.el,{overflowX:t,overflowY:n})},t.prototype.setHeight=function(e){m(this.el,"height",e)},t.prototype.getScrollbarWidths=function(){var e=T(this.el);return{left:e.scrollbarLeft,right:e.scrollbarRight,bottom:e.scrollbarBottom}},t}(sn),cn=function(){function e(e){this.calendarOptions=e,this.processIconOverride()}return e.prototype.processIconOverride=function(){this.iconOverrideOption&&this.setIconOverride(this.calendarOptions[this.iconOverrideOption])},e.prototype.setIconOverride=function(e){var t,n;if("object"==typeof e&&e){for(n in t=Se({},this.iconClasses),e)t[n]=this.applyIconOverridePrefix(e[n]);this.iconClasses=t}else!1===e&&(this.iconClasses={})},e.prototype.applyIconOverridePrefix=function(e){var t=this.iconOverridePrefix;return t&&0!==e.indexOf(t)&&(e=t+e),e},e.prototype.getClass=function(e){return this.classes[e]||""},e.prototype.getIconClass=function(e){var t=this.iconClasses[e];return t?this.baseIconClass+" "+t:""},e.prototype.getCustomButtonIconClass=function(e){var t;return this.iconOverrideCustomButtonOption&&(t=e[this.iconOverrideCustomButtonOption])?this.baseIconClass+" "+this.applyIconOverridePrefix(t):""},e}();cn.prototype.classes={},cn.prototype.iconClasses={},cn.prototype.baseIconClass="",cn.prototype.iconOverridePrefix="";var dn=0,fn=function(){function e(e,t){t&&(e.view=this),this.uid=String(dn++),this.context=e,this.dateEnv=e.dateEnv,this.theme=e.theme,this.view=e.view,this.calendar=e.calendar,this.isRtl="rtl"===this.opt("dir")}return e.addEqualityFuncs=function(e){this.prototype.equalityFuncs=Se({},this.prototype.equalityFuncs,e)},e.prototype.opt=function(e){return this.context.options[e]},e.prototype.receiveProps=function(e){var t=function(e,t,n){var r={},i=!1;for(var o in t)o in e&&(e[o]===t[o]||n[o]&&n[o](e[o],t[o]))?r[o]=e[o]:(r[o]=t[o],i=!0);for(var o in e)if(!(o in t)){i=!0;break}return{anyChanges:i,comboProps:r}}(this.props||{},e,this.equalityFuncs),n=t.anyChanges,r=t.comboProps;this.props=r,n&&this.render(r)},e.prototype.render=function(e){},e.prototype.destroy=function(){},e}();fn.prototype.equalityFuncs={};var pn=function(e){function t(t,n,r){var i=e.call(this,t,r)||this;return i.el=n,i}return Ee(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this),c(this.el)},t.prototype.buildPositionCaches=function(){},t.prototype.queryHit=function(e,t,n,r){return null},t.prototype.isInteractionValid=function(e){var t=this.calendar,n=this.props.dateProfile,r=e.mutatedEvents.instances;if(n)for(var i in r)if(!We(n.validRange,r[i].range))return!1;return Tt(e,t)},t.prototype.isDateSelectionValid=function(e){var t,n,r=this.props.dateProfile;return!(r&&!We(r.validRange,e.range))&&(t=e,n=this.calendar,wt({dateSelection:t},n))},t.prototype.publiclyTrigger=function(e,t){return this.calendar.publiclyTrigger(e,t)},t.prototype.publiclyTriggerAfterSizing=function(e,t){return this.calendar.publiclyTriggerAfterSizing(e,t)},t.prototype.hasPublicHandlers=function(e){return this.calendar.hasPublicHandlers(e)},t.prototype.triggerRenderedSegs=function(e,t){var n=this.calendar;if(this.hasPublicHandlers("eventPositioned"))for(var r=0,i=e;r<i.length;r++){var o=i[r];this.publiclyTriggerAfterSizing("eventPositioned",[{event:new ct(n,o.eventRange.def,o.eventRange.instance),isMirror:t,isStart:o.isStart,isEnd:o.isEnd,el:o.el,view:this}])}n.state.loadingLevel||(n.afterSizingTriggers._eventsPositioned=[null])},t.prototype.triggerWillRemoveSegs=function(e,t){for(var n=this.calendar,r=0,i=e;r<i.length;r++){var o=i[r];n.trigger("eventElRemove",o.el)}if(this.hasPublicHandlers("eventDestroy"))for(var a=0,s=e;a<s.length;a++){o=s[a];this.publiclyTrigger("eventDestroy",[{event:new ct(n,o.eventRange.def,o.eventRange.instance),isMirror:t,el:o.el,view:this}])}},t.prototype.isValidSegDownEl=function(e){return!this.props.eventDrag&&!this.props.eventResize&&!p(e,".fc-mirror")&&(this.isPopover()||!this.isInPopover(e))},t.prototype.isValidDateDownEl=function(e){var t=p(e,this.fgSegSelector);return(!t||t.classList.contains("fc-mirror"))&&!p(e,".fc-more")&&!p(e,"a[data-goto]")&&!this.isInPopover(e)},t.prototype.isPopover=function(){return this.el.classList.contains("fc-popover")},t.prototype.isInPopover=function(e){return Boolean(p(e,".fc-popover"))},t}(fn);pn.prototype.fgSegSelector=".fc-event-container > *",pn.prototype.bgSegSelector=".fc-bgevent:not(.fc-nonbusiness)";var hn=0;function vn(e){return{id:String(hn++),deps:e.deps||[],reducers:e.reducers||[],eventDefParsers:e.eventDefParsers||[],isDraggableTransformers:e.isDraggableTransformers||[],eventDragMutationMassagers:e.eventDragMutationMassagers||[],eventDefMutationAppliers:e.eventDefMutationAppliers||[],dateSelectionTransformers:e.dateSelectionTransformers||[],datePointTransforms:e.datePointTransforms||[],dateSpanTransforms:e.dateSpanTransforms||[],views:e.views||{},viewPropsTransformers:e.viewPropsTransformers||[],isPropsValid:e.isPropsValid||null,externalDefTransforms:e.externalDefTransforms||[],eventResizeJoinTransforms:e.eventResizeJoinTransforms||[],viewContainerModifiers:e.viewContainerModifiers||[],eventDropTransformers:e.eventDropTransformers||[],componentInteractions:e.componentInteractions||[],calendarInteractions:e.calendarInteractions||[],themeClasses:e.themeClasses||{},eventSourceDefs:e.eventSourceDefs||[],cmdFormatter:e.cmdFormatter,recurringTypes:e.recurringTypes||[],namedTimeZonedImpl:e.namedTimeZonedImpl,defaultView:e.defaultView||"",elementDraggingImpl:e.elementDraggingImpl,optionChangeHandlers:e.optionChangeHandlers||{}}}var gn=function(){function e(){this.hooks={reducers:[],eventDefParsers:[],isDraggableTransformers:[],eventDragMutationMassagers:[],eventDefMutationAppliers:[],dateSelectionTransformers:[],datePointTransforms:[],dateSpanTransforms:[],views:{},viewPropsTransformers:[],isPropsValid:null,externalDefTransforms:[],eventResizeJoinTransforms:[],viewContainerModifiers:[],eventDropTransformers:[],componentInteractions:[],calendarInteractions:[],themeClasses:{},eventSourceDefs:[],cmdFormatter:null,recurringTypes:[],namedTimeZonedImpl:null,defaultView:"",elementDraggingImpl:null,optionChangeHandlers:{}},this.addedHash={}}return e.prototype.add=function(e){if(!this.addedHash[e.id]){this.addedHash[e.id]=!0;for(var t=0,n=e.deps;t<n.length;t++){var r=n[t];this.add(r)}this.hooks=(i=this.hooks,o=e,{reducers:i.reducers.concat(o.reducers),eventDefParsers:i.eventDefParsers.concat(o.eventDefParsers),isDraggableTransformers:i.isDraggableTransformers.concat(o.isDraggableTransformers),eventDragMutationMassagers:i.eventDragMutationMassagers.concat(o.eventDragMutationMassagers),eventDefMutationAppliers:i.eventDefMutationAppliers.concat(o.eventDefMutationAppliers),dateSelectionTransformers:i.dateSelectionTransformers.concat(o.dateSelectionTransformers),datePointTransforms:i.datePointTransforms.concat(o.datePointTransforms),dateSpanTransforms:i.dateSpanTransforms.concat(o.dateSpanTransforms),views:Se({},i.views,o.views),viewPropsTransformers:i.viewPropsTransformers.concat(o.viewPropsTransformers),isPropsValid:o.isPropsValid||i.isPropsValid,externalDefTransforms:i.externalDefTransforms.concat(o.externalDefTransforms),eventResizeJoinTransforms:i.eventResizeJoinTransforms.concat(o.eventResizeJoinTransforms),viewContainerModifiers:i.viewContainerModifiers.concat(o.viewContainerModifiers),eventDropTransformers:i.eventDropTransformers.concat(o.eventDropTransformers),calendarInteractions:i.calendarInteractions.concat(o.calendarInteractions),componentInteractions:i.componentInteractions.concat(o.componentInteractions),themeClasses:Se({},i.themeClasses,o.themeClasses),eventSourceDefs:i.eventSourceDefs.concat(o.eventSourceDefs),cmdFormatter:o.cmdFormatter||i.cmdFormatter,recurringTypes:i.recurringTypes.concat(o.recurringTypes),namedTimeZonedImpl:o.namedTimeZonedImpl||i.namedTimeZonedImpl,defaultView:i.defaultView||o.defaultView,elementDraggingImpl:i.elementDraggingImpl||o.elementDraggingImpl,optionChangeHandlers:Se({},i.optionChangeHandlers,o.optionChangeHandlers)})}var i,o},e}();var yn=vn({eventSourceDefs:[{ignoreRange:!0,parseMeta:function(e){return Array.isArray(e)?e:Array.isArray(e.events)?e.events:null},fetch:function(e,t){t({rawEvents:e.eventSource.meta})}}]}),mn=vn({eventSourceDefs:[{parseMeta:function(e){return"function"==typeof e?e:"function"==typeof e.events?e.events:null},fetch:function(e,t,n){var r=e.calendar.dateEnv;$t(e.eventSource.meta.bind(null,{start:r.toDate(e.range.start),end:r.toDate(e.range.end),startStr:r.formatIso(e.range.start),endStr:r.formatIso(e.range.end),timeZone:r.timeZone}),function(e){t({rawEvents:e})},n)}}]});function En(e,t,n,r,i){var o=null;"GET"===(e=e.toUpperCase())?t=function(e,t){return e+(-1===e.indexOf("?")?"?":"&")+Sn(t)}(t,n):o=Sn(n);var a=new XMLHttpRequest;a.open(e,t,!0),"GET"!==e&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),a.onload=function(){if(a.status>=200&&a.status<400)try{var e=JSON.parse(a.responseText);r(e,a)}catch(e){i("Failure parsing JSON",a)}else i("Request failed",a)},a.onerror=function(){i("Request failed",a)},a.send(o)}function Sn(e){var t=[];for(var n in e)t.push(encodeURIComponent(n)+"="+encodeURIComponent(e[n]));return t.join("&")}var bn=vn({eventSourceDefs:[{parseMeta:function(e){if("string"==typeof e)e={url:e};else if(!e||"object"!=typeof e||!e.url)return null;return{url:e.url,method:(e.method||"GET").toUpperCase(),extraParams:e.extraParams,startParam:e.startParam,endParam:e.endParam,timeZoneParam:e.timeZoneParam}},fetch:function(e,t,n){var r=e.eventSource.meta,i=function(e,t,n){var r,i,o,a,s=n.dateEnv,u={};null==(r=e.startParam)&&(r=n.opt("startParam"));null==(i=e.endParam)&&(i=n.opt("endParam"));null==(o=e.timeZoneParam)&&(o=n.opt("timeZoneParam"));a="function"==typeof e.extraParams?e.extraParams():e.extraParams||{};Se(u,a),u[r]=s.formatIso(t.start),u[i]=s.formatIso(t.end),"local"!==s.timeZone&&(u[o]=s.timeZone);return u}(r,e.range,e.calendar);En(r.method,r.url,i,function(e,n){t({rawEvents:e,xhr:n})},function(e,t){n({message:e,xhr:t})})}}]});var Dn=vn({recurringTypes:[{parse:function(e,t,n){var r,i,o=n.createMarker.bind(n),a=he(e,{daysOfWeek:null,startTime:J,endTime:J,startRecur:o,endRecur:o},{},t),s=!1;for(var u in a)if(null!=a[u]){s=!0;break}if(s){var l=null;return"duration"in t&&(l=J(t.duration),delete t.duration),!l&&a.startTime&&a.endTime&&(r=a.endTime,i=a.startTime,l={years:r.years-i.years,months:r.months-i.months,days:r.days-i.days,milliseconds:r.milliseconds-i.milliseconds}),{allDayGuess:Boolean(!a.startTime&&!a.endTime),duration:l,typeData:a}}return null},expand:function(e,t,n){var r=Ve(t,{start:e.startRecur,end:e.endRecur});return r?function(e,t,n,r){var i=e?Ie(e):null,o=B(n.start),a=n.end,s=[];for(;o<a;){var u=void 0;i&&!i[o.getUTCDay()]||(u=t?r.add(o,t):o,s.push(u)),o=x(o,1)}return s}(e.daysOfWeek,e.startTime,r,n):[]}}]});var Tn=vn({optionChangeHandlers:{events:function(e,t,n){wn([e],t,n)},eventSources:wn,plugins:function(e,t){t.addPluginInputs(e)}}});function wn(e,t,n){for(var r=Ce(t.state.eventSources),i=[],o=0,a=e;o<a.length;o++){for(var s=a[o],u=!1,l=0;l<r.length;l++)if(n(r[l]._raw,s)){r.splice(l,1),u=!0;break}u||i.push(s)}for(var c=0,d=r;c<d.length;c++){var f=d[c];t.dispatch({type:"REMOVE_EVENT_SOURCE",sourceId:f.sourceId})}for(var p=0,h=i;p<h.length;p++){var v=h[p];t.addEventSource(v)}}var Rn={defaultRangeSeparator:" - ",titleRangeSeparator:" – ",defaultTimedEventDuration:"01:00:00",defaultAllDayEventDuration:{day:1},forceEventDuration:!1,nextDayThreshold:"00:00:00",columnHeader:!0,defaultView:"",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberCalculation:"local",editable:!1,scrollTime:"06:00:00",minTime:"00:00:00",maxTime:"24:00:00",showNonCurrentDates:!0,lazyFetching:!0,startParam:"start",endParam:"end",timeZoneParam:"timeZone",timeZone:"local",locales:[],locale:"",timeGridEventMinHeight:0,themeSystem:"standard",dragRevertDuration:500,dragScroll:!0,allDayMaintainDuration:!1,unselectAuto:!0,dropAccept:"*",eventOrder:"start,-duration,allDay,title",eventLimit:!1,eventLimitClick:"popover",dayPopoverFormat:{month:"long",day:"numeric",year:"numeric"},handleWindowResize:!0,windowResizeDelay:100,longPressDelay:1e3,eventDragMinDistance:5},In={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"fc-icon-chevron-right",next:"fc-icon-chevron-left",prevYear:"fc-icon-chevrons-right",nextYear:"fc-icon-chevrons-left"}},Cn=["header","footer","buttonText","buttonIcons"];var Mn=[yn,mn,bn,Dn,Tn];var kn={code:"en",week:{dow:0,doy:4},dir:"ltr",buttonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",year:"year",today:"today",month:"month",week:"week",day:"day",list:"list"},weekLabel:"W",allDayText:"all-day",eventLimitText:"more",noEventsMessage:"No events to display"};function On(e){for(var t=e.length>0?e[0].code:"en",n=window.FullCalendarLocalesAll||[],r=window.FullCalendarLocales||{},i=n.concat(Ce(r),e),o={en:kn},a=0,s=i;a<s.length;a++){var u=s[a];o[u.code]=u}return{map:o,defaultCode:t}}function _n(e,t){return"object"!=typeof e||Array.isArray(e)?function(e,t){var n=[].concat(e||[]),r=function(e,t){for(var n=0;n<e.length;n++)for(var r=e[n].toLocaleLowerCase().split("-"),i=r.length;i>0;i--){var o=r.slice(0,i).join("-");if(t[o])return t[o]}return null}(n,t)||kn;return Pn(e,n,r)}(e,t):Pn(e.code,[e.code],e)}function Pn(e,t,n){var r=Te([kn,n],["buttonText"]);delete r.code;var i=r.week;return delete r.week,{codeArg:e,codes:t,week:i,simpleNumberFormat:new Intl.NumberFormat(e),options:r}}var xn=function(){function e(e){this.overrides=Se({},e),this.dynamicOverrides={},this.compute()}return e.prototype.mutate=function(e,t,n){var r=n?this.dynamicOverrides:this.overrides;Se(r,e);for(var i=0,o=t;i<o.length;i++){delete r[o[i]]}this.compute()},e.prototype.compute=function(){var e=fe(this.dynamicOverrides.locales,this.overrides.locales,Rn.locales),t=fe(this.dynamicOverrides.locale,this.overrides.locale,Rn.locale),n=On(e),r=_n(t||n.defaultCode,n.map).options,i="rtl"===fe(this.dynamicOverrides.dir,this.overrides.dir,r.dir)?In:{};this.dirDefaults=i,this.localeDefaults=r,this.computed=Te([Rn,i,r,this.overrides,this.dynamicOverrides],Cn)},e}(),Hn={};var Nn,zn=function(){function e(){}return e.prototype.getMarkerYear=function(e){return e.getUTCFullYear()},e.prototype.getMarkerMonth=function(e){return e.getUTCMonth()},e.prototype.getMarkerDay=function(e){return e.getUTCDate()},e.prototype.arrayToMarker=function(e){return j(e)},e.prototype.markerToArray=function(e){return Z(e)},e}();Nn=zn,Hn["gregory"]=Nn;var Un=/^\s*(\d{4})(-(\d{2})(-(\d{2})([T ](\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|(([-+])(\d{2})(:?(\d{2}))?))?)?)?)?$/;function Ln(e){var t=Un.exec(e);if(t){var n=new Date(Date.UTC(Number(t[1]),t[3]?Number(t[3])-1:0,Number(t[5]||1),Number(t[7]||0),Number(t[8]||0),Number(t[10]||0),t[12]?1e3*Number("0."+t[12]):0));if(Y(n)){var r=null;return t[13]&&(r=("-"===t[15]?-1:1)*(60*Number(t[16]||0)+Number(t[18]||0))),{marker:n,isTimeUnspecified:!t[6],timeZoneOffset:r}}}return null}var Bn=function(){function e(e){var t=this.timeZone=e.timeZone,n="local"!==t&&"UTC"!==t;e.namedTimeZoneImpl&&n&&(this.namedTimeZoneImpl=new e.namedTimeZoneImpl(t)),this.canComputeOffset=Boolean(!n||this.namedTimeZoneImpl),this.calendarSystem=function(e){return new Hn[e]}(e.calendarSystem),this.locale=e.locale,this.weekDow=e.locale.week.dow,this.weekDoy=e.locale.week.doy,"ISO"===e.weekNumberCalculation&&(this.weekDow=1,this.weekDoy=4),"number"==typeof e.firstDay&&(this.weekDow=e.firstDay),"function"==typeof e.weekNumberCalculation&&(this.weekNumberFunc=e.weekNumberCalculation),this.weekLabel=null!=e.weekLabel?e.weekLabel:e.locale.options.weekLabel,this.cmdFormatter=e.cmdFormatter}return e.prototype.createMarker=function(e){var t=this.createMarkerMeta(e);return null===t?null:t.marker},e.prototype.createNowMarker=function(){return this.canComputeOffset?this.timestampToMarker((new Date).valueOf()):j(F(new Date))},e.prototype.createMarkerMeta=function(e){if("string"==typeof e)return this.parse(e);var t=null;return"number"==typeof e?t=this.timestampToMarker(e):e instanceof Date?(e=e.valueOf(),isNaN(e)||(t=this.timestampToMarker(e))):Array.isArray(e)&&(t=j(e)),null!==t&&Y(t)?{marker:t,isTimeUnspecified:!1,forcedTzo:null}:null},e.prototype.parse=function(e){var t=Ln(e);if(null===t)return null;var n=t.marker,r=null;return null!==t.timeZoneOffset&&(this.canComputeOffset?n=this.timestampToMarker(n.valueOf()-60*t.timeZoneOffset*1e3):r=t.timeZoneOffset),{marker:n,isTimeUnspecified:t.isTimeUnspecified,forcedTzo:r}},e.prototype.getYear=function(e){return this.calendarSystem.getMarkerYear(e)},e.prototype.getMonth=function(e){return this.calendarSystem.getMarkerMonth(e)},e.prototype.add=function(e,t){var n=this.calendarSystem.markerToArray(e);return n[0]+=t.years,n[1]+=t.months,n[2]+=t.days,n[6]+=t.milliseconds,this.calendarSystem.arrayToMarker(n)},e.prototype.subtract=function(e,t){var n=this.calendarSystem.markerToArray(e);return n[0]-=t.years,n[1]-=t.months,n[2]-=t.days,n[6]-=t.milliseconds,this.calendarSystem.arrayToMarker(n)},e.prototype.addYears=function(e,t){var n=this.calendarSystem.markerToArray(e);return n[0]+=t,this.calendarSystem.arrayToMarker(n)},e.prototype.addMonths=function(e,t){var n=this.calendarSystem.markerToArray(e);return n[1]+=t,this.calendarSystem.arrayToMarker(n)},e.prototype.diffWholeYears=function(e,t){var n=this.calendarSystem;return q(e)===q(t)&&n.getMarkerDay(e)===n.getMarkerDay(t)&&n.getMarkerMonth(e)===n.getMarkerMonth(t)?n.getMarkerYear(t)-n.getMarkerYear(e):null},e.prototype.diffWholeMonths=function(e,t){var n=this.calendarSystem;return q(e)===q(t)&&n.getMarkerDay(e)===n.getMarkerDay(t)?n.getMarkerMonth(t)-n.getMarkerMonth(e)+12*(n.getMarkerYear(t)-n.getMarkerYear(e)):null},e.prototype.greatestWholeUnit=function(e,t){var n=this.diffWholeYears(e,t);return null!==n?{unit:"year",value:n}:null!==(n=this.diffWholeMonths(e,t))?{unit:"month",value:n}:null!==(n=U(e,t))?{unit:"week",value:n}:null!==(n=L(e,t))?{unit:"day",value:n}:ce(n=function(e,t){return(t.valueOf()-e.valueOf())/36e5}(e,t))?{unit:"hour",value:n}:ce(n=function(e,t){return(t.valueOf()-e.valueOf())/6e4}(e,t))?{unit:"minute",value:n}:ce(n=function(e,t){return(t.valueOf()-e.valueOf())/1e3}(e,t))?{unit:"second",value:n}:{unit:"millisecond",value:t.valueOf()-e.valueOf()}},e.prototype.countDurationsBetween=function(e,t,n){var r;return n.years&&null!==(r=this.diffWholeYears(e,t))?r/(ee(n)/365):n.months&&null!==(r=this.diffWholeMonths(e,t))?r/function(e){return ee(e)/30}(n):n.days&&null!==(r=L(e,t))?r/ee(n):(t.valueOf()-e.valueOf())/te(n)},e.prototype.startOf=function(e,t){return"year"===t?this.startOfYear(e):"month"===t?this.startOfMonth(e):"week"===t?this.startOfWeek(e):"day"===t?B(e):"hour"===t?function(e){return j([e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours()])}(e):"minute"===t?function(e){return j([e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes()])}(e):"second"===t?function(e){return j([e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds()])}(e):void 0},e.prototype.startOfYear=function(e){return this.calendarSystem.arrayToMarker([this.calendarSystem.getMarkerYear(e)])},e.prototype.startOfMonth=function(e){return this.calendarSystem.arrayToMarker([this.calendarSystem.getMarkerYear(e),this.calendarSystem.getMarkerMonth(e)])},e.prototype.startOfWeek=function(e){return this.calendarSystem.arrayToMarker([this.calendarSystem.getMarkerYear(e),this.calendarSystem.getMarkerMonth(e),e.getUTCDate()-(e.getUTCDay()-this.weekDow+7)%7])},e.prototype.computeWeekNumber=function(e){return this.weekNumberFunc?this.weekNumberFunc(this.toDate(e)):function(e,t,n){var r=e.getUTCFullYear(),i=V(e,r,t,n);if(i<1)return V(e,r-1,t,n);var o=V(e,r+1,t,n);return o>=1?Math.min(i,o):i}(e,this.weekDow,this.weekDoy)},e.prototype.format=function(e,t,n){return void 0===n&&(n={}),t.format({marker:e,timeZoneOffset:null!=n.forcedTzo?n.forcedTzo:this.offsetForMarker(e)},this)},e.prototype.formatRange=function(e,t,n,r){return void 0===r&&(r={}),r.isEndExclusive&&(t=H(t,-1)),n.formatRange({marker:e,timeZoneOffset:null!=r.forcedStartTzo?r.forcedStartTzo:this.offsetForMarker(e)},{marker:t,timeZoneOffset:null!=r.forcedEndTzo?r.forcedEndTzo:this.offsetForMarker(t)},this)},e.prototype.formatIso=function(e,t){void 0===t&&(t={});var n=null;return t.omitTimeZoneOffset||(n=null!=t.forcedTzo?t.forcedTzo:this.offsetForMarker(e)),function(e,t,n){void 0===n&&(n=!1);var r=e.toISOString();return r=r.replace(".000",""),n&&(r=r.replace("T00:00:00Z","")),r.length>10&&(null==t?r=r.replace("Z",""):0!==t&&(r=r.replace("Z",at(t,!0)))),r}(e,n,t.omitTime)},e.prototype.timestampToMarker=function(e){return"local"===this.timeZone?j(F(new Date(e))):"UTC"!==this.timeZone&&this.namedTimeZoneImpl?j(this.namedTimeZoneImpl.timestampToArray(e)):new Date(e)},e.prototype.offsetForMarker=function(e){return"local"===this.timeZone?-W(Z(e)).getTimezoneOffset():"UTC"===this.timeZone?0:this.namedTimeZoneImpl?this.namedTimeZoneImpl.offsetForArray(Z(e)):null},e.prototype.toDate=function(e,t){return"local"===this.timeZone?W(Z(e)):"UTC"===this.timeZone?new Date(e.valueOf()):this.namedTimeZoneImpl?new Date(e.valueOf()-1e3*this.namedTimeZoneImpl.offsetForArray(Z(e))*60):new Date(e.valueOf()-(t||0))},e}(),Vn={id:String,allDayDefault:Boolean,eventDataTransform:Function,success:Function,failure:Function},An=0;function Fn(e,t){return!t.pluginSystem.hooks.eventSourceDefs[e.sourceDefId].ignoreRange}function Wn(e,t){for(var n=t.pluginSystem.hooks.eventSourceDefs,r=n.length-1;r>=0;r--){var i=n[r].parseMeta(e);if(i){var o=Zn("object"==typeof e?e:{},i,r,t);return o._raw=e,o}}return null}function Zn(e,t,n,r){var i={},o=he(e,Vn,{},i),a={},s=Ht(i,r,a);return o.isFetching=!1,o.latestFetchId="",o.fetchRange=null,o.publicId=String(e.id||""),o.sourceId=String(An++),o.sourceDefId=n,o.meta=t,o.ui=s,o.extendedProps=a,o}function jn(e,t,n,r){switch(t.type){case"ADD_EVENT_SOURCES":return function(e,t,n,r){for(var i={},o=0,a=t;o<a.length;o++){var s=a[o];i[s.sourceId]=s}n&&(i=qn(i,n,r));return Se({},e,i)}(e,t.sources,n?n.activeRange:null,r);case"REMOVE_EVENT_SOURCE":return i=e,o=t.sourceId,we(i,function(e){return e.sourceId!==o});case"PREV":case"NEXT":case"SET_DATE":case"SET_VIEW_TYPE":return n?qn(e,n.activeRange,r):e;case"FETCH_EVENT_SOURCES":case"CHANGE_TIMEZONE":return Gn(e,t.sourceIds?Ie(t.sourceIds):function(e,t){return we(e,function(e){return Fn(e,t)})}(e,r),n?n.activeRange:null,r);case"RECEIVE_EVENTS":case"RECEIVE_EVENT_ERROR":return function(e,t,n,r){var i,o=e[t];if(o&&n===o.latestFetchId)return Se({},e,((i={})[t]=Se({},o,{isFetching:!1,fetchRange:r}),i));return e}(e,t.sourceId,t.fetchId,t.fetchRange);case"REMOVE_ALL_EVENT_SOURCES":return{};default:return e}var i,o}var Yn=0;function qn(e,t,n){return Gn(e,we(e,function(e){return function(e,t,n){return Fn(e,n)?!n.opt("lazyFetching")||!e.fetchRange||t.start<e.fetchRange.start||t.end>e.fetchRange.end:!e.latestFetchId}(e,t,n)}),t,n)}function Gn(e,t,n,r){var i={};for(var o in e){var a=e[o];t[o]?i[o]=Xn(a,n,r):i[o]=a}return i}function Xn(e,t,n){var r=n.pluginSystem.hooks.eventSourceDefs[e.sourceDefId],i=String(Yn++);return r.fetch({eventSource:e,calendar:n,range:t},function(r){var o,a,s=r.rawEvents,u=n.opt("eventSourceSuccess");e.success&&(a=e.success(s,r.xhr)),u&&(o=u(s,r.xhr)),s=a||o||s,n.dispatch({type:"RECEIVE_EVENTS",sourceId:e.sourceId,fetchId:i,fetchRange:t,rawEvents:s})},function(r){var o=n.opt("eventSourceFailure");console.warn(r.message,r),e.failure&&e.failure(r),o&&o(r),n.dispatch({type:"RECEIVE_EVENT_ERROR",sourceId:e.sourceId,fetchId:i,fetchRange:t,error:r})}),Se({},e,{isFetching:!0,latestFetchId:i})}var Jn=function(){function e(e,t){this.viewSpec=e,this.options=e.options,this.dateEnv=t.dateEnv,this.calendar=t,this.initHiddenDays()}return e.prototype.buildPrev=function(e,t){var n=this.dateEnv,r=n.subtract(n.startOf(t,e.currentRangeUnit),e.dateIncrement);return this.build(r,-1)},e.prototype.buildNext=function(e,t){var n=this.dateEnv,r=n.add(n.startOf(t,e.currentRangeUnit),e.dateIncrement);return this.build(r,1)},e.prototype.build=function(e,t,n){var r;void 0===n&&(n=!1);var i,o,a,s,u,l,c,d,f;return r=this.buildValidRange(),r=this.trimHiddenDays(r),n&&(d=e,e=null!=(f=r).start&&d<f.start?f.start:null!=f.end&&d>=f.end?new Date(f.end.valueOf()-1):d),a=this.buildCurrentRangeInfo(e,t),s=/^(year|month|week|day)$/.test(a.unit),u=this.buildRenderRange(this.trimHiddenDays(a.range),a.unit,s),l=u=this.trimHiddenDays(u),this.options.showNonCurrentDates||(l=Ve(l,a.range)),i=J(this.options.minTime),o=J(this.options.maxTime),l=Ve(l=this.adjustActiveRange(l,i,o),r),c=Fe(a.range,r),{validRange:r,currentRange:a.range,currentRangeUnit:a.unit,isRangeAllDay:s,activeRange:l,renderRange:u,minTime:i,maxTime:o,isValid:c,dateIncrement:this.buildDateIncrement(a.duration)}},e.prototype.buildValidRange=function(){return this.getRangeOption("validRange",this.calendar.getNow())||{start:null,end:null}},e.prototype.buildCurrentRangeInfo=function(e,t){var n,r=this.viewSpec,i=this.dateEnv,o=null,a=null,s=null;return r.duration?(o=r.duration,a=r.durationUnit,s=this.buildRangeFromDuration(e,t,o,a)):(n=this.options.dayCount)?(a="day",s=this.buildRangeFromDayCount(e,t,n)):(s=this.buildCustomVisibleRange(e))?a=i.greatestWholeUnit(s.start,s.end).unit:(a=ne(o=this.getFallbackDuration()).unit,s=this.buildRangeFromDuration(e,t,o,a)),{duration:o,unit:a,range:s}},e.prototype.getFallbackDuration=function(){return J({day:1})},e.prototype.adjustActiveRange=function(e,t,n){var r=this.dateEnv,i=e.start,o=e.end;return this.viewSpec.class.prototype.usesMinMaxTime&&(ee(t)<0&&(i=B(i),i=r.add(i,t)),ee(n)>1&&(o=x(o=B(o),-1),o=r.add(o,n))),{start:i,end:o}},e.prototype.buildRangeFromDuration=function(e,t,n,r){var i,o,a,s,u,l=this.dateEnv,c=this.options.dateAlignment;function d(){a=l.startOf(e,c),s=l.add(a,n),u={start:a,end:s}}return c||((i=this.options.dateIncrement)?(o=J(i),c=te(o)<te(n)?ne(o,!Q(i)).unit:r):c=r),ee(n)<=1&&this.isHiddenDay(a)&&(a=B(a=this.skipHiddenDays(a,t))),d(),this.trimHiddenDays(u)||(e=this.skipHiddenDays(e,t),d()),u},e.prototype.buildRangeFromDayCount=function(e,t,n){var r,i=this.dateEnv,o=this.options.dateAlignment,a=0,s=e;o&&(s=i.startOf(s,o)),s=B(s),r=s=this.skipHiddenDays(s,t);do{r=x(r,1),this.isHiddenDay(r)||a++}while(a<n);return{start:s,end:r}},e.prototype.buildCustomVisibleRange=function(e){var t=this.dateEnv,n=this.getRangeOption("visibleRange",t.toDate(e));return!n||null!=n.start&&null!=n.end?n:null},e.prototype.buildRenderRange=function(e,t,n){return e},e.prototype.buildDateIncrement=function(e){var t,n=this.options.dateIncrement;return n?J(n):(t=this.options.dateAlignment)?J(1,t):e||J({days:1})},e.prototype.getRangeOption=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];var r=this.options[e];return"function"==typeof r&&(r=r.apply(null,t)),r&&(r=Ue(r,this.dateEnv)),r&&(r=ge(r)),r},e.prototype.initHiddenDays=function(){var e,t=this.options.hiddenDays||[],n=[],r=0;for(!1===this.options.weekends&&t.push(0,6),e=0;e<7;e++)(n[e]=-1!==t.indexOf(e))||r++;if(!r)throw new Error("invalid hiddenDays");this.isHiddenDayHash=n},e.prototype.trimHiddenDays=function(e){var t=e.start,n=e.end;return t&&(t=this.skipHiddenDays(t)),n&&(n=this.skipHiddenDays(n,-1,!0)),null==t||null==n||t<n?{start:t,end:n}:null},e.prototype.isHiddenDay=function(e){return e instanceof Date&&(e=e.getUTCDay()),this.isHiddenDayHash[e]},e.prototype.skipHiddenDays=function(e,t,n){for(void 0===t&&(t=1),void 0===n&&(n=!1);this.isHiddenDayHash[(e.getUTCDay()+(n?t:0)+7)%7];)e=x(e,t);return e},e}();function Kn(e,t,n){for(var r=function(e,t){switch(t.type){case"SET_VIEW_TYPE":return t.viewType;default:return e}}(e.viewType,t),i=function(e,t,n,r,i){var o;switch(t.type){case"PREV":o=i.dateProfileGenerators[r].buildPrev(e,n);break;case"NEXT":o=i.dateProfileGenerators[r].buildNext(e,n);break;case"SET_DATE":e.activeRange&&Ze(e.currentRange,t.dateMarker)||(o=i.dateProfileGenerators[r].build(t.dateMarker,void 0,!0));break;case"SET_VIEW_TYPE":var a=i.dateProfileGenerators[r];if(!a)throw new Error(r?'The FullCalendar view "'+r+'" does not exist. Make sure your plugins are loaded correctly.':"No available FullCalendar view plugins.");o=a.build(t.dateMarker||n,void 0,!0)}return!o||!o.isValid||e&&(s=e,u=o,Ae(s.validRange,u.validRange)&&Ae(s.activeRange,u.activeRange)&&Ae(s.renderRange,u.renderRange)&&$(s.minTime,u.minTime)&&$(s.maxTime,u.maxTime))?e:o;var s,u}(e.dateProfile,t,e.currentDate,r,n),o=jn(e.eventSources,t,i,n),a=Se({},e,{viewType:r,dateProfile:i,currentDate:Qn(e.currentDate,t,i),eventSources:o,eventStore:St(e.eventStore,t,o,i,n),dateSelection:$n(e.dateSelection,t,n),eventSelection:er(e.eventSelection,t),eventDrag:tr(e.eventDrag,t,o,n),eventResize:nr(e.eventResize,t,o,n),eventSourceLoadingLevel:rr(o),loadingLevel:rr(o)}),s=0,u=n.pluginSystem.hooks.reducers;s<u.length;s++){a=(0,u[s])(a,t,n)}return a}function Qn(e,t,n){switch(t.type){case"PREV":case"NEXT":return Ze(n.currentRange,e)?e:n.currentRange.start;case"SET_DATE":case"SET_VIEW_TYPE":var r=t.dateMarker||e;return n.activeRange&&!Ze(n.activeRange,r)?n.currentRange.start:r;default:return e}}function $n(e,t,n){switch(t.type){case"SELECT_DATES":return t.selection;case"UNSELECT_DATES":return null;default:return e}}function er(e,t){switch(t.type){case"SELECT_EVENT":return t.eventInstanceId;case"UNSELECT_EVENT":return"";default:return e}}function tr(e,t,n,r){switch(t.type){case"SET_EVENT_DRAG":var i=t.state;return{affectedEvents:i.affectedEvents,mutatedEvents:i.mutatedEvents,isEvent:i.isEvent,origSeg:i.origSeg};case"UNSET_EVENT_DRAG":return null;default:return e}}function nr(e,t,n,r){switch(t.type){case"SET_EVENT_RESIZE":var i=t.state;return{affectedEvents:i.affectedEvents,mutatedEvents:i.mutatedEvents,isEvent:i.isEvent,origSeg:i.origSeg};case"UNSET_EVENT_RESIZE":return null;default:return e}}function rr(e){var t=0;for(var n in e)e[n].isFetching&&t++;return t}var ir={start:null,end:null,allDay:Boolean};function or(e,t,n){var r=function(e,t){var n={},r=he(e,ir,{},n),i=r.start?t.createMarkerMeta(r.start):null,o=r.end?t.createMarkerMeta(r.end):null,a=r.allDay;null==a&&(a=i&&i.isTimeUnspecified&&(!o||o.isTimeUnspecified));return n.range={start:i?i.marker:null,end:o?o.marker:null},n.allDay=a,n}(e,t),i=r.range;if(!i.start)return null;if(!i.end){if(null==n)return null;i.end=t.add(i.start,n)}return r}function ar(e,t,n){var r=Wt({editable:!1},"",e.allDay,!0,n);return{def:r,ui:gt(r,t),instance:Zt(r.defId,e.range),range:e.range,isStart:!0,isEnd:!0}}function sr(e,t,n,r){if(t[e])return t[e];var i=function(e,t,n,r){var i=n[e],o=r[e],a=function(e){return i&&null!==i[e]?i[e]:o&&null!==o[e]?o[e]:null},s=a("class"),u=a("superType");!u&&s&&(u=ur(s,r)||ur(s,n));var l=null;if(u){if(u===e)throw new Error("Can't have a custom view type that references itself");l=sr(u,t,n,r)}!s&&l&&(s=l.class);if(!s)return null;return{type:e,class:s,defaults:Se({},l?l.defaults:{},i?i.options:{}),overrides:Se({},l?l.overrides:{},o?o.options:{})}}(e,t,n,r);return i&&(t[e]=i),i}function ur(e,t){var n=Object.getPrototypeOf(e.prototype);for(var r in t){var i=t[r];if(i.class&&i.class.prototype===n)return r}return""}function lr(e){return Re(e,dr)}var cr={type:String,class:null};function dr(e){"function"==typeof e&&(e={class:e});var t={},n=he(e,cr,{},t);return{superType:n.type,class:n.class,options:t}}function fr(e,t){var n=lr(e),r=lr(t.overrides.views);return Re(function(e,t){var n,r={};for(n in e)sr(n,r,e,t);for(n in t)sr(n,r,e,t);return r}(n,r),function(e){return function(e,t,n){var r=e.overrides.duration||e.defaults.duration||n.dynamicOverrides.duration||n.overrides.duration,i=null,o="",a="",s={};if(r&&(i=J(r))){var u=ne(i,!Q(r));o=u.unit,1===u.value&&(a=o,s=t[o]?t[o].options:{})}var l=function(t){var n=t.buttonText||{},r=e.defaults.buttonTextKey;return null!=r&&null!=n[r]?n[r]:null!=n[e.type]?n[e.type]:null!=n[a]?n[a]:void 0};return{type:e.type,class:e.class,duration:i,durationUnit:o,singleUnit:a,options:Se({},Rn,e.defaults,n.dirDefaults,n.localeDefaults,n.overrides,s,e.overrides,n.dynamicOverrides),buttonTextOverride:l(n.dynamicOverrides)||l(n.overrides)||e.overrides.buttonText,buttonTextDefault:l(n.localeDefaults)||l(n.dirDefaults)||e.defaults.buttonText||l(Rn)||e.type}}(e,r,t)})}var pr=function(e){function t(t,n){var i=e.call(this,t)||this;return i._renderLayout=qt(i.renderLayout,i.unrenderLayout),i._updateTitle=qt(i.updateTitle,null,[i._renderLayout]),i._updateActiveButton=qt(i.updateActiveButton,null,[i._renderLayout]),i._updateToday=qt(i.updateToday,null,[i._renderLayout]),i._updatePrev=qt(i.updatePrev,null,[i._renderLayout]),i._updateNext=qt(i.updateNext,null,[i._renderLayout]),i.el=r("div",{className:"fc-toolbar "+n}),i}return Ee(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this),this._renderLayout.unrender(),c(this.el)},t.prototype.render=function(e){this._renderLayout(e.layout),this._updateTitle(e.title),this._updateActiveButton(e.activeButton),this._updateToday(e.isTodayEnabled),this._updatePrev(e.isPrevEnabled),this._updateNext(e.isNextEnabled)},t.prototype.renderLayout=function(e){var t=this.el;this.viewsWithButtons=[],s(t,this.renderSection("left",e.left)),s(t,this.renderSection("center",e.center)),s(t,this.renderSection("right",e.right))},t.prototype.unrenderLayout=function(){this.el.innerHTML=""},t.prototype.renderSection=function(e,t){var n=this,o=this.theme,a=this.calendar,u=a.optionsManager,l=a.viewSpecs,c=r("div",{className:"fc-"+e}),d=u.computed.customButtons||{},f=u.overrides.buttonText||{},p=u.computed.buttonText||{};return t&&t.split(" ").forEach(function(e,t){var r,u=[],h=!0;if(e.split(",").forEach(function(e,t){var r,s,c,v,g,y,m,E,S;"title"===e?(u.push(i("<h2>&nbsp;</h2>")),h=!1):((r=d[e])?(c=function(e){r.click&&r.click.call(E,e)},(v=o.getCustomButtonIconClass(r))||(v=o.getIconClass(e))||(g=r.text)):(s=l[e])?(n.viewsWithButtons.push(e),c=function(){a.changeView(e)},(g=s.buttonTextOverride)||(v=o.getIconClass(e))||(g=s.buttonTextDefault)):a[e]&&(c=function(){a[e]()},(g=f[e])||(v=o.getIconClass(e))||(g=p[e])),c&&(m=["fc-"+e+"-button",o.getClass("button")],g?(y=Ot(g),S=""):v&&(y="<span class='"+v+"'></span>",S=' aria-label="'+e+'"'),(E=i('<button type="button" class="'+m.join(" ")+'"'+S+">"+y+"</button>")).addEventListener("click",c),u.push(E)))}),u.length>1){r=document.createElement("div");var v=o.getClass("buttonGroup");h&&v&&r.classList.add(v),s(r,u),c.appendChild(r)}else s(c,u)}),c},t.prototype.updateToday=function(e){this.toggleButtonEnabled("today",e)},t.prototype.updatePrev=function(e){this.toggleButtonEnabled("prev",e)},t.prototype.updateNext=function(e){this.toggleButtonEnabled("next",e)},t.prototype.updateTitle=function(e){v(this.el,"h2").forEach(function(t){t.innerText=e})},t.prototype.updateActiveButton=function(e){var t=this.theme.getClass("buttonActive");v(this.el,"button").forEach(function(n){e&&n.classList.contains("fc-"+e+"-button")?n.classList.add(t):n.classList.remove(t)})},t.prototype.toggleButtonEnabled=function(e,t){v(this.el,".fc-"+e+"-button").forEach(function(e){e.disabled=!t})},t}(fn),hr=function(e){function t(t,n){var i=e.call(this,t)||this;i._renderToolbars=qt(i.renderToolbars),i.buildViewPropTransformers=Ye(gr),i.el=n,u(n,i.contentEl=r("div",{className:"fc-view-container"}));for(var o=i.calendar,a=0,s=o.pluginSystem.hooks.viewContainerModifiers;a<s.length;a++){(0,s[a])(i.contentEl,o)}return i.toggleElClassNames(!0),i.computeTitle=Ye(vr),i.parseBusinessHours=Ye(function(e){return Yt(e,i.calendar)}),i}return Ee(t,e),t.prototype.destroy=function(){this.header&&this.header.destroy(),this.footer&&this.footer.destroy(),this.view&&this.view.destroy(),c(this.contentEl),this.toggleElClassNames(!1),e.prototype.destroy.call(this)},t.prototype.toggleElClassNames=function(e){var t=this.el.classList,n="fc-"+this.opt("dir"),r=this.theme.getClass("widget");e?(t.add("fc"),t.add(n),t.add(r)):(t.remove("fc"),t.remove(n),t.remove(r))},t.prototype.render=function(e){this.freezeHeight();var t=this.computeTitle(e.dateProfile,e.viewSpec.options);this._renderToolbars(e.viewSpec,e.dateProfile,e.currentDate,e.dateProfileGenerator,t),this.renderView(e,t),this.updateSize(),this.thawHeight()},t.prototype.renderToolbars=function(e,t,n,r,i){var o=this.opt("header"),a=this.opt("footer"),l=this.calendar.getNow(),c=r.build(l),d=r.buildPrev(t,n),f=r.buildNext(t,n),p={title:i,activeButton:e.type,isTodayEnabled:c.isValid&&!Ze(t.currentRange,l),isPrevEnabled:d.isValid,isNextEnabled:f.isValid};o?(this.header||(this.header=new pr(this.context,"fc-header-toolbar"),u(this.el,this.header.el)),this.header.receiveProps(Se({layout:o},p))):this.header&&(this.header.destroy(),this.header=null),a?(this.footer||(this.footer=new pr(this.context,"fc-footer-toolbar"),s(this.el,this.footer.el)),this.footer.receiveProps(Se({layout:a},p))):this.footer&&(this.footer.destroy(),this.footer=null)},t.prototype.renderView=function(e,t){var n=this.view,r=e.viewSpec,i=e.dateProfileGenerator;n&&n.viewSpec===r?n.addScroll(n.queryScroll()):(n&&n.destroy(),n=this.view=new r.class({calendar:this.calendar,view:null,dateEnv:this.dateEnv,theme:this.theme,options:r.options},r,i,this.contentEl)),n.title=t;for(var o={dateProfile:e.dateProfile,businessHours:this.parseBusinessHours(r.options.businessHours),eventStore:e.eventStore,eventUiBases:e.eventUiBases,dateSelection:e.dateSelection,eventSelection:e.eventSelection,eventDrag:e.eventDrag,eventResize:e.eventResize},a=0,s=this.buildViewPropTransformers(this.calendar.pluginSystem.hooks.viewPropsTransformers);a<s.length;a++){var u=s[a];Se(o,u.transform(o,r,e,n))}n.receiveProps(o)},t.prototype.updateSize=function(e){void 0===e&&(e=!1);var t=this.view;e&&t.addScroll(t.queryScroll()),(e||null==this.isHeightAuto)&&this.computeHeightVars(),t.updateSize(e,this.viewHeight,this.isHeightAuto),t.updateNowIndicator(),t.popScroll(e)},t.prototype.computeHeightVars=function(){var e=this.calendar,t=e.opt("height"),n=e.opt("contentHeight");if(this.isHeightAuto="auto"===t||"auto"===n,"number"==typeof n)this.viewHeight=n;else if("function"==typeof n)this.viewHeight=n();else if("number"==typeof t)this.viewHeight=t-this.queryToolbarsHeight();else if("function"==typeof t)this.viewHeight=t()-this.queryToolbarsHeight();else if("parent"===t){var r=this.el.parentNode;this.viewHeight=r.getBoundingClientRect().height-this.queryToolbarsHeight()}else this.viewHeight=Math.round(this.contentEl.getBoundingClientRect().width/Math.max(e.opt("aspectRatio"),.5))},t.prototype.queryToolbarsHeight=function(){var e=0;return this.header&&(e+=I(this.header.el)),this.footer&&(e+=I(this.footer.el)),e},t.prototype.freezeHeight=function(){y(this.el,{height:this.el.getBoundingClientRect().height,overflow:"hidden"})},t.prototype.thawHeight=function(){y(this.el,{height:"",overflow:""})},t}(fn);function vr(e,t){var n;return n=/^(year|month)$/.test(e.currentRangeUnit)?e.currentRange:e.activeRange,this.dateEnv.formatRange(n.start,n.end,ot(t.titleFormat||function(e){var t=e.currentRangeUnit;if("year"===t)return{year:"numeric"};if("month"===t)return{year:"numeric",month:"long"};var n=L(e.currentRange.start,e.currentRange.end);return null!==n&&n>1?{year:"numeric",month:"short",day:"numeric"}:{year:"numeric",month:"long",day:"numeric"}}(e),t.titleRangeSeparator),{isEndExclusive:e.isRangeAllDay})}function gr(e){return e.map(function(e){return new e})}var yr=function(){function e(e){this.component=e.component}return e.prototype.destroy=function(){},e}();var mr={},Er=function(e){function t(t){var n=e.call(this,t)||this;n.handleSegClick=function(e,t){var r=n.component,i=ht(t);if(i&&r.isValidSegDownEl(e.target)){var o=p(e.target,".fc-has-url"),a=o?o.querySelector("a[href]").href:"";r.publiclyTrigger("eventClick",[{el:t,event:new ct(r.calendar,i.eventRange.def,i.eventRange.instance),jsEvent:e,view:r.view}]),a&&!e.defaultPrevented&&(window.location.href=a)}};var r=t.component;return n.destroy=O(r.el,"click",r.fgSegSelector+","+r.bgSegSelector,n.handleSegClick),n}return Ee(t,e),t}(yr),Sr=function(e){function t(t){var n=e.call(this,t)||this;n.handleEventElRemove=function(e){e===n.currentSegEl&&n.handleSegLeave(null,n.currentSegEl)},n.handleSegEnter=function(e,t){ht(t)&&(t.classList.add("fc-allow-mouse-resize"),n.currentSegEl=t,n.triggerEvent("eventMouseEnter",e,t))},n.handleSegLeave=function(e,t){n.currentSegEl&&(t.classList.remove("fc-allow-mouse-resize"),n.currentSegEl=null,n.triggerEvent("eventMouseLeave",e,t))};var r,i,o,a,s,u=t.component;return n.removeHoverListeners=(r=u.el,i=u.fgSegSelector+","+u.bgSegSelector,o=n.handleSegEnter,a=n.handleSegLeave,O(r,"mouseover",i,function(e,t){if(t!==s){s=t,o(e,t);var n=function(e){s=null,a(e,t),t.removeEventListener("mouseleave",n)};t.addEventListener("mouseleave",n)}})),u.calendar.on("eventElRemove",n.handleEventElRemove),n}return Ee(t,e),t.prototype.destroy=function(){this.removeHoverListeners(),this.component.calendar.off("eventElRemove",this.handleEventElRemove)},t.prototype.triggerEvent=function(e,t,n){var r=this.component,i=ht(n);t&&!r.isValidSegDownEl(t.target)||r.publiclyTrigger(e,[{el:n,event:new ct(this.component.calendar,i.eventRange.def,i.eventRange.instance),jsEvent:t,view:r.view}])},t}(yr),br=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return Ee(t,e),t}(cn);br.prototype.classes={widget:"fc-unthemed",widgetHeader:"fc-widget-header",widgetContent:"fc-widget-content",buttonGroup:"fc-button-group",button:"fc-button fc-button-primary",buttonActive:"fc-button-active",popoverHeader:"fc-widget-header",popoverContent:"fc-widget-content",headerRow:"fc-widget-header",dayRow:"fc-widget-content",listView:"fc-widget-content"},br.prototype.baseIconClass="fc-icon",br.prototype.iconClasses={close:"fc-icon-x",prev:"fc-icon-chevron-left",next:"fc-icon-chevron-right",prevYear:"fc-icon-chevrons-left",nextYear:"fc-icon-chevrons-right"},br.prototype.iconOverrideOption="buttonIcons",br.prototype.iconOverrideCustomButtonOption="icon",br.prototype.iconOverridePrefix="fc-icon-";var Dr=function(){function e(e,t){var n=this;this.parseRawLocales=Ye(On),this.buildLocale=Ye(_n),this.buildDateEnv=Ye(Tr),this.buildTheme=Ye(wr),this.buildEventUiSingleBase=Ye(this._buildEventUiSingleBase),this.buildSelectionConfig=Ye(this._buildSelectionConfig),this.buildEventUiBySource=qe(Ir,Me),this.buildEventUiBases=Ye(Cr),this.interactionsStore={},this.actionQueue=[],this.isReducing=!1,this.needsRerender=!1,this.needsFullRerender=!1,this.isRendering=!1,this.renderingPauseDepth=0,this.buildDelayedRerender=Ye(Rr),this.afterSizingTriggers={},this.isViewUpdated=!1,this.isDatesUpdated=!1,this.isEventsUpdated=!1,this.el=e,this.optionsManager=new xn(t||{}),this.pluginSystem=new gn,this.addPluginInputs(this.optionsManager.computed.plugins||[]),this.handleOptions(this.optionsManager.computed),this.publiclyTrigger("_init"),this.hydrate(),this.calendarInteractions=this.pluginSystem.hooks.calendarInteractions.map(function(e){return new e(n)})}return e.prototype.addPluginInputs=function(e){for(var t=function(e){for(var t=[],n=0,r=e;n<r.length;n++){var i=r[n];if("string"==typeof i){var o="FullCalendar"+ue(i);window[o]?t.push(window[o].default):console.warn("Plugin file not loaded for "+i)}else t.push(i)}return Mn.concat(t)}(e),n=0,r=t;n<r.length;n++){var i=r[n];this.pluginSystem.add(i)}},Object.defineProperty(e.prototype,"view",{get:function(){return this.component?this.component.view:null},enumerable:!0,configurable:!0}),e.prototype.render=function(){this.component?this.requestRerender(!0):(this.renderableEventStore={defs:{},instances:{}},this.bindHandlers(),this.executeRender())},e.prototype.destroy=function(){if(this.component){this.unbindHandlers(),this.component.destroy(),this.component=null;for(var e=0,t=this.calendarInteractions;e<t.length;e++){t[e].destroy()}this.publiclyTrigger("_destroyed")}},e.prototype.bindHandlers=function(){var e=this;this.removeNavLinkListener=O(this.el,"click","a[data-goto]",function(t,n){var r=n.getAttribute("data-goto");r=r?JSON.parse(r):{};var i=e.dateEnv,o=i.createMarker(r.date),a=r.type,s=e.viewOpt("navLink"+ue(a)+"Click");"function"==typeof s?s(i.toDate(o),t):("string"==typeof s&&(a=s),e.zoomTo(o,a))}),this.opt("handleWindowResize")&&window.addEventListener("resize",this.windowResizeProxy=pe(this.windowResize.bind(this),this.opt("windowResizeDelay")))},e.prototype.unbindHandlers=function(){this.removeNavLinkListener(),this.windowResizeProxy&&(window.removeEventListener("resize",this.windowResizeProxy),this.windowResizeProxy=null)},e.prototype.hydrate=function(){var e=this;this.state=this.buildInitialState();var t=this.opt("eventSources")||[],n=this.opt("events"),r=[];n&&t.unshift(n);for(var i=0,o=t;i<o.length;i++){var a=Wn(o[i],this);a&&r.push(a)}this.batchRendering(function(){e.dispatch({type:"INIT"}),e.dispatch({type:"ADD_EVENT_SOURCES",sources:r}),e.dispatch({type:"SET_VIEW_TYPE",viewType:e.opt("defaultView")||e.pluginSystem.hooks.defaultView})})},e.prototype.buildInitialState=function(){return{viewType:null,loadingLevel:0,eventSourceLoadingLevel:0,currentDate:this.getInitialDate(),dateProfile:null,eventSources:{},eventStore:{defs:{},instances:{}},dateSelection:null,eventSelection:"",eventDrag:null,eventResize:null}},e.prototype.dispatch=function(e){if(this.actionQueue.push(e),!this.isReducing){this.isReducing=!0;for(var t=this.state;this.actionQueue.length;)this.state=this.reduce(this.state,this.actionQueue.shift(),this);var n=this.state;this.isReducing=!1,!t.loadingLevel&&n.loadingLevel?this.publiclyTrigger("loading",[!0]):t.loadingLevel&&!n.loadingLevel&&this.publiclyTrigger("loading",[!1]);var r=this.component&&this.component.view;(t.eventStore!==n.eventStore||this.needsFullRerender)&&t.eventStore&&(this.isEventsUpdated=!0),(t.dateProfile!==n.dateProfile||this.needsFullRerender)&&(t.dateProfile&&r&&this.publiclyTrigger("datesDestroy",[{view:r,el:r.el}]),this.isDatesUpdated=!0),(t.viewType!==n.viewType||this.needsFullRerender)&&(t.viewType&&r&&this.publiclyTrigger("viewSkeletonDestroy",[{view:r,el:r.el}]),this.isViewUpdated=!0),this.requestRerender()}},e.prototype.reduce=function(e,t,n){return Kn(e,t,n)},e.prototype.requestRerender=function(e){void 0===e&&(e=!1),this.needsRerender=!0,this.needsFullRerender=this.needsFullRerender||e,this.delayedRerender()},e.prototype.tryRerender=function(){this.component&&this.needsRerender&&!this.renderingPauseDepth&&!this.isRendering&&this.executeRender()},e.prototype.batchRendering=function(e){this.renderingPauseDepth++,e(),this.renderingPauseDepth--,this.needsRerender&&this.requestRerender()},e.prototype.executeRender=function(){var e=this.needsFullRerender;this.needsRerender=!1,this.needsFullRerender=!1,this.isRendering=!0,this.renderComponent(e),this.isRendering=!1,this.needsRerender&&this.delayedRerender()},e.prototype.renderComponent=function(e){var t=this.state,n=this.component,r=t.viewType,i=this.viewSpecs[r],o=e&&n?n.view.queryScroll():null;if(!i)throw new Error('View type "'+r+'" is not valid');var a=this.renderableEventStore=t.eventSourceLoadingLevel&&!this.opt("progressiveEventRendering")?this.renderableEventStore:t.eventStore,s=this.buildEventUiSingleBase(i.options),u=this.buildEventUiBySource(t.eventSources),l=this.eventUiBases=this.buildEventUiBases(a.defs,s,u);!e&&n||(n&&(n.freezeHeight(),n.destroy()),n=this.component=new hr({calendar:this,view:null,dateEnv:this.dateEnv,theme:this.theme,options:this.optionsManager.computed},this.el),this.isViewUpdated=!0,this.isDatesUpdated=!0,this.isEventsUpdated=!0),n.receiveProps(Se({},t,{viewSpec:i,dateProfile:t.dateProfile,dateProfileGenerator:this.dateProfileGenerators[r],eventStore:a,eventUiBases:l,dateSelection:t.dateSelection,eventSelection:t.eventSelection,eventDrag:t.eventDrag,eventResize:t.eventResize})),o&&n.view.applyScroll(o,!1),this.isViewUpdated&&(this.isViewUpdated=!1,this.publiclyTrigger("viewSkeletonRender",[{view:n.view,el:n.view.el}])),this.isDatesUpdated&&(this.isDatesUpdated=!1,this.publiclyTrigger("datesRender",[{view:n.view,el:n.view.el}])),this.isEventsUpdated&&(this.isEventsUpdated=!1),this.releaseAfterSizingTriggers()},e.prototype.setOption=function(e,t){var n;this.mutateOptions(((n={})[e]=t,n),[],!0)},e.prototype.getOption=function(e){return this.optionsManager.computed[e]},e.prototype.opt=function(e){return this.optionsManager.computed[e]},e.prototype.viewOpt=function(e){return this.viewOpts()[e]},e.prototype.viewOpts=function(){return this.viewSpecs[this.state.viewType].options},e.prototype.mutateOptions=function(e,t,n,r){var i=this,o=this.pluginSystem.hooks.optionChangeHandlers,a={},s={},u=this.dateEnv,l=!1,c=!1,d=Boolean(t.length);for(var f in e)o[f]?s[f]=e[f]:a[f]=e[f];for(var p in a)/^(height|contentHeight|aspectRatio)$/.test(p)?c=!0:/^(defaultDate|defaultView)$/.test(p)||(d=!0,"timeZone"===p&&(l=!0));this.optionsManager.mutate(a,t,n),d&&(this.handleOptions(this.optionsManager.computed),this.needsFullRerender=!0),this.batchRendering(function(){if(d?(l&&i.dispatch({type:"CHANGE_TIMEZONE",oldDateEnv:u}),i.dispatch({type:"SET_VIEW_TYPE",viewType:i.state.viewType})):c&&i.updateSize(),r)for(var e in s)o[e](s[e],i,r)})},e.prototype.handleOptions=function(e){var t=this,n=this.pluginSystem.hooks;this.defaultAllDayEventDuration=J(e.defaultAllDayEventDuration),this.defaultTimedEventDuration=J(e.defaultTimedEventDuration),this.delayedRerender=this.buildDelayedRerender(e.rerenderDelay),this.theme=this.buildTheme(e);var r=this.parseRawLocales(e.locales);this.availableRawLocales=r.map;var i=this.buildLocale(e.locale||r.defaultCode,r.map);this.dateEnv=this.buildDateEnv(i,e.timeZone,n.namedTimeZonedImpl,e.firstDay,e.weekNumberCalculation,e.weekLabel,n.cmdFormatter),this.selectionConfig=this.buildSelectionConfig(e),this.viewSpecs=fr(n.views,this.optionsManager),this.dateProfileGenerators=Re(this.viewSpecs,function(e){return new e.class.prototype.dateProfileGeneratorClass(e,t)})},e.prototype.getAvailableLocaleCodes=function(){return Object.keys(this.availableRawLocales)},e.prototype._buildSelectionConfig=function(e){return Nt("select",e,this)},e.prototype._buildEventUiSingleBase=function(e){return e.editable&&(e=Se({},e,{eventEditable:!0})),Nt("event",e,this)},e.prototype.hasPublicHandlers=function(e){return this.hasHandlers(e)||this.opt(e)},e.prototype.publiclyTrigger=function(e,t){var n=this.opt(e);if(this.triggerWith(e,this,t),n)return n.apply(this,t)},e.prototype.publiclyTriggerAfterSizing=function(e,t){var n=this.afterSizingTriggers;(n[e]||(n[e]=[])).push(t)},e.prototype.releaseAfterSizingTriggers=function(){var e=this.afterSizingTriggers;for(var t in e)for(var n=0,r=e[t];n<r.length;n++){var i=r[n];this.publiclyTrigger(t,i)}this.afterSizingTriggers={}},e.prototype.isValidViewType=function(e){return Boolean(this.viewSpecs[e])},e.prototype.changeView=function(e,t){var n=null;t&&(t.start&&t.end?(this.optionsManager.mutate({visibleRange:t},[]),this.handleOptions(this.optionsManager.computed)):n=this.dateEnv.createMarker(t)),this.unselect(),this.dispatch({type:"SET_VIEW_TYPE",viewType:e,dateMarker:n})},e.prototype.zoomTo=function(e,t){var n;t=t||"day",n=this.viewSpecs[t]||this.getUnitViewSpec(t),this.unselect(),n?this.dispatch({type:"SET_VIEW_TYPE",viewType:n.type,dateMarker:e}):this.dispatch({type:"SET_DATE",dateMarker:e})},e.prototype.getUnitViewSpec=function(e){var t,n,r=this.component,i=[];for(var o in r.header&&i.push.apply(i,r.header.viewsWithButtons),r.footer&&i.push.apply(i,r.footer.viewsWithButtons),this.viewSpecs)i.push(o);for(t=0;t<i.length;t++)if((n=this.viewSpecs[i[t]])&&n.singleUnit===e)return n},e.prototype.getInitialDate=function(){var e=this.opt("defaultDate");return null!=e?this.dateEnv.createMarker(e):this.getNow()},e.prototype.prev=function(){this.unselect(),this.dispatch({type:"PREV"})},e.prototype.next=function(){this.unselect(),this.dispatch({type:"NEXT"})},e.prototype.prevYear=function(){this.unselect(),this.dispatch({type:"SET_DATE",dateMarker:this.dateEnv.addYears(this.state.currentDate,-1)})},e.prototype.nextYear=function(){this.unselect(),this.dispatch({type:"SET_DATE",dateMarker:this.dateEnv.addYears(this.state.currentDate,1)})},e.prototype.today=function(){this.unselect(),this.dispatch({type:"SET_DATE",dateMarker:this.getNow()})},e.prototype.gotoDate=function(e){this.unselect(),this.dispatch({type:"SET_DATE",dateMarker:this.dateEnv.createMarker(e)})},e.prototype.incrementDate=function(e){var t=J(e);t&&(this.unselect(),this.dispatch({type:"SET_DATE",dateMarker:this.dateEnv.add(this.state.currentDate,t)}))},e.prototype.getDate=function(){return this.dateEnv.toDate(this.state.currentDate)},e.prototype.formatDate=function(e,t){var n=this.dateEnv;return n.format(n.createMarker(e),ot(t))},e.prototype.formatRange=function(e,t,n){var r=this.dateEnv;return r.formatRange(r.createMarker(e),r.createMarker(t),ot(n,this.opt("defaultRangeSeparator")),n)},e.prototype.formatIso=function(e,t){var n=this.dateEnv;return n.formatIso(n.createMarker(e),{omitTime:t})},e.prototype.windowResize=function(e){!this.isHandlingWindowResize&&this.component&&e.target===window&&(this.isHandlingWindowResize=!0,this.updateSize(),this.publiclyTrigger("windowResize",[this.view]),this.isHandlingWindowResize=!1)},e.prototype.updateSize=function(){this.component&&this.component.updateSize(!0)},e.prototype.registerInteractiveComponent=function(e,t){var n=function(e,t){return{component:e,el:t.el,useEventCenter:null==t.useEventCenter||t.useEventCenter}}(e,t),r=[Er,Sr].concat(this.pluginSystem.hooks.componentInteractions).map(function(e){return new e(n)});this.interactionsStore[e.uid]=r,mr[e.uid]=n},e.prototype.unregisterInteractiveComponent=function(e){for(var t=0,n=this.interactionsStore[e.uid];t<n.length;t++){n[t].destroy()}delete this.interactionsStore[e.uid],delete mr[e.uid]},e.prototype.select=function(e,t){var n=or(null==t?null!=e.start?e:{start:e,end:null}:{start:e,end:t},this.dateEnv,J({days:1}));n&&(this.dispatch({type:"SELECT_DATES",selection:n}),this.triggerDateSelect(n))},e.prototype.unselect=function(e){this.state.dateSelection&&(this.dispatch({type:"UNSELECT_DATES"}),this.triggerDateUnselect(e))},e.prototype.triggerDateSelect=function(e,t){var n=Se({},this.buildDateSpanApi(e),{jsEvent:t?t.origEvent:null,view:this.view});this.publiclyTrigger("select",[n])},e.prototype.triggerDateUnselect=function(e){this.publiclyTrigger("unselect",[{jsEvent:e?e.origEvent:null,view:this.view}])},e.prototype.triggerDateClick=function(e,t,n,r){var i=Se({},this.buildDatePointApi(e),{dayEl:t,jsEvent:r,view:n});this.publiclyTrigger("dateClick",[i])},e.prototype.buildDatePointApi=function(e){for(var t,n,r={},i=0,o=this.pluginSystem.hooks.datePointTransforms;i<o.length;i++){var a=o[i];Se(r,a(e,this))}return Se(r,(t=e,{date:(n=this.dateEnv).toDate(t.range.start),dateStr:n.formatIso(t.range.start,{omitTime:t.allDay}),allDay:t.allDay})),r},e.prototype.buildDateSpanApi=function(e){for(var t,n,r={},i=0,o=this.pluginSystem.hooks.dateSpanTransforms;i<o.length;i++){var a=o[i];Se(r,a(e,this))}return Se(r,(t=e,{start:(n=this.dateEnv).toDate(t.range.start),end:n.toDate(t.range.end),startStr:n.formatIso(t.range.start,{omitTime:t.allDay}),endStr:n.formatIso(t.range.end,{omitTime:t.allDay}),allDay:t.allDay})),r},e.prototype.getNow=function(){var e=this.opt("now");return"function"==typeof e&&(e=e()),null==e?this.dateEnv.createNowMarker():this.dateEnv.createMarker(e)},e.prototype.getDefaultEventEnd=function(e,t){var n=t;return e?(n=B(n),n=this.dateEnv.add(n,this.defaultAllDayEventDuration)):n=this.dateEnv.add(n,this.defaultTimedEventDuration),n},e.prototype.addEvent=function(e,t){if(e instanceof ct){var n=e._def,r=e._instance;return this.state.eventStore.defs[n.defId]||this.dispatch({type:"ADD_EVENTS",eventStore:Oe({def:n,instance:r})}),e}var i;if(t instanceof lt)i=t.internalEventSource.sourceId;else if(null!=t){var o=this.getEventSourceById(t);if(!o)return console.warn('Could not find an event source with ID "'+t+'"'),null;i=o.internalEventSource.sourceId}var a=Ft(e,i,this);return a?(this.dispatch({type:"ADD_EVENTS",eventStore:Oe(a)}),new ct(this,a.def,a.def.recurringDef?null:a.instance)):null},e.prototype.getEventById=function(e){var t=this.state.eventStore,n=t.defs,r=t.instances;for(var i in e=String(e),n){var o=n[i];if(o.publicId===e){if(o.recurringDef)return new ct(this,o,null);for(var a in r){var s=r[a];if(s.defId===o.defId)return new ct(this,o,s)}}}return null},e.prototype.getEvents=function(){var e=this.state.eventStore,t=e.defs,n=e.instances,r=[];for(var i in n){var o=n[i],a=t[o.defId];r.push(new ct(this,a,o))}return r},e.prototype.removeAllEvents=function(){this.dispatch({type:"REMOVE_ALL_EVENTS"})},e.prototype.rerenderEvents=function(){this.dispatch({type:"RESET_EVENTS"})},e.prototype.getEventSources=function(){var e=this.state.eventSources,t=[];for(var n in e)t.push(new lt(this,e[n]));return t},e.prototype.getEventSourceById=function(e){var t=this.state.eventSources;for(var n in e=String(e),t)if(t[n].publicId===e)return new lt(this,t[n]);return null},e.prototype.addEventSource=function(e){if(e instanceof lt)return this.state.eventSources[e.internalEventSource.sourceId]||this.dispatch({type:"ADD_EVENT_SOURCES",sources:[e.internalEventSource]}),e;var t=Wn(e,this);return t?(this.dispatch({type:"ADD_EVENT_SOURCES",sources:[t]}),new lt(this,t)):null},e.prototype.removeAllEventSources=function(){this.dispatch({type:"REMOVE_ALL_EVENT_SOURCES"})},e.prototype.refetchEvents=function(){this.dispatch({type:"FETCH_EVENT_SOURCES"})},e.prototype.scrollToTime=function(e){var t=J(e);t&&this.component.view.scrollToDuration(t)},e}();function Tr(e,t,n,r,i,o,a){return new Bn({calendarSystem:"gregory",timeZone:t,namedTimeZoneImpl:n,locale:e,weekNumberCalculation:i,firstDay:r,weekLabel:o,cmdFormatter:a})}function wr(e){return new(this.pluginSystem.hooks.themeClasses[e.themeSystem]||br)(e)}function Rr(e){var t=this.tryRerender.bind(this);return null!=e&&(t=pe(t,e)),t}function Ir(e){return Re(e,function(e){return e.ui})}function Cr(e,t,n){var r={"":t};for(var i in e){var o=e[i];o.sourceId&&n[o.sourceId]&&(r[i]=n[o.sourceId])}return r}tn.mixInto(Dr);var Mr=function(e){function t(t,n,i,o){var a=e.call(this,t,r("div",{className:"fc-view fc-"+n.type+"-view"}),!0)||this;return a.renderDatesMem=qt(a.renderDatesWrap,a.unrenderDatesWrap),a.renderBusinessHoursMem=qt(a.renderBusinessHours,a.unrenderBusinessHours,[a.renderDatesMem]),a.renderDateSelectionMem=qt(a.renderDateSelectionWrap,a.unrenderDateSelectionWrap,[a.renderDatesMem]),a.renderEventsMem=qt(a.renderEvents,a.unrenderEvents,[a.renderDatesMem]),a.renderEventSelectionMem=qt(a.renderEventSelectionWrap,a.unrenderEventSelectionWrap,[a.renderEventsMem]),a.renderEventDragMem=qt(a.renderEventDragWrap,a.unrenderEventDragWrap,[a.renderDatesMem]),a.renderEventResizeMem=qt(a.renderEventResizeWrap,a.unrenderEventResizeWrap,[a.renderDatesMem]),a.viewSpec=n,a.dateProfileGenerator=i,a.type=n.type,a.eventOrderSpecs=ie(a.opt("eventOrder")),a.nextDayThreshold=J(a.opt("nextDayThreshold")),o.appendChild(a.el),a.initialize(),a}return Ee(t,e),t.prototype.initialize=function(){},Object.defineProperty(t.prototype,"activeStart",{get:function(){return this.dateEnv.toDate(this.props.dateProfile.activeRange.start)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"activeEnd",{get:function(){return this.dateEnv.toDate(this.props.dateProfile.activeRange.end)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"currentStart",{get:function(){return this.dateEnv.toDate(this.props.dateProfile.currentRange.start)},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"currentEnd",{get:function(){return this.dateEnv.toDate(this.props.dateProfile.currentRange.end)},enumerable:!0,configurable:!0}),t.prototype.render=function(e){this.renderDatesMem(e.dateProfile),this.renderBusinessHoursMem(e.businessHours),this.renderDateSelectionMem(e.dateSelection),this.renderEventsMem(e.eventStore),this.renderEventSelectionMem(e.eventSelection),this.renderEventDragMem(e.eventDrag),this.renderEventResizeMem(e.eventResize)},t.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderDatesMem.unrender()},t.prototype.updateSize=function(e,t,n){var r=this.calendar;(e||r.isViewUpdated||r.isDatesUpdated||r.isEventsUpdated)&&this.updateBaseSize(e,t,n)},t.prototype.updateBaseSize=function(e,t,n){},t.prototype.renderDatesWrap=function(e){this.renderDates(e),this.addScroll({duration:J(this.opt("scrollTime"))}),this.startNowIndicator(e)},t.prototype.unrenderDatesWrap=function(){this.stopNowIndicator(),this.unrenderDates()},t.prototype.renderDates=function(e){},t.prototype.unrenderDates=function(){},t.prototype.renderBusinessHours=function(e){},t.prototype.unrenderBusinessHours=function(){},t.prototype.renderDateSelectionWrap=function(e){e&&this.renderDateSelection(e)},t.prototype.unrenderDateSelectionWrap=function(e){e&&this.unrenderDateSelection(e)},t.prototype.renderDateSelection=function(e){},t.prototype.unrenderDateSelection=function(e){},t.prototype.renderEvents=function(e){},t.prototype.unrenderEvents=function(){},t.prototype.sliceEvents=function(e,t){var n=this.props;return dt(e,n.eventUiBases,n.dateProfile.activeRange,t?this.nextDayThreshold:null).fg},t.prototype.computeEventDraggable=function(e,t){for(var n=this.calendar.pluginSystem.hooks.isDraggableTransformers,r=t.startEditable,i=0,o=n;i<o.length;i++){r=(0,o[i])(r,e,t,this)}return r},t.prototype.computeEventStartResizable=function(e,t){return t.durationEditable&&this.opt("eventResizableFromStart")},t.prototype.computeEventEndResizable=function(e,t){return t.durationEditable},t.prototype.renderEventSelectionWrap=function(e){e&&this.renderEventSelection(e)},t.prototype.unrenderEventSelectionWrap=function(e){e&&this.unrenderEventSelection(e)},t.prototype.renderEventSelection=function(e){},t.prototype.unrenderEventSelection=function(e){},t.prototype.renderEventDragWrap=function(e){e&&this.renderEventDrag(e)},t.prototype.unrenderEventDragWrap=function(e){e&&this.unrenderEventDrag(e)},t.prototype.renderEventDrag=function(e){},t.prototype.unrenderEventDrag=function(e){},t.prototype.renderEventResizeWrap=function(e){e&&this.renderEventResize(e)},t.prototype.unrenderEventResizeWrap=function(e){e&&this.unrenderEventResize(e)},t.prototype.renderEventResize=function(e){},t.prototype.unrenderEventResize=function(e){},t.prototype.startNowIndicator=function(e){var t,n,r,i=this,o=this.dateEnv;this.opt("nowIndicator")&&(t=this.getNowIndicatorUnit(e))&&(n=this.updateNowIndicator.bind(this),this.initialNowDate=this.calendar.getNow(),this.initialNowQueriedMs=(new Date).valueOf(),r=o.add(o.startOf(this.initialNowDate,t),J(1,t)).valueOf()-this.initialNowDate.valueOf(),this.nowIndicatorTimeoutID=setTimeout(function(){i.nowIndicatorTimeoutID=null,n(),r="second"===t?1e3:6e4,i.nowIndicatorIntervalID=setInterval(n,r)},r))},t.prototype.updateNowIndicator=function(){this.props.dateProfile&&this.initialNowDate&&(this.unrenderNowIndicator(),this.renderNowIndicator(H(this.initialNowDate,(new Date).valueOf()-this.initialNowQueriedMs)),this.isNowIndicatorRendered=!0)},t.prototype.stopNowIndicator=function(){this.isNowIndicatorRendered&&(this.nowIndicatorTimeoutID&&(clearTimeout(this.nowIndicatorTimeoutID),this.nowIndicatorTimeoutID=null),this.nowIndicatorIntervalID&&(clearInterval(this.nowIndicatorIntervalID),this.nowIndicatorIntervalID=null),this.unrenderNowIndicator(),this.isNowIndicatorRendered=!1)},t.prototype.getNowIndicatorUnit=function(e){},t.prototype.renderNowIndicator=function(e){},t.prototype.unrenderNowIndicator=function(){},t.prototype.addScroll=function(e){var t=this.queuedScroll||(this.queuedScroll={});Se(t,e)},t.prototype.popScroll=function(e){this.applyQueuedScroll(e),this.queuedScroll=null},t.prototype.applyQueuedScroll=function(e){this.applyScroll(this.queuedScroll||{},e)},t.prototype.queryScroll=function(){var e={};return this.props.dateProfile&&Se(e,this.queryDateScroll()),e},t.prototype.applyScroll=function(e,t){var n=e.duration;null!=n&&(delete e.duration,this.props.dateProfile&&Se(e,this.computeDateScroll(n))),this.props.dateProfile&&this.applyDateScroll(e)},t.prototype.computeDateScroll=function(e){return{}},t.prototype.queryDateScroll=function(){return{}},t.prototype.applyDateScroll=function(e){},t.prototype.scrollToDuration=function(e){this.applyScroll({duration:e},!1)},t}(pn);tn.mixInto(Mr),Mr.prototype.usesMinMaxTime=!1,Mr.prototype.dateProfileGeneratorClass=Jn;var kr=function(){function e(e){this.segs=[],this.isSizeDirty=!1,this.context=e}return e.prototype.renderSegs=function(e,t){this.rangeUpdated(),e=this.renderSegEls(e,t),this.segs=e,this.attachSegs(e,t),this.isSizeDirty=!0,this.context.view.triggerRenderedSegs(this.segs,Boolean(t))},e.prototype.unrender=function(e,t){this.context.view.triggerWillRemoveSegs(this.segs,Boolean(t)),this.detachSegs(this.segs),this.segs=[]},e.prototype.rangeUpdated=function(){var e,t,n=this.context.options;this.eventTimeFormat=ot(n.eventTimeFormat||this.computeEventTimeFormat(),n.defaultRangeSeparator),null==(e=n.displayEventTime)&&(e=this.computeDisplayEventTime()),null==(t=n.displayEventEnd)&&(t=this.computeDisplayEventEnd()),this.displayEventTime=e,this.displayEventEnd=t},e.prototype.renderSegEls=function(e,t){var n,r="";if(e.length){for(n=0;n<e.length;n++)r+=this.renderSegHtml(e[n],t);o(r).forEach(function(t,n){var r=e[n];t&&(r.el=t)}),e=ft(this.context.view,e,Boolean(t))}return e},e.prototype.getSegClasses=function(e,t,n,r){var i=["fc-event",e.isStart?"fc-start":"fc-not-start",e.isEnd?"fc-end":"fc-not-end"].concat(e.eventRange.ui.classNames);return t&&i.push("fc-draggable"),n&&i.push("fc-resizable"),r&&(i.push("fc-mirror"),r.isDragging&&i.push("fc-dragging"),r.isResizing&&i.push("fc-resizing")),i},e.prototype.getTimeText=function(e,t,n){var r=e.def,i=e.instance;return this._getTimeText(i.range.start,r.hasEnd?i.range.end:null,r.allDay,t,n,i.forcedStartTzo,i.forcedEndTzo)},e.prototype._getTimeText=function(e,t,n,r,i,o,a){var s=this.context.dateEnv;return null==r&&(r=this.eventTimeFormat),null==i&&(i=this.displayEventEnd),this.displayEventTime&&!n?i&&t?s.formatRange(e,t,r,{forcedStartTzo:o,forcedEndTzo:a}):s.format(e,r,{forcedTzo:o}):""},e.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",omitZeroMinute:!0}},e.prototype.computeDisplayEventTime=function(){return!0},e.prototype.computeDisplayEventEnd=function(){return!0},e.prototype.getSkinCss=function(e){return{"background-color":e.backgroundColor,"border-color":e.borderColor,color:e.textColor}},e.prototype.sortEventSegs=function(e){var t=this.context.view.eventOrderSpecs,n=e.map(Or);return n.sort(function(e,n){return oe(e,n,t)}),n.map(function(e){return e._seg})},e.prototype.computeSizes=function(e){(e||this.isSizeDirty)&&this.computeSegSizes(this.segs)},e.prototype.assignSizes=function(e){(e||this.isSizeDirty)&&(this.assignSegSizes(this.segs),this.isSizeDirty=!1)},e.prototype.computeSegSizes=function(e){},e.prototype.assignSegSizes=function(e){},e.prototype.hideByHash=function(e){if(e)for(var t=0,n=this.segs;t<n.length;t++){var r=n[t];e[r.eventRange.instance.instanceId]&&(r.el.style.visibility="hidden")}},e.prototype.showByHash=function(e){if(e)for(var t=0,n=this.segs;t<n.length;t++){var r=n[t];e[r.eventRange.instance.instanceId]&&(r.el.style.visibility="")}},e.prototype.selectByInstanceId=function(e){if(e)for(var t=0,n=this.segs;t<n.length;t++){var r=n[t],i=r.eventRange.instance;i&&i.instanceId===e&&r.el&&r.el.classList.add("fc-selected")}},e.prototype.unselectByInstanceId=function(e){if(e)for(var t=0,n=this.segs;t<n.length;t++){var r=n[t];r.el&&r.el.classList.remove("fc-selected")}},e}();function Or(e){var t=e.eventRange.def,n=e.eventRange.instance.range,r=n.start?n.start.valueOf():0,i=n.end?n.end.valueOf():0;return Se({},t.extendedProps,t,{id:t.publicId,start:r,end:i,duration:i-r,allDay:Number(t.allDay),_seg:e})}var _r=function(){function e(e){this.fillSegTag="div",this.dirtySizeFlags={},this.context=e,this.containerElsByType={},this.segsByType={}}return e.prototype.getSegsByType=function(e){return this.segsByType[e]||[]},e.prototype.renderSegs=function(e,t){var n,r=this.renderSegEls(e,t),i=this.attachSegs(e,r);i&&(n=this.containerElsByType[e]||(this.containerElsByType[e]=[])).push.apply(n,i),this.segsByType[e]=r,"bgEvent"===e&&this.context.view.triggerRenderedSegs(r,!1),this.dirtySizeFlags[e]=!0},e.prototype.unrender=function(e){var t=this.segsByType[e];t&&("bgEvent"===e&&this.context.view.triggerWillRemoveSegs(t,!1),this.detachSegs(e,t))},e.prototype.renderSegEls=function(e,t){var n,r=this,i="";if(t.length){for(n=0;n<t.length;n++)i+=this.renderSegHtml(e,t[n]);o(i).forEach(function(e,n){var r=t[n];e&&(r.el=e)}),"bgEvent"===e&&(t=ft(this.context.view,t,!1)),t=t.filter(function(e){return h(e.el,r.fillSegTag)})}return t},e.prototype.renderSegHtml=function(e,t){var n=null,r=[];return"highlight"!==e&&"businessHours"!==e&&(n={"background-color":t.eventRange.ui.backgroundColor}),"highlight"!==e&&(r=r.concat(t.eventRange.ui.classNames)),"businessHours"===e?r.push("fc-bgevent"):r.push("fc-"+e.toLowerCase()),"<"+this.fillSegTag+(r.length?' class="'+r.join(" ")+'"':"")+(n?' style="'+_t(n)+'"':"")+"></"+this.fillSegTag+">"},e.prototype.detachSegs=function(e,t){var n=this.containerElsByType[e];n&&(n.forEach(c),delete this.containerElsByType[e])},e.prototype.computeSizes=function(e){for(var t in this.segsByType)(e||this.dirtySizeFlags[t])&&this.computeSegSizes(this.segsByType[t])},e.prototype.assignSizes=function(e){for(var t in this.segsByType)(e||this.dirtySizeFlags[t])&&this.assignSegSizes(this.segsByType[t]);this.dirtySizeFlags={}},e.prototype.computeSegSizes=function(e){},e.prototype.assignSegSizes=function(e){},e}(),Pr=function(e){this.timeZoneName=e},xr=function(){function e(e){this.emitter=new tn}return e.prototype.destroy=function(){},e.prototype.setMirrorIsVisible=function(e){},e.prototype.setMirrorNeedsRevert=function(e){},e.prototype.setAutoScrollEnabled=function(e){},e}();function Hr(e){var t=_n(e.locale||"en",On([]).map);return e=Se({timeZone:Rn.timeZone,calendarSystem:"gregory"},e,{locale:t}),new Bn(e)}var Nr={startTime:J,duration:J,create:Boolean,sourceId:String},zr={create:!0};function Ur(e,t){return!e||t>10?{weekday:"short"}:t>1?{weekday:"short",month:"numeric",day:"numeric",omitCommas:!0}:{weekday:"long"}}function Lr(e,t,n,r,i,o,a,s){var u,l=o.view,c=o.dateEnv,d=o.theme,f=o.options,p=Ze(t.activeRange,e),h=["fc-day-header",d.getClass("widgetHeader")];return u="function"==typeof f.columnHeaderHtml?f.columnHeaderHtml(c.toDate(e)):"function"==typeof f.columnHeaderText?Ot(f.columnHeaderText(c.toDate(e))):Ot(c.format(e,i)),n?h=h.concat(Qt(e,t,o,!0)):h.push("fc-"+P[e.getUTCDay()]),'<th class="'+h.join(" ")+'"'+(p&&n?' data-date="'+c.formatIso(e,{omitTime:!0})+'"':"")+(a>1?' colspan="'+a+'"':"")+(s?" "+s:"")+">"+(p?Kt(l,{date:e,forceOff:!n||1===r},u):u)+"</th>"}var Br=function(e){function t(t,n){var r=e.call(this,t)||this;return n.innerHTML="",n.appendChild(r.el=i('<div class="fc-row '+r.theme.getClass("headerRow")+'"><table class="'+r.theme.getClass("tableGrid")+'"><thead></thead></table></div>')),r.thead=r.el.querySelector("thead"),r}return Ee(t,e),t.prototype.destroy=function(){c(this.el)},t.prototype.render=function(e){var t=e.dates,n=e.datesRepDistinctDays,r=[];e.renderIntroHtml&&r.push(e.renderIntroHtml());for(var i=ot(this.opt("columnHeaderFormat")||Ur(n,t.length)),o=0,a=t;o<a.length;o++){var s=a[o];r.push(Lr(s,e.dateProfile,n,t.length,i,this.context))}this.isRtl&&r.reverse(),this.thead.innerHTML="<tr>"+r.join("")+"</tr>"},t}(fn),Vr=function(){function e(e,t){for(var n=e.start,r=e.end,i=[],o=[],a=-1;n<r;)t.isHiddenDay(n)?i.push(a+.5):(a++,i.push(a),o.push(n)),n=x(n,1);this.dates=o,this.indices=i,this.cnt=o.length}return e.prototype.sliceRange=function(e){var t=this.getDateDayIndex(e.start),n=this.getDateDayIndex(x(e.end,-1)),r=Math.max(0,t),i=Math.min(this.cnt-1,n);return(r=Math.ceil(r))<=(i=Math.floor(i))?{firstIndex:r,lastIndex:i,isStart:t===r,isEnd:n===i}:null},e.prototype.getDateDayIndex=function(e){var t=this.indices,n=Math.floor(N(this.dates[0],e));return n<0?t[0]-1:n>=t.length?t[t.length-1]+1:t[n]},e}(),Ar=function(){function e(e,t){var n,r,i,o=e.dates;if(t){for(r=o[0].getUTCDay(),n=1;n<o.length&&o[n].getUTCDay()!==r;n++);i=Math.ceil(o.length/n)}else i=1,n=o.length;this.rowCnt=i,this.colCnt=n,this.daySeries=e,this.cells=this.buildCells(),this.headerDates=this.buildHeaderDates()}return e.prototype.buildCells=function(){for(var e=[],t=0;t<this.rowCnt;t++){for(var n=[],r=0;r<this.colCnt;r++)n.push(this.buildCell(t,r));e.push(n)}return e},e.prototype.buildCell=function(e,t){return{date:this.daySeries.dates[e*this.colCnt+t]}},e.prototype.buildHeaderDates=function(){for(var e=[],t=0;t<this.colCnt;t++)e.push(this.cells[0][t].date);return e},e.prototype.sliceRange=function(e){var t=this.colCnt,n=this.daySeries.sliceRange(e),r=[];if(n)for(var i=n.firstIndex,o=n.lastIndex,a=i;a<=o;){var s=Math.floor(a/t),u=Math.min((s+1)*t,o+1);r.push({row:s,firstCol:a%t,lastCol:(u-1)%t,isStart:n.isStart&&a===i,isEnd:n.isEnd&&u-1===o}),a=u}return r},e}(),Fr=function(){function e(){this.sliceBusinessHours=Ye(this._sliceBusinessHours),this.sliceDateSelection=Ye(this._sliceDateSpan),this.sliceEventStore=Ye(this._sliceEventStore),this.sliceEventDrag=Ye(this._sliceInteraction),this.sliceEventResize=Ye(this._sliceInteraction)}return e.prototype.sliceProps=function(e,t,n,r){for(var i=[],o=4;o<arguments.length;o++)i[o-4]=arguments[o];var a=e.eventUiBases,s=this.sliceEventStore.apply(this,[e.eventStore,a,t,n,r].concat(i));return{dateSelectionSegs:this.sliceDateSelection.apply(this,[e.dateSelection,a,r].concat(i)),businessHourSegs:this.sliceBusinessHours.apply(this,[e.businessHours,t,n,r].concat(i)),fgEventSegs:s.fg,bgEventSegs:s.bg,eventDrag:this.sliceEventDrag.apply(this,[e.eventDrag,a,t,n,r].concat(i)),eventResize:this.sliceEventResize.apply(this,[e.eventResize,a,t,n,r].concat(i)),eventSelection:e.eventSelection}},e.prototype.sliceNowDate=function(e,t){for(var n=[],r=2;r<arguments.length;r++)n[r-2]=arguments[r];return this._sliceDateSpan.apply(this,[{range:{start:e,end:H(e,1)},allDay:!1},{},t].concat(n))},e.prototype._sliceBusinessHours=function(e,t,n,r){for(var i=[],o=4;o<arguments.length;o++)i[o-4]=arguments[o];return e?this._sliceEventStore.apply(this,[_e(e,Wr(t,Boolean(n)),r.calendar),{},t,n,r].concat(i)).bg:[]},e.prototype._sliceEventStore=function(e,t,n,r,i){for(var o=[],a=5;a<arguments.length;a++)o[a-5]=arguments[a];if(e){var s=dt(e,t,Wr(n,Boolean(r)),r);return{bg:this.sliceEventRanges(s.bg,i,o),fg:this.sliceEventRanges(s.fg,i,o)}}return{bg:[],fg:[]}},e.prototype._sliceInteraction=function(e,t,n,r,i){for(var o=[],a=5;a<arguments.length;a++)o[a-5]=arguments[a];if(!e)return null;var s=dt(e.mutatedEvents,t,Wr(n,Boolean(r)),r);return{segs:this.sliceEventRanges(s.fg,i,o),affectedInstances:e.affectedEvents.instances,isEvent:e.isEvent,sourceSeg:e.origSeg}},e.prototype._sliceDateSpan=function(e,t,n){for(var r=[],i=3;i<arguments.length;i++)r[i-3]=arguments[i];if(!e)return[];for(var o=ar(e,t,n.calendar),a=this.sliceRange.apply(this,[e.range].concat(r)),s=0,u=a;s<u.length;s++){var l=u[s];l.component=n,l.eventRange=o}return a},e.prototype.sliceEventRanges=function(e,t,n){for(var r=[],i=0,o=e;i<o.length;i++){var a=o[i];r.push.apply(r,this.sliceEventRange(a,t,n))}return r},e.prototype.sliceEventRange=function(e,t,n){for(var r=this.sliceRange.apply(this,[e.range].concat(n)),i=0,o=r;i<o.length;i++){var a=o[i];a.component=t,a.eventRange=e,a.isStart=e.isStart&&a.isStart,a.isEnd=e.isEnd&&a.isEnd}return r},e}();function Wr(e,t){var n=e.activeRange;return t?n:{start:H(n.start,e.minTime.milliseconds),end:H(n.end,e.maxTime.milliseconds-864e5)}}e.Calendar=Dr,e.Component=fn,e.DateComponent=pn,e.DateEnv=Bn,e.DateProfileGenerator=Jn,e.DayHeader=Br,e.DaySeries=Vr,e.DayTable=Ar,e.ElementDragging=xr,e.ElementScrollController=sn,e.EmitterMixin=tn,e.EventApi=ct,e.FgEventRenderer=kr,e.FillRenderer=_r,e.Interaction=yr,e.Mixin=en,e.NamedTimeZoneImpl=Pr,e.PositionCache=on,e.ScrollComponent=ln,e.ScrollController=an,e.Slicer=Fr,e.Splitter=Xt,e.Theme=cn,e.View=Mr,e.WindowScrollController=un,e.addDays=x,e.addDurations=function(e,t){return{years:e.years+t.years,months:e.months+t.months,days:e.days+t.days,milliseconds:e.milliseconds+t.milliseconds}},e.addMs=H,e.addWeeks=function(e,t){var n=Z(e);return n[2]+=7*t,j(n)},e.allowContextMenu=function(e){e.removeEventListener("contextmenu",k)},e.allowSelection=function(e){e.classList.remove("fc-unselectable"),e.removeEventListener("selectstart",k)},e.appendToElement=s,e.applyAll=de,e.applyMutationToEventStore=yt,e.applyStyle=y,e.applyStyleProp=m,e.asRoughMinutes=function(e){return te(e)/6e4},e.asRoughMs=te,e.asRoughSeconds=function(e){return te(e)/1e3},e.buildGotoAnchorHtml=Kt,e.buildSegCompareObj=Or,e.capitaliseFirstLetter=ue,e.combineEventUis=Ut,e.compareByFieldSpec=ae,e.compareByFieldSpecs=oe,e.compareNumbers=function(e,t){return e-t},e.compensateScroll=function(e,t){t.left&&y(e,{borderLeftWidth:1,marginLeft:t.left-1}),t.right&&y(e,{borderRightWidth:1,marginRight:t.right-1})},e.computeClippingRect=function(e){return M(e).map(function(e){return w(e)}).concat({left:window.pageXOffset,right:window.pageXOffset+document.documentElement.clientWidth,top:window.pageYOffset,bottom:window.pageYOffset+document.documentElement.clientHeight}).reduce(function(e,t){return E(e,t)||t})},e.computeEdges=T,e.computeFallbackHeaderFormat=Ur,e.computeHeightAndMargins=I,e.computeInnerRect=w,e.computeRect=R,e.computeVisibleDayRange=ge,e.config={},e.constrainPoint=function(e,t){return{left:Math.min(Math.max(e.left,t.left),t.right),top:Math.min(Math.max(e.top,t.top),t.bottom)}},e.createDuration=J,e.createElement=r,e.createEmptyEventStore=He,e.createEventInstance=Zt,e.createFormatter=ot,e.createPlugin=vn,e.cssToStr=_t,e.debounce=pe,e.diffDates=ye,e.diffDayAndTime=z,e.diffDays=N,e.diffPoints=function(e,t){return{left:e.left-t.left,top:e.top-t.top}},e.diffWeeks=function(e,t){return N(e,t)/7},e.diffWholeDays=L,e.diffWholeWeeks=U,e.disableCursor=function(){document.body.classList.add("fc-not-allowed")},e.distributeHeight=function(e,t,n){var r=Math.floor(t/e.length),i=Math.floor(t-r*(e.length-1)),o=[],a=[],s=[],u=0;re(e),e.forEach(function(t,n){var l=n===e.length-1?i:r,c=t.getBoundingClientRect().height,d=c+C(t);d<l?(o.push(t),a.push(d),s.push(c)):u+=d}),n&&(t-=u,r=Math.floor(t/o.length),i=Math.floor(t-r*(o.length-1))),o.forEach(function(e,t){var n=t===o.length-1?i:r,u=a[t],l=n-(u-s[t]);u<n&&(e.style.height=l+"px")})},e.elementClosest=p,e.elementMatches=h,e.enableCursor=function(){document.body.classList.remove("fc-not-allowed")},e.eventTupleToStore=Oe,e.filterEventStoreDefs=ze,e.filterHash=we,e.findChildren=function(e,t){for(var n=e instanceof HTMLElement?[e]:e,r=[],i=0;i<n.length;i++)for(var o=n[i].children,a=0;a<o.length;a++){var s=o[a];t&&!h(s,t)||r.push(s)}return r},e.findElements=v,e.flexibleCompare=se,e.forceClassName=function(e,t,n){n?e.classList.add(t):e.classList.remove(t)},e.formatDate=function(e,t){void 0===t&&(t={});var n=Hr(t),r=ot(t),i=n.createMarkerMeta(e);return i?n.format(i.marker,r,{forcedTzo:i.forcedTzo}):""},e.formatIsoTimeString=function(e){return le(e.getUTCHours(),2)+":"+le(e.getUTCMinutes(),2)+":"+le(e.getUTCSeconds(),2)},e.formatRange=function(e,t,n){var r=Hr("object"==typeof n&&n?n:{}),i=ot(n,Rn.defaultRangeSeparator),o=r.createMarkerMeta(e),a=r.createMarkerMeta(t);return o&&a?r.formatRange(o.marker,a.marker,i,{forcedStartTzo:o.forcedTzo,forcedEndTzo:a.forcedTzo,isEndExclusive:n.isEndExclusive}):""},e.getAllDayHtml=function(e){return e.opt("allDayHtml")||Ot(e.opt("allDayText"))},e.getClippingParents=M,e.getDayClasses=Qt,e.getElSeg=ht,e.getRectCenter=function(e){return{left:(e.left+e.right)/2,top:(e.top+e.bottom)/2}},e.getRelevantEvents=Pe,e.globalDefaults=Rn,e.greatestDurationDenominator=ne,e.hasBgRendering=function(e){return"background"===e.rendering||"inverse-background"===e.rendering},e.htmlEscape=Ot,e.htmlToElement=i,e.insertAfterElement=function(e,t){for(var n=l(t),r=e.nextSibling||null,i=0;i<n.length;i++)e.parentNode.insertBefore(n[i],r)},e.interactionSettingsStore=mr,e.interactionSettingsToStore=function(e){var t;return(t={})[e.component.uid]=e,t},e.intersectRanges=Ve,e.intersectRects=E,e.isArraysEqual=je,e.isDateSpansEqual=function(e,t){return Ae(e.range,t.range)&&e.allDay===t.allDay&&function(e,t){for(var n in t)if("range"!==n&&"allDay"!==n&&e[n]!==t[n])return!1;for(var n in e)if(!(n in t))return!1;return!0}(e,t)},e.isInt=ce,e.isInteractionValid=Tt,e.isMultiDayRange=function(e){var t=ge(e);return N(t.start,t.end)>1},e.isPropsEqual=Me,e.isPropsValid=Rt,e.isSingleDay=function(e){return 0===e.years&&0===e.months&&1===e.days&&0===e.milliseconds},e.isValidDate=Y,e.listenBySelector=O,e.mapHash=Re,e.matchCellWidths=function(e){var t=0;return e.forEach(function(e){var n=e.firstChild;if(n instanceof HTMLElement){var r=n.getBoundingClientRect().width;r>t&&(t=r)}}),t++,e.forEach(function(e){e.style.width=t+"px"}),t},e.memoize=Ye,e.memoizeOutput=qe,e.memoizeRendering=qt,e.mergeEventStores=Ne,e.multiplyDuration=function(e,t){return{years:e.years*t,months:e.months*t,days:e.days*t,milliseconds:e.milliseconds*t}},e.padStart=le,e.parseBusinessHours=Yt,e.parseDragMeta=function(e){var t={},n=he(e,Nr,zr,t);return n.leftoverProps=t,n},e.parseEventDef=Wt,e.parseFieldSpecs=ie,e.parseMarker=Ln,e.pointInsideRect=function(e,t){return e.left>=t.left&&e.left<t.right&&e.top>=t.top&&e.top<t.bottom},e.prependToElement=u,e.preventContextMenu=function(e){e.addEventListener("contextmenu",k)},e.preventDefault=k,e.preventSelection=function(e){e.classList.add("fc-unselectable"),e.addEventListener("selectstart",k)},e.processScopedUiProps=Nt,e.rangeContainsMarker=Ze,e.rangeContainsRange=We,e.rangesEqual=Ae,e.rangesIntersect=Fe,e.refineProps=he,e.removeElement=c,e.removeExact=function(e,t){for(var n=0,r=0;r<e.length;)e[r]===t?(e.splice(r,1),n++):r++;return n},e.renderDateCell=Lr,e.requestJson=En,e.sliceEventStore=dt,e.startOfDay=B,e.subtractInnerElHeight=function(e,t){var n={position:"relative",left:-1};y(e,n),y(t,n);var r=e.getBoundingClientRect().height-t.getBoundingClientRect().height,i={position:"",left:""};return y(e,i),y(t,i),r},e.translateRect=function(e,t,n){return{left:e.left+t,right:e.right+t,top:e.top+n,bottom:e.bottom+n}},e.uncompensateScroll=function(e){y(e,{marginLeft:"",marginRight:"",borderLeftWidth:"",borderRightWidth:""})},e.undistributeHeight=re,e.unpromisify=$t,e.version="4.3.1",e.whenTransitionDone=function(e,t){var n=function(r){t(r),_.forEach(function(t){e.removeEventListener(t,n)})};_.forEach(function(t){e.addEventListener(t,n)})},e.wholeDivideDurations=function(e,t){for(var n=null,r=0;r<G.length;r++){var i=G[r];if(t[i]){var o=e[i]/t[i];if(!ce(o)||null!==n&&n!==o)return null;n=o}else if(e[i])return null}return n},Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/core/package.json b/AKPlan/static/AKPlan/fullcalendar/core/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..67f976df5a7656e4e5a1f4379e710c961066d726
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/core/package.json
@@ -0,0 +1,30 @@
+{
+  "name": "@fullcalendar/core",
+  "version": "4.3.1",
+  "title": "FullCalendar Core Package",
+  "description": "Provides core functionality, including the Calendar class",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/initialize-es6",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/daygrid/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/README.md b/AKPlan/static/AKPlan/fullcalendar/daygrid/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e00936542f3578a9099539227bfdac16008fa220
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Day Grid Plugin
+
+Display events on Month view or DayGrid view
+
+[View the docs &raquo;](https://fullcalendar.io/docs/month-view)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/main.css b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..81f58955496b41ea5e151f26207e068e5b29698b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.css
@@ -0,0 +1,78 @@
+/* DayGridView
+--------------------------------------------------------------------------------------------------*/
+/* day row structure */
+.fc-dayGridWeek-view .fc-content-skeleton,
+.fc-dayGridDay-view .fc-content-skeleton {
+  /* there may be week numbers in these views, so no padding-top */
+  padding-bottom: 1em;
+  /* ensure a space at bottom of cell for user selecting/clicking */
+}
+
+.fc-dayGrid-view .fc-body .fc-row {
+  min-height: 4em;
+  /* ensure that all rows are at least this tall */
+}
+
+/* a "rigid" row will take up a constant amount of height because content-skeleton is absolute */
+.fc-row.fc-rigid {
+  overflow: hidden;
+}
+
+.fc-row.fc-rigid .fc-content-skeleton {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+}
+
+/* week and day number styling */
+.fc-day-top.fc-other-month {
+  opacity: 0.3;
+}
+
+.fc-dayGrid-view .fc-week-number,
+.fc-dayGrid-view .fc-day-number {
+  padding: 2px;
+}
+
+.fc-dayGrid-view th.fc-week-number,
+.fc-dayGrid-view th.fc-day-number {
+  padding: 0 2px;
+  /* column headers can't have as much v space */
+}
+
+.fc-ltr .fc-dayGrid-view .fc-day-top .fc-day-number {
+  float: right;
+}
+
+.fc-rtl .fc-dayGrid-view .fc-day-top .fc-day-number {
+  float: left;
+}
+
+.fc-ltr .fc-dayGrid-view .fc-day-top .fc-week-number {
+  float: left;
+  border-radius: 0 0 3px 0;
+}
+
+.fc-rtl .fc-dayGrid-view .fc-day-top .fc-week-number {
+  float: right;
+  border-radius: 0 0 0 3px;
+}
+
+.fc-dayGrid-view .fc-day-top .fc-week-number {
+  min-width: 1.5em;
+  text-align: center;
+  background-color: #f2f2f2;
+  color: #808080;
+}
+
+/* when week/day number have own column */
+.fc-dayGrid-view td.fc-week-number {
+  text-align: center;
+}
+
+.fc-dayGrid-view td.fc-week-number > * {
+  /* work around the way we do column resizing and ensure a minimum width */
+  display: inline-block;
+  min-width: 1.25em;
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed214f2d81684b8876d1416571797df713d0e9b0
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.d.ts
@@ -0,0 +1,310 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/daygrid' {
+    export { default as SimpleDayGrid, DayGridSlicer } from '@fullcalendar/daygrid/SimpleDayGrid';
+    export { default as DayGrid, DayGridSeg } from '@fullcalendar/daygrid/DayGrid';
+    export { default as AbstractDayGridView } from '@fullcalendar/daygrid/AbstractDayGridView';
+    export { default as DayGridView, buildDayTable as buildBasicDayTable } from '@fullcalendar/daygrid/DayGridView';
+    export { default as DayBgRow } from '@fullcalendar/daygrid/DayBgRow';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/daygrid/SimpleDayGrid' {
+    import { DateProfile, EventStore, EventUiHash, DateSpan, EventInteractionState, DayTable, Duration, DateComponent, DateRange, Slicer, Hit, ComponentContext } from '@fullcalendar/core';
+    import { default as DayGrid, DayGridSeg } from '@fullcalendar/daygrid/DayGrid';
+    export interface SimpleDayGridProps {
+        dateProfile: DateProfile | null;
+        dayTable: DayTable;
+        nextDayThreshold: Duration;
+        businessHours: EventStore;
+        eventStore: EventStore;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+        isRigid: boolean;
+    }
+    export { SimpleDayGrid as default, SimpleDayGrid };
+    class SimpleDayGrid extends DateComponent<SimpleDayGridProps> {
+        dayGrid: DayGrid;
+        constructor(context: ComponentContext, dayGrid: DayGrid);
+        destroy(): void;
+        render(props: SimpleDayGridProps): void;
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number): Hit;
+    }
+    export class DayGridSlicer extends Slicer<DayGridSeg, [DayTable]> {
+        sliceRange(dateRange: DateRange, dayTable: DayTable): DayGridSeg[];
+    }
+}
+
+declare module '@fullcalendar/daygrid/DayGrid' {
+    import { PositionCache, DateMarker, DateComponent, EventSegUiInteractionState, Seg, DateProfile } from '@fullcalendar/core';
+    import Popover from '@fullcalendar/daygrid/Popover';
+    import DayGridEventRenderer from '@fullcalendar/daygrid/DayGridEventRenderer';
+    import DayTile from '@fullcalendar/daygrid/DayTile';
+    export interface RenderProps {
+        renderNumberIntroHtml: (row: number, dayGrid: DayGrid) => string;
+        renderBgIntroHtml: () => string;
+        renderIntroHtml: () => string;
+        colWeekNumbersVisible: boolean;
+        cellWeekNumbersVisible: boolean;
+    }
+    export interface DayGridSeg extends Seg {
+        row: number;
+        firstCol: number;
+        lastCol: number;
+    }
+    export interface DayGridCell {
+        date: DateMarker;
+        htmlAttrs?: string;
+    }
+    export interface DayGridProps {
+        dateProfile: DateProfile;
+        cells: DayGridCell[][];
+        businessHourSegs: DayGridSeg[];
+        bgEventSegs: DayGridSeg[];
+        fgEventSegs: DayGridSeg[];
+        dateSelectionSegs: DayGridSeg[];
+        eventSelection: string;
+        eventDrag: EventSegUiInteractionState | null;
+        eventResize: EventSegUiInteractionState | null;
+        isRigid: boolean;
+    }
+    export { DayGrid as default, DayGrid };
+    class DayGrid extends DateComponent<DayGridProps> {
+        eventRenderer: DayGridEventRenderer;
+        renderProps: RenderProps;
+        rowCnt: number;
+        colCnt: number;
+        bottomCoordPadding: number;
+        rowEls: HTMLElement[];
+        cellEls: HTMLElement[];
+        isCellSizesDirty: boolean;
+        rowPositions: PositionCache;
+        colPositions: PositionCache;
+        segPopover: Popover;
+        segPopoverTile: DayTile;
+        constructor(context: any, el: any, renderProps: RenderProps);
+        render(props: DayGridProps): void;
+        destroy(): void;
+        getCellRange(row: any, col: any): {
+            start: Date;
+            end: Date;
+        };
+        updateSegPopoverTile(date?: any, segs?: any): void;
+        _renderCells(cells: DayGridCell[][], isRigid: boolean): void;
+        _unrenderCells(): void;
+        renderDayRowHtml(row: any, isRigid: any): string;
+        getIsNumbersVisible(): boolean;
+        getIsDayNumbersVisible(): boolean;
+        renderNumberTrHtml(row: number): string;
+        renderNumberCellsHtml(row: any): string;
+        renderNumberCellHtml(date: any): string;
+        updateSize(isResize: boolean): void;
+        buildPositionCaches(): void;
+        buildColPositions(): void;
+        buildRowPositions(): void;
+        positionToHit(leftPosition: any, topPosition: any): {
+            row: any;
+            col: any;
+            dateSpan: {
+                range: {
+                    start: Date;
+                    end: Date;
+                };
+                allDay: boolean;
+            };
+            dayEl: HTMLElement;
+            relativeRect: {
+                left: any;
+                right: any;
+                top: any;
+                bottom: any;
+            };
+        };
+        getCellEl(row: any, col: any): HTMLElement;
+        _renderEventDrag(state: EventSegUiInteractionState): void;
+        _unrenderEventDrag(state: EventSegUiInteractionState): void;
+        _renderEventResize(state: EventSegUiInteractionState): void;
+        _unrenderEventResize(state: EventSegUiInteractionState): void;
+        removeSegPopover(): void;
+        limitRows(levelLimit: any): void;
+        computeRowLevelLimit(row: any): (number | false);
+        limitRow(row: any, levelLimit: any): void;
+        unlimitRow(row: any): void;
+        renderMoreLink(row: any, col: any, hiddenSegs: any): HTMLElement;
+        showSegPopover(row: any, col: any, moreLink: HTMLElement, segs: any): void;
+        resliceDaySegs(segs: any, dayDate: any): any[];
+        getMoreLinkText(num: any): any;
+        getCellSegs(row: any, col: any, startLevel?: any): any[];
+    }
+}
+
+declare module '@fullcalendar/daygrid/AbstractDayGridView' {
+    import { ScrollComponent, View, ComponentContext, ViewSpec, DateProfileGenerator, Duration } from '@fullcalendar/core';
+    import DayGrid from '@fullcalendar/daygrid/DayGrid';
+    export { DayGridView as default, DayGridView };
+    abstract class DayGridView extends View {
+        scroller: ScrollComponent;
+        dayGrid: DayGrid;
+        colWeekNumbersVisible: boolean;
+        weekNumberWidth: number;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        renderSkeletonHtml(): string;
+        weekNumberStyleAttr(): string;
+        hasRigidRows(): boolean;
+        updateSize(isResize: boolean, viewHeight: number, isAuto: boolean): void;
+        updateBaseSize(isResize: boolean, viewHeight: number, isAuto: boolean): void;
+        computeScrollerHeight(viewHeight: any): number;
+        setGridHeight(height: any, isAuto: any): void;
+        computeDateScroll(duration: Duration): {
+            top: number;
+        };
+        queryDateScroll(): {
+            top: number;
+        };
+        applyDateScroll(scroll: any): void;
+        renderHeadIntroHtml: () => string;
+        renderDayGridNumberIntroHtml: (row: number, dayGrid: DayGrid) => string;
+        renderDayGridBgIntroHtml: () => string;
+        renderDayGridIntroHtml: () => string;
+    }
+}
+
+declare module '@fullcalendar/daygrid/DayGridView' {
+    import { DayHeader, ComponentContext, ViewSpec, DateProfileGenerator, DateProfile, ViewProps, DayTable } from '@fullcalendar/core';
+    import AbstractDayGridView from '@fullcalendar/daygrid/AbstractDayGridView';
+    import SimpleDayGrid from '@fullcalendar/daygrid/SimpleDayGrid';
+    export { DayGridView as default, DayGridView };
+    class DayGridView extends AbstractDayGridView {
+        header: DayHeader;
+        simpleDayGrid: SimpleDayGrid;
+        dayTable: DayTable;
+        constructor(_context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: ViewProps): void;
+    }
+    export function buildDayTable(dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator): DayTable;
+}
+
+declare module '@fullcalendar/daygrid/DayBgRow' {
+    import { ComponentContext, DateMarker, DateProfile } from '@fullcalendar/core';
+    export interface DayBgCell {
+        date: DateMarker;
+        htmlAttrs?: string;
+    }
+    export interface DayBgRowProps {
+        cells: DayBgCell[];
+        dateProfile: DateProfile;
+        renderIntroHtml?: () => string;
+    }
+    export { DayBgRow as default, DayBgRow };
+    class DayBgRow {
+        context: ComponentContext;
+        constructor(context: ComponentContext);
+        renderHtml(props: DayBgRowProps): string;
+    }
+}
+
+declare module '@fullcalendar/daygrid/Popover' {
+    export interface PopoverOptions {
+        className?: string;
+        content?: (el: HTMLElement) => void;
+        parentEl: HTMLElement;
+        autoHide?: boolean;
+        top?: number;
+        left?: number;
+        right?: number;
+        viewportConstrain?: boolean;
+    }
+    export { Popover as default, Popover };
+    class Popover {
+        isHidden: boolean;
+        options: PopoverOptions;
+        el: HTMLElement;
+        margin: number;
+        constructor(options: PopoverOptions);
+        show(): void;
+        hide(): void;
+        render(): void;
+        documentMousedown: (ev: any) => void;
+        destroy(): void;
+        position(): void;
+        trigger(name: any): void;
+    }
+}
+
+declare module '@fullcalendar/daygrid/DayGridEventRenderer' {
+    import { Seg } from '@fullcalendar/core';
+    import DayGrid from '@fullcalendar/daygrid/DayGrid';
+    import SimpleDayGridEventRenderer from '@fullcalendar/daygrid/SimpleDayGridEventRenderer';
+    export { DayGridEventRenderer as default, DayGridEventRenderer };
+    class DayGridEventRenderer extends SimpleDayGridEventRenderer {
+        dayGrid: DayGrid;
+        rowStructs: any;
+        constructor(dayGrid: DayGrid);
+        attachSegs(segs: Seg[], mirrorInfo: any): void;
+        detachSegs(): void;
+        renderSegRows(segs: Seg[]): any[];
+        renderSegRow(row: any, rowSegs: any): {
+            row: any;
+            tbodyEl: HTMLTableSectionElement;
+            cellMatrix: any[];
+            segMatrix: any[];
+            segLevels: any[];
+            segs: any;
+        };
+        buildSegLevels(segs: Seg[]): any[];
+        groupSegRows(segs: Seg[]): any[];
+        computeDisplayEventEnd(): boolean;
+    }
+}
+
+declare module '@fullcalendar/daygrid/DayTile' {
+    import { DateComponent, Seg, Hit, DateMarker, ComponentContext, EventInstanceHash } from '@fullcalendar/core';
+    import SimpleDayGridEventRenderer from '@fullcalendar/daygrid/SimpleDayGridEventRenderer';
+    export interface DayTileProps {
+        date: DateMarker;
+        fgSegs: Seg[];
+        eventSelection: string;
+        eventDragInstances: EventInstanceHash;
+        eventResizeInstances: EventInstanceHash;
+    }
+    export { DayTile as default, DayTile };
+    class DayTile extends DateComponent<DayTileProps> {
+        segContainerEl: HTMLElement;
+        constructor(context: ComponentContext, el: HTMLElement);
+        render(props: DayTileProps): void;
+        destroy(): void;
+        _renderFrame(date: DateMarker): void;
+        queryHit(positionLeft: number, positionTop: number, elWidth: number, elHeight: number): Hit | null;
+    }
+    export class DayTileEventRenderer extends SimpleDayGridEventRenderer {
+        dayTile: DayTile;
+        constructor(dayTile: any);
+        attachSegs(segs: Seg[]): void;
+        detachSegs(segs: Seg[]): void;
+    }
+}
+
+declare module '@fullcalendar/daygrid/SimpleDayGridEventRenderer' {
+    import { FgEventRenderer, Seg } from '@fullcalendar/core';
+    export { SimpleDayGridEventRenderer as default, SimpleDayGridEventRenderer };
+    abstract class SimpleDayGridEventRenderer extends FgEventRenderer {
+        renderSegHtml(seg: Seg, mirrorInfo: any): string;
+        computeEventTimeFormat(): {
+            hour: string;
+            minute: string;
+            omitZeroMinute: boolean;
+            meridiem: string;
+        };
+        computeDisplayEventEnd(): boolean;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..28c2a24c08c990a12d9c498893f4ba130c78a91f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.esm.js
@@ -0,0 +1,1627 @@
+/*!
+FullCalendar Day Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { addWeeks, diffWeeks, DateProfileGenerator, createElement, listenBySelector, removeElement, computeRect, computeClippingRect, applyStyle, cssToStr, htmlEscape, FgEventRenderer, appendToElement, prependToElement, htmlToElement, FillRenderer, memoizeRendering, createFormatter, addDays, DateComponent, rangeContainsMarker, getDayClasses, findElements, PositionCache, buildGotoAnchorHtml, findChildren, insertAfterElement, intersectRanges, ScrollComponent, matchCellWidths, uncompensateScroll, compensateScroll, subtractInnerElHeight, distributeHeight, undistributeHeight, View, Slicer, memoize, DayHeader, DaySeries, DayTable, createPlugin } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+var DayGridDateProfileGenerator = /** @class */ (function (_super) {
+    __extends(DayGridDateProfileGenerator, _super);
+    function DayGridDateProfileGenerator() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    // Computes the date range that will be rendered.
+    DayGridDateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) {
+        var dateEnv = this.dateEnv;
+        var renderRange = _super.prototype.buildRenderRange.call(this, currentRange, currentRangeUnit, isRangeAllDay);
+        var start = renderRange.start;
+        var end = renderRange.end;
+        var endOfWeek;
+        // year and month views should be aligned with weeks. this is already done for week
+        if (/^(year|month)$/.test(currentRangeUnit)) {
+            start = dateEnv.startOfWeek(start);
+            // make end-of-week if not already
+            endOfWeek = dateEnv.startOfWeek(end);
+            if (endOfWeek.valueOf() !== end.valueOf()) {
+                end = addWeeks(endOfWeek, 1);
+            }
+        }
+        // ensure 6 weeks
+        if (this.options.monthMode &&
+            this.options.fixedWeekCount) {
+            var rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
+            diffWeeks(start, end));
+            end = addWeeks(end, 6 - rowCnt);
+        }
+        return { start: start, end: end };
+    };
+    return DayGridDateProfileGenerator;
+}(DateProfileGenerator));
+
+/* A rectangular panel that is absolutely positioned over other content
+------------------------------------------------------------------------------------------------------------------------
+Options:
+  - className (string)
+  - content (HTML string, element, or element array)
+  - parentEl
+  - top
+  - left
+  - right (the x coord of where the right edge should be. not a "CSS" right)
+  - autoHide (boolean)
+  - show (callback)
+  - hide (callback)
+*/
+var Popover = /** @class */ (function () {
+    function Popover(options) {
+        var _this = this;
+        this.isHidden = true;
+        this.margin = 10; // the space required between the popover and the edges of the scroll container
+        // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
+        this.documentMousedown = function (ev) {
+            // only hide the popover if the click happened outside the popover
+            if (_this.el && !_this.el.contains(ev.target)) {
+                _this.hide();
+            }
+        };
+        this.options = options;
+    }
+    // Shows the popover on the specified position. Renders it if not already
+    Popover.prototype.show = function () {
+        if (this.isHidden) {
+            if (!this.el) {
+                this.render();
+            }
+            this.el.style.display = '';
+            this.position();
+            this.isHidden = false;
+            this.trigger('show');
+        }
+    };
+    // Hides the popover, through CSS, but does not remove it from the DOM
+    Popover.prototype.hide = function () {
+        if (!this.isHidden) {
+            this.el.style.display = 'none';
+            this.isHidden = true;
+            this.trigger('hide');
+        }
+    };
+    // Creates `this.el` and renders content inside of it
+    Popover.prototype.render = function () {
+        var _this = this;
+        var options = this.options;
+        var el = this.el = createElement('div', {
+            className: 'fc-popover ' + (options.className || ''),
+            style: {
+                top: '0',
+                left: '0'
+            }
+        });
+        if (typeof options.content === 'function') {
+            options.content(el);
+        }
+        options.parentEl.appendChild(el);
+        // when a click happens on anything inside with a 'fc-close' className, hide the popover
+        listenBySelector(el, 'click', '.fc-close', function (ev) {
+            _this.hide();
+        });
+        if (options.autoHide) {
+            document.addEventListener('mousedown', this.documentMousedown);
+        }
+    };
+    // Hides and unregisters any handlers
+    Popover.prototype.destroy = function () {
+        this.hide();
+        if (this.el) {
+            removeElement(this.el);
+            this.el = null;
+        }
+        document.removeEventListener('mousedown', this.documentMousedown);
+    };
+    // Positions the popover optimally, using the top/left/right options
+    Popover.prototype.position = function () {
+        var options = this.options;
+        var el = this.el;
+        var elDims = el.getBoundingClientRect(); // only used for width,height
+        var origin = computeRect(el.offsetParent);
+        var clippingRect = computeClippingRect(options.parentEl);
+        var top; // the "position" (not "offset") values for the popover
+        var left; //
+        // compute top and left
+        top = options.top || 0;
+        if (options.left !== undefined) {
+            left = options.left;
+        }
+        else if (options.right !== undefined) {
+            left = options.right - elDims.width; // derive the left value from the right value
+        }
+        else {
+            left = 0;
+        }
+        // constrain to the view port. if constrained by two edges, give precedence to top/left
+        top = Math.min(top, clippingRect.bottom - elDims.height - this.margin);
+        top = Math.max(top, clippingRect.top + this.margin);
+        left = Math.min(left, clippingRect.right - elDims.width - this.margin);
+        left = Math.max(left, clippingRect.left + this.margin);
+        applyStyle(el, {
+            top: top - origin.top,
+            left: left - origin.left
+        });
+    };
+    // Triggers a callback. Calls a function in the option hash of the same name.
+    // Arguments beyond the first `name` are forwarded on.
+    // TODO: better code reuse for this. Repeat code
+    // can kill this???
+    Popover.prototype.trigger = function (name) {
+        if (this.options[name]) {
+            this.options[name].apply(this, Array.prototype.slice.call(arguments, 1));
+        }
+    };
+    return Popover;
+}());
+
+/* Event-rendering methods for the DayGrid class
+----------------------------------------------------------------------------------------------------------------------*/
+// "Simple" is bad a name. has nothing to do with SimpleDayGrid
+var SimpleDayGridEventRenderer = /** @class */ (function (_super) {
+    __extends(SimpleDayGridEventRenderer, _super);
+    function SimpleDayGridEventRenderer() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    // Builds the HTML to be used for the default element for an individual segment
+    SimpleDayGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
+        var _a = this.context, view = _a.view, options = _a.options;
+        var eventRange = seg.eventRange;
+        var eventDef = eventRange.def;
+        var eventUi = eventRange.ui;
+        var allDay = eventDef.allDay;
+        var isDraggable = view.computeEventDraggable(eventDef, eventUi);
+        var isResizableFromStart = allDay && seg.isStart && view.computeEventStartResizable(eventDef, eventUi);
+        var isResizableFromEnd = allDay && seg.isEnd && view.computeEventEndResizable(eventDef, eventUi);
+        var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
+        var skinCss = cssToStr(this.getSkinCss(eventUi));
+        var timeHtml = '';
+        var timeText;
+        var titleHtml;
+        classes.unshift('fc-day-grid-event', 'fc-h-event');
+        // Only display a timed events time if it is the starting segment
+        if (seg.isStart) {
+            timeText = this.getTimeText(eventRange);
+            if (timeText) {
+                timeHtml = '<span class="fc-time">' + htmlEscape(timeText) + '</span>';
+            }
+        }
+        titleHtml =
+            '<span class="fc-title">' +
+                (htmlEscape(eventDef.title || '') || '&nbsp;') + // we always want one line of height
+                '</span>';
+        return '<a class="' + classes.join(' ') + '"' +
+            (eventDef.url ?
+                ' href="' + htmlEscape(eventDef.url) + '"' :
+                '') +
+            (skinCss ?
+                ' style="' + skinCss + '"' :
+                '') +
+            '>' +
+            '<div class="fc-content">' +
+            (options.dir === 'rtl' ?
+                titleHtml + ' ' + timeHtml : // put a natural space in between
+                timeHtml + ' ' + titleHtml //
+            ) +
+            '</div>' +
+            (isResizableFromStart ?
+                '<div class="fc-resizer fc-start-resizer"></div>' :
+                '') +
+            (isResizableFromEnd ?
+                '<div class="fc-resizer fc-end-resizer"></div>' :
+                '') +
+            '</a>';
+    };
+    // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
+    SimpleDayGridEventRenderer.prototype.computeEventTimeFormat = function () {
+        return {
+            hour: 'numeric',
+            minute: '2-digit',
+            omitZeroMinute: true,
+            meridiem: 'narrow'
+        };
+    };
+    SimpleDayGridEventRenderer.prototype.computeDisplayEventEnd = function () {
+        return false; // TODO: somehow consider the originating DayGrid's column count
+    };
+    return SimpleDayGridEventRenderer;
+}(FgEventRenderer));
+
+/* Event-rendering methods for the DayGrid class
+----------------------------------------------------------------------------------------------------------------------*/
+var DayGridEventRenderer = /** @class */ (function (_super) {
+    __extends(DayGridEventRenderer, _super);
+    function DayGridEventRenderer(dayGrid) {
+        var _this = _super.call(this, dayGrid.context) || this;
+        _this.dayGrid = dayGrid;
+        return _this;
+    }
+    // Renders the given foreground event segments onto the grid
+    DayGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+        var rowStructs = this.rowStructs = this.renderSegRows(segs);
+        // append to each row's content skeleton
+        this.dayGrid.rowEls.forEach(function (rowNode, i) {
+            rowNode.querySelector('.fc-content-skeleton > table').appendChild(rowStructs[i].tbodyEl);
+        });
+        // removes the "more.." events popover
+        if (!mirrorInfo) {
+            this.dayGrid.removeSegPopover();
+        }
+    };
+    // Unrenders all currently rendered foreground event segments
+    DayGridEventRenderer.prototype.detachSegs = function () {
+        var rowStructs = this.rowStructs || [];
+        var rowStruct;
+        while ((rowStruct = rowStructs.pop())) {
+            removeElement(rowStruct.tbodyEl);
+        }
+        this.rowStructs = null;
+    };
+    // Uses the given events array to generate <tbody> elements that should be appended to each row's content skeleton.
+    // Returns an array of rowStruct objects (see the bottom of `renderSegRow`).
+    // PRECONDITION: each segment shoud already have a rendered and assigned `.el`
+    DayGridEventRenderer.prototype.renderSegRows = function (segs) {
+        var rowStructs = [];
+        var segRows;
+        var row;
+        segRows = this.groupSegRows(segs); // group into nested arrays
+        // iterate each row of segment groupings
+        for (row = 0; row < segRows.length; row++) {
+            rowStructs.push(this.renderSegRow(row, segRows[row]));
+        }
+        return rowStructs;
+    };
+    // Given a row # and an array of segments all in the same row, render a <tbody> element, a skeleton that contains
+    // the segments. Returns object with a bunch of internal data about how the render was calculated.
+    // NOTE: modifies rowSegs
+    DayGridEventRenderer.prototype.renderSegRow = function (row, rowSegs) {
+        var dayGrid = this.dayGrid;
+        var colCnt = dayGrid.colCnt, isRtl = dayGrid.isRtl;
+        var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels
+        var levelCnt = Math.max(1, segLevels.length); // ensure at least one level
+        var tbody = document.createElement('tbody');
+        var segMatrix = []; // lookup for which segments are rendered into which level+col cells
+        var cellMatrix = []; // lookup for all <td> elements of the level+col matrix
+        var loneCellMatrix = []; // lookup for <td> elements that only take up a single column
+        var i;
+        var levelSegs;
+        var col;
+        var tr;
+        var j;
+        var seg;
+        var td;
+        // populates empty cells from the current column (`col`) to `endCol`
+        function emptyCellsUntil(endCol) {
+            while (col < endCol) {
+                // try to grab a cell from the level above and extend its rowspan. otherwise, create a fresh cell
+                td = (loneCellMatrix[i - 1] || [])[col];
+                if (td) {
+                    td.rowSpan = (td.rowSpan || 1) + 1;
+                }
+                else {
+                    td = document.createElement('td');
+                    tr.appendChild(td);
+                }
+                cellMatrix[i][col] = td;
+                loneCellMatrix[i][col] = td;
+                col++;
+            }
+        }
+        for (i = 0; i < levelCnt; i++) { // iterate through all levels
+            levelSegs = segLevels[i];
+            col = 0;
+            tr = document.createElement('tr');
+            segMatrix.push([]);
+            cellMatrix.push([]);
+            loneCellMatrix.push([]);
+            // levelCnt might be 1 even though there are no actual levels. protect against this.
+            // this single empty row is useful for styling.
+            if (levelSegs) {
+                for (j = 0; j < levelSegs.length; j++) { // iterate through segments in level
+                    seg = levelSegs[j];
+                    var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol;
+                    var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol;
+                    emptyCellsUntil(leftCol);
+                    // create a container that occupies or more columns. append the event element.
+                    td = createElement('td', { className: 'fc-event-container' }, seg.el);
+                    if (leftCol !== rightCol) {
+                        td.colSpan = rightCol - leftCol + 1;
+                    }
+                    else { // a single-column segment
+                        loneCellMatrix[i][col] = td;
+                    }
+                    while (col <= rightCol) {
+                        cellMatrix[i][col] = td;
+                        segMatrix[i][col] = seg;
+                        col++;
+                    }
+                    tr.appendChild(td);
+                }
+            }
+            emptyCellsUntil(colCnt); // finish off the row
+            var introHtml = dayGrid.renderProps.renderIntroHtml();
+            if (introHtml) {
+                if (dayGrid.isRtl) {
+                    appendToElement(tr, introHtml);
+                }
+                else {
+                    prependToElement(tr, introHtml);
+                }
+            }
+            tbody.appendChild(tr);
+        }
+        return {
+            row: row,
+            tbodyEl: tbody,
+            cellMatrix: cellMatrix,
+            segMatrix: segMatrix,
+            segLevels: segLevels,
+            segs: rowSegs
+        };
+    };
+    // Stacks a flat array of segments, which are all assumed to be in the same row, into subarrays of vertical levels.
+    // NOTE: modifies segs
+    DayGridEventRenderer.prototype.buildSegLevels = function (segs) {
+        var _a = this.dayGrid, isRtl = _a.isRtl, colCnt = _a.colCnt;
+        var levels = [];
+        var i;
+        var seg;
+        var j;
+        // Give preference to elements with certain criteria, so they have
+        // a chance to be closer to the top.
+        segs = this.sortEventSegs(segs);
+        for (i = 0; i < segs.length; i++) {
+            seg = segs[i];
+            // loop through levels, starting with the topmost, until the segment doesn't collide with other segments
+            for (j = 0; j < levels.length; j++) {
+                if (!isDaySegCollision(seg, levels[j])) {
+                    break;
+                }
+            }
+            // `j` now holds the desired subrow index
+            seg.level = j;
+            seg.leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol; // for sorting only
+            seg.rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol // for sorting only
+            ;
+            (levels[j] || (levels[j] = [])).push(seg);
+        }
+        // order segments left-to-right. very important if calendar is RTL
+        for (j = 0; j < levels.length; j++) {
+            levels[j].sort(compareDaySegCols);
+        }
+        return levels;
+    };
+    // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's row
+    DayGridEventRenderer.prototype.groupSegRows = function (segs) {
+        var segRows = [];
+        var i;
+        for (i = 0; i < this.dayGrid.rowCnt; i++) {
+            segRows.push([]);
+        }
+        for (i = 0; i < segs.length; i++) {
+            segRows[segs[i].row].push(segs[i]);
+        }
+        return segRows;
+    };
+    // Computes a default `displayEventEnd` value if one is not expliclty defined
+    DayGridEventRenderer.prototype.computeDisplayEventEnd = function () {
+        return this.dayGrid.colCnt === 1; // we'll likely have space if there's only one day
+    };
+    return DayGridEventRenderer;
+}(SimpleDayGridEventRenderer));
+// Computes whether two segments' columns collide. They are assumed to be in the same row.
+function isDaySegCollision(seg, otherSegs) {
+    var i;
+    var otherSeg;
+    for (i = 0; i < otherSegs.length; i++) {
+        otherSeg = otherSegs[i];
+        if (otherSeg.firstCol <= seg.lastCol &&
+            otherSeg.lastCol >= seg.firstCol) {
+            return true;
+        }
+    }
+    return false;
+}
+// A cmp function for determining the leftmost event
+function compareDaySegCols(a, b) {
+    return a.leftCol - b.leftCol;
+}
+
+var DayGridMirrorRenderer = /** @class */ (function (_super) {
+    __extends(DayGridMirrorRenderer, _super);
+    function DayGridMirrorRenderer() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    DayGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+        var sourceSeg = mirrorInfo.sourceSeg;
+        var rowStructs = this.rowStructs = this.renderSegRows(segs);
+        // inject each new event skeleton into each associated row
+        this.dayGrid.rowEls.forEach(function (rowNode, row) {
+            var skeletonEl = htmlToElement('<div class="fc-mirror-skeleton"><table></table></div>'); // will be absolutely positioned
+            var skeletonTopEl;
+            var skeletonTop;
+            // If there is an original segment, match the top position. Otherwise, put it at the row's top level
+            if (sourceSeg && sourceSeg.row === row) {
+                skeletonTopEl = sourceSeg.el;
+            }
+            else {
+                skeletonTopEl = rowNode.querySelector('.fc-content-skeleton tbody');
+                if (!skeletonTopEl) { // when no events
+                    skeletonTopEl = rowNode.querySelector('.fc-content-skeleton table');
+                }
+            }
+            skeletonTop = skeletonTopEl.getBoundingClientRect().top -
+                rowNode.getBoundingClientRect().top; // the offsetParent origin
+            skeletonEl.style.top = skeletonTop + 'px';
+            skeletonEl.querySelector('table').appendChild(rowStructs[row].tbodyEl);
+            rowNode.appendChild(skeletonEl);
+        });
+    };
+    return DayGridMirrorRenderer;
+}(DayGridEventRenderer));
+
+var EMPTY_CELL_HTML = '<td style="pointer-events:none"></td>';
+var DayGridFillRenderer = /** @class */ (function (_super) {
+    __extends(DayGridFillRenderer, _super);
+    function DayGridFillRenderer(dayGrid) {
+        var _this = _super.call(this, dayGrid.context) || this;
+        _this.fillSegTag = 'td'; // override the default tag name
+        _this.dayGrid = dayGrid;
+        return _this;
+    }
+    DayGridFillRenderer.prototype.renderSegs = function (type, segs) {
+        // don't render timed background events
+        if (type === 'bgEvent') {
+            segs = segs.filter(function (seg) {
+                return seg.eventRange.def.allDay;
+            });
+        }
+        _super.prototype.renderSegs.call(this, type, segs);
+    };
+    DayGridFillRenderer.prototype.attachSegs = function (type, segs) {
+        var els = [];
+        var i;
+        var seg;
+        var skeletonEl;
+        for (i = 0; i < segs.length; i++) {
+            seg = segs[i];
+            skeletonEl = this.renderFillRow(type, seg);
+            this.dayGrid.rowEls[seg.row].appendChild(skeletonEl);
+            els.push(skeletonEl);
+        }
+        return els;
+    };
+    // Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
+    DayGridFillRenderer.prototype.renderFillRow = function (type, seg) {
+        var dayGrid = this.dayGrid;
+        var colCnt = dayGrid.colCnt, isRtl = dayGrid.isRtl;
+        var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol;
+        var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol;
+        var startCol = leftCol;
+        var endCol = rightCol + 1;
+        var className;
+        var skeletonEl;
+        var trEl;
+        if (type === 'businessHours') {
+            className = 'bgevent';
+        }
+        else {
+            className = type.toLowerCase();
+        }
+        skeletonEl = htmlToElement('<div class="fc-' + className + '-skeleton">' +
+            '<table><tr></tr></table>' +
+            '</div>');
+        trEl = skeletonEl.getElementsByTagName('tr')[0];
+        if (startCol > 0) {
+            appendToElement(trEl, 
+            // will create (startCol + 1) td's
+            new Array(startCol + 1).join(EMPTY_CELL_HTML));
+        }
+        seg.el.colSpan = endCol - startCol;
+        trEl.appendChild(seg.el);
+        if (endCol < colCnt) {
+            appendToElement(trEl, 
+            // will create (colCnt - endCol) td's
+            new Array(colCnt - endCol + 1).join(EMPTY_CELL_HTML));
+        }
+        var introHtml = dayGrid.renderProps.renderIntroHtml();
+        if (introHtml) {
+            if (dayGrid.isRtl) {
+                appendToElement(trEl, introHtml);
+            }
+            else {
+                prependToElement(trEl, introHtml);
+            }
+        }
+        return skeletonEl;
+    };
+    return DayGridFillRenderer;
+}(FillRenderer));
+
+var DayTile = /** @class */ (function (_super) {
+    __extends(DayTile, _super);
+    function DayTile(context, el) {
+        var _this = _super.call(this, context, el) || this;
+        var eventRenderer = _this.eventRenderer = new DayTileEventRenderer(_this);
+        var renderFrame = _this.renderFrame = memoizeRendering(_this._renderFrame);
+        _this.renderFgEvents = memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderFrame]);
+        _this.renderEventSelection = memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+        _this.renderEventDrag = memoizeRendering(eventRenderer.hideByHash.bind(eventRenderer), eventRenderer.showByHash.bind(eventRenderer), [renderFrame]);
+        _this.renderEventResize = memoizeRendering(eventRenderer.hideByHash.bind(eventRenderer), eventRenderer.showByHash.bind(eventRenderer), [renderFrame]);
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.el,
+            useEventCenter: false
+        });
+        return _this;
+    }
+    DayTile.prototype.render = function (props) {
+        this.renderFrame(props.date);
+        this.renderFgEvents(props.fgSegs);
+        this.renderEventSelection(props.eventSelection);
+        this.renderEventDrag(props.eventDragInstances);
+        this.renderEventResize(props.eventResizeInstances);
+    };
+    DayTile.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.renderFrame.unrender(); // should unrender everything else
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    DayTile.prototype._renderFrame = function (date) {
+        var _a = this, theme = _a.theme, dateEnv = _a.dateEnv;
+        var title = dateEnv.format(date, createFormatter(this.opt('dayPopoverFormat')) // TODO: cache
+        );
+        this.el.innerHTML =
+            '<div class="fc-header ' + theme.getClass('popoverHeader') + '">' +
+                '<span class="fc-title">' +
+                htmlEscape(title) +
+                '</span>' +
+                '<span class="fc-close ' + theme.getIconClass('close') + '"></span>' +
+                '</div>' +
+                '<div class="fc-body ' + theme.getClass('popoverContent') + '">' +
+                '<div class="fc-event-container"></div>' +
+                '</div>';
+        this.segContainerEl = this.el.querySelector('.fc-event-container');
+    };
+    DayTile.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+        var date = this.props.date; // HACK
+        if (positionLeft < elWidth && positionTop < elHeight) {
+            return {
+                component: this,
+                dateSpan: {
+                    allDay: true,
+                    range: { start: date, end: addDays(date, 1) }
+                },
+                dayEl: this.el,
+                rect: {
+                    left: 0,
+                    top: 0,
+                    right: elWidth,
+                    bottom: elHeight
+                },
+                layer: 1
+            };
+        }
+    };
+    return DayTile;
+}(DateComponent));
+var DayTileEventRenderer = /** @class */ (function (_super) {
+    __extends(DayTileEventRenderer, _super);
+    function DayTileEventRenderer(dayTile) {
+        var _this = _super.call(this, dayTile.context) || this;
+        _this.dayTile = dayTile;
+        return _this;
+    }
+    DayTileEventRenderer.prototype.attachSegs = function (segs) {
+        for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+            var seg = segs_1[_i];
+            this.dayTile.segContainerEl.appendChild(seg.el);
+        }
+    };
+    DayTileEventRenderer.prototype.detachSegs = function (segs) {
+        for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+            var seg = segs_2[_i];
+            removeElement(seg.el);
+        }
+    };
+    return DayTileEventRenderer;
+}(SimpleDayGridEventRenderer));
+
+var DayBgRow = /** @class */ (function () {
+    function DayBgRow(context) {
+        this.context = context;
+    }
+    DayBgRow.prototype.renderHtml = function (props) {
+        var parts = [];
+        if (props.renderIntroHtml) {
+            parts.push(props.renderIntroHtml());
+        }
+        for (var _i = 0, _a = props.cells; _i < _a.length; _i++) {
+            var cell = _a[_i];
+            parts.push(renderCellHtml(cell.date, props.dateProfile, this.context, cell.htmlAttrs));
+        }
+        if (!props.cells.length) {
+            parts.push('<td class="fc-day ' + this.context.theme.getClass('widgetContent') + '"></td>');
+        }
+        if (this.context.options.dir === 'rtl') {
+            parts.reverse();
+        }
+        return '<tr>' + parts.join('') + '</tr>';
+    };
+    return DayBgRow;
+}());
+function renderCellHtml(date, dateProfile, context, otherAttrs) {
+    var dateEnv = context.dateEnv, theme = context.theme;
+    var isDateValid = rangeContainsMarker(dateProfile.activeRange, date); // TODO: called too frequently. cache somehow.
+    var classes = getDayClasses(date, dateProfile, context);
+    classes.unshift('fc-day', theme.getClass('widgetContent'));
+    return '<td class="' + classes.join(' ') + '"' +
+        (isDateValid ?
+            ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' :
+            '') +
+        (otherAttrs ?
+            ' ' + otherAttrs :
+            '') +
+        '></td>';
+}
+
+var DAY_NUM_FORMAT = createFormatter({ day: 'numeric' });
+var WEEK_NUM_FORMAT = createFormatter({ week: 'numeric' });
+var DayGrid = /** @class */ (function (_super) {
+    __extends(DayGrid, _super);
+    function DayGrid(context, el, renderProps) {
+        var _this = _super.call(this, context, el) || this;
+        _this.bottomCoordPadding = 0; // hack for extending the hit area for the last row of the coordinate grid
+        _this.isCellSizesDirty = false;
+        var eventRenderer = _this.eventRenderer = new DayGridEventRenderer(_this);
+        var fillRenderer = _this.fillRenderer = new DayGridFillRenderer(_this);
+        _this.mirrorRenderer = new DayGridMirrorRenderer(_this);
+        var renderCells = _this.renderCells = memoizeRendering(_this._renderCells, _this._unrenderCells);
+        _this.renderBusinessHours = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderCells]);
+        _this.renderDateSelection = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'), [renderCells]);
+        _this.renderBgEvents = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderCells]);
+        _this.renderFgEvents = memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderCells]);
+        _this.renderEventSelection = memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+        _this.renderEventDrag = memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderCells]);
+        _this.renderEventResize = memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderCells]);
+        _this.renderProps = renderProps;
+        return _this;
+    }
+    DayGrid.prototype.render = function (props) {
+        var cells = props.cells;
+        this.rowCnt = cells.length;
+        this.colCnt = cells[0].length;
+        this.renderCells(cells, props.isRigid);
+        this.renderBusinessHours(props.businessHourSegs);
+        this.renderDateSelection(props.dateSelectionSegs);
+        this.renderBgEvents(props.bgEventSegs);
+        this.renderFgEvents(props.fgEventSegs);
+        this.renderEventSelection(props.eventSelection);
+        this.renderEventDrag(props.eventDrag);
+        this.renderEventResize(props.eventResize);
+        if (this.segPopoverTile) {
+            this.updateSegPopoverTile();
+        }
+    };
+    DayGrid.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.renderCells.unrender(); // will unrender everything else
+    };
+    DayGrid.prototype.getCellRange = function (row, col) {
+        var start = this.props.cells[row][col].date;
+        var end = addDays(start, 1);
+        return { start: start, end: end };
+    };
+    DayGrid.prototype.updateSegPopoverTile = function (date, segs) {
+        var ownProps = this.props;
+        this.segPopoverTile.receiveProps({
+            date: date || this.segPopoverTile.props.date,
+            fgSegs: segs || this.segPopoverTile.props.fgSegs,
+            eventSelection: ownProps.eventSelection,
+            eventDragInstances: ownProps.eventDrag ? ownProps.eventDrag.affectedInstances : null,
+            eventResizeInstances: ownProps.eventResize ? ownProps.eventResize.affectedInstances : null
+        });
+    };
+    /* Date Rendering
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype._renderCells = function (cells, isRigid) {
+        var _a = this, view = _a.view, dateEnv = _a.dateEnv;
+        var _b = this, rowCnt = _b.rowCnt, colCnt = _b.colCnt;
+        var html = '';
+        var row;
+        var col;
+        for (row = 0; row < rowCnt; row++) {
+            html += this.renderDayRowHtml(row, isRigid);
+        }
+        this.el.innerHTML = html;
+        this.rowEls = findElements(this.el, '.fc-row');
+        this.cellEls = findElements(this.el, '.fc-day, .fc-disabled-day');
+        if (this.isRtl) {
+            this.cellEls.reverse();
+        }
+        this.rowPositions = new PositionCache(this.el, this.rowEls, false, true // vertical
+        );
+        this.colPositions = new PositionCache(this.el, this.cellEls.slice(0, colCnt), // only the first row
+        true, false // horizontal
+        );
+        // trigger dayRender with each cell's element
+        for (row = 0; row < rowCnt; row++) {
+            for (col = 0; col < colCnt; col++) {
+                this.publiclyTrigger('dayRender', [
+                    {
+                        date: dateEnv.toDate(cells[row][col].date),
+                        el: this.getCellEl(row, col),
+                        view: view
+                    }
+                ]);
+            }
+        }
+        this.isCellSizesDirty = true;
+    };
+    DayGrid.prototype._unrenderCells = function () {
+        this.removeSegPopover();
+    };
+    // Generates the HTML for a single row, which is a div that wraps a table.
+    // `row` is the row number.
+    DayGrid.prototype.renderDayRowHtml = function (row, isRigid) {
+        var theme = this.theme;
+        var classes = ['fc-row', 'fc-week', theme.getClass('dayRow')];
+        if (isRigid) {
+            classes.push('fc-rigid');
+        }
+        var bgRow = new DayBgRow(this.context);
+        return '' +
+            '<div class="' + classes.join(' ') + '">' +
+            '<div class="fc-bg">' +
+            '<table class="' + theme.getClass('tableGrid') + '">' +
+            bgRow.renderHtml({
+                cells: this.props.cells[row],
+                dateProfile: this.props.dateProfile,
+                renderIntroHtml: this.renderProps.renderBgIntroHtml
+            }) +
+            '</table>' +
+            '</div>' +
+            '<div class="fc-content-skeleton">' +
+            '<table>' +
+            (this.getIsNumbersVisible() ?
+                '<thead>' +
+                    this.renderNumberTrHtml(row) +
+                    '</thead>' :
+                '') +
+            '</table>' +
+            '</div>' +
+            '</div>';
+    };
+    DayGrid.prototype.getIsNumbersVisible = function () {
+        return this.getIsDayNumbersVisible() ||
+            this.renderProps.cellWeekNumbersVisible ||
+            this.renderProps.colWeekNumbersVisible;
+    };
+    DayGrid.prototype.getIsDayNumbersVisible = function () {
+        return this.rowCnt > 1;
+    };
+    /* Grid Number Rendering
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype.renderNumberTrHtml = function (row) {
+        var intro = this.renderProps.renderNumberIntroHtml(row, this);
+        return '' +
+            '<tr>' +
+            (this.isRtl ? '' : intro) +
+            this.renderNumberCellsHtml(row) +
+            (this.isRtl ? intro : '') +
+            '</tr>';
+    };
+    DayGrid.prototype.renderNumberCellsHtml = function (row) {
+        var htmls = [];
+        var col;
+        var date;
+        for (col = 0; col < this.colCnt; col++) {
+            date = this.props.cells[row][col].date;
+            htmls.push(this.renderNumberCellHtml(date));
+        }
+        if (this.isRtl) {
+            htmls.reverse();
+        }
+        return htmls.join('');
+    };
+    // Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton.
+    // The number row will only exist if either day numbers or week numbers are turned on.
+    DayGrid.prototype.renderNumberCellHtml = function (date) {
+        var _a = this, view = _a.view, dateEnv = _a.dateEnv;
+        var html = '';
+        var isDateValid = rangeContainsMarker(this.props.dateProfile.activeRange, date); // TODO: called too frequently. cache somehow.
+        var isDayNumberVisible = this.getIsDayNumbersVisible() && isDateValid;
+        var classes;
+        var weekCalcFirstDow;
+        if (!isDayNumberVisible && !this.renderProps.cellWeekNumbersVisible) {
+            // no numbers in day cell (week number must be along the side)
+            return '<td></td>'; //  will create an empty space above events :(
+        }
+        classes = getDayClasses(date, this.props.dateProfile, this.context);
+        classes.unshift('fc-day-top');
+        if (this.renderProps.cellWeekNumbersVisible) {
+            weekCalcFirstDow = dateEnv.weekDow;
+        }
+        html += '<td class="' + classes.join(' ') + '"' +
+            (isDateValid ?
+                ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' :
+                '') +
+            '>';
+        if (this.renderProps.cellWeekNumbersVisible && (date.getUTCDay() === weekCalcFirstDow)) {
+            html += buildGotoAnchorHtml(view, { date: date, type: 'week' }, { 'class': 'fc-week-number' }, dateEnv.format(date, WEEK_NUM_FORMAT) // inner HTML
+            );
+        }
+        if (isDayNumberVisible) {
+            html += buildGotoAnchorHtml(view, date, { 'class': 'fc-day-number' }, dateEnv.format(date, DAY_NUM_FORMAT) // inner HTML
+            );
+        }
+        html += '</td>';
+        return html;
+    };
+    /* Sizing
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype.updateSize = function (isResize) {
+        var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
+        if (isResize ||
+            this.isCellSizesDirty ||
+            this.view.calendar.isEventsUpdated // hack
+        ) {
+            this.buildPositionCaches();
+            this.isCellSizesDirty = false;
+        }
+        fillRenderer.computeSizes(isResize);
+        eventRenderer.computeSizes(isResize);
+        mirrorRenderer.computeSizes(isResize);
+        fillRenderer.assignSizes(isResize);
+        eventRenderer.assignSizes(isResize);
+        mirrorRenderer.assignSizes(isResize);
+    };
+    DayGrid.prototype.buildPositionCaches = function () {
+        this.buildColPositions();
+        this.buildRowPositions();
+    };
+    DayGrid.prototype.buildColPositions = function () {
+        this.colPositions.build();
+    };
+    DayGrid.prototype.buildRowPositions = function () {
+        this.rowPositions.build();
+        this.rowPositions.bottoms[this.rowCnt - 1] += this.bottomCoordPadding; // hack
+    };
+    /* Hit System
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype.positionToHit = function (leftPosition, topPosition) {
+        var _a = this, colPositions = _a.colPositions, rowPositions = _a.rowPositions;
+        var col = colPositions.leftToIndex(leftPosition);
+        var row = rowPositions.topToIndex(topPosition);
+        if (row != null && col != null) {
+            return {
+                row: row,
+                col: col,
+                dateSpan: {
+                    range: this.getCellRange(row, col),
+                    allDay: true
+                },
+                dayEl: this.getCellEl(row, col),
+                relativeRect: {
+                    left: colPositions.lefts[col],
+                    right: colPositions.rights[col],
+                    top: rowPositions.tops[row],
+                    bottom: rowPositions.bottoms[row]
+                }
+            };
+        }
+    };
+    /* Cell System
+    ------------------------------------------------------------------------------------------------------------------*/
+    // FYI: the first column is the leftmost column, regardless of date
+    DayGrid.prototype.getCellEl = function (row, col) {
+        return this.cellEls[row * this.colCnt + col];
+    };
+    /* Event Drag Visualization
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype._renderEventDrag = function (state) {
+        if (state) {
+            this.eventRenderer.hideByHash(state.affectedInstances);
+            this.fillRenderer.renderSegs('highlight', state.segs);
+        }
+    };
+    DayGrid.prototype._unrenderEventDrag = function (state) {
+        if (state) {
+            this.eventRenderer.showByHash(state.affectedInstances);
+            this.fillRenderer.unrender('highlight');
+        }
+    };
+    /* Event Resize Visualization
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype._renderEventResize = function (state) {
+        if (state) {
+            this.eventRenderer.hideByHash(state.affectedInstances);
+            this.fillRenderer.renderSegs('highlight', state.segs);
+            this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    DayGrid.prototype._unrenderEventResize = function (state) {
+        if (state) {
+            this.eventRenderer.showByHash(state.affectedInstances);
+            this.fillRenderer.unrender('highlight');
+            this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    /* More+ Link Popover
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGrid.prototype.removeSegPopover = function () {
+        if (this.segPopover) {
+            this.segPopover.hide(); // in handler, will call segPopover's removeElement
+        }
+    };
+    // Limits the number of "levels" (vertically stacking layers of events) for each row of the grid.
+    // `levelLimit` can be false (don't limit), a number, or true (should be computed).
+    DayGrid.prototype.limitRows = function (levelLimit) {
+        var rowStructs = this.eventRenderer.rowStructs || [];
+        var row; // row #
+        var rowLevelLimit;
+        for (row = 0; row < rowStructs.length; row++) {
+            this.unlimitRow(row);
+            if (!levelLimit) {
+                rowLevelLimit = false;
+            }
+            else if (typeof levelLimit === 'number') {
+                rowLevelLimit = levelLimit;
+            }
+            else {
+                rowLevelLimit = this.computeRowLevelLimit(row);
+            }
+            if (rowLevelLimit !== false) {
+                this.limitRow(row, rowLevelLimit);
+            }
+        }
+    };
+    // Computes the number of levels a row will accomodate without going outside its bounds.
+    // Assumes the row is "rigid" (maintains a constant height regardless of what is inside).
+    // `row` is the row number.
+    DayGrid.prototype.computeRowLevelLimit = function (row) {
+        var rowEl = this.rowEls[row]; // the containing "fake" row div
+        var rowBottom = rowEl.getBoundingClientRect().bottom; // relative to viewport!
+        var trEls = findChildren(this.eventRenderer.rowStructs[row].tbodyEl);
+        var i;
+        var trEl;
+        // Reveal one level <tr> at a time and stop when we find one out of bounds
+        for (i = 0; i < trEls.length; i++) {
+            trEl = trEls[i];
+            trEl.classList.remove('fc-limited'); // reset to original state (reveal)
+            if (trEl.getBoundingClientRect().bottom > rowBottom) {
+                return i;
+            }
+        }
+        return false; // should not limit at all
+    };
+    // Limits the given grid row to the maximum number of levels and injects "more" links if necessary.
+    // `row` is the row number.
+    // `levelLimit` is a number for the maximum (inclusive) number of levels allowed.
+    DayGrid.prototype.limitRow = function (row, levelLimit) {
+        var _this = this;
+        var _a = this, colCnt = _a.colCnt, isRtl = _a.isRtl;
+        var rowStruct = this.eventRenderer.rowStructs[row];
+        var moreNodes = []; // array of "more" <a> links and <td> DOM nodes
+        var col = 0; // col #, left-to-right (not chronologically)
+        var levelSegs; // array of segment objects in the last allowable level, ordered left-to-right
+        var cellMatrix; // a matrix (by level, then column) of all <td> elements in the row
+        var limitedNodes; // array of temporarily hidden level <tr> and segment <td> DOM nodes
+        var i;
+        var seg;
+        var segsBelow; // array of segment objects below `seg` in the current `col`
+        var totalSegsBelow; // total number of segments below `seg` in any of the columns `seg` occupies
+        var colSegsBelow; // array of segment arrays, below seg, one for each column (offset from segs's first column)
+        var td;
+        var rowSpan;
+        var segMoreNodes; // array of "more" <td> cells that will stand-in for the current seg's cell
+        var j;
+        var moreTd;
+        var moreWrap;
+        var moreLink;
+        // Iterates through empty level cells and places "more" links inside if need be
+        var emptyCellsUntil = function (endCol) {
+            while (col < endCol) {
+                segsBelow = _this.getCellSegs(row, col, levelLimit);
+                if (segsBelow.length) {
+                    td = cellMatrix[levelLimit - 1][col];
+                    moreLink = _this.renderMoreLink(row, col, segsBelow);
+                    moreWrap = createElement('div', null, moreLink);
+                    td.appendChild(moreWrap);
+                    moreNodes.push(moreWrap);
+                }
+                col++;
+            }
+        };
+        if (levelLimit && levelLimit < rowStruct.segLevels.length) { // is it actually over the limit?
+            levelSegs = rowStruct.segLevels[levelLimit - 1];
+            cellMatrix = rowStruct.cellMatrix;
+            limitedNodes = findChildren(rowStruct.tbodyEl).slice(levelLimit); // get level <tr> elements past the limit
+            limitedNodes.forEach(function (node) {
+                node.classList.add('fc-limited'); // hide elements and get a simple DOM-nodes array
+            });
+            // iterate though segments in the last allowable level
+            for (i = 0; i < levelSegs.length; i++) {
+                seg = levelSegs[i];
+                var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol;
+                var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol;
+                emptyCellsUntil(leftCol); // process empty cells before the segment
+                // determine *all* segments below `seg` that occupy the same columns
+                colSegsBelow = [];
+                totalSegsBelow = 0;
+                while (col <= rightCol) {
+                    segsBelow = this.getCellSegs(row, col, levelLimit);
+                    colSegsBelow.push(segsBelow);
+                    totalSegsBelow += segsBelow.length;
+                    col++;
+                }
+                if (totalSegsBelow) { // do we need to replace this segment with one or many "more" links?
+                    td = cellMatrix[levelLimit - 1][leftCol]; // the segment's parent cell
+                    rowSpan = td.rowSpan || 1;
+                    segMoreNodes = [];
+                    // make a replacement <td> for each column the segment occupies. will be one for each colspan
+                    for (j = 0; j < colSegsBelow.length; j++) {
+                        moreTd = createElement('td', { className: 'fc-more-cell', rowSpan: rowSpan });
+                        segsBelow = colSegsBelow[j];
+                        moreLink = this.renderMoreLink(row, leftCol + j, [seg].concat(segsBelow) // count seg as hidden too
+                        );
+                        moreWrap = createElement('div', null, moreLink);
+                        moreTd.appendChild(moreWrap);
+                        segMoreNodes.push(moreTd);
+                        moreNodes.push(moreTd);
+                    }
+                    td.classList.add('fc-limited');
+                    insertAfterElement(td, segMoreNodes);
+                    limitedNodes.push(td);
+                }
+            }
+            emptyCellsUntil(this.colCnt); // finish off the level
+            rowStruct.moreEls = moreNodes; // for easy undoing later
+            rowStruct.limitedEls = limitedNodes; // for easy undoing later
+        }
+    };
+    // Reveals all levels and removes all "more"-related elements for a grid's row.
+    // `row` is a row number.
+    DayGrid.prototype.unlimitRow = function (row) {
+        var rowStruct = this.eventRenderer.rowStructs[row];
+        if (rowStruct.moreEls) {
+            rowStruct.moreEls.forEach(removeElement);
+            rowStruct.moreEls = null;
+        }
+        if (rowStruct.limitedEls) {
+            rowStruct.limitedEls.forEach(function (limitedEl) {
+                limitedEl.classList.remove('fc-limited');
+            });
+            rowStruct.limitedEls = null;
+        }
+    };
+    // Renders an <a> element that represents hidden event element for a cell.
+    // Responsible for attaching click handler as well.
+    DayGrid.prototype.renderMoreLink = function (row, col, hiddenSegs) {
+        var _this = this;
+        var _a = this, view = _a.view, dateEnv = _a.dateEnv;
+        var a = createElement('a', { className: 'fc-more' });
+        a.innerText = this.getMoreLinkText(hiddenSegs.length);
+        a.addEventListener('click', function (ev) {
+            var clickOption = _this.opt('eventLimitClick');
+            var _col = _this.isRtl ? _this.colCnt - col - 1 : col; // HACK: props.cells has different dir system?
+            var date = _this.props.cells[row][_col].date;
+            var moreEl = ev.currentTarget;
+            var dayEl = _this.getCellEl(row, col);
+            var allSegs = _this.getCellSegs(row, col);
+            // rescope the segments to be within the cell's date
+            var reslicedAllSegs = _this.resliceDaySegs(allSegs, date);
+            var reslicedHiddenSegs = _this.resliceDaySegs(hiddenSegs, date);
+            if (typeof clickOption === 'function') {
+                // the returned value can be an atomic option
+                clickOption = _this.publiclyTrigger('eventLimitClick', [
+                    {
+                        date: dateEnv.toDate(date),
+                        allDay: true,
+                        dayEl: dayEl,
+                        moreEl: moreEl,
+                        segs: reslicedAllSegs,
+                        hiddenSegs: reslicedHiddenSegs,
+                        jsEvent: ev,
+                        view: view
+                    }
+                ]);
+            }
+            if (clickOption === 'popover') {
+                _this.showSegPopover(row, col, moreEl, reslicedAllSegs);
+            }
+            else if (typeof clickOption === 'string') { // a view name
+                view.calendar.zoomTo(date, clickOption);
+            }
+        });
+        return a;
+    };
+    // Reveals the popover that displays all events within a cell
+    DayGrid.prototype.showSegPopover = function (row, col, moreLink, segs) {
+        var _this = this;
+        var _a = this, calendar = _a.calendar, view = _a.view, theme = _a.theme;
+        var _col = this.isRtl ? this.colCnt - col - 1 : col; // HACK: props.cells has different dir system?
+        var moreWrap = moreLink.parentNode; // the <div> wrapper around the <a>
+        var topEl; // the element we want to match the top coordinate of
+        var options;
+        if (this.rowCnt === 1) {
+            topEl = view.el; // will cause the popover to cover any sort of header
+        }
+        else {
+            topEl = this.rowEls[row]; // will align with top of row
+        }
+        options = {
+            className: 'fc-more-popover ' + theme.getClass('popover'),
+            parentEl: view.el,
+            top: computeRect(topEl).top,
+            autoHide: true,
+            content: function (el) {
+                _this.segPopoverTile = new DayTile(_this.context, el);
+                _this.updateSegPopoverTile(_this.props.cells[row][_col].date, segs);
+            },
+            hide: function () {
+                _this.segPopoverTile.destroy();
+                _this.segPopoverTile = null;
+                _this.segPopover.destroy();
+                _this.segPopover = null;
+            }
+        };
+        // Determine horizontal coordinate.
+        // We use the moreWrap instead of the <td> to avoid border confusion.
+        if (this.isRtl) {
+            options.right = computeRect(moreWrap).right + 1; // +1 to be over cell border
+        }
+        else {
+            options.left = computeRect(moreWrap).left - 1; // -1 to be over cell border
+        }
+        this.segPopover = new Popover(options);
+        this.segPopover.show();
+        calendar.releaseAfterSizingTriggers(); // hack for eventPositioned
+    };
+    // Given the events within an array of segment objects, reslice them to be in a single day
+    DayGrid.prototype.resliceDaySegs = function (segs, dayDate) {
+        var dayStart = dayDate;
+        var dayEnd = addDays(dayStart, 1);
+        var dayRange = { start: dayStart, end: dayEnd };
+        var newSegs = [];
+        for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+            var seg = segs_1[_i];
+            var eventRange = seg.eventRange;
+            var origRange = eventRange.range;
+            var slicedRange = intersectRanges(origRange, dayRange);
+            if (slicedRange) {
+                newSegs.push(__assign({}, seg, { eventRange: {
+                        def: eventRange.def,
+                        ui: __assign({}, eventRange.ui, { durationEditable: false }),
+                        instance: eventRange.instance,
+                        range: slicedRange
+                    }, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() }));
+            }
+        }
+        return newSegs;
+    };
+    // Generates the text that should be inside a "more" link, given the number of events it represents
+    DayGrid.prototype.getMoreLinkText = function (num) {
+        var opt = this.opt('eventLimitText');
+        if (typeof opt === 'function') {
+            return opt(num);
+        }
+        else {
+            return '+' + num + ' ' + opt;
+        }
+    };
+    // Returns segments within a given cell.
+    // If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs.
+    DayGrid.prototype.getCellSegs = function (row, col, startLevel) {
+        var segMatrix = this.eventRenderer.rowStructs[row].segMatrix;
+        var level = startLevel || 0;
+        var segs = [];
+        var seg;
+        while (level < segMatrix.length) {
+            seg = segMatrix[level][col];
+            if (seg) {
+                segs.push(seg);
+            }
+            level++;
+        }
+        return segs;
+    };
+    return DayGrid;
+}(DateComponent));
+
+var WEEK_NUM_FORMAT$1 = createFormatter({ week: 'numeric' });
+/* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells.
+----------------------------------------------------------------------------------------------------------------------*/
+// It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
+// It is responsible for managing width/height.
+var DayGridView = /** @class */ (function (_super) {
+    __extends(DayGridView, _super);
+    function DayGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        /* Header Rendering
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Generates the HTML that will go before the day-of week header cells
+        _this.renderHeadIntroHtml = function () {
+            var theme = _this.theme;
+            if (_this.colWeekNumbersVisible) {
+                return '' +
+                    '<th class="fc-week-number ' + theme.getClass('widgetHeader') + '" ' + _this.weekNumberStyleAttr() + '>' +
+                    '<span>' + // needed for matchCellWidths
+                    htmlEscape(_this.opt('weekLabel')) +
+                    '</span>' +
+                    '</th>';
+            }
+            return '';
+        };
+        /* Day Grid Rendering
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Generates the HTML that will go before content-skeleton cells that display the day/week numbers
+        _this.renderDayGridNumberIntroHtml = function (row, dayGrid) {
+            var dateEnv = _this.dateEnv;
+            var weekStart = dayGrid.props.cells[row][0].date;
+            if (_this.colWeekNumbersVisible) {
+                return '' +
+                    '<td class="fc-week-number" ' + _this.weekNumberStyleAttr() + '>' +
+                    buildGotoAnchorHtml(// aside from link, important for matchCellWidths
+                    _this, { date: weekStart, type: 'week', forceOff: dayGrid.colCnt === 1 }, dateEnv.format(weekStart, WEEK_NUM_FORMAT$1) // inner HTML
+                    ) +
+                    '</td>';
+            }
+            return '';
+        };
+        // Generates the HTML that goes before the day bg cells for each day-row
+        _this.renderDayGridBgIntroHtml = function () {
+            var theme = _this.theme;
+            if (_this.colWeekNumbersVisible) {
+                return '<td class="fc-week-number ' + theme.getClass('widgetContent') + '" ' + _this.weekNumberStyleAttr() + '></td>';
+            }
+            return '';
+        };
+        // Generates the HTML that goes before every other type of row generated by DayGrid.
+        // Affects mirror-skeleton and highlight-skeleton rows.
+        _this.renderDayGridIntroHtml = function () {
+            if (_this.colWeekNumbersVisible) {
+                return '<td class="fc-week-number" ' + _this.weekNumberStyleAttr() + '></td>';
+            }
+            return '';
+        };
+        _this.el.classList.add('fc-dayGrid-view');
+        _this.el.innerHTML = _this.renderSkeletonHtml();
+        _this.scroller = new ScrollComponent('hidden', // overflow x
+        'auto' // overflow y
+        );
+        var dayGridContainerEl = _this.scroller.el;
+        _this.el.querySelector('.fc-body > tr > td').appendChild(dayGridContainerEl);
+        dayGridContainerEl.classList.add('fc-day-grid-container');
+        var dayGridEl = createElement('div', { className: 'fc-day-grid' });
+        dayGridContainerEl.appendChild(dayGridEl);
+        var cellWeekNumbersVisible;
+        if (_this.opt('weekNumbers')) {
+            if (_this.opt('weekNumbersWithinDays')) {
+                cellWeekNumbersVisible = true;
+                _this.colWeekNumbersVisible = false;
+            }
+            else {
+                cellWeekNumbersVisible = false;
+                _this.colWeekNumbersVisible = true;
+            }
+        }
+        else {
+            _this.colWeekNumbersVisible = false;
+            cellWeekNumbersVisible = false;
+        }
+        _this.dayGrid = new DayGrid(_this.context, dayGridEl, {
+            renderNumberIntroHtml: _this.renderDayGridNumberIntroHtml,
+            renderBgIntroHtml: _this.renderDayGridBgIntroHtml,
+            renderIntroHtml: _this.renderDayGridIntroHtml,
+            colWeekNumbersVisible: _this.colWeekNumbersVisible,
+            cellWeekNumbersVisible: cellWeekNumbersVisible
+        });
+        return _this;
+    }
+    DayGridView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.dayGrid.destroy();
+        this.scroller.destroy();
+    };
+    // Builds the HTML skeleton for the view.
+    // The day-grid component will render inside of a container defined by this HTML.
+    DayGridView.prototype.renderSkeletonHtml = function () {
+        var theme = this.theme;
+        return '' +
+            '<table class="' + theme.getClass('tableGrid') + '">' +
+            (this.opt('columnHeader') ?
+                '<thead class="fc-head">' +
+                    '<tr>' +
+                    '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
+                    '</tr>' +
+                    '</thead>' :
+                '') +
+            '<tbody class="fc-body">' +
+            '<tr>' +
+            '<td class="' + theme.getClass('widgetContent') + '"></td>' +
+            '</tr>' +
+            '</tbody>' +
+            '</table>';
+    };
+    // Generates an HTML attribute string for setting the width of the week number column, if it is known
+    DayGridView.prototype.weekNumberStyleAttr = function () {
+        if (this.weekNumberWidth != null) {
+            return 'style="width:' + this.weekNumberWidth + 'px"';
+        }
+        return '';
+    };
+    // Determines whether each row should have a constant height
+    DayGridView.prototype.hasRigidRows = function () {
+        var eventLimit = this.opt('eventLimit');
+        return eventLimit && typeof eventLimit !== 'number';
+    };
+    /* Dimensions
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+        _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
+        this.dayGrid.updateSize(isResize);
+    };
+    // Refreshes the horizontal dimensions of the view
+    DayGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
+        var dayGrid = this.dayGrid;
+        var eventLimit = this.opt('eventLimit');
+        var headRowEl = this.header ? this.header.el : null; // HACK
+        var scrollerHeight;
+        var scrollbarWidths;
+        // hack to give the view some height prior to dayGrid's columns being rendered
+        // TODO: separate setting height from scroller VS dayGrid.
+        if (!dayGrid.rowEls) {
+            if (!isAuto) {
+                scrollerHeight = this.computeScrollerHeight(viewHeight);
+                this.scroller.setHeight(scrollerHeight);
+            }
+            return;
+        }
+        if (this.colWeekNumbersVisible) {
+            // Make sure all week number cells running down the side have the same width.
+            this.weekNumberWidth = matchCellWidths(findElements(this.el, '.fc-week-number'));
+        }
+        // reset all heights to be natural
+        this.scroller.clear();
+        if (headRowEl) {
+            uncompensateScroll(headRowEl);
+        }
+        dayGrid.removeSegPopover(); // kill the "more" popover if displayed
+        // is the event limit a constant level number?
+        if (eventLimit && typeof eventLimit === 'number') {
+            dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
+        }
+        // distribute the height to the rows
+        // (viewHeight is a "recommended" value if isAuto)
+        scrollerHeight = this.computeScrollerHeight(viewHeight);
+        this.setGridHeight(scrollerHeight, isAuto);
+        // is the event limit dynamically calculated?
+        if (eventLimit && typeof eventLimit !== 'number') {
+            dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
+        }
+        if (!isAuto) { // should we force dimensions of the scroll container?
+            this.scroller.setHeight(scrollerHeight);
+            scrollbarWidths = this.scroller.getScrollbarWidths();
+            if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
+                if (headRowEl) {
+                    compensateScroll(headRowEl, scrollbarWidths);
+                }
+                // doing the scrollbar compensation might have created text overflow which created more height. redo
+                scrollerHeight = this.computeScrollerHeight(viewHeight);
+                this.scroller.setHeight(scrollerHeight);
+            }
+            // guarantees the same scrollbar widths
+            this.scroller.lockOverflow(scrollbarWidths);
+        }
+    };
+    // given a desired total height of the view, returns what the height of the scroller should be
+    DayGridView.prototype.computeScrollerHeight = function (viewHeight) {
+        return viewHeight -
+            subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
+    };
+    // Sets the height of just the DayGrid component in this view
+    DayGridView.prototype.setGridHeight = function (height, isAuto) {
+        if (this.opt('monthMode')) {
+            // if auto, make the height of each row the height that it would be if there were 6 weeks
+            if (isAuto) {
+                height *= this.dayGrid.rowCnt / 6;
+            }
+            distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows
+        }
+        else {
+            if (isAuto) {
+                undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
+            }
+            else {
+                distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
+            }
+        }
+    };
+    /* Scroll
+    ------------------------------------------------------------------------------------------------------------------*/
+    DayGridView.prototype.computeDateScroll = function (duration) {
+        return { top: 0 };
+    };
+    DayGridView.prototype.queryDateScroll = function () {
+        return { top: this.scroller.getScrollTop() };
+    };
+    DayGridView.prototype.applyDateScroll = function (scroll) {
+        if (scroll.top !== undefined) {
+            this.scroller.setScrollTop(scroll.top);
+        }
+    };
+    return DayGridView;
+}(View));
+DayGridView.prototype.dateProfileGeneratorClass = DayGridDateProfileGenerator;
+
+var SimpleDayGrid = /** @class */ (function (_super) {
+    __extends(SimpleDayGrid, _super);
+    function SimpleDayGrid(context, dayGrid) {
+        var _this = _super.call(this, context, dayGrid.el) || this;
+        _this.slicer = new DayGridSlicer();
+        _this.dayGrid = dayGrid;
+        context.calendar.registerInteractiveComponent(_this, { el: _this.dayGrid.el });
+        return _this;
+    }
+    SimpleDayGrid.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    SimpleDayGrid.prototype.render = function (props) {
+        var dayGrid = this.dayGrid;
+        var dateProfile = props.dateProfile, dayTable = props.dayTable;
+        dayGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, props.nextDayThreshold, dayGrid, dayTable), { dateProfile: dateProfile, cells: dayTable.cells, isRigid: props.isRigid }));
+    };
+    SimpleDayGrid.prototype.buildPositionCaches = function () {
+        this.dayGrid.buildPositionCaches();
+    };
+    SimpleDayGrid.prototype.queryHit = function (positionLeft, positionTop) {
+        var rawHit = this.dayGrid.positionToHit(positionLeft, positionTop);
+        if (rawHit) {
+            return {
+                component: this.dayGrid,
+                dateSpan: rawHit.dateSpan,
+                dayEl: rawHit.dayEl,
+                rect: {
+                    left: rawHit.relativeRect.left,
+                    right: rawHit.relativeRect.right,
+                    top: rawHit.relativeRect.top,
+                    bottom: rawHit.relativeRect.bottom
+                },
+                layer: 0
+            };
+        }
+    };
+    return SimpleDayGrid;
+}(DateComponent));
+var DayGridSlicer = /** @class */ (function (_super) {
+    __extends(DayGridSlicer, _super);
+    function DayGridSlicer() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    DayGridSlicer.prototype.sliceRange = function (dateRange, dayTable) {
+        return dayTable.sliceRange(dateRange);
+    };
+    return DayGridSlicer;
+}(Slicer));
+
+var DayGridView$1 = /** @class */ (function (_super) {
+    __extends(DayGridView, _super);
+    function DayGridView(_context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, _context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.buildDayTable = memoize(buildDayTable);
+        if (_this.opt('columnHeader')) {
+            _this.header = new DayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+        }
+        _this.simpleDayGrid = new SimpleDayGrid(_this.context, _this.dayGrid);
+        return _this;
+    }
+    DayGridView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        if (this.header) {
+            this.header.destroy();
+        }
+        this.simpleDayGrid.destroy();
+    };
+    DayGridView.prototype.render = function (props) {
+        _super.prototype.render.call(this, props);
+        var dateProfile = this.props.dateProfile;
+        var dayTable = this.dayTable =
+            this.buildDayTable(dateProfile, this.dateProfileGenerator);
+        if (this.header) {
+            this.header.receiveProps({
+                dateProfile: dateProfile,
+                dates: dayTable.headerDates,
+                datesRepDistinctDays: dayTable.rowCnt === 1,
+                renderIntroHtml: this.renderHeadIntroHtml
+            });
+        }
+        this.simpleDayGrid.receiveProps({
+            dateProfile: dateProfile,
+            dayTable: dayTable,
+            businessHours: props.businessHours,
+            dateSelection: props.dateSelection,
+            eventStore: props.eventStore,
+            eventUiBases: props.eventUiBases,
+            eventSelection: props.eventSelection,
+            eventDrag: props.eventDrag,
+            eventResize: props.eventResize,
+            isRigid: this.hasRigidRows(),
+            nextDayThreshold: this.nextDayThreshold
+        });
+    };
+    return DayGridView;
+}(DayGridView));
+function buildDayTable(dateProfile, dateProfileGenerator) {
+    var daySeries = new DaySeries(dateProfile.renderRange, dateProfileGenerator);
+    return new DayTable(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
+}
+
+var main = createPlugin({
+    defaultView: 'dayGridMonth',
+    views: {
+        dayGrid: DayGridView$1,
+        dayGridDay: {
+            type: 'dayGrid',
+            duration: { days: 1 }
+        },
+        dayGridWeek: {
+            type: 'dayGrid',
+            duration: { weeks: 1 }
+        },
+        dayGridMonth: {
+            type: 'dayGrid',
+            duration: { months: 1 },
+            monthMode: true,
+            fixedWeekCount: true
+        }
+    }
+});
+
+export default main;
+export { DayGridView as AbstractDayGridView, DayBgRow, DayGrid, DayGridSlicer, DayGridView$1 as DayGridView, SimpleDayGrid, buildDayTable as buildBasicDayTable };
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/main.js b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..d1cc407f11f50292b3c7e09b2fe07116f2f02768
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.js
@@ -0,0 +1,1641 @@
+/*!
+FullCalendar Day Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarDayGrid = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    var DayGridDateProfileGenerator = /** @class */ (function (_super) {
+        __extends(DayGridDateProfileGenerator, _super);
+        function DayGridDateProfileGenerator() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        // Computes the date range that will be rendered.
+        DayGridDateProfileGenerator.prototype.buildRenderRange = function (currentRange, currentRangeUnit, isRangeAllDay) {
+            var dateEnv = this.dateEnv;
+            var renderRange = _super.prototype.buildRenderRange.call(this, currentRange, currentRangeUnit, isRangeAllDay);
+            var start = renderRange.start;
+            var end = renderRange.end;
+            var endOfWeek;
+            // year and month views should be aligned with weeks. this is already done for week
+            if (/^(year|month)$/.test(currentRangeUnit)) {
+                start = dateEnv.startOfWeek(start);
+                // make end-of-week if not already
+                endOfWeek = dateEnv.startOfWeek(end);
+                if (endOfWeek.valueOf() !== end.valueOf()) {
+                    end = core.addWeeks(endOfWeek, 1);
+                }
+            }
+            // ensure 6 weeks
+            if (this.options.monthMode &&
+                this.options.fixedWeekCount) {
+                var rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
+                core.diffWeeks(start, end));
+                end = core.addWeeks(end, 6 - rowCnt);
+            }
+            return { start: start, end: end };
+        };
+        return DayGridDateProfileGenerator;
+    }(core.DateProfileGenerator));
+
+    /* A rectangular panel that is absolutely positioned over other content
+    ------------------------------------------------------------------------------------------------------------------------
+    Options:
+      - className (string)
+      - content (HTML string, element, or element array)
+      - parentEl
+      - top
+      - left
+      - right (the x coord of where the right edge should be. not a "CSS" right)
+      - autoHide (boolean)
+      - show (callback)
+      - hide (callback)
+    */
+    var Popover = /** @class */ (function () {
+        function Popover(options) {
+            var _this = this;
+            this.isHidden = true;
+            this.margin = 10; // the space required between the popover and the edges of the scroll container
+            // Triggered when the user clicks *anywhere* in the document, for the autoHide feature
+            this.documentMousedown = function (ev) {
+                // only hide the popover if the click happened outside the popover
+                if (_this.el && !_this.el.contains(ev.target)) {
+                    _this.hide();
+                }
+            };
+            this.options = options;
+        }
+        // Shows the popover on the specified position. Renders it if not already
+        Popover.prototype.show = function () {
+            if (this.isHidden) {
+                if (!this.el) {
+                    this.render();
+                }
+                this.el.style.display = '';
+                this.position();
+                this.isHidden = false;
+                this.trigger('show');
+            }
+        };
+        // Hides the popover, through CSS, but does not remove it from the DOM
+        Popover.prototype.hide = function () {
+            if (!this.isHidden) {
+                this.el.style.display = 'none';
+                this.isHidden = true;
+                this.trigger('hide');
+            }
+        };
+        // Creates `this.el` and renders content inside of it
+        Popover.prototype.render = function () {
+            var _this = this;
+            var options = this.options;
+            var el = this.el = core.createElement('div', {
+                className: 'fc-popover ' + (options.className || ''),
+                style: {
+                    top: '0',
+                    left: '0'
+                }
+            });
+            if (typeof options.content === 'function') {
+                options.content(el);
+            }
+            options.parentEl.appendChild(el);
+            // when a click happens on anything inside with a 'fc-close' className, hide the popover
+            core.listenBySelector(el, 'click', '.fc-close', function (ev) {
+                _this.hide();
+            });
+            if (options.autoHide) {
+                document.addEventListener('mousedown', this.documentMousedown);
+            }
+        };
+        // Hides and unregisters any handlers
+        Popover.prototype.destroy = function () {
+            this.hide();
+            if (this.el) {
+                core.removeElement(this.el);
+                this.el = null;
+            }
+            document.removeEventListener('mousedown', this.documentMousedown);
+        };
+        // Positions the popover optimally, using the top/left/right options
+        Popover.prototype.position = function () {
+            var options = this.options;
+            var el = this.el;
+            var elDims = el.getBoundingClientRect(); // only used for width,height
+            var origin = core.computeRect(el.offsetParent);
+            var clippingRect = core.computeClippingRect(options.parentEl);
+            var top; // the "position" (not "offset") values for the popover
+            var left; //
+            // compute top and left
+            top = options.top || 0;
+            if (options.left !== undefined) {
+                left = options.left;
+            }
+            else if (options.right !== undefined) {
+                left = options.right - elDims.width; // derive the left value from the right value
+            }
+            else {
+                left = 0;
+            }
+            // constrain to the view port. if constrained by two edges, give precedence to top/left
+            top = Math.min(top, clippingRect.bottom - elDims.height - this.margin);
+            top = Math.max(top, clippingRect.top + this.margin);
+            left = Math.min(left, clippingRect.right - elDims.width - this.margin);
+            left = Math.max(left, clippingRect.left + this.margin);
+            core.applyStyle(el, {
+                top: top - origin.top,
+                left: left - origin.left
+            });
+        };
+        // Triggers a callback. Calls a function in the option hash of the same name.
+        // Arguments beyond the first `name` are forwarded on.
+        // TODO: better code reuse for this. Repeat code
+        // can kill this???
+        Popover.prototype.trigger = function (name) {
+            if (this.options[name]) {
+                this.options[name].apply(this, Array.prototype.slice.call(arguments, 1));
+            }
+        };
+        return Popover;
+    }());
+
+    /* Event-rendering methods for the DayGrid class
+    ----------------------------------------------------------------------------------------------------------------------*/
+    // "Simple" is bad a name. has nothing to do with SimpleDayGrid
+    var SimpleDayGridEventRenderer = /** @class */ (function (_super) {
+        __extends(SimpleDayGridEventRenderer, _super);
+        function SimpleDayGridEventRenderer() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        // Builds the HTML to be used for the default element for an individual segment
+        SimpleDayGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
+            var _a = this.context, view = _a.view, options = _a.options;
+            var eventRange = seg.eventRange;
+            var eventDef = eventRange.def;
+            var eventUi = eventRange.ui;
+            var allDay = eventDef.allDay;
+            var isDraggable = view.computeEventDraggable(eventDef, eventUi);
+            var isResizableFromStart = allDay && seg.isStart && view.computeEventStartResizable(eventDef, eventUi);
+            var isResizableFromEnd = allDay && seg.isEnd && view.computeEventEndResizable(eventDef, eventUi);
+            var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
+            var skinCss = core.cssToStr(this.getSkinCss(eventUi));
+            var timeHtml = '';
+            var timeText;
+            var titleHtml;
+            classes.unshift('fc-day-grid-event', 'fc-h-event');
+            // Only display a timed events time if it is the starting segment
+            if (seg.isStart) {
+                timeText = this.getTimeText(eventRange);
+                if (timeText) {
+                    timeHtml = '<span class="fc-time">' + core.htmlEscape(timeText) + '</span>';
+                }
+            }
+            titleHtml =
+                '<span class="fc-title">' +
+                    (core.htmlEscape(eventDef.title || '') || '&nbsp;') + // we always want one line of height
+                    '</span>';
+            return '<a class="' + classes.join(' ') + '"' +
+                (eventDef.url ?
+                    ' href="' + core.htmlEscape(eventDef.url) + '"' :
+                    '') +
+                (skinCss ?
+                    ' style="' + skinCss + '"' :
+                    '') +
+                '>' +
+                '<div class="fc-content">' +
+                (options.dir === 'rtl' ?
+                    titleHtml + ' ' + timeHtml : // put a natural space in between
+                    timeHtml + ' ' + titleHtml //
+                ) +
+                '</div>' +
+                (isResizableFromStart ?
+                    '<div class="fc-resizer fc-start-resizer"></div>' :
+                    '') +
+                (isResizableFromEnd ?
+                    '<div class="fc-resizer fc-end-resizer"></div>' :
+                    '') +
+                '</a>';
+        };
+        // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
+        SimpleDayGridEventRenderer.prototype.computeEventTimeFormat = function () {
+            return {
+                hour: 'numeric',
+                minute: '2-digit',
+                omitZeroMinute: true,
+                meridiem: 'narrow'
+            };
+        };
+        SimpleDayGridEventRenderer.prototype.computeDisplayEventEnd = function () {
+            return false; // TODO: somehow consider the originating DayGrid's column count
+        };
+        return SimpleDayGridEventRenderer;
+    }(core.FgEventRenderer));
+
+    /* Event-rendering methods for the DayGrid class
+    ----------------------------------------------------------------------------------------------------------------------*/
+    var DayGridEventRenderer = /** @class */ (function (_super) {
+        __extends(DayGridEventRenderer, _super);
+        function DayGridEventRenderer(dayGrid) {
+            var _this = _super.call(this, dayGrid.context) || this;
+            _this.dayGrid = dayGrid;
+            return _this;
+        }
+        // Renders the given foreground event segments onto the grid
+        DayGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+            var rowStructs = this.rowStructs = this.renderSegRows(segs);
+            // append to each row's content skeleton
+            this.dayGrid.rowEls.forEach(function (rowNode, i) {
+                rowNode.querySelector('.fc-content-skeleton > table').appendChild(rowStructs[i].tbodyEl);
+            });
+            // removes the "more.." events popover
+            if (!mirrorInfo) {
+                this.dayGrid.removeSegPopover();
+            }
+        };
+        // Unrenders all currently rendered foreground event segments
+        DayGridEventRenderer.prototype.detachSegs = function () {
+            var rowStructs = this.rowStructs || [];
+            var rowStruct;
+            while ((rowStruct = rowStructs.pop())) {
+                core.removeElement(rowStruct.tbodyEl);
+            }
+            this.rowStructs = null;
+        };
+        // Uses the given events array to generate <tbody> elements that should be appended to each row's content skeleton.
+        // Returns an array of rowStruct objects (see the bottom of `renderSegRow`).
+        // PRECONDITION: each segment shoud already have a rendered and assigned `.el`
+        DayGridEventRenderer.prototype.renderSegRows = function (segs) {
+            var rowStructs = [];
+            var segRows;
+            var row;
+            segRows = this.groupSegRows(segs); // group into nested arrays
+            // iterate each row of segment groupings
+            for (row = 0; row < segRows.length; row++) {
+                rowStructs.push(this.renderSegRow(row, segRows[row]));
+            }
+            return rowStructs;
+        };
+        // Given a row # and an array of segments all in the same row, render a <tbody> element, a skeleton that contains
+        // the segments. Returns object with a bunch of internal data about how the render was calculated.
+        // NOTE: modifies rowSegs
+        DayGridEventRenderer.prototype.renderSegRow = function (row, rowSegs) {
+            var dayGrid = this.dayGrid;
+            var colCnt = dayGrid.colCnt, isRtl = dayGrid.isRtl;
+            var segLevels = this.buildSegLevels(rowSegs); // group into sub-arrays of levels
+            var levelCnt = Math.max(1, segLevels.length); // ensure at least one level
+            var tbody = document.createElement('tbody');
+            var segMatrix = []; // lookup for which segments are rendered into which level+col cells
+            var cellMatrix = []; // lookup for all <td> elements of the level+col matrix
+            var loneCellMatrix = []; // lookup for <td> elements that only take up a single column
+            var i;
+            var levelSegs;
+            var col;
+            var tr;
+            var j;
+            var seg;
+            var td;
+            // populates empty cells from the current column (`col`) to `endCol`
+            function emptyCellsUntil(endCol) {
+                while (col < endCol) {
+                    // try to grab a cell from the level above and extend its rowspan. otherwise, create a fresh cell
+                    td = (loneCellMatrix[i - 1] || [])[col];
+                    if (td) {
+                        td.rowSpan = (td.rowSpan || 1) + 1;
+                    }
+                    else {
+                        td = document.createElement('td');
+                        tr.appendChild(td);
+                    }
+                    cellMatrix[i][col] = td;
+                    loneCellMatrix[i][col] = td;
+                    col++;
+                }
+            }
+            for (i = 0; i < levelCnt; i++) { // iterate through all levels
+                levelSegs = segLevels[i];
+                col = 0;
+                tr = document.createElement('tr');
+                segMatrix.push([]);
+                cellMatrix.push([]);
+                loneCellMatrix.push([]);
+                // levelCnt might be 1 even though there are no actual levels. protect against this.
+                // this single empty row is useful for styling.
+                if (levelSegs) {
+                    for (j = 0; j < levelSegs.length; j++) { // iterate through segments in level
+                        seg = levelSegs[j];
+                        var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol;
+                        var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol;
+                        emptyCellsUntil(leftCol);
+                        // create a container that occupies or more columns. append the event element.
+                        td = core.createElement('td', { className: 'fc-event-container' }, seg.el);
+                        if (leftCol !== rightCol) {
+                            td.colSpan = rightCol - leftCol + 1;
+                        }
+                        else { // a single-column segment
+                            loneCellMatrix[i][col] = td;
+                        }
+                        while (col <= rightCol) {
+                            cellMatrix[i][col] = td;
+                            segMatrix[i][col] = seg;
+                            col++;
+                        }
+                        tr.appendChild(td);
+                    }
+                }
+                emptyCellsUntil(colCnt); // finish off the row
+                var introHtml = dayGrid.renderProps.renderIntroHtml();
+                if (introHtml) {
+                    if (dayGrid.isRtl) {
+                        core.appendToElement(tr, introHtml);
+                    }
+                    else {
+                        core.prependToElement(tr, introHtml);
+                    }
+                }
+                tbody.appendChild(tr);
+            }
+            return {
+                row: row,
+                tbodyEl: tbody,
+                cellMatrix: cellMatrix,
+                segMatrix: segMatrix,
+                segLevels: segLevels,
+                segs: rowSegs
+            };
+        };
+        // Stacks a flat array of segments, which are all assumed to be in the same row, into subarrays of vertical levels.
+        // NOTE: modifies segs
+        DayGridEventRenderer.prototype.buildSegLevels = function (segs) {
+            var _a = this.dayGrid, isRtl = _a.isRtl, colCnt = _a.colCnt;
+            var levels = [];
+            var i;
+            var seg;
+            var j;
+            // Give preference to elements with certain criteria, so they have
+            // a chance to be closer to the top.
+            segs = this.sortEventSegs(segs);
+            for (i = 0; i < segs.length; i++) {
+                seg = segs[i];
+                // loop through levels, starting with the topmost, until the segment doesn't collide with other segments
+                for (j = 0; j < levels.length; j++) {
+                    if (!isDaySegCollision(seg, levels[j])) {
+                        break;
+                    }
+                }
+                // `j` now holds the desired subrow index
+                seg.level = j;
+                seg.leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol; // for sorting only
+                seg.rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol // for sorting only
+                ;
+                (levels[j] || (levels[j] = [])).push(seg);
+            }
+            // order segments left-to-right. very important if calendar is RTL
+            for (j = 0; j < levels.length; j++) {
+                levels[j].sort(compareDaySegCols);
+            }
+            return levels;
+        };
+        // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's row
+        DayGridEventRenderer.prototype.groupSegRows = function (segs) {
+            var segRows = [];
+            var i;
+            for (i = 0; i < this.dayGrid.rowCnt; i++) {
+                segRows.push([]);
+            }
+            for (i = 0; i < segs.length; i++) {
+                segRows[segs[i].row].push(segs[i]);
+            }
+            return segRows;
+        };
+        // Computes a default `displayEventEnd` value if one is not expliclty defined
+        DayGridEventRenderer.prototype.computeDisplayEventEnd = function () {
+            return this.dayGrid.colCnt === 1; // we'll likely have space if there's only one day
+        };
+        return DayGridEventRenderer;
+    }(SimpleDayGridEventRenderer));
+    // Computes whether two segments' columns collide. They are assumed to be in the same row.
+    function isDaySegCollision(seg, otherSegs) {
+        var i;
+        var otherSeg;
+        for (i = 0; i < otherSegs.length; i++) {
+            otherSeg = otherSegs[i];
+            if (otherSeg.firstCol <= seg.lastCol &&
+                otherSeg.lastCol >= seg.firstCol) {
+                return true;
+            }
+        }
+        return false;
+    }
+    // A cmp function for determining the leftmost event
+    function compareDaySegCols(a, b) {
+        return a.leftCol - b.leftCol;
+    }
+
+    var DayGridMirrorRenderer = /** @class */ (function (_super) {
+        __extends(DayGridMirrorRenderer, _super);
+        function DayGridMirrorRenderer() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        DayGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+            var sourceSeg = mirrorInfo.sourceSeg;
+            var rowStructs = this.rowStructs = this.renderSegRows(segs);
+            // inject each new event skeleton into each associated row
+            this.dayGrid.rowEls.forEach(function (rowNode, row) {
+                var skeletonEl = core.htmlToElement('<div class="fc-mirror-skeleton"><table></table></div>'); // will be absolutely positioned
+                var skeletonTopEl;
+                var skeletonTop;
+                // If there is an original segment, match the top position. Otherwise, put it at the row's top level
+                if (sourceSeg && sourceSeg.row === row) {
+                    skeletonTopEl = sourceSeg.el;
+                }
+                else {
+                    skeletonTopEl = rowNode.querySelector('.fc-content-skeleton tbody');
+                    if (!skeletonTopEl) { // when no events
+                        skeletonTopEl = rowNode.querySelector('.fc-content-skeleton table');
+                    }
+                }
+                skeletonTop = skeletonTopEl.getBoundingClientRect().top -
+                    rowNode.getBoundingClientRect().top; // the offsetParent origin
+                skeletonEl.style.top = skeletonTop + 'px';
+                skeletonEl.querySelector('table').appendChild(rowStructs[row].tbodyEl);
+                rowNode.appendChild(skeletonEl);
+            });
+        };
+        return DayGridMirrorRenderer;
+    }(DayGridEventRenderer));
+
+    var EMPTY_CELL_HTML = '<td style="pointer-events:none"></td>';
+    var DayGridFillRenderer = /** @class */ (function (_super) {
+        __extends(DayGridFillRenderer, _super);
+        function DayGridFillRenderer(dayGrid) {
+            var _this = _super.call(this, dayGrid.context) || this;
+            _this.fillSegTag = 'td'; // override the default tag name
+            _this.dayGrid = dayGrid;
+            return _this;
+        }
+        DayGridFillRenderer.prototype.renderSegs = function (type, segs) {
+            // don't render timed background events
+            if (type === 'bgEvent') {
+                segs = segs.filter(function (seg) {
+                    return seg.eventRange.def.allDay;
+                });
+            }
+            _super.prototype.renderSegs.call(this, type, segs);
+        };
+        DayGridFillRenderer.prototype.attachSegs = function (type, segs) {
+            var els = [];
+            var i;
+            var seg;
+            var skeletonEl;
+            for (i = 0; i < segs.length; i++) {
+                seg = segs[i];
+                skeletonEl = this.renderFillRow(type, seg);
+                this.dayGrid.rowEls[seg.row].appendChild(skeletonEl);
+                els.push(skeletonEl);
+            }
+            return els;
+        };
+        // Generates the HTML needed for one row of a fill. Requires the seg's el to be rendered.
+        DayGridFillRenderer.prototype.renderFillRow = function (type, seg) {
+            var dayGrid = this.dayGrid;
+            var colCnt = dayGrid.colCnt, isRtl = dayGrid.isRtl;
+            var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol;
+            var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol;
+            var startCol = leftCol;
+            var endCol = rightCol + 1;
+            var className;
+            var skeletonEl;
+            var trEl;
+            if (type === 'businessHours') {
+                className = 'bgevent';
+            }
+            else {
+                className = type.toLowerCase();
+            }
+            skeletonEl = core.htmlToElement('<div class="fc-' + className + '-skeleton">' +
+                '<table><tr></tr></table>' +
+                '</div>');
+            trEl = skeletonEl.getElementsByTagName('tr')[0];
+            if (startCol > 0) {
+                core.appendToElement(trEl, 
+                // will create (startCol + 1) td's
+                new Array(startCol + 1).join(EMPTY_CELL_HTML));
+            }
+            seg.el.colSpan = endCol - startCol;
+            trEl.appendChild(seg.el);
+            if (endCol < colCnt) {
+                core.appendToElement(trEl, 
+                // will create (colCnt - endCol) td's
+                new Array(colCnt - endCol + 1).join(EMPTY_CELL_HTML));
+            }
+            var introHtml = dayGrid.renderProps.renderIntroHtml();
+            if (introHtml) {
+                if (dayGrid.isRtl) {
+                    core.appendToElement(trEl, introHtml);
+                }
+                else {
+                    core.prependToElement(trEl, introHtml);
+                }
+            }
+            return skeletonEl;
+        };
+        return DayGridFillRenderer;
+    }(core.FillRenderer));
+
+    var DayTile = /** @class */ (function (_super) {
+        __extends(DayTile, _super);
+        function DayTile(context, el) {
+            var _this = _super.call(this, context, el) || this;
+            var eventRenderer = _this.eventRenderer = new DayTileEventRenderer(_this);
+            var renderFrame = _this.renderFrame = core.memoizeRendering(_this._renderFrame);
+            _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderFrame]);
+            _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+            _this.renderEventDrag = core.memoizeRendering(eventRenderer.hideByHash.bind(eventRenderer), eventRenderer.showByHash.bind(eventRenderer), [renderFrame]);
+            _this.renderEventResize = core.memoizeRendering(eventRenderer.hideByHash.bind(eventRenderer), eventRenderer.showByHash.bind(eventRenderer), [renderFrame]);
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.el,
+                useEventCenter: false
+            });
+            return _this;
+        }
+        DayTile.prototype.render = function (props) {
+            this.renderFrame(props.date);
+            this.renderFgEvents(props.fgSegs);
+            this.renderEventSelection(props.eventSelection);
+            this.renderEventDrag(props.eventDragInstances);
+            this.renderEventResize(props.eventResizeInstances);
+        };
+        DayTile.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.renderFrame.unrender(); // should unrender everything else
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        DayTile.prototype._renderFrame = function (date) {
+            var _a = this, theme = _a.theme, dateEnv = _a.dateEnv;
+            var title = dateEnv.format(date, core.createFormatter(this.opt('dayPopoverFormat')) // TODO: cache
+            );
+            this.el.innerHTML =
+                '<div class="fc-header ' + theme.getClass('popoverHeader') + '">' +
+                    '<span class="fc-title">' +
+                    core.htmlEscape(title) +
+                    '</span>' +
+                    '<span class="fc-close ' + theme.getIconClass('close') + '"></span>' +
+                    '</div>' +
+                    '<div class="fc-body ' + theme.getClass('popoverContent') + '">' +
+                    '<div class="fc-event-container"></div>' +
+                    '</div>';
+            this.segContainerEl = this.el.querySelector('.fc-event-container');
+        };
+        DayTile.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+            var date = this.props.date; // HACK
+            if (positionLeft < elWidth && positionTop < elHeight) {
+                return {
+                    component: this,
+                    dateSpan: {
+                        allDay: true,
+                        range: { start: date, end: core.addDays(date, 1) }
+                    },
+                    dayEl: this.el,
+                    rect: {
+                        left: 0,
+                        top: 0,
+                        right: elWidth,
+                        bottom: elHeight
+                    },
+                    layer: 1
+                };
+            }
+        };
+        return DayTile;
+    }(core.DateComponent));
+    var DayTileEventRenderer = /** @class */ (function (_super) {
+        __extends(DayTileEventRenderer, _super);
+        function DayTileEventRenderer(dayTile) {
+            var _this = _super.call(this, dayTile.context) || this;
+            _this.dayTile = dayTile;
+            return _this;
+        }
+        DayTileEventRenderer.prototype.attachSegs = function (segs) {
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                this.dayTile.segContainerEl.appendChild(seg.el);
+            }
+        };
+        DayTileEventRenderer.prototype.detachSegs = function (segs) {
+            for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+                var seg = segs_2[_i];
+                core.removeElement(seg.el);
+            }
+        };
+        return DayTileEventRenderer;
+    }(SimpleDayGridEventRenderer));
+
+    var DayBgRow = /** @class */ (function () {
+        function DayBgRow(context) {
+            this.context = context;
+        }
+        DayBgRow.prototype.renderHtml = function (props) {
+            var parts = [];
+            if (props.renderIntroHtml) {
+                parts.push(props.renderIntroHtml());
+            }
+            for (var _i = 0, _a = props.cells; _i < _a.length; _i++) {
+                var cell = _a[_i];
+                parts.push(renderCellHtml(cell.date, props.dateProfile, this.context, cell.htmlAttrs));
+            }
+            if (!props.cells.length) {
+                parts.push('<td class="fc-day ' + this.context.theme.getClass('widgetContent') + '"></td>');
+            }
+            if (this.context.options.dir === 'rtl') {
+                parts.reverse();
+            }
+            return '<tr>' + parts.join('') + '</tr>';
+        };
+        return DayBgRow;
+    }());
+    function renderCellHtml(date, dateProfile, context, otherAttrs) {
+        var dateEnv = context.dateEnv, theme = context.theme;
+        var isDateValid = core.rangeContainsMarker(dateProfile.activeRange, date); // TODO: called too frequently. cache somehow.
+        var classes = core.getDayClasses(date, dateProfile, context);
+        classes.unshift('fc-day', theme.getClass('widgetContent'));
+        return '<td class="' + classes.join(' ') + '"' +
+            (isDateValid ?
+                ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' :
+                '') +
+            (otherAttrs ?
+                ' ' + otherAttrs :
+                '') +
+            '></td>';
+    }
+
+    var DAY_NUM_FORMAT = core.createFormatter({ day: 'numeric' });
+    var WEEK_NUM_FORMAT = core.createFormatter({ week: 'numeric' });
+    var DayGrid = /** @class */ (function (_super) {
+        __extends(DayGrid, _super);
+        function DayGrid(context, el, renderProps) {
+            var _this = _super.call(this, context, el) || this;
+            _this.bottomCoordPadding = 0; // hack for extending the hit area for the last row of the coordinate grid
+            _this.isCellSizesDirty = false;
+            var eventRenderer = _this.eventRenderer = new DayGridEventRenderer(_this);
+            var fillRenderer = _this.fillRenderer = new DayGridFillRenderer(_this);
+            _this.mirrorRenderer = new DayGridMirrorRenderer(_this);
+            var renderCells = _this.renderCells = core.memoizeRendering(_this._renderCells, _this._unrenderCells);
+            _this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderCells]);
+            _this.renderDateSelection = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'), [renderCells]);
+            _this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderCells]);
+            _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderCells]);
+            _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+            _this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderCells]);
+            _this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderCells]);
+            _this.renderProps = renderProps;
+            return _this;
+        }
+        DayGrid.prototype.render = function (props) {
+            var cells = props.cells;
+            this.rowCnt = cells.length;
+            this.colCnt = cells[0].length;
+            this.renderCells(cells, props.isRigid);
+            this.renderBusinessHours(props.businessHourSegs);
+            this.renderDateSelection(props.dateSelectionSegs);
+            this.renderBgEvents(props.bgEventSegs);
+            this.renderFgEvents(props.fgEventSegs);
+            this.renderEventSelection(props.eventSelection);
+            this.renderEventDrag(props.eventDrag);
+            this.renderEventResize(props.eventResize);
+            if (this.segPopoverTile) {
+                this.updateSegPopoverTile();
+            }
+        };
+        DayGrid.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.renderCells.unrender(); // will unrender everything else
+        };
+        DayGrid.prototype.getCellRange = function (row, col) {
+            var start = this.props.cells[row][col].date;
+            var end = core.addDays(start, 1);
+            return { start: start, end: end };
+        };
+        DayGrid.prototype.updateSegPopoverTile = function (date, segs) {
+            var ownProps = this.props;
+            this.segPopoverTile.receiveProps({
+                date: date || this.segPopoverTile.props.date,
+                fgSegs: segs || this.segPopoverTile.props.fgSegs,
+                eventSelection: ownProps.eventSelection,
+                eventDragInstances: ownProps.eventDrag ? ownProps.eventDrag.affectedInstances : null,
+                eventResizeInstances: ownProps.eventResize ? ownProps.eventResize.affectedInstances : null
+            });
+        };
+        /* Date Rendering
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype._renderCells = function (cells, isRigid) {
+            var _a = this, view = _a.view, dateEnv = _a.dateEnv;
+            var _b = this, rowCnt = _b.rowCnt, colCnt = _b.colCnt;
+            var html = '';
+            var row;
+            var col;
+            for (row = 0; row < rowCnt; row++) {
+                html += this.renderDayRowHtml(row, isRigid);
+            }
+            this.el.innerHTML = html;
+            this.rowEls = core.findElements(this.el, '.fc-row');
+            this.cellEls = core.findElements(this.el, '.fc-day, .fc-disabled-day');
+            if (this.isRtl) {
+                this.cellEls.reverse();
+            }
+            this.rowPositions = new core.PositionCache(this.el, this.rowEls, false, true // vertical
+            );
+            this.colPositions = new core.PositionCache(this.el, this.cellEls.slice(0, colCnt), // only the first row
+            true, false // horizontal
+            );
+            // trigger dayRender with each cell's element
+            for (row = 0; row < rowCnt; row++) {
+                for (col = 0; col < colCnt; col++) {
+                    this.publiclyTrigger('dayRender', [
+                        {
+                            date: dateEnv.toDate(cells[row][col].date),
+                            el: this.getCellEl(row, col),
+                            view: view
+                        }
+                    ]);
+                }
+            }
+            this.isCellSizesDirty = true;
+        };
+        DayGrid.prototype._unrenderCells = function () {
+            this.removeSegPopover();
+        };
+        // Generates the HTML for a single row, which is a div that wraps a table.
+        // `row` is the row number.
+        DayGrid.prototype.renderDayRowHtml = function (row, isRigid) {
+            var theme = this.theme;
+            var classes = ['fc-row', 'fc-week', theme.getClass('dayRow')];
+            if (isRigid) {
+                classes.push('fc-rigid');
+            }
+            var bgRow = new DayBgRow(this.context);
+            return '' +
+                '<div class="' + classes.join(' ') + '">' +
+                '<div class="fc-bg">' +
+                '<table class="' + theme.getClass('tableGrid') + '">' +
+                bgRow.renderHtml({
+                    cells: this.props.cells[row],
+                    dateProfile: this.props.dateProfile,
+                    renderIntroHtml: this.renderProps.renderBgIntroHtml
+                }) +
+                '</table>' +
+                '</div>' +
+                '<div class="fc-content-skeleton">' +
+                '<table>' +
+                (this.getIsNumbersVisible() ?
+                    '<thead>' +
+                        this.renderNumberTrHtml(row) +
+                        '</thead>' :
+                    '') +
+                '</table>' +
+                '</div>' +
+                '</div>';
+        };
+        DayGrid.prototype.getIsNumbersVisible = function () {
+            return this.getIsDayNumbersVisible() ||
+                this.renderProps.cellWeekNumbersVisible ||
+                this.renderProps.colWeekNumbersVisible;
+        };
+        DayGrid.prototype.getIsDayNumbersVisible = function () {
+            return this.rowCnt > 1;
+        };
+        /* Grid Number Rendering
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype.renderNumberTrHtml = function (row) {
+            var intro = this.renderProps.renderNumberIntroHtml(row, this);
+            return '' +
+                '<tr>' +
+                (this.isRtl ? '' : intro) +
+                this.renderNumberCellsHtml(row) +
+                (this.isRtl ? intro : '') +
+                '</tr>';
+        };
+        DayGrid.prototype.renderNumberCellsHtml = function (row) {
+            var htmls = [];
+            var col;
+            var date;
+            for (col = 0; col < this.colCnt; col++) {
+                date = this.props.cells[row][col].date;
+                htmls.push(this.renderNumberCellHtml(date));
+            }
+            if (this.isRtl) {
+                htmls.reverse();
+            }
+            return htmls.join('');
+        };
+        // Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton.
+        // The number row will only exist if either day numbers or week numbers are turned on.
+        DayGrid.prototype.renderNumberCellHtml = function (date) {
+            var _a = this, view = _a.view, dateEnv = _a.dateEnv;
+            var html = '';
+            var isDateValid = core.rangeContainsMarker(this.props.dateProfile.activeRange, date); // TODO: called too frequently. cache somehow.
+            var isDayNumberVisible = this.getIsDayNumbersVisible() && isDateValid;
+            var classes;
+            var weekCalcFirstDow;
+            if (!isDayNumberVisible && !this.renderProps.cellWeekNumbersVisible) {
+                // no numbers in day cell (week number must be along the side)
+                return '<td></td>'; //  will create an empty space above events :(
+            }
+            classes = core.getDayClasses(date, this.props.dateProfile, this.context);
+            classes.unshift('fc-day-top');
+            if (this.renderProps.cellWeekNumbersVisible) {
+                weekCalcFirstDow = dateEnv.weekDow;
+            }
+            html += '<td class="' + classes.join(' ') + '"' +
+                (isDateValid ?
+                    ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' :
+                    '') +
+                '>';
+            if (this.renderProps.cellWeekNumbersVisible && (date.getUTCDay() === weekCalcFirstDow)) {
+                html += core.buildGotoAnchorHtml(view, { date: date, type: 'week' }, { 'class': 'fc-week-number' }, dateEnv.format(date, WEEK_NUM_FORMAT) // inner HTML
+                );
+            }
+            if (isDayNumberVisible) {
+                html += core.buildGotoAnchorHtml(view, date, { 'class': 'fc-day-number' }, dateEnv.format(date, DAY_NUM_FORMAT) // inner HTML
+                );
+            }
+            html += '</td>';
+            return html;
+        };
+        /* Sizing
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype.updateSize = function (isResize) {
+            var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
+            if (isResize ||
+                this.isCellSizesDirty ||
+                this.view.calendar.isEventsUpdated // hack
+            ) {
+                this.buildPositionCaches();
+                this.isCellSizesDirty = false;
+            }
+            fillRenderer.computeSizes(isResize);
+            eventRenderer.computeSizes(isResize);
+            mirrorRenderer.computeSizes(isResize);
+            fillRenderer.assignSizes(isResize);
+            eventRenderer.assignSizes(isResize);
+            mirrorRenderer.assignSizes(isResize);
+        };
+        DayGrid.prototype.buildPositionCaches = function () {
+            this.buildColPositions();
+            this.buildRowPositions();
+        };
+        DayGrid.prototype.buildColPositions = function () {
+            this.colPositions.build();
+        };
+        DayGrid.prototype.buildRowPositions = function () {
+            this.rowPositions.build();
+            this.rowPositions.bottoms[this.rowCnt - 1] += this.bottomCoordPadding; // hack
+        };
+        /* Hit System
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype.positionToHit = function (leftPosition, topPosition) {
+            var _a = this, colPositions = _a.colPositions, rowPositions = _a.rowPositions;
+            var col = colPositions.leftToIndex(leftPosition);
+            var row = rowPositions.topToIndex(topPosition);
+            if (row != null && col != null) {
+                return {
+                    row: row,
+                    col: col,
+                    dateSpan: {
+                        range: this.getCellRange(row, col),
+                        allDay: true
+                    },
+                    dayEl: this.getCellEl(row, col),
+                    relativeRect: {
+                        left: colPositions.lefts[col],
+                        right: colPositions.rights[col],
+                        top: rowPositions.tops[row],
+                        bottom: rowPositions.bottoms[row]
+                    }
+                };
+            }
+        };
+        /* Cell System
+        ------------------------------------------------------------------------------------------------------------------*/
+        // FYI: the first column is the leftmost column, regardless of date
+        DayGrid.prototype.getCellEl = function (row, col) {
+            return this.cellEls[row * this.colCnt + col];
+        };
+        /* Event Drag Visualization
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype._renderEventDrag = function (state) {
+            if (state) {
+                this.eventRenderer.hideByHash(state.affectedInstances);
+                this.fillRenderer.renderSegs('highlight', state.segs);
+            }
+        };
+        DayGrid.prototype._unrenderEventDrag = function (state) {
+            if (state) {
+                this.eventRenderer.showByHash(state.affectedInstances);
+                this.fillRenderer.unrender('highlight');
+            }
+        };
+        /* Event Resize Visualization
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype._renderEventResize = function (state) {
+            if (state) {
+                this.eventRenderer.hideByHash(state.affectedInstances);
+                this.fillRenderer.renderSegs('highlight', state.segs);
+                this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        DayGrid.prototype._unrenderEventResize = function (state) {
+            if (state) {
+                this.eventRenderer.showByHash(state.affectedInstances);
+                this.fillRenderer.unrender('highlight');
+                this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        /* More+ Link Popover
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGrid.prototype.removeSegPopover = function () {
+            if (this.segPopover) {
+                this.segPopover.hide(); // in handler, will call segPopover's removeElement
+            }
+        };
+        // Limits the number of "levels" (vertically stacking layers of events) for each row of the grid.
+        // `levelLimit` can be false (don't limit), a number, or true (should be computed).
+        DayGrid.prototype.limitRows = function (levelLimit) {
+            var rowStructs = this.eventRenderer.rowStructs || [];
+            var row; // row #
+            var rowLevelLimit;
+            for (row = 0; row < rowStructs.length; row++) {
+                this.unlimitRow(row);
+                if (!levelLimit) {
+                    rowLevelLimit = false;
+                }
+                else if (typeof levelLimit === 'number') {
+                    rowLevelLimit = levelLimit;
+                }
+                else {
+                    rowLevelLimit = this.computeRowLevelLimit(row);
+                }
+                if (rowLevelLimit !== false) {
+                    this.limitRow(row, rowLevelLimit);
+                }
+            }
+        };
+        // Computes the number of levels a row will accomodate without going outside its bounds.
+        // Assumes the row is "rigid" (maintains a constant height regardless of what is inside).
+        // `row` is the row number.
+        DayGrid.prototype.computeRowLevelLimit = function (row) {
+            var rowEl = this.rowEls[row]; // the containing "fake" row div
+            var rowBottom = rowEl.getBoundingClientRect().bottom; // relative to viewport!
+            var trEls = core.findChildren(this.eventRenderer.rowStructs[row].tbodyEl);
+            var i;
+            var trEl;
+            // Reveal one level <tr> at a time and stop when we find one out of bounds
+            for (i = 0; i < trEls.length; i++) {
+                trEl = trEls[i];
+                trEl.classList.remove('fc-limited'); // reset to original state (reveal)
+                if (trEl.getBoundingClientRect().bottom > rowBottom) {
+                    return i;
+                }
+            }
+            return false; // should not limit at all
+        };
+        // Limits the given grid row to the maximum number of levels and injects "more" links if necessary.
+        // `row` is the row number.
+        // `levelLimit` is a number for the maximum (inclusive) number of levels allowed.
+        DayGrid.prototype.limitRow = function (row, levelLimit) {
+            var _this = this;
+            var _a = this, colCnt = _a.colCnt, isRtl = _a.isRtl;
+            var rowStruct = this.eventRenderer.rowStructs[row];
+            var moreNodes = []; // array of "more" <a> links and <td> DOM nodes
+            var col = 0; // col #, left-to-right (not chronologically)
+            var levelSegs; // array of segment objects in the last allowable level, ordered left-to-right
+            var cellMatrix; // a matrix (by level, then column) of all <td> elements in the row
+            var limitedNodes; // array of temporarily hidden level <tr> and segment <td> DOM nodes
+            var i;
+            var seg;
+            var segsBelow; // array of segment objects below `seg` in the current `col`
+            var totalSegsBelow; // total number of segments below `seg` in any of the columns `seg` occupies
+            var colSegsBelow; // array of segment arrays, below seg, one for each column (offset from segs's first column)
+            var td;
+            var rowSpan;
+            var segMoreNodes; // array of "more" <td> cells that will stand-in for the current seg's cell
+            var j;
+            var moreTd;
+            var moreWrap;
+            var moreLink;
+            // Iterates through empty level cells and places "more" links inside if need be
+            var emptyCellsUntil = function (endCol) {
+                while (col < endCol) {
+                    segsBelow = _this.getCellSegs(row, col, levelLimit);
+                    if (segsBelow.length) {
+                        td = cellMatrix[levelLimit - 1][col];
+                        moreLink = _this.renderMoreLink(row, col, segsBelow);
+                        moreWrap = core.createElement('div', null, moreLink);
+                        td.appendChild(moreWrap);
+                        moreNodes.push(moreWrap);
+                    }
+                    col++;
+                }
+            };
+            if (levelLimit && levelLimit < rowStruct.segLevels.length) { // is it actually over the limit?
+                levelSegs = rowStruct.segLevels[levelLimit - 1];
+                cellMatrix = rowStruct.cellMatrix;
+                limitedNodes = core.findChildren(rowStruct.tbodyEl).slice(levelLimit); // get level <tr> elements past the limit
+                limitedNodes.forEach(function (node) {
+                    node.classList.add('fc-limited'); // hide elements and get a simple DOM-nodes array
+                });
+                // iterate though segments in the last allowable level
+                for (i = 0; i < levelSegs.length; i++) {
+                    seg = levelSegs[i];
+                    var leftCol = isRtl ? (colCnt - 1 - seg.lastCol) : seg.firstCol;
+                    var rightCol = isRtl ? (colCnt - 1 - seg.firstCol) : seg.lastCol;
+                    emptyCellsUntil(leftCol); // process empty cells before the segment
+                    // determine *all* segments below `seg` that occupy the same columns
+                    colSegsBelow = [];
+                    totalSegsBelow = 0;
+                    while (col <= rightCol) {
+                        segsBelow = this.getCellSegs(row, col, levelLimit);
+                        colSegsBelow.push(segsBelow);
+                        totalSegsBelow += segsBelow.length;
+                        col++;
+                    }
+                    if (totalSegsBelow) { // do we need to replace this segment with one or many "more" links?
+                        td = cellMatrix[levelLimit - 1][leftCol]; // the segment's parent cell
+                        rowSpan = td.rowSpan || 1;
+                        segMoreNodes = [];
+                        // make a replacement <td> for each column the segment occupies. will be one for each colspan
+                        for (j = 0; j < colSegsBelow.length; j++) {
+                            moreTd = core.createElement('td', { className: 'fc-more-cell', rowSpan: rowSpan });
+                            segsBelow = colSegsBelow[j];
+                            moreLink = this.renderMoreLink(row, leftCol + j, [seg].concat(segsBelow) // count seg as hidden too
+                            );
+                            moreWrap = core.createElement('div', null, moreLink);
+                            moreTd.appendChild(moreWrap);
+                            segMoreNodes.push(moreTd);
+                            moreNodes.push(moreTd);
+                        }
+                        td.classList.add('fc-limited');
+                        core.insertAfterElement(td, segMoreNodes);
+                        limitedNodes.push(td);
+                    }
+                }
+                emptyCellsUntil(this.colCnt); // finish off the level
+                rowStruct.moreEls = moreNodes; // for easy undoing later
+                rowStruct.limitedEls = limitedNodes; // for easy undoing later
+            }
+        };
+        // Reveals all levels and removes all "more"-related elements for a grid's row.
+        // `row` is a row number.
+        DayGrid.prototype.unlimitRow = function (row) {
+            var rowStruct = this.eventRenderer.rowStructs[row];
+            if (rowStruct.moreEls) {
+                rowStruct.moreEls.forEach(core.removeElement);
+                rowStruct.moreEls = null;
+            }
+            if (rowStruct.limitedEls) {
+                rowStruct.limitedEls.forEach(function (limitedEl) {
+                    limitedEl.classList.remove('fc-limited');
+                });
+                rowStruct.limitedEls = null;
+            }
+        };
+        // Renders an <a> element that represents hidden event element for a cell.
+        // Responsible for attaching click handler as well.
+        DayGrid.prototype.renderMoreLink = function (row, col, hiddenSegs) {
+            var _this = this;
+            var _a = this, view = _a.view, dateEnv = _a.dateEnv;
+            var a = core.createElement('a', { className: 'fc-more' });
+            a.innerText = this.getMoreLinkText(hiddenSegs.length);
+            a.addEventListener('click', function (ev) {
+                var clickOption = _this.opt('eventLimitClick');
+                var _col = _this.isRtl ? _this.colCnt - col - 1 : col; // HACK: props.cells has different dir system?
+                var date = _this.props.cells[row][_col].date;
+                var moreEl = ev.currentTarget;
+                var dayEl = _this.getCellEl(row, col);
+                var allSegs = _this.getCellSegs(row, col);
+                // rescope the segments to be within the cell's date
+                var reslicedAllSegs = _this.resliceDaySegs(allSegs, date);
+                var reslicedHiddenSegs = _this.resliceDaySegs(hiddenSegs, date);
+                if (typeof clickOption === 'function') {
+                    // the returned value can be an atomic option
+                    clickOption = _this.publiclyTrigger('eventLimitClick', [
+                        {
+                            date: dateEnv.toDate(date),
+                            allDay: true,
+                            dayEl: dayEl,
+                            moreEl: moreEl,
+                            segs: reslicedAllSegs,
+                            hiddenSegs: reslicedHiddenSegs,
+                            jsEvent: ev,
+                            view: view
+                        }
+                    ]);
+                }
+                if (clickOption === 'popover') {
+                    _this.showSegPopover(row, col, moreEl, reslicedAllSegs);
+                }
+                else if (typeof clickOption === 'string') { // a view name
+                    view.calendar.zoomTo(date, clickOption);
+                }
+            });
+            return a;
+        };
+        // Reveals the popover that displays all events within a cell
+        DayGrid.prototype.showSegPopover = function (row, col, moreLink, segs) {
+            var _this = this;
+            var _a = this, calendar = _a.calendar, view = _a.view, theme = _a.theme;
+            var _col = this.isRtl ? this.colCnt - col - 1 : col; // HACK: props.cells has different dir system?
+            var moreWrap = moreLink.parentNode; // the <div> wrapper around the <a>
+            var topEl; // the element we want to match the top coordinate of
+            var options;
+            if (this.rowCnt === 1) {
+                topEl = view.el; // will cause the popover to cover any sort of header
+            }
+            else {
+                topEl = this.rowEls[row]; // will align with top of row
+            }
+            options = {
+                className: 'fc-more-popover ' + theme.getClass('popover'),
+                parentEl: view.el,
+                top: core.computeRect(topEl).top,
+                autoHide: true,
+                content: function (el) {
+                    _this.segPopoverTile = new DayTile(_this.context, el);
+                    _this.updateSegPopoverTile(_this.props.cells[row][_col].date, segs);
+                },
+                hide: function () {
+                    _this.segPopoverTile.destroy();
+                    _this.segPopoverTile = null;
+                    _this.segPopover.destroy();
+                    _this.segPopover = null;
+                }
+            };
+            // Determine horizontal coordinate.
+            // We use the moreWrap instead of the <td> to avoid border confusion.
+            if (this.isRtl) {
+                options.right = core.computeRect(moreWrap).right + 1; // +1 to be over cell border
+            }
+            else {
+                options.left = core.computeRect(moreWrap).left - 1; // -1 to be over cell border
+            }
+            this.segPopover = new Popover(options);
+            this.segPopover.show();
+            calendar.releaseAfterSizingTriggers(); // hack for eventPositioned
+        };
+        // Given the events within an array of segment objects, reslice them to be in a single day
+        DayGrid.prototype.resliceDaySegs = function (segs, dayDate) {
+            var dayStart = dayDate;
+            var dayEnd = core.addDays(dayStart, 1);
+            var dayRange = { start: dayStart, end: dayEnd };
+            var newSegs = [];
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                var eventRange = seg.eventRange;
+                var origRange = eventRange.range;
+                var slicedRange = core.intersectRanges(origRange, dayRange);
+                if (slicedRange) {
+                    newSegs.push(__assign({}, seg, { eventRange: {
+                            def: eventRange.def,
+                            ui: __assign({}, eventRange.ui, { durationEditable: false }),
+                            instance: eventRange.instance,
+                            range: slicedRange
+                        }, isStart: seg.isStart && slicedRange.start.valueOf() === origRange.start.valueOf(), isEnd: seg.isEnd && slicedRange.end.valueOf() === origRange.end.valueOf() }));
+                }
+            }
+            return newSegs;
+        };
+        // Generates the text that should be inside a "more" link, given the number of events it represents
+        DayGrid.prototype.getMoreLinkText = function (num) {
+            var opt = this.opt('eventLimitText');
+            if (typeof opt === 'function') {
+                return opt(num);
+            }
+            else {
+                return '+' + num + ' ' + opt;
+            }
+        };
+        // Returns segments within a given cell.
+        // If `startLevel` is specified, returns only events including and below that level. Otherwise returns all segs.
+        DayGrid.prototype.getCellSegs = function (row, col, startLevel) {
+            var segMatrix = this.eventRenderer.rowStructs[row].segMatrix;
+            var level = startLevel || 0;
+            var segs = [];
+            var seg;
+            while (level < segMatrix.length) {
+                seg = segMatrix[level][col];
+                if (seg) {
+                    segs.push(seg);
+                }
+                level++;
+            }
+            return segs;
+        };
+        return DayGrid;
+    }(core.DateComponent));
+
+    var WEEK_NUM_FORMAT$1 = core.createFormatter({ week: 'numeric' });
+    /* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells.
+    ----------------------------------------------------------------------------------------------------------------------*/
+    // It is a manager for a DayGrid subcomponent, which does most of the heavy lifting.
+    // It is responsible for managing width/height.
+    var DayGridView = /** @class */ (function (_super) {
+        __extends(DayGridView, _super);
+        function DayGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            /* Header Rendering
+            ------------------------------------------------------------------------------------------------------------------*/
+            // Generates the HTML that will go before the day-of week header cells
+            _this.renderHeadIntroHtml = function () {
+                var theme = _this.theme;
+                if (_this.colWeekNumbersVisible) {
+                    return '' +
+                        '<th class="fc-week-number ' + theme.getClass('widgetHeader') + '" ' + _this.weekNumberStyleAttr() + '>' +
+                        '<span>' + // needed for matchCellWidths
+                        core.htmlEscape(_this.opt('weekLabel')) +
+                        '</span>' +
+                        '</th>';
+                }
+                return '';
+            };
+            /* Day Grid Rendering
+            ------------------------------------------------------------------------------------------------------------------*/
+            // Generates the HTML that will go before content-skeleton cells that display the day/week numbers
+            _this.renderDayGridNumberIntroHtml = function (row, dayGrid) {
+                var dateEnv = _this.dateEnv;
+                var weekStart = dayGrid.props.cells[row][0].date;
+                if (_this.colWeekNumbersVisible) {
+                    return '' +
+                        '<td class="fc-week-number" ' + _this.weekNumberStyleAttr() + '>' +
+                        core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths
+                        _this, { date: weekStart, type: 'week', forceOff: dayGrid.colCnt === 1 }, dateEnv.format(weekStart, WEEK_NUM_FORMAT$1) // inner HTML
+                        ) +
+                        '</td>';
+                }
+                return '';
+            };
+            // Generates the HTML that goes before the day bg cells for each day-row
+            _this.renderDayGridBgIntroHtml = function () {
+                var theme = _this.theme;
+                if (_this.colWeekNumbersVisible) {
+                    return '<td class="fc-week-number ' + theme.getClass('widgetContent') + '" ' + _this.weekNumberStyleAttr() + '></td>';
+                }
+                return '';
+            };
+            // Generates the HTML that goes before every other type of row generated by DayGrid.
+            // Affects mirror-skeleton and highlight-skeleton rows.
+            _this.renderDayGridIntroHtml = function () {
+                if (_this.colWeekNumbersVisible) {
+                    return '<td class="fc-week-number" ' + _this.weekNumberStyleAttr() + '></td>';
+                }
+                return '';
+            };
+            _this.el.classList.add('fc-dayGrid-view');
+            _this.el.innerHTML = _this.renderSkeletonHtml();
+            _this.scroller = new core.ScrollComponent('hidden', // overflow x
+            'auto' // overflow y
+            );
+            var dayGridContainerEl = _this.scroller.el;
+            _this.el.querySelector('.fc-body > tr > td').appendChild(dayGridContainerEl);
+            dayGridContainerEl.classList.add('fc-day-grid-container');
+            var dayGridEl = core.createElement('div', { className: 'fc-day-grid' });
+            dayGridContainerEl.appendChild(dayGridEl);
+            var cellWeekNumbersVisible;
+            if (_this.opt('weekNumbers')) {
+                if (_this.opt('weekNumbersWithinDays')) {
+                    cellWeekNumbersVisible = true;
+                    _this.colWeekNumbersVisible = false;
+                }
+                else {
+                    cellWeekNumbersVisible = false;
+                    _this.colWeekNumbersVisible = true;
+                }
+            }
+            else {
+                _this.colWeekNumbersVisible = false;
+                cellWeekNumbersVisible = false;
+            }
+            _this.dayGrid = new DayGrid(_this.context, dayGridEl, {
+                renderNumberIntroHtml: _this.renderDayGridNumberIntroHtml,
+                renderBgIntroHtml: _this.renderDayGridBgIntroHtml,
+                renderIntroHtml: _this.renderDayGridIntroHtml,
+                colWeekNumbersVisible: _this.colWeekNumbersVisible,
+                cellWeekNumbersVisible: cellWeekNumbersVisible
+            });
+            return _this;
+        }
+        DayGridView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.dayGrid.destroy();
+            this.scroller.destroy();
+        };
+        // Builds the HTML skeleton for the view.
+        // The day-grid component will render inside of a container defined by this HTML.
+        DayGridView.prototype.renderSkeletonHtml = function () {
+            var theme = this.theme;
+            return '' +
+                '<table class="' + theme.getClass('tableGrid') + '">' +
+                (this.opt('columnHeader') ?
+                    '<thead class="fc-head">' +
+                        '<tr>' +
+                        '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
+                        '</tr>' +
+                        '</thead>' :
+                    '') +
+                '<tbody class="fc-body">' +
+                '<tr>' +
+                '<td class="' + theme.getClass('widgetContent') + '"></td>' +
+                '</tr>' +
+                '</tbody>' +
+                '</table>';
+        };
+        // Generates an HTML attribute string for setting the width of the week number column, if it is known
+        DayGridView.prototype.weekNumberStyleAttr = function () {
+            if (this.weekNumberWidth != null) {
+                return 'style="width:' + this.weekNumberWidth + 'px"';
+            }
+            return '';
+        };
+        // Determines whether each row should have a constant height
+        DayGridView.prototype.hasRigidRows = function () {
+            var eventLimit = this.opt('eventLimit');
+            return eventLimit && typeof eventLimit !== 'number';
+        };
+        /* Dimensions
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+            _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
+            this.dayGrid.updateSize(isResize);
+        };
+        // Refreshes the horizontal dimensions of the view
+        DayGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
+            var dayGrid = this.dayGrid;
+            var eventLimit = this.opt('eventLimit');
+            var headRowEl = this.header ? this.header.el : null; // HACK
+            var scrollerHeight;
+            var scrollbarWidths;
+            // hack to give the view some height prior to dayGrid's columns being rendered
+            // TODO: separate setting height from scroller VS dayGrid.
+            if (!dayGrid.rowEls) {
+                if (!isAuto) {
+                    scrollerHeight = this.computeScrollerHeight(viewHeight);
+                    this.scroller.setHeight(scrollerHeight);
+                }
+                return;
+            }
+            if (this.colWeekNumbersVisible) {
+                // Make sure all week number cells running down the side have the same width.
+                this.weekNumberWidth = core.matchCellWidths(core.findElements(this.el, '.fc-week-number'));
+            }
+            // reset all heights to be natural
+            this.scroller.clear();
+            if (headRowEl) {
+                core.uncompensateScroll(headRowEl);
+            }
+            dayGrid.removeSegPopover(); // kill the "more" popover if displayed
+            // is the event limit a constant level number?
+            if (eventLimit && typeof eventLimit === 'number') {
+                dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
+            }
+            // distribute the height to the rows
+            // (viewHeight is a "recommended" value if isAuto)
+            scrollerHeight = this.computeScrollerHeight(viewHeight);
+            this.setGridHeight(scrollerHeight, isAuto);
+            // is the event limit dynamically calculated?
+            if (eventLimit && typeof eventLimit !== 'number') {
+                dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
+            }
+            if (!isAuto) { // should we force dimensions of the scroll container?
+                this.scroller.setHeight(scrollerHeight);
+                scrollbarWidths = this.scroller.getScrollbarWidths();
+                if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
+                    if (headRowEl) {
+                        core.compensateScroll(headRowEl, scrollbarWidths);
+                    }
+                    // doing the scrollbar compensation might have created text overflow which created more height. redo
+                    scrollerHeight = this.computeScrollerHeight(viewHeight);
+                    this.scroller.setHeight(scrollerHeight);
+                }
+                // guarantees the same scrollbar widths
+                this.scroller.lockOverflow(scrollbarWidths);
+            }
+        };
+        // given a desired total height of the view, returns what the height of the scroller should be
+        DayGridView.prototype.computeScrollerHeight = function (viewHeight) {
+            return viewHeight -
+                core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
+        };
+        // Sets the height of just the DayGrid component in this view
+        DayGridView.prototype.setGridHeight = function (height, isAuto) {
+            if (this.opt('monthMode')) {
+                // if auto, make the height of each row the height that it would be if there were 6 weeks
+                if (isAuto) {
+                    height *= this.dayGrid.rowCnt / 6;
+                }
+                core.distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows
+            }
+            else {
+                if (isAuto) {
+                    core.undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
+                }
+                else {
+                    core.distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
+                }
+            }
+        };
+        /* Scroll
+        ------------------------------------------------------------------------------------------------------------------*/
+        DayGridView.prototype.computeDateScroll = function (duration) {
+            return { top: 0 };
+        };
+        DayGridView.prototype.queryDateScroll = function () {
+            return { top: this.scroller.getScrollTop() };
+        };
+        DayGridView.prototype.applyDateScroll = function (scroll) {
+            if (scroll.top !== undefined) {
+                this.scroller.setScrollTop(scroll.top);
+            }
+        };
+        return DayGridView;
+    }(core.View));
+    DayGridView.prototype.dateProfileGeneratorClass = DayGridDateProfileGenerator;
+
+    var SimpleDayGrid = /** @class */ (function (_super) {
+        __extends(SimpleDayGrid, _super);
+        function SimpleDayGrid(context, dayGrid) {
+            var _this = _super.call(this, context, dayGrid.el) || this;
+            _this.slicer = new DayGridSlicer();
+            _this.dayGrid = dayGrid;
+            context.calendar.registerInteractiveComponent(_this, { el: _this.dayGrid.el });
+            return _this;
+        }
+        SimpleDayGrid.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        SimpleDayGrid.prototype.render = function (props) {
+            var dayGrid = this.dayGrid;
+            var dateProfile = props.dateProfile, dayTable = props.dayTable;
+            dayGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, props.nextDayThreshold, dayGrid, dayTable), { dateProfile: dateProfile, cells: dayTable.cells, isRigid: props.isRigid }));
+        };
+        SimpleDayGrid.prototype.buildPositionCaches = function () {
+            this.dayGrid.buildPositionCaches();
+        };
+        SimpleDayGrid.prototype.queryHit = function (positionLeft, positionTop) {
+            var rawHit = this.dayGrid.positionToHit(positionLeft, positionTop);
+            if (rawHit) {
+                return {
+                    component: this.dayGrid,
+                    dateSpan: rawHit.dateSpan,
+                    dayEl: rawHit.dayEl,
+                    rect: {
+                        left: rawHit.relativeRect.left,
+                        right: rawHit.relativeRect.right,
+                        top: rawHit.relativeRect.top,
+                        bottom: rawHit.relativeRect.bottom
+                    },
+                    layer: 0
+                };
+            }
+        };
+        return SimpleDayGrid;
+    }(core.DateComponent));
+    var DayGridSlicer = /** @class */ (function (_super) {
+        __extends(DayGridSlicer, _super);
+        function DayGridSlicer() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        DayGridSlicer.prototype.sliceRange = function (dateRange, dayTable) {
+            return dayTable.sliceRange(dateRange);
+        };
+        return DayGridSlicer;
+    }(core.Slicer));
+
+    var DayGridView$1 = /** @class */ (function (_super) {
+        __extends(DayGridView, _super);
+        function DayGridView(_context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, _context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.buildDayTable = core.memoize(buildDayTable);
+            if (_this.opt('columnHeader')) {
+                _this.header = new core.DayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+            }
+            _this.simpleDayGrid = new SimpleDayGrid(_this.context, _this.dayGrid);
+            return _this;
+        }
+        DayGridView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            if (this.header) {
+                this.header.destroy();
+            }
+            this.simpleDayGrid.destroy();
+        };
+        DayGridView.prototype.render = function (props) {
+            _super.prototype.render.call(this, props);
+            var dateProfile = this.props.dateProfile;
+            var dayTable = this.dayTable =
+                this.buildDayTable(dateProfile, this.dateProfileGenerator);
+            if (this.header) {
+                this.header.receiveProps({
+                    dateProfile: dateProfile,
+                    dates: dayTable.headerDates,
+                    datesRepDistinctDays: dayTable.rowCnt === 1,
+                    renderIntroHtml: this.renderHeadIntroHtml
+                });
+            }
+            this.simpleDayGrid.receiveProps({
+                dateProfile: dateProfile,
+                dayTable: dayTable,
+                businessHours: props.businessHours,
+                dateSelection: props.dateSelection,
+                eventStore: props.eventStore,
+                eventUiBases: props.eventUiBases,
+                eventSelection: props.eventSelection,
+                eventDrag: props.eventDrag,
+                eventResize: props.eventResize,
+                isRigid: this.hasRigidRows(),
+                nextDayThreshold: this.nextDayThreshold
+            });
+        };
+        return DayGridView;
+    }(DayGridView));
+    function buildDayTable(dateProfile, dateProfileGenerator) {
+        var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator);
+        return new core.DayTable(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
+    }
+
+    var main = core.createPlugin({
+        defaultView: 'dayGridMonth',
+        views: {
+            dayGrid: DayGridView$1,
+            dayGridDay: {
+                type: 'dayGrid',
+                duration: { days: 1 }
+            },
+            dayGridWeek: {
+                type: 'dayGrid',
+                duration: { weeks: 1 }
+            },
+            dayGridMonth: {
+                type: 'dayGrid',
+                duration: { months: 1 },
+                monthMode: true,
+                fixedWeekCount: true
+            }
+        }
+    });
+
+    exports.AbstractDayGridView = DayGridView;
+    exports.DayBgRow = DayBgRow;
+    exports.DayGrid = DayGrid;
+    exports.DayGridSlicer = DayGridSlicer;
+    exports.DayGridView = DayGridView$1;
+    exports.SimpleDayGrid = SimpleDayGrid;
+    exports.buildBasicDayTable = buildDayTable;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/main.min.css b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..55a5724128e7b4a119ad0873bdd48b1406c15a8c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.min.css
@@ -0,0 +1 @@
+.fc-dayGridDay-view .fc-content-skeleton,.fc-dayGridWeek-view .fc-content-skeleton{padding-bottom:1em}.fc-dayGrid-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid{overflow:hidden}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-day-top.fc-other-month{opacity:.3}.fc-dayGrid-view .fc-day-number,.fc-dayGrid-view .fc-week-number{padding:2px}.fc-dayGrid-view th.fc-day-number,.fc-dayGrid-view th.fc-week-number{padding:0 2px}.fc-ltr .fc-dayGrid-view .fc-day-top .fc-day-number{float:right}.fc-rtl .fc-dayGrid-view .fc-day-top .fc-day-number{float:left}.fc-ltr .fc-dayGrid-view .fc-day-top .fc-week-number{float:left;border-radius:0 0 3px}.fc-rtl .fc-dayGrid-view .fc-day-top .fc-week-number{float:right;border-radius:0 0 0 3px}.fc-dayGrid-view .fc-day-top .fc-week-number{min-width:1.5em;text-align:center;background-color:#f2f2f2;color:grey}.fc-dayGrid-view td.fc-week-number{text-align:center}.fc-dayGrid-view td.fc-week-number>*{display:inline-block;min-width:1.25em}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/main.min.js b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..2f4c0d45fea64ac338b7e5d41d542d0fe7afd1ec
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Day Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarDayGrid={},e.FullCalendar)}(this,function(e,t){"use strict";var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function n(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var i=function(){return(i=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},o=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.buildRenderRange=function(r,n,i){var o,s=this.dateEnv,l=e.prototype.buildRenderRange.call(this,r,n,i),a=l.start,d=l.end;if(/^(year|month)$/.test(n)&&(a=s.startOfWeek(a),(o=s.startOfWeek(d)).valueOf()!==d.valueOf()&&(d=t.addWeeks(o,1))),this.options.monthMode&&this.options.fixedWeekCount){var c=Math.ceil(t.diffWeeks(a,d));d=t.addWeeks(d,6-c)}return{start:a,end:d}},r}(t.DateProfileGenerator),s=function(){function e(e){var t=this;this.isHidden=!0,this.margin=10,this.documentMousedown=function(e){t.el&&!t.el.contains(e.target)&&t.hide()},this.options=e}return e.prototype.show=function(){this.isHidden&&(this.el||this.render(),this.el.style.display="",this.position(),this.isHidden=!1,this.trigger("show"))},e.prototype.hide=function(){this.isHidden||(this.el.style.display="none",this.isHidden=!0,this.trigger("hide"))},e.prototype.render=function(){var e=this,r=this.options,n=this.el=t.createElement("div",{className:"fc-popover "+(r.className||""),style:{top:"0",left:"0"}});"function"==typeof r.content&&r.content(n),r.parentEl.appendChild(n),t.listenBySelector(n,"click",".fc-close",function(t){e.hide()}),r.autoHide&&document.addEventListener("mousedown",this.documentMousedown)},e.prototype.destroy=function(){this.hide(),this.el&&(t.removeElement(this.el),this.el=null),document.removeEventListener("mousedown",this.documentMousedown)},e.prototype.position=function(){var e,r,n=this.options,i=this.el,o=i.getBoundingClientRect(),s=t.computeRect(i.offsetParent),l=t.computeClippingRect(n.parentEl);e=n.top||0,r=void 0!==n.left?n.left:void 0!==n.right?n.right-o.width:0,e=Math.min(e,l.bottom-o.height-this.margin),e=Math.max(e,l.top+this.margin),r=Math.min(r,l.right-o.width-this.margin),r=Math.max(r,l.left+this.margin),t.applyStyle(i,{top:e-s.top,left:r-s.left})},e.prototype.trigger=function(e){this.options[e]&&this.options[e].apply(this,Array.prototype.slice.call(arguments,1))},e}(),l=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.renderSegHtml=function(e,r){var n,i,o=this.context,s=o.view,l=o.options,a=e.eventRange,d=a.def,c=a.ui,h=d.allDay,p=s.computeEventDraggable(d,c),u=h&&e.isStart&&s.computeEventStartResizable(d,c),f=h&&e.isEnd&&s.computeEventEndResizable(d,c),g=this.getSegClasses(e,p,u||f,r),m=t.cssToStr(this.getSkinCss(c)),y="";return g.unshift("fc-day-grid-event","fc-h-event"),e.isStart&&(n=this.getTimeText(a))&&(y='<span class="fc-time">'+t.htmlEscape(n)+"</span>"),i='<span class="fc-title">'+(t.htmlEscape(d.title||"")||"&nbsp;")+"</span>",'<a class="'+g.join(" ")+'"'+(d.url?' href="'+t.htmlEscape(d.url)+'"':"")+(m?' style="'+m+'"':"")+'><div class="fc-content">'+("rtl"===l.dir?i+" "+y:y+" "+i)+"</div>"+(u?'<div class="fc-resizer fc-start-resizer"></div>':"")+(f?'<div class="fc-resizer fc-end-resizer"></div>':"")+"</a>"},r.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"narrow"}},r.prototype.computeDisplayEventEnd=function(){return!1},r}(t.FgEventRenderer),a=function(e){function r(t){var r=e.call(this,t.context)||this;return r.dayGrid=t,r}return n(r,e),r.prototype.attachSegs=function(e,t){var r=this.rowStructs=this.renderSegRows(e);this.dayGrid.rowEls.forEach(function(e,t){e.querySelector(".fc-content-skeleton > table").appendChild(r[t].tbodyEl)}),t||this.dayGrid.removeSegPopover()},r.prototype.detachSegs=function(){for(var e,r=this.rowStructs||[];e=r.pop();)t.removeElement(e.tbodyEl);this.rowStructs=null},r.prototype.renderSegRows=function(e){var t,r,n=[];for(t=this.groupSegRows(e),r=0;r<t.length;r++)n.push(this.renderSegRow(r,t[r]));return n},r.prototype.renderSegRow=function(e,r){var n,i,o,s,l,a,d,c=this.dayGrid,h=c.colCnt,p=c.isRtl,u=this.buildSegLevels(r),f=Math.max(1,u.length),g=document.createElement("tbody"),m=[],y=[],v=[];function b(e){for(;o<e;)(d=(v[n-1]||[])[o])?d.rowSpan=(d.rowSpan||1)+1:(d=document.createElement("td"),s.appendChild(d)),y[n][o]=d,v[n][o]=d,o++}for(n=0;n<f;n++){if(i=u[n],o=0,s=document.createElement("tr"),m.push([]),y.push([]),v.push([]),i)for(l=0;l<i.length;l++){a=i[l];var w=p?h-1-a.lastCol:a.firstCol,S=p?h-1-a.firstCol:a.lastCol;for(b(w),d=t.createElement("td",{className:"fc-event-container"},a.el),w!==S?d.colSpan=S-w+1:v[n][o]=d;o<=S;)y[n][o]=d,m[n][o]=a,o++;s.appendChild(d)}b(h);var C=c.renderProps.renderIntroHtml();C&&(c.isRtl?t.appendToElement(s,C):t.prependToElement(s,C)),g.appendChild(s)}return{row:e,tbodyEl:g,cellMatrix:y,segMatrix:m,segLevels:u,segs:r}},r.prototype.buildSegLevels=function(e){var t,r,n,i=this.dayGrid,o=i.isRtl,s=i.colCnt,l=[];for(e=this.sortEventSegs(e),t=0;t<e.length;t++){for(r=e[t],n=0;n<l.length&&d(r,l[n]);n++);r.level=n,r.leftCol=o?s-1-r.lastCol:r.firstCol,r.rightCol=o?s-1-r.firstCol:r.lastCol,(l[n]||(l[n]=[])).push(r)}for(n=0;n<l.length;n++)l[n].sort(c);return l},r.prototype.groupSegRows=function(e){var t,r=[];for(t=0;t<this.dayGrid.rowCnt;t++)r.push([]);for(t=0;t<e.length;t++)r[e[t].row].push(e[t]);return r},r.prototype.computeDisplayEventEnd=function(){return 1===this.dayGrid.colCnt},r}(l);function d(e,t){var r,n;for(r=0;r<t.length;r++)if((n=t[r]).firstCol<=e.lastCol&&n.lastCol>=e.firstCol)return!0;return!1}function c(e,t){return e.leftCol-t.leftCol}var h=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.attachSegs=function(e,r){var n=r.sourceSeg,i=this.rowStructs=this.renderSegRows(e);this.dayGrid.rowEls.forEach(function(e,r){var o,s,l=t.htmlToElement('<div class="fc-mirror-skeleton"><table></table></div>');n&&n.row===r?o=n.el:(o=e.querySelector(".fc-content-skeleton tbody"))||(o=e.querySelector(".fc-content-skeleton table")),s=o.getBoundingClientRect().top-e.getBoundingClientRect().top,l.style.top=s+"px",l.querySelector("table").appendChild(i[r].tbodyEl),e.appendChild(l)})},r}(a),p=function(e){function r(t){var r=e.call(this,t.context)||this;return r.fillSegTag="td",r.dayGrid=t,r}return n(r,e),r.prototype.renderSegs=function(t,r){"bgEvent"===t&&(r=r.filter(function(e){return e.eventRange.def.allDay})),e.prototype.renderSegs.call(this,t,r)},r.prototype.attachSegs=function(e,t){var r,n,i,o=[];for(r=0;r<t.length;r++)n=t[r],i=this.renderFillRow(e,n),this.dayGrid.rowEls[n.row].appendChild(i),o.push(i);return o},r.prototype.renderFillRow=function(e,r){var n,i,o,s=this.dayGrid,l=s.colCnt,a=s.isRtl,d=a?l-1-r.lastCol:r.firstCol,c=(a?l-1-r.firstCol:r.lastCol)+1;n="businessHours"===e?"bgevent":e.toLowerCase(),o=(i=t.htmlToElement('<div class="fc-'+n+'-skeleton"><table><tr></tr></table></div>')).getElementsByTagName("tr")[0],d>0&&t.appendToElement(o,new Array(d+1).join('<td style="pointer-events:none"></td>')),r.el.colSpan=c-d,o.appendChild(r.el),c<l&&t.appendToElement(o,new Array(l-c+1).join('<td style="pointer-events:none"></td>'));var h=s.renderProps.renderIntroHtml();return h&&(s.isRtl?t.appendToElement(o,h):t.prependToElement(o,h)),i},r}(t.FillRenderer),u=function(e){function r(r,n){var i=e.call(this,r,n)||this,o=i.eventRenderer=new f(i),s=i.renderFrame=t.memoizeRendering(i._renderFrame);return i.renderFgEvents=t.memoizeRendering(o.renderSegs.bind(o),o.unrender.bind(o),[s]),i.renderEventSelection=t.memoizeRendering(o.selectByInstanceId.bind(o),o.unselectByInstanceId.bind(o),[i.renderFgEvents]),i.renderEventDrag=t.memoizeRendering(o.hideByHash.bind(o),o.showByHash.bind(o),[s]),i.renderEventResize=t.memoizeRendering(o.hideByHash.bind(o),o.showByHash.bind(o),[s]),r.calendar.registerInteractiveComponent(i,{el:i.el,useEventCenter:!1}),i}return n(r,e),r.prototype.render=function(e){this.renderFrame(e.date),this.renderFgEvents(e.fgSegs),this.renderEventSelection(e.eventSelection),this.renderEventDrag(e.eventDragInstances),this.renderEventResize(e.eventResizeInstances)},r.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderFrame.unrender(),this.calendar.unregisterInteractiveComponent(this)},r.prototype._renderFrame=function(e){var r=this.theme,n=this.dateEnv.format(e,t.createFormatter(this.opt("dayPopoverFormat")));this.el.innerHTML='<div class="fc-header '+r.getClass("popoverHeader")+'"><span class="fc-title">'+t.htmlEscape(n)+'</span><span class="fc-close '+r.getIconClass("close")+'"></span></div><div class="fc-body '+r.getClass("popoverContent")+'"><div class="fc-event-container"></div></div>',this.segContainerEl=this.el.querySelector(".fc-event-container")},r.prototype.queryHit=function(e,r,n,i){var o=this.props.date;if(e<n&&r<i)return{component:this,dateSpan:{allDay:!0,range:{start:o,end:t.addDays(o,1)}},dayEl:this.el,rect:{left:0,top:0,right:n,bottom:i},layer:1}},r}(t.DateComponent),f=function(e){function r(t){var r=e.call(this,t.context)||this;return r.dayTile=t,r}return n(r,e),r.prototype.attachSegs=function(e){for(var t=0,r=e;t<r.length;t++){var n=r[t];this.dayTile.segContainerEl.appendChild(n.el)}},r.prototype.detachSegs=function(e){for(var r=0,n=e;r<n.length;r++){var i=n[r];t.removeElement(i.el)}},r}(l),g=function(){function e(e){this.context=e}return e.prototype.renderHtml=function(e){var t=[];e.renderIntroHtml&&t.push(e.renderIntroHtml());for(var r=0,n=e.cells;r<n.length;r++){var i=n[r];t.push(m(i.date,e.dateProfile,this.context,i.htmlAttrs))}return e.cells.length||t.push('<td class="fc-day '+this.context.theme.getClass("widgetContent")+'"></td>'),"rtl"===this.context.options.dir&&t.reverse(),"<tr>"+t.join("")+"</tr>"},e}();function m(e,r,n,i){var o=n.dateEnv,s=n.theme,l=t.rangeContainsMarker(r.activeRange,e),a=t.getDayClasses(e,r,n);return a.unshift("fc-day",s.getClass("widgetContent")),'<td class="'+a.join(" ")+'"'+(l?' data-date="'+o.formatIso(e,{omitTime:!0})+'"':"")+(i?" "+i:"")+"></td>"}var y=t.createFormatter({day:"numeric"}),v=t.createFormatter({week:"numeric"}),b=function(e){function r(r,n,i){var o=e.call(this,r,n)||this;o.bottomCoordPadding=0,o.isCellSizesDirty=!1;var s=o.eventRenderer=new a(o),l=o.fillRenderer=new p(o);o.mirrorRenderer=new h(o);var d=o.renderCells=t.memoizeRendering(o._renderCells,o._unrenderCells);return o.renderBusinessHours=t.memoizeRendering(l.renderSegs.bind(l,"businessHours"),l.unrender.bind(l,"businessHours"),[d]),o.renderDateSelection=t.memoizeRendering(l.renderSegs.bind(l,"highlight"),l.unrender.bind(l,"highlight"),[d]),o.renderBgEvents=t.memoizeRendering(l.renderSegs.bind(l,"bgEvent"),l.unrender.bind(l,"bgEvent"),[d]),o.renderFgEvents=t.memoizeRendering(s.renderSegs.bind(s),s.unrender.bind(s),[d]),o.renderEventSelection=t.memoizeRendering(s.selectByInstanceId.bind(s),s.unselectByInstanceId.bind(s),[o.renderFgEvents]),o.renderEventDrag=t.memoizeRendering(o._renderEventDrag,o._unrenderEventDrag,[d]),o.renderEventResize=t.memoizeRendering(o._renderEventResize,o._unrenderEventResize,[d]),o.renderProps=i,o}return n(r,e),r.prototype.render=function(e){var t=e.cells;this.rowCnt=t.length,this.colCnt=t[0].length,this.renderCells(t,e.isRigid),this.renderBusinessHours(e.businessHourSegs),this.renderDateSelection(e.dateSelectionSegs),this.renderBgEvents(e.bgEventSegs),this.renderFgEvents(e.fgEventSegs),this.renderEventSelection(e.eventSelection),this.renderEventDrag(e.eventDrag),this.renderEventResize(e.eventResize),this.segPopoverTile&&this.updateSegPopoverTile()},r.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderCells.unrender()},r.prototype.getCellRange=function(e,r){var n=this.props.cells[e][r].date;return{start:n,end:t.addDays(n,1)}},r.prototype.updateSegPopoverTile=function(e,t){var r=this.props;this.segPopoverTile.receiveProps({date:e||this.segPopoverTile.props.date,fgSegs:t||this.segPopoverTile.props.fgSegs,eventSelection:r.eventSelection,eventDragInstances:r.eventDrag?r.eventDrag.affectedInstances:null,eventResizeInstances:r.eventResize?r.eventResize.affectedInstances:null})},r.prototype._renderCells=function(e,r){var n,i,o=this.view,s=this.dateEnv,l=this.rowCnt,a=this.colCnt,d="";for(n=0;n<l;n++)d+=this.renderDayRowHtml(n,r);for(this.el.innerHTML=d,this.rowEls=t.findElements(this.el,".fc-row"),this.cellEls=t.findElements(this.el,".fc-day, .fc-disabled-day"),this.isRtl&&this.cellEls.reverse(),this.rowPositions=new t.PositionCache(this.el,this.rowEls,!1,!0),this.colPositions=new t.PositionCache(this.el,this.cellEls.slice(0,a),!0,!1),n=0;n<l;n++)for(i=0;i<a;i++)this.publiclyTrigger("dayRender",[{date:s.toDate(e[n][i].date),el:this.getCellEl(n,i),view:o}]);this.isCellSizesDirty=!0},r.prototype._unrenderCells=function(){this.removeSegPopover()},r.prototype.renderDayRowHtml=function(e,t){var r=this.theme,n=["fc-row","fc-week",r.getClass("dayRow")];t&&n.push("fc-rigid");var i=new g(this.context);return'<div class="'+n.join(" ")+'"><div class="fc-bg"><table class="'+r.getClass("tableGrid")+'">'+i.renderHtml({cells:this.props.cells[e],dateProfile:this.props.dateProfile,renderIntroHtml:this.renderProps.renderBgIntroHtml})+'</table></div><div class="fc-content-skeleton"><table>'+(this.getIsNumbersVisible()?"<thead>"+this.renderNumberTrHtml(e)+"</thead>":"")+"</table></div></div>"},r.prototype.getIsNumbersVisible=function(){return this.getIsDayNumbersVisible()||this.renderProps.cellWeekNumbersVisible||this.renderProps.colWeekNumbersVisible},r.prototype.getIsDayNumbersVisible=function(){return this.rowCnt>1},r.prototype.renderNumberTrHtml=function(e){var t=this.renderProps.renderNumberIntroHtml(e,this);return"<tr>"+(this.isRtl?"":t)+this.renderNumberCellsHtml(e)+(this.isRtl?t:"")+"</tr>"},r.prototype.renderNumberCellsHtml=function(e){var t,r,n=[];for(t=0;t<this.colCnt;t++)r=this.props.cells[e][t].date,n.push(this.renderNumberCellHtml(r));return this.isRtl&&n.reverse(),n.join("")},r.prototype.renderNumberCellHtml=function(e){var r,n,i=this.view,o=this.dateEnv,s="",l=t.rangeContainsMarker(this.props.dateProfile.activeRange,e),a=this.getIsDayNumbersVisible()&&l;return a||this.renderProps.cellWeekNumbersVisible?((r=t.getDayClasses(e,this.props.dateProfile,this.context)).unshift("fc-day-top"),this.renderProps.cellWeekNumbersVisible&&(n=o.weekDow),s+='<td class="'+r.join(" ")+'"'+(l?' data-date="'+o.formatIso(e,{omitTime:!0})+'"':"")+">",this.renderProps.cellWeekNumbersVisible&&e.getUTCDay()===n&&(s+=t.buildGotoAnchorHtml(i,{date:e,type:"week"},{class:"fc-week-number"},o.format(e,v))),a&&(s+=t.buildGotoAnchorHtml(i,e,{class:"fc-day-number"},o.format(e,y))),s+="</td>"):"<td></td>"},r.prototype.updateSize=function(e){var t=this.fillRenderer,r=this.eventRenderer,n=this.mirrorRenderer;(e||this.isCellSizesDirty||this.view.calendar.isEventsUpdated)&&(this.buildPositionCaches(),this.isCellSizesDirty=!1),t.computeSizes(e),r.computeSizes(e),n.computeSizes(e),t.assignSizes(e),r.assignSizes(e),n.assignSizes(e)},r.prototype.buildPositionCaches=function(){this.buildColPositions(),this.buildRowPositions()},r.prototype.buildColPositions=function(){this.colPositions.build()},r.prototype.buildRowPositions=function(){this.rowPositions.build(),this.rowPositions.bottoms[this.rowCnt-1]+=this.bottomCoordPadding},r.prototype.positionToHit=function(e,t){var r=this.colPositions,n=this.rowPositions,i=r.leftToIndex(e),o=n.topToIndex(t);if(null!=o&&null!=i)return{row:o,col:i,dateSpan:{range:this.getCellRange(o,i),allDay:!0},dayEl:this.getCellEl(o,i),relativeRect:{left:r.lefts[i],right:r.rights[i],top:n.tops[o],bottom:n.bottoms[o]}}},r.prototype.getCellEl=function(e,t){return this.cellEls[e*this.colCnt+t]},r.prototype._renderEventDrag=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),this.fillRenderer.renderSegs("highlight",e.segs))},r.prototype._unrenderEventDrag=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.fillRenderer.unrender("highlight"))},r.prototype._renderEventResize=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),this.fillRenderer.renderSegs("highlight",e.segs),this.mirrorRenderer.renderSegs(e.segs,{isResizing:!0,sourceSeg:e.sourceSeg}))},r.prototype._unrenderEventResize=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.fillRenderer.unrender("highlight"),this.mirrorRenderer.unrender(e.segs,{isResizing:!0,sourceSeg:e.sourceSeg}))},r.prototype.removeSegPopover=function(){this.segPopover&&this.segPopover.hide()},r.prototype.limitRows=function(e){var t,r,n=this.eventRenderer.rowStructs||[];for(t=0;t<n.length;t++)this.unlimitRow(t),!1!==(r=!!e&&("number"==typeof e?e:this.computeRowLevelLimit(t)))&&this.limitRow(t,r)},r.prototype.computeRowLevelLimit=function(e){var r,n,i=this.rowEls[e].getBoundingClientRect().bottom,o=t.findChildren(this.eventRenderer.rowStructs[e].tbodyEl);for(r=0;r<o.length;r++)if((n=o[r]).classList.remove("fc-limited"),n.getBoundingClientRect().bottom>i)return r;return!1},r.prototype.limitRow=function(e,r){var n,i,o,s,l,a,d,c,h,p,u,f,g,m,y,v=this,b=this.colCnt,w=this.isRtl,S=this.eventRenderer.rowStructs[e],C=[],E=0,R=function(n){for(;E<n;)(a=v.getCellSegs(e,E,r)).length&&(h=i[r-1][E],y=v.renderMoreLink(e,E,a),m=t.createElement("div",null,y),h.appendChild(m),C.push(m)),E++};if(r&&r<S.segLevels.length){for(n=S.segLevels[r-1],i=S.cellMatrix,(o=t.findChildren(S.tbodyEl).slice(r)).forEach(function(e){e.classList.add("fc-limited")}),s=0;s<n.length;s++){l=n[s];var H=w?b-1-l.lastCol:l.firstCol,D=w?b-1-l.firstCol:l.lastCol;for(R(H),c=[],d=0;E<=D;)a=this.getCellSegs(e,E,r),c.push(a),d+=a.length,E++;if(d){for(p=(h=i[r-1][H]).rowSpan||1,u=[],f=0;f<c.length;f++)g=t.createElement("td",{className:"fc-more-cell",rowSpan:p}),a=c[f],y=this.renderMoreLink(e,H+f,[l].concat(a)),m=t.createElement("div",null,y),g.appendChild(m),u.push(g),C.push(g);h.classList.add("fc-limited"),t.insertAfterElement(h,u),o.push(h)}}R(this.colCnt),S.moreEls=C,S.limitedEls=o}},r.prototype.unlimitRow=function(e){var r=this.eventRenderer.rowStructs[e];r.moreEls&&(r.moreEls.forEach(t.removeElement),r.moreEls=null),r.limitedEls&&(r.limitedEls.forEach(function(e){e.classList.remove("fc-limited")}),r.limitedEls=null)},r.prototype.renderMoreLink=function(e,r,n){var i=this,o=this.view,s=this.dateEnv,l=t.createElement("a",{className:"fc-more"});return l.innerText=this.getMoreLinkText(n.length),l.addEventListener("click",function(t){var l=i.opt("eventLimitClick"),a=i.isRtl?i.colCnt-r-1:r,d=i.props.cells[e][a].date,c=t.currentTarget,h=i.getCellEl(e,r),p=i.getCellSegs(e,r),u=i.resliceDaySegs(p,d),f=i.resliceDaySegs(n,d);"function"==typeof l&&(l=i.publiclyTrigger("eventLimitClick",[{date:s.toDate(d),allDay:!0,dayEl:h,moreEl:c,segs:u,hiddenSegs:f,jsEvent:t,view:o}])),"popover"===l?i.showSegPopover(e,r,c,u):"string"==typeof l&&o.calendar.zoomTo(d,l)}),l},r.prototype.showSegPopover=function(e,r,n,i){var o,l,a=this,d=this.calendar,c=this.view,h=this.theme,p=this.isRtl?this.colCnt-r-1:r,f=n.parentNode;o=1===this.rowCnt?c.el:this.rowEls[e],l={className:"fc-more-popover "+h.getClass("popover"),parentEl:c.el,top:t.computeRect(o).top,autoHide:!0,content:function(t){a.segPopoverTile=new u(a.context,t),a.updateSegPopoverTile(a.props.cells[e][p].date,i)},hide:function(){a.segPopoverTile.destroy(),a.segPopoverTile=null,a.segPopover.destroy(),a.segPopover=null}},this.isRtl?l.right=t.computeRect(f).right+1:l.left=t.computeRect(f).left-1,this.segPopover=new s(l),this.segPopover.show(),d.releaseAfterSizingTriggers()},r.prototype.resliceDaySegs=function(e,r){for(var n=r,o={start:n,end:t.addDays(n,1)},s=[],l=0,a=e;l<a.length;l++){var d=a[l],c=d.eventRange,h=c.range,p=t.intersectRanges(h,o);p&&s.push(i({},d,{eventRange:{def:c.def,ui:i({},c.ui,{durationEditable:!1}),instance:c.instance,range:p},isStart:d.isStart&&p.start.valueOf()===h.start.valueOf(),isEnd:d.isEnd&&p.end.valueOf()===h.end.valueOf()}))}return s},r.prototype.getMoreLinkText=function(e){var t=this.opt("eventLimitText");return"function"==typeof t?t(e):"+"+e+" "+t},r.prototype.getCellSegs=function(e,t,r){for(var n,i=this.eventRenderer.rowStructs[e].segMatrix,o=r||0,s=[];o<i.length;)(n=i[o][t])&&s.push(n),o++;return s},r}(t.DateComponent),w=t.createFormatter({week:"numeric"}),S=function(e){function r(r,n,i,o){var s=e.call(this,r,n,i,o)||this;s.renderHeadIntroHtml=function(){var e=s.theme;return s.colWeekNumbersVisible?'<th class="fc-week-number '+e.getClass("widgetHeader")+'" '+s.weekNumberStyleAttr()+"><span>"+t.htmlEscape(s.opt("weekLabel"))+"</span></th>":""},s.renderDayGridNumberIntroHtml=function(e,r){var n=s.dateEnv,i=r.props.cells[e][0].date;return s.colWeekNumbersVisible?'<td class="fc-week-number" '+s.weekNumberStyleAttr()+">"+t.buildGotoAnchorHtml(s,{date:i,type:"week",forceOff:1===r.colCnt},n.format(i,w))+"</td>":""},s.renderDayGridBgIntroHtml=function(){var e=s.theme;return s.colWeekNumbersVisible?'<td class="fc-week-number '+e.getClass("widgetContent")+'" '+s.weekNumberStyleAttr()+"></td>":""},s.renderDayGridIntroHtml=function(){return s.colWeekNumbersVisible?'<td class="fc-week-number" '+s.weekNumberStyleAttr()+"></td>":""},s.el.classList.add("fc-dayGrid-view"),s.el.innerHTML=s.renderSkeletonHtml(),s.scroller=new t.ScrollComponent("hidden","auto");var l=s.scroller.el;s.el.querySelector(".fc-body > tr > td").appendChild(l),l.classList.add("fc-day-grid-container");var a,d=t.createElement("div",{className:"fc-day-grid"});return l.appendChild(d),s.opt("weekNumbers")?s.opt("weekNumbersWithinDays")?(a=!0,s.colWeekNumbersVisible=!1):(a=!1,s.colWeekNumbersVisible=!0):(s.colWeekNumbersVisible=!1,a=!1),s.dayGrid=new b(s.context,d,{renderNumberIntroHtml:s.renderDayGridNumberIntroHtml,renderBgIntroHtml:s.renderDayGridBgIntroHtml,renderIntroHtml:s.renderDayGridIntroHtml,colWeekNumbersVisible:s.colWeekNumbersVisible,cellWeekNumbersVisible:a}),s}return n(r,e),r.prototype.destroy=function(){e.prototype.destroy.call(this),this.dayGrid.destroy(),this.scroller.destroy()},r.prototype.renderSkeletonHtml=function(){var e=this.theme;return'<table class="'+e.getClass("tableGrid")+'">'+(this.opt("columnHeader")?'<thead class="fc-head"><tr><td class="fc-head-container '+e.getClass("widgetHeader")+'">&nbsp;</td></tr></thead>':"")+'<tbody class="fc-body"><tr><td class="'+e.getClass("widgetContent")+'"></td></tr></tbody></table>'},r.prototype.weekNumberStyleAttr=function(){return null!=this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},r.prototype.hasRigidRows=function(){var e=this.opt("eventLimit");return e&&"number"!=typeof e},r.prototype.updateSize=function(t,r,n){e.prototype.updateSize.call(this,t,r,n),this.dayGrid.updateSize(t)},r.prototype.updateBaseSize=function(e,r,n){var i,o,s=this.dayGrid,l=this.opt("eventLimit"),a=this.header?this.header.el:null;s.rowEls?(this.colWeekNumbersVisible&&(this.weekNumberWidth=t.matchCellWidths(t.findElements(this.el,".fc-week-number"))),this.scroller.clear(),a&&t.uncompensateScroll(a),s.removeSegPopover(),l&&"number"==typeof l&&s.limitRows(l),i=this.computeScrollerHeight(r),this.setGridHeight(i,n),l&&"number"!=typeof l&&s.limitRows(l),n||(this.scroller.setHeight(i),((o=this.scroller.getScrollbarWidths()).left||o.right)&&(a&&t.compensateScroll(a,o),i=this.computeScrollerHeight(r),this.scroller.setHeight(i)),this.scroller.lockOverflow(o))):n||(i=this.computeScrollerHeight(r),this.scroller.setHeight(i))},r.prototype.computeScrollerHeight=function(e){return e-t.subtractInnerElHeight(this.el,this.scroller.el)},r.prototype.setGridHeight=function(e,r){this.opt("monthMode")?(r&&(e*=this.dayGrid.rowCnt/6),t.distributeHeight(this.dayGrid.rowEls,e,!r)):r?t.undistributeHeight(this.dayGrid.rowEls):t.distributeHeight(this.dayGrid.rowEls,e,!0)},r.prototype.computeDateScroll=function(e){return{top:0}},r.prototype.queryDateScroll=function(){return{top:this.scroller.getScrollTop()}},r.prototype.applyDateScroll=function(e){void 0!==e.top&&this.scroller.setScrollTop(e.top)},r}(t.View);S.prototype.dateProfileGeneratorClass=o;var C=function(e){function t(t,r){var n=e.call(this,t,r.el)||this;return n.slicer=new E,n.dayGrid=r,t.calendar.registerInteractiveComponent(n,{el:n.dayGrid.el}),n}return n(t,e),t.prototype.destroy=function(){e.prototype.destroy.call(this),this.calendar.unregisterInteractiveComponent(this)},t.prototype.render=function(e){var t=this.dayGrid,r=e.dateProfile,n=e.dayTable;t.receiveProps(i({},this.slicer.sliceProps(e,r,e.nextDayThreshold,t,n),{dateProfile:r,cells:n.cells,isRigid:e.isRigid}))},t.prototype.buildPositionCaches=function(){this.dayGrid.buildPositionCaches()},t.prototype.queryHit=function(e,t){var r=this.dayGrid.positionToHit(e,t);if(r)return{component:this.dayGrid,dateSpan:r.dateSpan,dayEl:r.dayEl,rect:{left:r.relativeRect.left,right:r.relativeRect.right,top:r.relativeRect.top,bottom:r.relativeRect.bottom},layer:0}},t}(t.DateComponent),E=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.sliceRange=function(e,t){return t.sliceRange(e)},t}(t.Slicer),R=function(e){function r(r,n,i,o){var s=e.call(this,r,n,i,o)||this;return s.buildDayTable=t.memoize(H),s.opt("columnHeader")&&(s.header=new t.DayHeader(s.context,s.el.querySelector(".fc-head-container"))),s.simpleDayGrid=new C(s.context,s.dayGrid),s}return n(r,e),r.prototype.destroy=function(){e.prototype.destroy.call(this),this.header&&this.header.destroy(),this.simpleDayGrid.destroy()},r.prototype.render=function(t){e.prototype.render.call(this,t);var r=this.props.dateProfile,n=this.dayTable=this.buildDayTable(r,this.dateProfileGenerator);this.header&&this.header.receiveProps({dateProfile:r,dates:n.headerDates,datesRepDistinctDays:1===n.rowCnt,renderIntroHtml:this.renderHeadIntroHtml}),this.simpleDayGrid.receiveProps({dateProfile:r,dayTable:n,businessHours:t.businessHours,dateSelection:t.dateSelection,eventStore:t.eventStore,eventUiBases:t.eventUiBases,eventSelection:t.eventSelection,eventDrag:t.eventDrag,eventResize:t.eventResize,isRigid:this.hasRigidRows(),nextDayThreshold:this.nextDayThreshold})},r}(S);function H(e,r){var n=new t.DaySeries(e.renderRange,r);return new t.DayTable(n,/year|month|week/.test(e.currentRangeUnit))}var D=t.createPlugin({defaultView:"dayGridMonth",views:{dayGrid:R,dayGridDay:{type:"dayGrid",duration:{days:1}},dayGridWeek:{type:"dayGrid",duration:{weeks:1}},dayGridMonth:{type:"dayGrid",duration:{months:1},monthMode:!0,fixedWeekCount:!0}}});e.AbstractDayGridView=S,e.DayBgRow=g,e.DayGrid=b,e.DayGridSlicer=E,e.DayGridView=R,e.SimpleDayGrid=C,e.buildBasicDayTable=H,e.default=D,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/daygrid/package.json b/AKPlan/static/AKPlan/fullcalendar/daygrid/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..c6e16ce1fce594471df59aa7d4a3477c25082344
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/daygrid/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/daygrid",
+  "version": "4.3.0",
+  "title": "FullCalendar Day Grid Plugin",
+  "description": "Display events on Month view or DayGrid view",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/month-view",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/google-calendar/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/README.md b/AKPlan/static/AKPlan/fullcalendar/google-calendar/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a4d6c5cc06dfad2280783ff825697733b73caae6
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Google Calendar Plugin
+
+Fetch events from a public Google Calendar feed
+
+[View the docs &raquo;](https://fullcalendar.io/docs/google-calendar)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..105a6c6d355b22305eb1954888af1d9194bc85d0
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.d.ts
@@ -0,0 +1,20 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   main.d.ts
+
+declare module '@fullcalendar/google-calendar' {
+    module '@fullcalendar/core' {
+        interface OptionsInput {
+            googleCalendarApiKey?: string;
+        }
+    }
+    module '@fullcalendar/core/structs/event-source' {
+        interface ExtendedEventSourceInput {
+            googleCalendarApiKey?: string;
+            googleCalendarId?: string;
+        }
+    }
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..274fc5dfa0923e9a9c25193265cce92f57b728bc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.esm.js
@@ -0,0 +1,162 @@
+/*!
+FullCalendar Google Calendar Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { createPlugin, refineProps, requestJson, addDays } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+// TODO: expose somehow
+var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars';
+var STANDARD_PROPS = {
+    url: String,
+    googleCalendarApiKey: String,
+    googleCalendarId: String,
+    data: null
+};
+var eventSourceDef = {
+    parseMeta: function (raw) {
+        if (typeof raw === 'string') {
+            raw = { url: raw };
+        }
+        if (typeof raw === 'object') {
+            var standardProps = refineProps(raw, STANDARD_PROPS);
+            if (!standardProps.googleCalendarId && standardProps.url) {
+                standardProps.googleCalendarId = parseGoogleCalendarId(standardProps.url);
+            }
+            delete standardProps.url;
+            if (standardProps.googleCalendarId) {
+                return standardProps;
+            }
+        }
+        return null;
+    },
+    fetch: function (arg, onSuccess, onFailure) {
+        var calendar = arg.calendar;
+        var meta = arg.eventSource.meta;
+        var apiKey = meta.googleCalendarApiKey || calendar.opt('googleCalendarApiKey');
+        if (!apiKey) {
+            onFailure({
+                message: 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/'
+            });
+        }
+        else {
+            var url = buildUrl(meta);
+            var requestParams_1 = buildRequestParams(arg.range, apiKey, meta.data, calendar.dateEnv);
+            requestJson('GET', url, requestParams_1, function (body, xhr) {
+                if (body.error) {
+                    onFailure({
+                        message: 'Google Calendar API: ' + body.error.message,
+                        errors: body.error.errors,
+                        xhr: xhr
+                    });
+                }
+                else {
+                    onSuccess({
+                        rawEvents: gcalItemsToRawEventDefs(body.items, requestParams_1.timeZone),
+                        xhr: xhr
+                    });
+                }
+            }, function (message, xhr) {
+                onFailure({ message: message, xhr: xhr });
+            });
+        }
+    }
+};
+function parseGoogleCalendarId(url) {
+    var match;
+    // detect if the ID was specified as a single string.
+    // will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars.
+    if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) {
+        return url;
+    }
+    else if ((match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) ||
+        (match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url))) {
+        return decodeURIComponent(match[1]);
+    }
+}
+function buildUrl(meta) {
+    return API_BASE + '/' + encodeURIComponent(meta.googleCalendarId) + '/events';
+}
+function buildRequestParams(range, apiKey, extraParams, dateEnv) {
+    var params;
+    var startStr;
+    var endStr;
+    if (dateEnv.canComputeOffset) {
+        // strings will naturally have offsets, which GCal needs
+        startStr = dateEnv.formatIso(range.start);
+        endStr = dateEnv.formatIso(range.end);
+    }
+    else {
+        // when timezone isn't known, we don't know what the UTC offset should be, so ask for +/- 1 day
+        // from the UTC day-start to guarantee we're getting all the events
+        // (start/end will be UTC-coerced dates, so toISOString is okay)
+        startStr = addDays(range.start, -1).toISOString();
+        endStr = addDays(range.end, 1).toISOString();
+    }
+    params = __assign({}, (extraParams || {}), { key: apiKey, timeMin: startStr, timeMax: endStr, singleEvents: true, maxResults: 9999 });
+    if (dateEnv.timeZone !== 'local') {
+        params.timeZone = dateEnv.timeZone;
+    }
+    return params;
+}
+function gcalItemsToRawEventDefs(items, gcalTimezone) {
+    return items.map(function (item) {
+        return gcalItemToRawEventDef(item, gcalTimezone);
+    });
+}
+function gcalItemToRawEventDef(item, gcalTimezone) {
+    var url = item.htmlLink || null;
+    // make the URLs for each event show times in the correct timezone
+    if (url && gcalTimezone) {
+        url = injectQsComponent(url, 'ctz=' + gcalTimezone);
+    }
+    return {
+        id: item.id,
+        title: item.summary,
+        start: item.start.dateTime || item.start.date,
+        end: item.end.dateTime || item.end.date,
+        url: url,
+        location: item.location,
+        description: item.description
+    };
+}
+// Injects a string like "arg=value" into the querystring of a URL
+// TODO: move to a general util file?
+function injectQsComponent(url, component) {
+    // inject it after the querystring but before the fragment
+    return url.replace(/(\?.*?)?(#|$)/, function (whole, qs, hash) {
+        return (qs ? qs + '&' : '?') + component + hash;
+    });
+}
+var main = createPlugin({
+    eventSourceDefs: [eventSourceDef]
+});
+
+export default main;
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.js b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..fca85d45e656a3a2a143694c5b0325e6bae5a7d9
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.js
@@ -0,0 +1,170 @@
+/*!
+FullCalendar Google Calendar Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarGoogleCalendar = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    // TODO: expose somehow
+    var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars';
+    var STANDARD_PROPS = {
+        url: String,
+        googleCalendarApiKey: String,
+        googleCalendarId: String,
+        data: null
+    };
+    var eventSourceDef = {
+        parseMeta: function (raw) {
+            if (typeof raw === 'string') {
+                raw = { url: raw };
+            }
+            if (typeof raw === 'object') {
+                var standardProps = core.refineProps(raw, STANDARD_PROPS);
+                if (!standardProps.googleCalendarId && standardProps.url) {
+                    standardProps.googleCalendarId = parseGoogleCalendarId(standardProps.url);
+                }
+                delete standardProps.url;
+                if (standardProps.googleCalendarId) {
+                    return standardProps;
+                }
+            }
+            return null;
+        },
+        fetch: function (arg, onSuccess, onFailure) {
+            var calendar = arg.calendar;
+            var meta = arg.eventSource.meta;
+            var apiKey = meta.googleCalendarApiKey || calendar.opt('googleCalendarApiKey');
+            if (!apiKey) {
+                onFailure({
+                    message: 'Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/'
+                });
+            }
+            else {
+                var url = buildUrl(meta);
+                var requestParams_1 = buildRequestParams(arg.range, apiKey, meta.data, calendar.dateEnv);
+                core.requestJson('GET', url, requestParams_1, function (body, xhr) {
+                    if (body.error) {
+                        onFailure({
+                            message: 'Google Calendar API: ' + body.error.message,
+                            errors: body.error.errors,
+                            xhr: xhr
+                        });
+                    }
+                    else {
+                        onSuccess({
+                            rawEvents: gcalItemsToRawEventDefs(body.items, requestParams_1.timeZone),
+                            xhr: xhr
+                        });
+                    }
+                }, function (message, xhr) {
+                    onFailure({ message: message, xhr: xhr });
+                });
+            }
+        }
+    };
+    function parseGoogleCalendarId(url) {
+        var match;
+        // detect if the ID was specified as a single string.
+        // will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars.
+        if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) {
+            return url;
+        }
+        else if ((match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) ||
+            (match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url))) {
+            return decodeURIComponent(match[1]);
+        }
+    }
+    function buildUrl(meta) {
+        return API_BASE + '/' + encodeURIComponent(meta.googleCalendarId) + '/events';
+    }
+    function buildRequestParams(range, apiKey, extraParams, dateEnv) {
+        var params;
+        var startStr;
+        var endStr;
+        if (dateEnv.canComputeOffset) {
+            // strings will naturally have offsets, which GCal needs
+            startStr = dateEnv.formatIso(range.start);
+            endStr = dateEnv.formatIso(range.end);
+        }
+        else {
+            // when timezone isn't known, we don't know what the UTC offset should be, so ask for +/- 1 day
+            // from the UTC day-start to guarantee we're getting all the events
+            // (start/end will be UTC-coerced dates, so toISOString is okay)
+            startStr = core.addDays(range.start, -1).toISOString();
+            endStr = core.addDays(range.end, 1).toISOString();
+        }
+        params = __assign({}, (extraParams || {}), { key: apiKey, timeMin: startStr, timeMax: endStr, singleEvents: true, maxResults: 9999 });
+        if (dateEnv.timeZone !== 'local') {
+            params.timeZone = dateEnv.timeZone;
+        }
+        return params;
+    }
+    function gcalItemsToRawEventDefs(items, gcalTimezone) {
+        return items.map(function (item) {
+            return gcalItemToRawEventDef(item, gcalTimezone);
+        });
+    }
+    function gcalItemToRawEventDef(item, gcalTimezone) {
+        var url = item.htmlLink || null;
+        // make the URLs for each event show times in the correct timezone
+        if (url && gcalTimezone) {
+            url = injectQsComponent(url, 'ctz=' + gcalTimezone);
+        }
+        return {
+            id: item.id,
+            title: item.summary,
+            start: item.start.dateTime || item.start.date,
+            end: item.end.dateTime || item.end.date,
+            url: url,
+            location: item.location,
+            description: item.description
+        };
+    }
+    // Injects a string like "arg=value" into the querystring of a URL
+    // TODO: move to a general util file?
+    function injectQsComponent(url, component) {
+        // inject it after the querystring but before the fragment
+        return url.replace(/(\?.*?)?(#|$)/, function (whole, qs, hash) {
+            return (qs ? qs + '&' : '?') + component + hash;
+        });
+    }
+    var main = core.createPlugin({
+        eventSourceDefs: [eventSourceDef]
+    });
+
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.min.js b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..e7626129e483209896e12bd208072405a96279d8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Google Calendar Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],r):r((e=e||self).FullCalendarGoogleCalendar={},e.FullCalendar)}(this,function(e,r){"use strict";var t=function(){return(t=Object.assign||function(e){for(var r,t=1,n=arguments.length;t<n;t++)for(var o in r=arguments[t])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e}).apply(this,arguments)},n="https://www.googleapis.com/calendar/v3/calendars",o={url:String,googleCalendarApiKey:String,googleCalendarId:String,data:null},a={parseMeta:function(e){if("string"==typeof e&&(e={url:e}),"object"==typeof e){var t=r.refineProps(e,o);if(!t.googleCalendarId&&t.url&&(t.googleCalendarId=function(e){var r;if(/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(e))return e;if((r=/^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(e))||(r=/^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(e)))return decodeURIComponent(r[1])}(t.url)),delete t.url,t.googleCalendarId)return t}return null},fetch:function(e,o,a){var l=e.calendar,i=e.eventSource.meta,d=i.googleCalendarApiKey||l.opt("googleCalendarApiKey");if(d){var s=function(e){return n+"/"+encodeURIComponent(e.googleCalendarId)+"/events"}(i),c=function(e,n,o,a){var l,i,d;a.canComputeOffset?(i=a.formatIso(e.start),d=a.formatIso(e.end)):(i=r.addDays(e.start,-1).toISOString(),d=r.addDays(e.end,1).toISOString());l=t({},o||{},{key:n,timeMin:i,timeMax:d,singleEvents:!0,maxResults:9999}),"local"!==a.timeZone&&(l.timeZone=a.timeZone);return l}(e.range,d,i.data,l.dateEnv);r.requestJson("GET",s,c,function(e,r){var t,n;e.error?a({message:"Google Calendar API: "+e.error.message,errors:e.error.errors,xhr:r}):o({rawEvents:(t=e.items,n=c.timeZone,t.map(function(e){return function(e,r){var t=e.htmlLink||null;t&&r&&(t=function(e,r){return e.replace(/(\?.*?)?(#|$)/,function(e,t,n){return(t?t+"&":"?")+r+n})}(t,"ctz="+r));return{id:e.id,title:e.summary,start:e.start.dateTime||e.start.date,end:e.end.dateTime||e.end.date,url:t,location:e.location,description:e.description}}(e,n)})),xhr:r})},function(e,r){a({message:e,xhr:r})})}else a({message:"Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/"})}};var l=r.createPlugin({eventSourceDefs:[a]});e.default=l,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/google-calendar/package.json b/AKPlan/static/AKPlan/fullcalendar/google-calendar/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..58b1dc6f3244029ae18376acd027dcbaa3e3dc36
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/google-calendar/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/google-calendar",
+  "version": "4.3.0",
+  "title": "FullCalendar Google Calendar Plugin",
+  "description": "Fetch events from a public Google Calendar feed",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/google-calendar",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/interaction/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/README.md b/AKPlan/static/AKPlan/fullcalendar/interaction/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ee5c738abaaa64a9bd5cc58e67810cee00ecf5be
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Interaction Plugin
+
+Provides functionality for event drag-n-drop, resizing, dateClick, and selectable actions
+
+[View the docs &raquo;](https://fullcalendar.io/docs/editable)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/interaction/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7398ba769aaa38a5e04504d3121abec90ba81e9
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/main.d.ts
@@ -0,0 +1,323 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/interaction' {
+    import FeaturefulElementDragging from '@fullcalendar/interaction/dnd/FeaturefulElementDragging';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+    export { FeaturefulElementDragging };
+    export { default as PointerDragging } from '@fullcalendar/interaction/dnd/PointerDragging';
+    export { default as Draggable } from '@fullcalendar/interaction/interactions-external/ExternalDraggable';
+    export { default as ThirdPartyDraggable } from '@fullcalendar/interaction/interactions-external/ThirdPartyDraggable';
+}
+
+declare module '@fullcalendar/interaction/dnd/FeaturefulElementDragging' {
+    import { PointerDragEvent, ElementDragging } from '@fullcalendar/core';
+    import PointerDragging from '@fullcalendar/interaction/dnd/PointerDragging';
+    import ElementMirror from '@fullcalendar/interaction/dnd/ElementMirror';
+    import AutoScroller from '@fullcalendar/interaction/dnd/AutoScroller';
+    export { FeaturefulElementDragging as default, FeaturefulElementDragging };
+    class FeaturefulElementDragging extends ElementDragging {
+        pointer: PointerDragging;
+        mirror: ElementMirror;
+        autoScroller: AutoScroller;
+        delay: number | null;
+        minDistance: number;
+        touchScrollAllowed: boolean;
+        mirrorNeedsRevert: boolean;
+        isInteracting: boolean;
+        isDragging: boolean;
+        isDelayEnded: boolean;
+        isDistanceSurpassed: boolean;
+        delayTimeoutId: number | null;
+        constructor(containerEl: HTMLElement);
+        destroy(): void;
+        onPointerDown: (ev: PointerDragEvent) => void;
+        onPointerMove: (ev: PointerDragEvent) => void;
+        onPointerUp: (ev: PointerDragEvent) => void;
+        startDelay(ev: PointerDragEvent): void;
+        handleDelayEnd(ev: PointerDragEvent): void;
+        handleDistanceSurpassed(ev: PointerDragEvent): void;
+        tryStartDrag(ev: PointerDragEvent): void;
+        tryStopDrag(ev: PointerDragEvent): void;
+        stopDrag(ev: PointerDragEvent): void;
+        setIgnoreMove(bool: boolean): void;
+        setMirrorIsVisible(bool: boolean): void;
+        setMirrorNeedsRevert(bool: boolean): void;
+        setAutoScrollEnabled(bool: boolean): void;
+    }
+}
+
+declare module '@fullcalendar/interaction/dnd/PointerDragging' {
+    import { EmitterMixin, PointerDragEvent } from '@fullcalendar/core';
+    export { PointerDragging as default, PointerDragging };
+    class PointerDragging {
+        containerEl: EventTarget;
+        subjectEl: HTMLElement | null;
+        downEl: HTMLElement | null;
+        emitter: EmitterMixin;
+        selector: string;
+        handleSelector: string;
+        shouldIgnoreMove: boolean;
+        shouldWatchScroll: boolean;
+        isDragging: boolean;
+        isTouchDragging: boolean;
+        wasTouchScroll: boolean;
+        origPageX: number;
+        origPageY: number;
+        prevPageX: number;
+        prevPageY: number;
+        prevScrollX: number;
+        prevScrollY: number;
+        constructor(containerEl: EventTarget);
+        destroy(): void;
+        tryStart(ev: UIEvent): boolean;
+        cleanup(): void;
+        querySubjectEl(ev: UIEvent): HTMLElement;
+        handleMouseDown: (ev: MouseEvent) => void;
+        handleMouseMove: (ev: MouseEvent) => void;
+        handleMouseUp: (ev: MouseEvent) => void;
+        shouldIgnoreMouse(): number | boolean;
+        handleTouchStart: (ev: TouchEvent) => void;
+        handleTouchMove: (ev: TouchEvent) => void;
+        handleTouchEnd: (ev: TouchEvent) => void;
+        handleTouchScroll: () => void;
+        cancelTouchScroll(): void;
+        initScrollWatch(ev: PointerDragEvent): void;
+        recordCoords(ev: PointerDragEvent): void;
+        handleScroll: (ev: UIEvent) => void;
+        destroyScrollWatch(): void;
+        createEventFromMouse(ev: MouseEvent, isFirst?: boolean): PointerDragEvent;
+        createEventFromTouch(ev: TouchEvent, isFirst?: boolean): PointerDragEvent;
+    }
+}
+
+declare module '@fullcalendar/interaction/interactions-external/ExternalDraggable' {
+    import { PointerDragEvent } from '@fullcalendar/core';
+    import FeaturefulElementDragging from '@fullcalendar/interaction/dnd/FeaturefulElementDragging';
+    import { DragMetaGenerator } from '@fullcalendar/interaction/interactions-external/ExternalElementDragging';
+    export interface ExternalDraggableSettings {
+        eventData?: DragMetaGenerator;
+        itemSelector?: string;
+        minDistance?: number;
+        longPressDelay?: number;
+        appendTo?: HTMLElement;
+    }
+    export { ExternalDraggable as default, ExternalDraggable };
+    class ExternalDraggable {
+        dragging: FeaturefulElementDragging;
+        settings: ExternalDraggableSettings;
+        constructor(el: HTMLElement, settings?: ExternalDraggableSettings);
+        handlePointerDown: (ev: PointerDragEvent) => void;
+        handleDragStart: (ev: PointerDragEvent) => void;
+        destroy(): void;
+    }
+}
+
+declare module '@fullcalendar/interaction/interactions-external/ThirdPartyDraggable' {
+    import { DragMetaGenerator } from '@fullcalendar/interaction/interactions-external/ExternalElementDragging';
+    import InferredElementDragging from '@fullcalendar/interaction/interactions-external/InferredElementDragging';
+    export interface ThirdPartyDraggableSettings {
+        eventData?: DragMetaGenerator;
+        itemSelector?: string;
+        mirrorSelector?: string;
+    }
+    export { ThirdPartyDraggable as default, ThirdPartyDraggable };
+    class ThirdPartyDraggable {
+        dragging: InferredElementDragging;
+        constructor(containerOrSettings?: EventTarget | ThirdPartyDraggableSettings, settings?: ThirdPartyDraggableSettings);
+        destroy(): void;
+    }
+}
+
+declare module '@fullcalendar/interaction/dnd/ElementMirror' {
+    import { Rect } from '@fullcalendar/core';
+    export { ElementMirror as default, ElementMirror };
+    class ElementMirror {
+        isVisible: boolean;
+        origScreenX?: number;
+        origScreenY?: number;
+        deltaX?: number;
+        deltaY?: number;
+        sourceEl: HTMLElement | null;
+        mirrorEl: HTMLElement | null;
+        sourceElRect: Rect | null;
+        parentNode: HTMLElement;
+        zIndex: number;
+        revertDuration: number;
+        start(sourceEl: HTMLElement, pageX: number, pageY: number): void;
+        handleMove(pageX: number, pageY: number): void;
+        setIsVisible(bool: boolean): void;
+        stop(needsRevertAnimation: boolean, callback: () => void): void;
+        doRevertAnimation(callback: () => void, revertDuration: number): void;
+        cleanup(): void;
+        updateElPosition(): void;
+        getMirrorEl(): HTMLElement;
+    }
+}
+
+declare module '@fullcalendar/interaction/dnd/AutoScroller' {
+    import { ScrollGeomCache } from '@fullcalendar/interaction/scroll-geom-cache';
+    export { AutoScroller as default, AutoScroller };
+    class AutoScroller {
+        isEnabled: boolean;
+        scrollQuery: (Window | string)[];
+        edgeThreshold: number;
+        maxVelocity: number;
+        pointerScreenX: number | null;
+        pointerScreenY: number | null;
+        isAnimating: boolean;
+        scrollCaches: ScrollGeomCache[] | null;
+        msSinceRequest?: number;
+        everMovedUp: boolean;
+        everMovedDown: boolean;
+        everMovedLeft: boolean;
+        everMovedRight: boolean;
+        start(pageX: number, pageY: number): void;
+        handleMove(pageX: number, pageY: number): void;
+        stop(): void;
+        requestAnimation(now: number): void;
+    }
+}
+
+declare module '@fullcalendar/interaction/interactions-external/ExternalElementDragging' {
+    import { Hit, PointerDragEvent, EventTuple, DatePointApi, Calendar, EventInteractionState, DragMetaInput, DragMeta, View, ElementDragging } from '@fullcalendar/core';
+    import HitDragging from '@fullcalendar/interaction/interactions/HitDragging';
+    export type DragMetaGenerator = DragMetaInput | ((el: HTMLElement) => DragMetaInput);
+    export interface ExternalDropApi extends DatePointApi {
+        draggedEl: HTMLElement;
+        jsEvent: UIEvent;
+        view: View;
+    }
+    export { ExternalElementDragging as default, ExternalElementDragging };
+    class ExternalElementDragging {
+        hitDragging: HitDragging;
+        receivingCalendar: Calendar | null;
+        droppableEvent: EventTuple | null;
+        suppliedDragMeta: DragMetaGenerator | null;
+        dragMeta: DragMeta | null;
+        constructor(dragging: ElementDragging, suppliedDragMeta?: DragMetaGenerator);
+        handleDragStart: (ev: PointerDragEvent) => void;
+        buildDragMeta(subjectEl: HTMLElement): DragMeta;
+        handleHitUpdate: (hit: Hit, isFinal: boolean, ev: PointerDragEvent) => void;
+        handleDragEnd: (pev: PointerDragEvent) => void;
+        displayDrag(nextCalendar: Calendar | null, state: EventInteractionState): void;
+        clearDrag(): void;
+        canDropElOnCalendar(el: HTMLElement, receivingCalendar: Calendar): boolean;
+    }
+}
+
+declare module '@fullcalendar/interaction/interactions-external/InferredElementDragging' {
+    import { PointerDragEvent, ElementDragging } from '@fullcalendar/core';
+    import PointerDragging from '@fullcalendar/interaction/dnd/PointerDragging';
+    export { InferredElementDragging as default, InferredElementDragging };
+    class InferredElementDragging extends ElementDragging {
+        pointer: PointerDragging;
+        shouldIgnoreMove: boolean;
+        mirrorSelector: string;
+        currentMirrorEl: HTMLElement | null;
+        constructor(containerEl: HTMLElement);
+        destroy(): void;
+        handlePointerDown: (ev: PointerDragEvent) => void;
+        handlePointerMove: (ev: PointerDragEvent) => void;
+        handlePointerUp: (ev: PointerDragEvent) => void;
+        setIgnoreMove(bool: boolean): void;
+        setMirrorIsVisible(bool: boolean): void;
+    }
+}
+
+declare module '@fullcalendar/interaction/scroll-geom-cache' {
+    import { Rect, ScrollController } from '@fullcalendar/core';
+    export abstract class ScrollGeomCache extends ScrollController {
+        clientRect: Rect;
+        origScrollTop: number;
+        origScrollLeft: number;
+        protected scrollController: ScrollController;
+        protected doesListening: boolean;
+        protected scrollTop: number;
+        protected scrollLeft: number;
+        protected scrollWidth: number;
+        protected scrollHeight: number;
+        protected clientWidth: number;
+        protected clientHeight: number;
+        constructor(scrollController: ScrollController, doesListening: boolean);
+        abstract getEventTarget(): EventTarget;
+        abstract computeClientRect(): Rect;
+        destroy(): void;
+        handleScroll: () => void;
+        getScrollTop(): number;
+        getScrollLeft(): number;
+        setScrollTop(top: number): void;
+        setScrollLeft(top: number): void;
+        getClientWidth(): number;
+        getClientHeight(): number;
+        getScrollWidth(): number;
+        getScrollHeight(): number;
+        handleScrollChange(): void;
+    }
+    export class ElementScrollGeomCache extends ScrollGeomCache {
+        constructor(el: HTMLElement, doesListening: boolean);
+        getEventTarget(): EventTarget;
+        computeClientRect(): {
+            left: number;
+            right: number;
+            top: number;
+            bottom: number;
+        };
+    }
+    export class WindowScrollGeomCache extends ScrollGeomCache {
+        constructor(doesListening: boolean);
+        getEventTarget(): EventTarget;
+        computeClientRect(): Rect;
+        handleScrollChange(): void;
+    }
+}
+
+declare module '@fullcalendar/interaction/interactions/HitDragging' {
+    import { EmitterMixin, PointerDragEvent, Point, Hit, InteractionSettingsStore, ElementDragging } from '@fullcalendar/core';
+    import OffsetTracker from '@fullcalendar/interaction/OffsetTracker';
+    export { HitDragging as default, HitDragging };
+    class HitDragging {
+        droppableStore: InteractionSettingsStore;
+        dragging: ElementDragging;
+        emitter: EmitterMixin;
+        useSubjectCenter: boolean;
+        requireInitial: boolean;
+        offsetTrackers: {
+            [componentUid: string]: OffsetTracker;
+        };
+        initialHit: Hit | null;
+        movingHit: Hit | null;
+        finalHit: Hit | null;
+        coordAdjust?: Point;
+        constructor(dragging: ElementDragging, droppableStore: InteractionSettingsStore);
+        handlePointerDown: (ev: PointerDragEvent) => void;
+        processFirstCoord(ev: PointerDragEvent): void;
+        handleDragStart: (ev: PointerDragEvent) => void;
+        handleDragMove: (ev: PointerDragEvent) => void;
+        handlePointerUp: (ev: PointerDragEvent) => void;
+        handleDragEnd: (ev: PointerDragEvent) => void;
+        handleMove(ev: PointerDragEvent, forceHandle?: boolean): void;
+        prepareHits(): void;
+        releaseHits(): void;
+        queryHitForOffset(offsetLeft: number, offsetTop: number): Hit | null;
+    }
+    export function isHitsEqual(hit0: Hit | null, hit1: Hit | null): boolean;
+}
+
+declare module '@fullcalendar/interaction/OffsetTracker' {
+    import { Rect } from '@fullcalendar/core';
+    import { ElementScrollGeomCache } from '@fullcalendar/interaction/scroll-geom-cache';
+    export { OffsetTracker as default, OffsetTracker };
+    class OffsetTracker {
+        scrollCaches: ElementScrollGeomCache[];
+        origRect: Rect;
+        constructor(el: HTMLElement);
+        destroy(): void;
+        computeLeft(): number;
+        computeTop(): number;
+        isWithinClipping(pageX: number, pageY: number): boolean;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/interaction/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..13f834e7c5bfa91170f9e35ade54e51d2e3554ed
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/main.esm.js
@@ -0,0 +1,2132 @@
+/*!
+FullCalendar Interaction Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { config, elementClosest, EmitterMixin, applyStyle, whenTransitionDone, removeElement, ScrollController, ElementScrollController, computeInnerRect, WindowScrollController, preventSelection, preventContextMenu, allowSelection, allowContextMenu, ElementDragging, computeRect, getClippingParents, pointInsideRect, isDateSpansEqual, constrainPoint, intersectRects, getRectCenter, diffPoints, mapHash, rangeContainsRange, interactionSettingsToStore, Interaction, enableCursor, disableCursor, compareNumbers, getElSeg, getRelevantEvents, EventApi, createEmptyEventStore, applyMutationToEventStore, interactionSettingsStore, startOfDay, diffDates, createDuration, eventTupleToStore, isInteractionValid, parseDragMeta, elementMatches, parseEventDef, createEventInstance, globalDefaults, createPlugin } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+config.touchMouseIgnoreWait = 500;
+var ignoreMouseDepth = 0;
+var listenerCnt = 0;
+var isWindowTouchMoveCancelled = false;
+/*
+Uses a "pointer" abstraction, which monitors UI events for both mouse and touch.
+Tracks when the pointer "drags" on a certain element, meaning down+move+up.
+
+Also, tracks if there was touch-scrolling.
+Also, can prevent touch-scrolling from happening.
+Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement.
+
+emits:
+- pointerdown
+- pointermove
+- pointerup
+*/
+var PointerDragging = /** @class */ (function () {
+    function PointerDragging(containerEl) {
+        var _this = this;
+        this.subjectEl = null;
+        this.downEl = null;
+        // options that can be directly assigned by caller
+        this.selector = ''; // will cause subjectEl in all emitted events to be this element
+        this.handleSelector = '';
+        this.shouldIgnoreMove = false;
+        this.shouldWatchScroll = true; // for simulating pointermove on scroll
+        // internal states
+        this.isDragging = false;
+        this.isTouchDragging = false;
+        this.wasTouchScroll = false;
+        // Mouse
+        // ----------------------------------------------------------------------------------------------------
+        this.handleMouseDown = function (ev) {
+            if (!_this.shouldIgnoreMouse() &&
+                isPrimaryMouseButton(ev) &&
+                _this.tryStart(ev)) {
+                var pev = _this.createEventFromMouse(ev, true);
+                _this.emitter.trigger('pointerdown', pev);
+                _this.initScrollWatch(pev);
+                if (!_this.shouldIgnoreMove) {
+                    document.addEventListener('mousemove', _this.handleMouseMove);
+                }
+                document.addEventListener('mouseup', _this.handleMouseUp);
+            }
+        };
+        this.handleMouseMove = function (ev) {
+            var pev = _this.createEventFromMouse(ev);
+            _this.recordCoords(pev);
+            _this.emitter.trigger('pointermove', pev);
+        };
+        this.handleMouseUp = function (ev) {
+            document.removeEventListener('mousemove', _this.handleMouseMove);
+            document.removeEventListener('mouseup', _this.handleMouseUp);
+            _this.emitter.trigger('pointerup', _this.createEventFromMouse(ev));
+            _this.cleanup(); // call last so that pointerup has access to props
+        };
+        // Touch
+        // ----------------------------------------------------------------------------------------------------
+        this.handleTouchStart = function (ev) {
+            if (_this.tryStart(ev)) {
+                _this.isTouchDragging = true;
+                var pev = _this.createEventFromTouch(ev, true);
+                _this.emitter.trigger('pointerdown', pev);
+                _this.initScrollWatch(pev);
+                // unlike mouse, need to attach to target, not document
+                // https://stackoverflow.com/a/45760014
+                var target = ev.target;
+                if (!_this.shouldIgnoreMove) {
+                    target.addEventListener('touchmove', _this.handleTouchMove);
+                }
+                target.addEventListener('touchend', _this.handleTouchEnd);
+                target.addEventListener('touchcancel', _this.handleTouchEnd); // treat it as a touch end
+                // attach a handler to get called when ANY scroll action happens on the page.
+                // this was impossible to do with normal on/off because 'scroll' doesn't bubble.
+                // http://stackoverflow.com/a/32954565/96342
+                window.addEventListener('scroll', _this.handleTouchScroll, true // useCapture
+                );
+            }
+        };
+        this.handleTouchMove = function (ev) {
+            var pev = _this.createEventFromTouch(ev);
+            _this.recordCoords(pev);
+            _this.emitter.trigger('pointermove', pev);
+        };
+        this.handleTouchEnd = function (ev) {
+            if (_this.isDragging) { // done to guard against touchend followed by touchcancel
+                var target = ev.target;
+                target.removeEventListener('touchmove', _this.handleTouchMove);
+                target.removeEventListener('touchend', _this.handleTouchEnd);
+                target.removeEventListener('touchcancel', _this.handleTouchEnd);
+                window.removeEventListener('scroll', _this.handleTouchScroll, true); // useCaptured=true
+                _this.emitter.trigger('pointerup', _this.createEventFromTouch(ev));
+                _this.cleanup(); // call last so that pointerup has access to props
+                _this.isTouchDragging = false;
+                startIgnoringMouse();
+            }
+        };
+        this.handleTouchScroll = function () {
+            _this.wasTouchScroll = true;
+        };
+        this.handleScroll = function (ev) {
+            if (!_this.shouldIgnoreMove) {
+                var pageX = (window.pageXOffset - _this.prevScrollX) + _this.prevPageX;
+                var pageY = (window.pageYOffset - _this.prevScrollY) + _this.prevPageY;
+                _this.emitter.trigger('pointermove', {
+                    origEvent: ev,
+                    isTouch: _this.isTouchDragging,
+                    subjectEl: _this.subjectEl,
+                    pageX: pageX,
+                    pageY: pageY,
+                    deltaX: pageX - _this.origPageX,
+                    deltaY: pageY - _this.origPageY
+                });
+            }
+        };
+        this.containerEl = containerEl;
+        this.emitter = new EmitterMixin();
+        containerEl.addEventListener('mousedown', this.handleMouseDown);
+        containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true });
+        listenerCreated();
+    }
+    PointerDragging.prototype.destroy = function () {
+        this.containerEl.removeEventListener('mousedown', this.handleMouseDown);
+        this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
+        listenerDestroyed();
+    };
+    PointerDragging.prototype.tryStart = function (ev) {
+        var subjectEl = this.querySubjectEl(ev);
+        var downEl = ev.target;
+        if (subjectEl &&
+            (!this.handleSelector || elementClosest(downEl, this.handleSelector))) {
+            this.subjectEl = subjectEl;
+            this.downEl = downEl;
+            this.isDragging = true; // do this first so cancelTouchScroll will work
+            this.wasTouchScroll = false;
+            return true;
+        }
+        return false;
+    };
+    PointerDragging.prototype.cleanup = function () {
+        isWindowTouchMoveCancelled = false;
+        this.isDragging = false;
+        this.subjectEl = null;
+        this.downEl = null;
+        // keep wasTouchScroll around for later access
+        this.destroyScrollWatch();
+    };
+    PointerDragging.prototype.querySubjectEl = function (ev) {
+        if (this.selector) {
+            return elementClosest(ev.target, this.selector);
+        }
+        else {
+            return this.containerEl;
+        }
+    };
+    PointerDragging.prototype.shouldIgnoreMouse = function () {
+        return ignoreMouseDepth || this.isTouchDragging;
+    };
+    // can be called by user of this class, to cancel touch-based scrolling for the current drag
+    PointerDragging.prototype.cancelTouchScroll = function () {
+        if (this.isDragging) {
+            isWindowTouchMoveCancelled = true;
+        }
+    };
+    // Scrolling that simulates pointermoves
+    // ----------------------------------------------------------------------------------------------------
+    PointerDragging.prototype.initScrollWatch = function (ev) {
+        if (this.shouldWatchScroll) {
+            this.recordCoords(ev);
+            window.addEventListener('scroll', this.handleScroll, true); // useCapture=true
+        }
+    };
+    PointerDragging.prototype.recordCoords = function (ev) {
+        if (this.shouldWatchScroll) {
+            this.prevPageX = ev.pageX;
+            this.prevPageY = ev.pageY;
+            this.prevScrollX = window.pageXOffset;
+            this.prevScrollY = window.pageYOffset;
+        }
+    };
+    PointerDragging.prototype.destroyScrollWatch = function () {
+        if (this.shouldWatchScroll) {
+            window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true
+        }
+    };
+    // Event Normalization
+    // ----------------------------------------------------------------------------------------------------
+    PointerDragging.prototype.createEventFromMouse = function (ev, isFirst) {
+        var deltaX = 0;
+        var deltaY = 0;
+        // TODO: repeat code
+        if (isFirst) {
+            this.origPageX = ev.pageX;
+            this.origPageY = ev.pageY;
+        }
+        else {
+            deltaX = ev.pageX - this.origPageX;
+            deltaY = ev.pageY - this.origPageY;
+        }
+        return {
+            origEvent: ev,
+            isTouch: false,
+            subjectEl: this.subjectEl,
+            pageX: ev.pageX,
+            pageY: ev.pageY,
+            deltaX: deltaX,
+            deltaY: deltaY
+        };
+    };
+    PointerDragging.prototype.createEventFromTouch = function (ev, isFirst) {
+        var touches = ev.touches;
+        var pageX;
+        var pageY;
+        var deltaX = 0;
+        var deltaY = 0;
+        // if touch coords available, prefer,
+        // because FF would give bad ev.pageX ev.pageY
+        if (touches && touches.length) {
+            pageX = touches[0].pageX;
+            pageY = touches[0].pageY;
+        }
+        else {
+            pageX = ev.pageX;
+            pageY = ev.pageY;
+        }
+        // TODO: repeat code
+        if (isFirst) {
+            this.origPageX = pageX;
+            this.origPageY = pageY;
+        }
+        else {
+            deltaX = pageX - this.origPageX;
+            deltaY = pageY - this.origPageY;
+        }
+        return {
+            origEvent: ev,
+            isTouch: true,
+            subjectEl: this.subjectEl,
+            pageX: pageX,
+            pageY: pageY,
+            deltaX: deltaX,
+            deltaY: deltaY
+        };
+    };
+    return PointerDragging;
+}());
+// Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
+function isPrimaryMouseButton(ev) {
+    return ev.button === 0 && !ev.ctrlKey;
+}
+// Ignoring fake mouse events generated by touch
+// ----------------------------------------------------------------------------------------------------
+function startIgnoringMouse() {
+    ignoreMouseDepth++;
+    setTimeout(function () {
+        ignoreMouseDepth--;
+    }, config.touchMouseIgnoreWait);
+}
+// We want to attach touchmove as early as possible for Safari
+// ----------------------------------------------------------------------------------------------------
+function listenerCreated() {
+    if (!(listenerCnt++)) {
+        window.addEventListener('touchmove', onWindowTouchMove, { passive: false });
+    }
+}
+function listenerDestroyed() {
+    if (!(--listenerCnt)) {
+        window.removeEventListener('touchmove', onWindowTouchMove, { passive: false });
+    }
+}
+function onWindowTouchMove(ev) {
+    if (isWindowTouchMoveCancelled) {
+        ev.preventDefault();
+    }
+}
+
+/*
+An effect in which an element follows the movement of a pointer across the screen.
+The moving element is a clone of some other element.
+Must call start + handleMove + stop.
+*/
+var ElementMirror = /** @class */ (function () {
+    function ElementMirror() {
+        this.isVisible = false; // must be explicitly enabled
+        this.sourceEl = null;
+        this.mirrorEl = null;
+        this.sourceElRect = null; // screen coords relative to viewport
+        // options that can be set directly by caller
+        this.parentNode = document.body;
+        this.zIndex = 9999;
+        this.revertDuration = 0;
+    }
+    ElementMirror.prototype.start = function (sourceEl, pageX, pageY) {
+        this.sourceEl = sourceEl;
+        this.sourceElRect = this.sourceEl.getBoundingClientRect();
+        this.origScreenX = pageX - window.pageXOffset;
+        this.origScreenY = pageY - window.pageYOffset;
+        this.deltaX = 0;
+        this.deltaY = 0;
+        this.updateElPosition();
+    };
+    ElementMirror.prototype.handleMove = function (pageX, pageY) {
+        this.deltaX = (pageX - window.pageXOffset) - this.origScreenX;
+        this.deltaY = (pageY - window.pageYOffset) - this.origScreenY;
+        this.updateElPosition();
+    };
+    // can be called before start
+    ElementMirror.prototype.setIsVisible = function (bool) {
+        if (bool) {
+            if (!this.isVisible) {
+                if (this.mirrorEl) {
+                    this.mirrorEl.style.display = '';
+                }
+                this.isVisible = bool; // needs to happen before updateElPosition
+                this.updateElPosition(); // because was not updating the position while invisible
+            }
+        }
+        else {
+            if (this.isVisible) {
+                if (this.mirrorEl) {
+                    this.mirrorEl.style.display = 'none';
+                }
+                this.isVisible = bool;
+            }
+        }
+    };
+    // always async
+    ElementMirror.prototype.stop = function (needsRevertAnimation, callback) {
+        var _this = this;
+        var done = function () {
+            _this.cleanup();
+            callback();
+        };
+        if (needsRevertAnimation &&
+            this.mirrorEl &&
+            this.isVisible &&
+            this.revertDuration && // if 0, transition won't work
+            (this.deltaX || this.deltaY) // if same coords, transition won't work
+        ) {
+            this.doRevertAnimation(done, this.revertDuration);
+        }
+        else {
+            setTimeout(done, 0);
+        }
+    };
+    ElementMirror.prototype.doRevertAnimation = function (callback, revertDuration) {
+        var mirrorEl = this.mirrorEl;
+        var finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened
+        mirrorEl.style.transition =
+            'top ' + revertDuration + 'ms,' +
+                'left ' + revertDuration + 'ms';
+        applyStyle(mirrorEl, {
+            left: finalSourceElRect.left,
+            top: finalSourceElRect.top
+        });
+        whenTransitionDone(mirrorEl, function () {
+            mirrorEl.style.transition = '';
+            callback();
+        });
+    };
+    ElementMirror.prototype.cleanup = function () {
+        if (this.mirrorEl) {
+            removeElement(this.mirrorEl);
+            this.mirrorEl = null;
+        }
+        this.sourceEl = null;
+    };
+    ElementMirror.prototype.updateElPosition = function () {
+        if (this.sourceEl && this.isVisible) {
+            applyStyle(this.getMirrorEl(), {
+                left: this.sourceElRect.left + this.deltaX,
+                top: this.sourceElRect.top + this.deltaY
+            });
+        }
+    };
+    ElementMirror.prototype.getMirrorEl = function () {
+        var sourceElRect = this.sourceElRect;
+        var mirrorEl = this.mirrorEl;
+        if (!mirrorEl) {
+            mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true
+            // we don't want long taps or any mouse interaction causing selection/menus.
+            // would use preventSelection(), but that prevents selectstart, causing problems.
+            mirrorEl.classList.add('fc-unselectable');
+            mirrorEl.classList.add('fc-dragging');
+            applyStyle(mirrorEl, {
+                position: 'fixed',
+                zIndex: this.zIndex,
+                visibility: '',
+                boxSizing: 'border-box',
+                width: sourceElRect.right - sourceElRect.left,
+                height: sourceElRect.bottom - sourceElRect.top,
+                right: 'auto',
+                bottom: 'auto',
+                margin: 0
+            });
+            this.parentNode.appendChild(mirrorEl);
+        }
+        return mirrorEl;
+    };
+    return ElementMirror;
+}());
+
+/*
+Is a cache for a given element's scroll information (all the info that ScrollController stores)
+in addition the "client rectangle" of the element.. the area within the scrollbars.
+
+The cache can be in one of two modes:
+- doesListening:false - ignores when the container is scrolled by someone else
+- doesListening:true - watch for scrolling and update the cache
+*/
+var ScrollGeomCache = /** @class */ (function (_super) {
+    __extends(ScrollGeomCache, _super);
+    function ScrollGeomCache(scrollController, doesListening) {
+        var _this = _super.call(this) || this;
+        _this.handleScroll = function () {
+            _this.scrollTop = _this.scrollController.getScrollTop();
+            _this.scrollLeft = _this.scrollController.getScrollLeft();
+            _this.handleScrollChange();
+        };
+        _this.scrollController = scrollController;
+        _this.doesListening = doesListening;
+        _this.scrollTop = _this.origScrollTop = scrollController.getScrollTop();
+        _this.scrollLeft = _this.origScrollLeft = scrollController.getScrollLeft();
+        _this.scrollWidth = scrollController.getScrollWidth();
+        _this.scrollHeight = scrollController.getScrollHeight();
+        _this.clientWidth = scrollController.getClientWidth();
+        _this.clientHeight = scrollController.getClientHeight();
+        _this.clientRect = _this.computeClientRect(); // do last in case it needs cached values
+        if (_this.doesListening) {
+            _this.getEventTarget().addEventListener('scroll', _this.handleScroll);
+        }
+        return _this;
+    }
+    ScrollGeomCache.prototype.destroy = function () {
+        if (this.doesListening) {
+            this.getEventTarget().removeEventListener('scroll', this.handleScroll);
+        }
+    };
+    ScrollGeomCache.prototype.getScrollTop = function () {
+        return this.scrollTop;
+    };
+    ScrollGeomCache.prototype.getScrollLeft = function () {
+        return this.scrollLeft;
+    };
+    ScrollGeomCache.prototype.setScrollTop = function (top) {
+        this.scrollController.setScrollTop(top);
+        if (!this.doesListening) {
+            // we are not relying on the element to normalize out-of-bounds scroll values
+            // so we need to sanitize ourselves
+            this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0);
+            this.handleScrollChange();
+        }
+    };
+    ScrollGeomCache.prototype.setScrollLeft = function (top) {
+        this.scrollController.setScrollLeft(top);
+        if (!this.doesListening) {
+            // we are not relying on the element to normalize out-of-bounds scroll values
+            // so we need to sanitize ourselves
+            this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0);
+            this.handleScrollChange();
+        }
+    };
+    ScrollGeomCache.prototype.getClientWidth = function () {
+        return this.clientWidth;
+    };
+    ScrollGeomCache.prototype.getClientHeight = function () {
+        return this.clientHeight;
+    };
+    ScrollGeomCache.prototype.getScrollWidth = function () {
+        return this.scrollWidth;
+    };
+    ScrollGeomCache.prototype.getScrollHeight = function () {
+        return this.scrollHeight;
+    };
+    ScrollGeomCache.prototype.handleScrollChange = function () {
+    };
+    return ScrollGeomCache;
+}(ScrollController));
+var ElementScrollGeomCache = /** @class */ (function (_super) {
+    __extends(ElementScrollGeomCache, _super);
+    function ElementScrollGeomCache(el, doesListening) {
+        return _super.call(this, new ElementScrollController(el), doesListening) || this;
+    }
+    ElementScrollGeomCache.prototype.getEventTarget = function () {
+        return this.scrollController.el;
+    };
+    ElementScrollGeomCache.prototype.computeClientRect = function () {
+        return computeInnerRect(this.scrollController.el);
+    };
+    return ElementScrollGeomCache;
+}(ScrollGeomCache));
+var WindowScrollGeomCache = /** @class */ (function (_super) {
+    __extends(WindowScrollGeomCache, _super);
+    function WindowScrollGeomCache(doesListening) {
+        return _super.call(this, new WindowScrollController(), doesListening) || this;
+    }
+    WindowScrollGeomCache.prototype.getEventTarget = function () {
+        return window;
+    };
+    WindowScrollGeomCache.prototype.computeClientRect = function () {
+        return {
+            left: this.scrollLeft,
+            right: this.scrollLeft + this.clientWidth,
+            top: this.scrollTop,
+            bottom: this.scrollTop + this.clientHeight
+        };
+    };
+    // the window is the only scroll object that changes it's rectangle relative
+    // to the document's topleft as it scrolls
+    WindowScrollGeomCache.prototype.handleScrollChange = function () {
+        this.clientRect = this.computeClientRect();
+    };
+    return WindowScrollGeomCache;
+}(ScrollGeomCache));
+
+// If available we are using native "performance" API instead of "Date"
+// Read more about it on MDN:
+// https://developer.mozilla.org/en-US/docs/Web/API/Performance
+var getTime = typeof performance === 'function' ? performance.now : Date.now;
+/*
+For a pointer interaction, automatically scrolls certain scroll containers when the pointer
+approaches the edge.
+
+The caller must call start + handleMove + stop.
+*/
+var AutoScroller = /** @class */ (function () {
+    function AutoScroller() {
+        var _this = this;
+        // options that can be set by caller
+        this.isEnabled = true;
+        this.scrollQuery = [window, '.fc-scroller'];
+        this.edgeThreshold = 50; // pixels
+        this.maxVelocity = 300; // pixels per second
+        // internal state
+        this.pointerScreenX = null;
+        this.pointerScreenY = null;
+        this.isAnimating = false;
+        this.scrollCaches = null;
+        // protect against the initial pointerdown being too close to an edge and starting the scroll
+        this.everMovedUp = false;
+        this.everMovedDown = false;
+        this.everMovedLeft = false;
+        this.everMovedRight = false;
+        this.animate = function () {
+            if (_this.isAnimating) { // wasn't cancelled between animation calls
+                var edge = _this.computeBestEdge(_this.pointerScreenX + window.pageXOffset, _this.pointerScreenY + window.pageYOffset);
+                if (edge) {
+                    var now = getTime();
+                    _this.handleSide(edge, (now - _this.msSinceRequest) / 1000);
+                    _this.requestAnimation(now);
+                }
+                else {
+                    _this.isAnimating = false; // will stop animation
+                }
+            }
+        };
+    }
+    AutoScroller.prototype.start = function (pageX, pageY) {
+        if (this.isEnabled) {
+            this.scrollCaches = this.buildCaches();
+            this.pointerScreenX = null;
+            this.pointerScreenY = null;
+            this.everMovedUp = false;
+            this.everMovedDown = false;
+            this.everMovedLeft = false;
+            this.everMovedRight = false;
+            this.handleMove(pageX, pageY);
+        }
+    };
+    AutoScroller.prototype.handleMove = function (pageX, pageY) {
+        if (this.isEnabled) {
+            var pointerScreenX = pageX - window.pageXOffset;
+            var pointerScreenY = pageY - window.pageYOffset;
+            var yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY;
+            var xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX;
+            if (yDelta < 0) {
+                this.everMovedUp = true;
+            }
+            else if (yDelta > 0) {
+                this.everMovedDown = true;
+            }
+            if (xDelta < 0) {
+                this.everMovedLeft = true;
+            }
+            else if (xDelta > 0) {
+                this.everMovedRight = true;
+            }
+            this.pointerScreenX = pointerScreenX;
+            this.pointerScreenY = pointerScreenY;
+            if (!this.isAnimating) {
+                this.isAnimating = true;
+                this.requestAnimation(getTime());
+            }
+        }
+    };
+    AutoScroller.prototype.stop = function () {
+        if (this.isEnabled) {
+            this.isAnimating = false; // will stop animation
+            for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                var scrollCache = _a[_i];
+                scrollCache.destroy();
+            }
+            this.scrollCaches = null;
+        }
+    };
+    AutoScroller.prototype.requestAnimation = function (now) {
+        this.msSinceRequest = now;
+        requestAnimationFrame(this.animate);
+    };
+    AutoScroller.prototype.handleSide = function (edge, seconds) {
+        var scrollCache = edge.scrollCache;
+        var edgeThreshold = this.edgeThreshold;
+        var invDistance = edgeThreshold - edge.distance;
+        var velocity = // the closer to the edge, the faster we scroll
+         (invDistance * invDistance) / (edgeThreshold * edgeThreshold) * // quadratic
+            this.maxVelocity * seconds;
+        var sign = 1;
+        switch (edge.name) {
+            case 'left':
+                sign = -1;
+            // falls through
+            case 'right':
+                scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign);
+                break;
+            case 'top':
+                sign = -1;
+            // falls through
+            case 'bottom':
+                scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign);
+                break;
+        }
+    };
+    // left/top are relative to document topleft
+    AutoScroller.prototype.computeBestEdge = function (left, top) {
+        var edgeThreshold = this.edgeThreshold;
+        var bestSide = null;
+        for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+            var scrollCache = _a[_i];
+            var rect = scrollCache.clientRect;
+            var leftDist = left - rect.left;
+            var rightDist = rect.right - left;
+            var topDist = top - rect.top;
+            var bottomDist = rect.bottom - top;
+            // completely within the rect?
+            if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) {
+                if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() &&
+                    (!bestSide || bestSide.distance > topDist)) {
+                    bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist };
+                }
+                if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() &&
+                    (!bestSide || bestSide.distance > bottomDist)) {
+                    bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist };
+                }
+                if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() &&
+                    (!bestSide || bestSide.distance > leftDist)) {
+                    bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist };
+                }
+                if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() &&
+                    (!bestSide || bestSide.distance > rightDist)) {
+                    bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist };
+                }
+            }
+        }
+        return bestSide;
+    };
+    AutoScroller.prototype.buildCaches = function () {
+        return this.queryScrollEls().map(function (el) {
+            if (el === window) {
+                return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls
+            }
+            else {
+                return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls
+            }
+        });
+    };
+    AutoScroller.prototype.queryScrollEls = function () {
+        var els = [];
+        for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) {
+            var query = _a[_i];
+            if (typeof query === 'object') {
+                els.push(query);
+            }
+            else {
+                els.push.apply(els, Array.prototype.slice.call(document.querySelectorAll(query)));
+            }
+        }
+        return els;
+    };
+    return AutoScroller;
+}());
+
+/*
+Monitors dragging on an element. Has a number of high-level features:
+- minimum distance required before dragging
+- minimum wait time ("delay") before dragging
+- a mirror element that follows the pointer
+*/
+var FeaturefulElementDragging = /** @class */ (function (_super) {
+    __extends(FeaturefulElementDragging, _super);
+    function FeaturefulElementDragging(containerEl) {
+        var _this = _super.call(this, containerEl) || this;
+        // options that can be directly set by caller
+        // the caller can also set the PointerDragging's options as well
+        _this.delay = null;
+        _this.minDistance = 0;
+        _this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag
+        _this.mirrorNeedsRevert = false;
+        _this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup
+        _this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation
+        _this.isDelayEnded = false;
+        _this.isDistanceSurpassed = false;
+        _this.delayTimeoutId = null;
+        _this.onPointerDown = function (ev) {
+            if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going
+                _this.isInteracting = true;
+                _this.isDelayEnded = false;
+                _this.isDistanceSurpassed = false;
+                preventSelection(document.body);
+                preventContextMenu(document.body);
+                // prevent links from being visited if there's an eventual drag.
+                // also prevents selection in older browsers (maybe?).
+                // not necessary for touch, besides, browser would complain about passiveness.
+                if (!ev.isTouch) {
+                    ev.origEvent.preventDefault();
+                }
+                _this.emitter.trigger('pointerdown', ev);
+                if (!_this.pointer.shouldIgnoreMove) {
+                    // actions related to initiating dragstart+dragmove+dragend...
+                    _this.mirror.setIsVisible(false); // reset. caller must set-visible
+                    _this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down
+                    _this.startDelay(ev);
+                    if (!_this.minDistance) {
+                        _this.handleDistanceSurpassed(ev);
+                    }
+                }
+            }
+        };
+        _this.onPointerMove = function (ev) {
+            if (_this.isInteracting) { // if false, still waiting for previous drag's revert
+                _this.emitter.trigger('pointermove', ev);
+                if (!_this.isDistanceSurpassed) {
+                    var minDistance = _this.minDistance;
+                    var distanceSq = void 0; // current distance from the origin, squared
+                    var deltaX = ev.deltaX, deltaY = ev.deltaY;
+                    distanceSq = deltaX * deltaX + deltaY * deltaY;
+                    if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
+                        _this.handleDistanceSurpassed(ev);
+                    }
+                }
+                if (_this.isDragging) {
+                    // a real pointer move? (not one simulated by scrolling)
+                    if (ev.origEvent.type !== 'scroll') {
+                        _this.mirror.handleMove(ev.pageX, ev.pageY);
+                        _this.autoScroller.handleMove(ev.pageX, ev.pageY);
+                    }
+                    _this.emitter.trigger('dragmove', ev);
+                }
+            }
+        };
+        _this.onPointerUp = function (ev) {
+            if (_this.isInteracting) { // if false, still waiting for previous drag's revert
+                _this.isInteracting = false;
+                allowSelection(document.body);
+                allowContextMenu(document.body);
+                _this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert
+                if (_this.isDragging) {
+                    _this.autoScroller.stop();
+                    _this.tryStopDrag(ev); // which will stop the mirror
+                }
+                if (_this.delayTimeoutId) {
+                    clearTimeout(_this.delayTimeoutId);
+                    _this.delayTimeoutId = null;
+                }
+            }
+        };
+        var pointer = _this.pointer = new PointerDragging(containerEl);
+        pointer.emitter.on('pointerdown', _this.onPointerDown);
+        pointer.emitter.on('pointermove', _this.onPointerMove);
+        pointer.emitter.on('pointerup', _this.onPointerUp);
+        _this.mirror = new ElementMirror();
+        _this.autoScroller = new AutoScroller();
+        return _this;
+    }
+    FeaturefulElementDragging.prototype.destroy = function () {
+        this.pointer.destroy();
+    };
+    FeaturefulElementDragging.prototype.startDelay = function (ev) {
+        var _this = this;
+        if (typeof this.delay === 'number') {
+            this.delayTimeoutId = setTimeout(function () {
+                _this.delayTimeoutId = null;
+                _this.handleDelayEnd(ev);
+            }, this.delay); // not assignable to number!
+        }
+        else {
+            this.handleDelayEnd(ev);
+        }
+    };
+    FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) {
+        this.isDelayEnded = true;
+        this.tryStartDrag(ev);
+    };
+    FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) {
+        this.isDistanceSurpassed = true;
+        this.tryStartDrag(ev);
+    };
+    FeaturefulElementDragging.prototype.tryStartDrag = function (ev) {
+        if (this.isDelayEnded && this.isDistanceSurpassed) {
+            if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) {
+                this.isDragging = true;
+                this.mirrorNeedsRevert = false;
+                this.autoScroller.start(ev.pageX, ev.pageY);
+                this.emitter.trigger('dragstart', ev);
+                if (this.touchScrollAllowed === false) {
+                    this.pointer.cancelTouchScroll();
+                }
+            }
+        }
+    };
+    FeaturefulElementDragging.prototype.tryStopDrag = function (ev) {
+        // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events
+        // that come from the document to fire beforehand. much more convenient this way.
+        this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev) // bound with args
+        );
+    };
+    FeaturefulElementDragging.prototype.stopDrag = function (ev) {
+        this.isDragging = false;
+        this.emitter.trigger('dragend', ev);
+    };
+    // fill in the implementations...
+    FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) {
+        this.pointer.shouldIgnoreMove = bool;
+    };
+    FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) {
+        this.mirror.setIsVisible(bool);
+    };
+    FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) {
+        this.mirrorNeedsRevert = bool;
+    };
+    FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) {
+        this.autoScroller.isEnabled = bool;
+    };
+    return FeaturefulElementDragging;
+}(ElementDragging));
+
+/*
+When this class is instantiated, it records the offset of an element (relative to the document topleft),
+and continues to monitor scrolling, updating the cached coordinates if it needs to.
+Does not access the DOM after instantiation, so highly performant.
+
+Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element
+and an determine if a given point is inside the combined clipping rectangle.
+*/
+var OffsetTracker = /** @class */ (function () {
+    function OffsetTracker(el) {
+        this.origRect = computeRect(el);
+        // will work fine for divs that have overflow:hidden
+        this.scrollCaches = getClippingParents(el).map(function (el) {
+            return new ElementScrollGeomCache(el, true); // listen=true
+        });
+    }
+    OffsetTracker.prototype.destroy = function () {
+        for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+            var scrollCache = _a[_i];
+            scrollCache.destroy();
+        }
+    };
+    OffsetTracker.prototype.computeLeft = function () {
+        var left = this.origRect.left;
+        for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+            var scrollCache = _a[_i];
+            left += scrollCache.origScrollLeft - scrollCache.getScrollLeft();
+        }
+        return left;
+    };
+    OffsetTracker.prototype.computeTop = function () {
+        var top = this.origRect.top;
+        for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+            var scrollCache = _a[_i];
+            top += scrollCache.origScrollTop - scrollCache.getScrollTop();
+        }
+        return top;
+    };
+    OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) {
+        var point = { left: pageX, top: pageY };
+        for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+            var scrollCache = _a[_i];
+            if (!isIgnoredClipping(scrollCache.getEventTarget()) &&
+                !pointInsideRect(point, scrollCache.clientRect)) {
+                return false;
+            }
+        }
+        return true;
+    };
+    return OffsetTracker;
+}());
+// certain clipping containers should never constrain interactions, like <html> and <body>
+// https://github.com/fullcalendar/fullcalendar/issues/3615
+function isIgnoredClipping(node) {
+    var tagName = node.tagName;
+    return tagName === 'HTML' || tagName === 'BODY';
+}
+
+/*
+Tracks movement over multiple droppable areas (aka "hits")
+that exist in one or more DateComponents.
+Relies on an existing draggable.
+
+emits:
+- pointerdown
+- dragstart
+- hitchange - fires initially, even if not over a hit
+- pointerup
+- (hitchange - again, to null, if ended over a hit)
+- dragend
+*/
+var HitDragging = /** @class */ (function () {
+    function HitDragging(dragging, droppableStore) {
+        var _this = this;
+        // options that can be set by caller
+        this.useSubjectCenter = false;
+        this.requireInitial = true; // if doesn't start out on a hit, won't emit any events
+        this.initialHit = null;
+        this.movingHit = null;
+        this.finalHit = null; // won't ever be populated if shouldIgnoreMove
+        this.handlePointerDown = function (ev) {
+            var dragging = _this.dragging;
+            _this.initialHit = null;
+            _this.movingHit = null;
+            _this.finalHit = null;
+            _this.prepareHits();
+            _this.processFirstCoord(ev);
+            if (_this.initialHit || !_this.requireInitial) {
+                dragging.setIgnoreMove(false);
+                _this.emitter.trigger('pointerdown', ev); // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :(
+            }
+            else {
+                dragging.setIgnoreMove(true);
+            }
+        };
+        this.handleDragStart = function (ev) {
+            _this.emitter.trigger('dragstart', ev);
+            _this.handleMove(ev, true); // force = fire even if initially null
+        };
+        this.handleDragMove = function (ev) {
+            _this.emitter.trigger('dragmove', ev);
+            _this.handleMove(ev);
+        };
+        this.handlePointerUp = function (ev) {
+            _this.releaseHits();
+            _this.emitter.trigger('pointerup', ev);
+        };
+        this.handleDragEnd = function (ev) {
+            if (_this.movingHit) {
+                _this.emitter.trigger('hitupdate', null, true, ev);
+            }
+            _this.finalHit = _this.movingHit;
+            _this.movingHit = null;
+            _this.emitter.trigger('dragend', ev);
+        };
+        this.droppableStore = droppableStore;
+        dragging.emitter.on('pointerdown', this.handlePointerDown);
+        dragging.emitter.on('dragstart', this.handleDragStart);
+        dragging.emitter.on('dragmove', this.handleDragMove);
+        dragging.emitter.on('pointerup', this.handlePointerUp);
+        dragging.emitter.on('dragend', this.handleDragEnd);
+        this.dragging = dragging;
+        this.emitter = new EmitterMixin();
+    }
+    // sets initialHit
+    // sets coordAdjust
+    HitDragging.prototype.processFirstCoord = function (ev) {
+        var origPoint = { left: ev.pageX, top: ev.pageY };
+        var adjustedPoint = origPoint;
+        var subjectEl = ev.subjectEl;
+        var subjectRect;
+        if (subjectEl !== document) {
+            subjectRect = computeRect(subjectEl);
+            adjustedPoint = constrainPoint(adjustedPoint, subjectRect);
+        }
+        var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top);
+        if (initialHit) {
+            if (this.useSubjectCenter && subjectRect) {
+                var slicedSubjectRect = intersectRects(subjectRect, initialHit.rect);
+                if (slicedSubjectRect) {
+                    adjustedPoint = getRectCenter(slicedSubjectRect);
+                }
+            }
+            this.coordAdjust = diffPoints(adjustedPoint, origPoint);
+        }
+        else {
+            this.coordAdjust = { left: 0, top: 0 };
+        }
+    };
+    HitDragging.prototype.handleMove = function (ev, forceHandle) {
+        var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top);
+        if (forceHandle || !isHitsEqual(this.movingHit, hit)) {
+            this.movingHit = hit;
+            this.emitter.trigger('hitupdate', hit, false, ev);
+        }
+    };
+    HitDragging.prototype.prepareHits = function () {
+        this.offsetTrackers = mapHash(this.droppableStore, function (interactionSettings) {
+            interactionSettings.component.buildPositionCaches();
+            return new OffsetTracker(interactionSettings.el);
+        });
+    };
+    HitDragging.prototype.releaseHits = function () {
+        var offsetTrackers = this.offsetTrackers;
+        for (var id in offsetTrackers) {
+            offsetTrackers[id].destroy();
+        }
+        this.offsetTrackers = {};
+    };
+    HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) {
+        var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers;
+        var bestHit = null;
+        for (var id in droppableStore) {
+            var component = droppableStore[id].component;
+            var offsetTracker = offsetTrackers[id];
+            if (offsetTracker.isWithinClipping(offsetLeft, offsetTop)) {
+                var originLeft = offsetTracker.computeLeft();
+                var originTop = offsetTracker.computeTop();
+                var positionLeft = offsetLeft - originLeft;
+                var positionTop = offsetTop - originTop;
+                var origRect = offsetTracker.origRect;
+                var width = origRect.right - origRect.left;
+                var height = origRect.bottom - origRect.top;
+                if (
+                // must be within the element's bounds
+                positionLeft >= 0 && positionLeft < width &&
+                    positionTop >= 0 && positionTop < height) {
+                    var hit = component.queryHit(positionLeft, positionTop, width, height);
+                    if (hit &&
+                        (
+                        // make sure the hit is within activeRange, meaning it's not a deal cell
+                        !component.props.dateProfile || // hack for DayTile
+                            rangeContainsRange(component.props.dateProfile.activeRange, hit.dateSpan.range)) &&
+                        (!bestHit || hit.layer > bestHit.layer)) {
+                        // TODO: better way to re-orient rectangle
+                        hit.rect.left += originLeft;
+                        hit.rect.right += originLeft;
+                        hit.rect.top += originTop;
+                        hit.rect.bottom += originTop;
+                        bestHit = hit;
+                    }
+                }
+            }
+        }
+        return bestHit;
+    };
+    return HitDragging;
+}());
+function isHitsEqual(hit0, hit1) {
+    if (!hit0 && !hit1) {
+        return true;
+    }
+    if (Boolean(hit0) !== Boolean(hit1)) {
+        return false;
+    }
+    return isDateSpansEqual(hit0.dateSpan, hit1.dateSpan);
+}
+
+/*
+Monitors when the user clicks on a specific date/time of a component.
+A pointerdown+pointerup on the same "hit" constitutes a click.
+*/
+var DateClicking = /** @class */ (function (_super) {
+    __extends(DateClicking, _super);
+    function DateClicking(settings) {
+        var _this = _super.call(this, settings) || this;
+        _this.handlePointerDown = function (ev) {
+            var dragging = _this.dragging;
+            // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
+            dragging.setIgnoreMove(!_this.component.isValidDateDownEl(dragging.pointer.downEl));
+        };
+        // won't even fire if moving was ignored
+        _this.handleDragEnd = function (ev) {
+            var component = _this.component;
+            var pointer = _this.dragging.pointer;
+            if (!pointer.wasTouchScroll) {
+                var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit;
+                if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {
+                    component.calendar.triggerDateClick(initialHit.dateSpan, initialHit.dayEl, component.view, ev.origEvent);
+                }
+            }
+        };
+        var component = settings.component;
+        // we DO want to watch pointer moves because otherwise finalHit won't get populated
+        _this.dragging = new FeaturefulElementDragging(component.el);
+        _this.dragging.autoScroller.isEnabled = false;
+        var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings));
+        hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+        hitDragging.emitter.on('dragend', _this.handleDragEnd);
+        return _this;
+    }
+    DateClicking.prototype.destroy = function () {
+        this.dragging.destroy();
+    };
+    return DateClicking;
+}(Interaction));
+
+/*
+Tracks when the user selects a portion of time of a component,
+constituted by a drag over date cells, with a possible delay at the beginning of the drag.
+*/
+var DateSelecting = /** @class */ (function (_super) {
+    __extends(DateSelecting, _super);
+    function DateSelecting(settings) {
+        var _this = _super.call(this, settings) || this;
+        _this.dragSelection = null;
+        _this.handlePointerDown = function (ev) {
+            var _a = _this, component = _a.component, dragging = _a.dragging;
+            var canSelect = component.opt('selectable') &&
+                component.isValidDateDownEl(ev.origEvent.target);
+            // don't bother to watch expensive moves if component won't do selection
+            dragging.setIgnoreMove(!canSelect);
+            // if touch, require user to hold down
+            dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null;
+        };
+        _this.handleDragStart = function (ev) {
+            _this.component.calendar.unselect(ev); // unselect previous selections
+        };
+        _this.handleHitUpdate = function (hit, isFinal) {
+            var calendar = _this.component.calendar;
+            var dragSelection = null;
+            var isInvalid = false;
+            if (hit) {
+                dragSelection = joinHitsIntoSelection(_this.hitDragging.initialHit, hit, calendar.pluginSystem.hooks.dateSelectionTransformers);
+                if (!dragSelection || !_this.component.isDateSelectionValid(dragSelection)) {
+                    isInvalid = true;
+                    dragSelection = null;
+                }
+            }
+            if (dragSelection) {
+                calendar.dispatch({ type: 'SELECT_DATES', selection: dragSelection });
+            }
+            else if (!isFinal) { // only unselect if moved away while dragging
+                calendar.dispatch({ type: 'UNSELECT_DATES' });
+            }
+            if (!isInvalid) {
+                enableCursor();
+            }
+            else {
+                disableCursor();
+            }
+            if (!isFinal) {
+                _this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging
+            }
+        };
+        _this.handlePointerUp = function (pev) {
+            if (_this.dragSelection) {
+                // selection is already rendered, so just need to report selection
+                _this.component.calendar.triggerDateSelect(_this.dragSelection, pev);
+                _this.dragSelection = null;
+            }
+        };
+        var component = settings.component;
+        var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
+        dragging.touchScrollAllowed = false;
+        dragging.minDistance = component.opt('selectMinDistance') || 0;
+        dragging.autoScroller.isEnabled = component.opt('dragScroll');
+        var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings));
+        hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+        hitDragging.emitter.on('dragstart', _this.handleDragStart);
+        hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+        hitDragging.emitter.on('pointerup', _this.handlePointerUp);
+        return _this;
+    }
+    DateSelecting.prototype.destroy = function () {
+        this.dragging.destroy();
+    };
+    return DateSelecting;
+}(Interaction));
+function getComponentTouchDelay(component) {
+    var delay = component.opt('selectLongPressDelay');
+    if (delay == null) {
+        delay = component.opt('longPressDelay');
+    }
+    return delay;
+}
+function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) {
+    var dateSpan0 = hit0.dateSpan;
+    var dateSpan1 = hit1.dateSpan;
+    var ms = [
+        dateSpan0.range.start,
+        dateSpan0.range.end,
+        dateSpan1.range.start,
+        dateSpan1.range.end
+    ];
+    ms.sort(compareNumbers);
+    var props = {};
+    for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) {
+        var transformer = dateSelectionTransformers_1[_i];
+        var res = transformer(hit0, hit1);
+        if (res === false) {
+            return null;
+        }
+        else if (res) {
+            __assign(props, res);
+        }
+    }
+    props.range = { start: ms[0], end: ms[3] };
+    props.allDay = dateSpan0.allDay;
+    return props;
+}
+
+var EventDragging = /** @class */ (function (_super) {
+    __extends(EventDragging, _super);
+    function EventDragging(settings) {
+        var _this = _super.call(this, settings) || this;
+        // internal state
+        _this.subjectSeg = null; // the seg being selected/dragged
+        _this.isDragging = false;
+        _this.eventRange = null;
+        _this.relevantEvents = null; // the events being dragged
+        _this.receivingCalendar = null;
+        _this.validMutation = null;
+        _this.mutatedRelevantEvents = null;
+        _this.handlePointerDown = function (ev) {
+            var origTarget = ev.origEvent.target;
+            var _a = _this, component = _a.component, dragging = _a.dragging;
+            var mirror = dragging.mirror;
+            var initialCalendar = component.calendar;
+            var subjectSeg = _this.subjectSeg = getElSeg(ev.subjectEl);
+            var eventRange = _this.eventRange = subjectSeg.eventRange;
+            var eventInstanceId = eventRange.instance.instanceId;
+            _this.relevantEvents = getRelevantEvents(initialCalendar.state.eventStore, eventInstanceId);
+            dragging.minDistance = ev.isTouch ? 0 : component.opt('eventDragMinDistance');
+            dragging.delay =
+                // only do a touch delay if touch and this event hasn't been selected yet
+                (ev.isTouch && eventInstanceId !== component.props.eventSelection) ?
+                    getComponentTouchDelay$1(component) :
+                    null;
+            mirror.parentNode = initialCalendar.el;
+            mirror.revertDuration = component.opt('dragRevertDuration');
+            var isValid = component.isValidSegDownEl(origTarget) &&
+                !elementClosest(origTarget, '.fc-resizer'); // NOT on a resizer
+            dragging.setIgnoreMove(!isValid);
+            // disable dragging for elements that are resizable (ie, selectable)
+            // but are not draggable
+            _this.isDragging = isValid &&
+                ev.subjectEl.classList.contains('fc-draggable');
+        };
+        _this.handleDragStart = function (ev) {
+            var initialCalendar = _this.component.calendar;
+            var eventRange = _this.eventRange;
+            var eventInstanceId = eventRange.instance.instanceId;
+            if (ev.isTouch) {
+                // need to select a different event?
+                if (eventInstanceId !== _this.component.props.eventSelection) {
+                    initialCalendar.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId });
+                }
+            }
+            else {
+                // if now using mouse, but was previous touch interaction, clear selected event
+                initialCalendar.dispatch({ type: 'UNSELECT_EVENT' });
+            }
+            if (_this.isDragging) {
+                initialCalendar.unselect(ev); // unselect *date* selection
+                initialCalendar.publiclyTrigger('eventDragStart', [
+                    {
+                        el: _this.subjectSeg.el,
+                        event: new EventApi(initialCalendar, eventRange.def, eventRange.instance),
+                        jsEvent: ev.origEvent,
+                        view: _this.component.view
+                    }
+                ]);
+            }
+        };
+        _this.handleHitUpdate = function (hit, isFinal) {
+            if (!_this.isDragging) {
+                return;
+            }
+            var relevantEvents = _this.relevantEvents;
+            var initialHit = _this.hitDragging.initialHit;
+            var initialCalendar = _this.component.calendar;
+            // states based on new hit
+            var receivingCalendar = null;
+            var mutation = null;
+            var mutatedRelevantEvents = null;
+            var isInvalid = false;
+            var interaction = {
+                affectedEvents: relevantEvents,
+                mutatedEvents: createEmptyEventStore(),
+                isEvent: true,
+                origSeg: _this.subjectSeg
+            };
+            if (hit) {
+                var receivingComponent = hit.component;
+                receivingCalendar = receivingComponent.calendar;
+                if (initialCalendar === receivingCalendar ||
+                    receivingComponent.opt('editable') && receivingComponent.opt('droppable')) {
+                    mutation = computeEventMutation(initialHit, hit, receivingCalendar.pluginSystem.hooks.eventDragMutationMassagers);
+                    if (mutation) {
+                        mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, receivingCalendar.eventUiBases, mutation, receivingCalendar);
+                        interaction.mutatedEvents = mutatedRelevantEvents;
+                        if (!receivingComponent.isInteractionValid(interaction)) {
+                            isInvalid = true;
+                            mutation = null;
+                            mutatedRelevantEvents = null;
+                            interaction.mutatedEvents = createEmptyEventStore();
+                        }
+                    }
+                }
+                else {
+                    receivingCalendar = null;
+                }
+            }
+            _this.displayDrag(receivingCalendar, interaction);
+            if (!isInvalid) {
+                enableCursor();
+            }
+            else {
+                disableCursor();
+            }
+            if (!isFinal) {
+                if (initialCalendar === receivingCalendar && // TODO: write test for this
+                    isHitsEqual(initialHit, hit)) {
+                    mutation = null;
+                }
+                _this.dragging.setMirrorNeedsRevert(!mutation);
+                // render the mirror if no already-rendered mirror
+                // TODO: wish we could somehow wait for dispatch to guarantee render
+                _this.dragging.setMirrorIsVisible(!hit || !document.querySelector('.fc-mirror'));
+                // assign states based on new hit
+                _this.receivingCalendar = receivingCalendar;
+                _this.validMutation = mutation;
+                _this.mutatedRelevantEvents = mutatedRelevantEvents;
+            }
+        };
+        _this.handlePointerUp = function () {
+            if (!_this.isDragging) {
+                _this.cleanup(); // because handleDragEnd won't fire
+            }
+        };
+        _this.handleDragEnd = function (ev) {
+            if (_this.isDragging) {
+                var initialCalendar_1 = _this.component.calendar;
+                var initialView = _this.component.view;
+                var _a = _this, receivingCalendar = _a.receivingCalendar, validMutation = _a.validMutation;
+                var eventDef = _this.eventRange.def;
+                var eventInstance = _this.eventRange.instance;
+                var eventApi = new EventApi(initialCalendar_1, eventDef, eventInstance);
+                var relevantEvents_1 = _this.relevantEvents;
+                var mutatedRelevantEvents = _this.mutatedRelevantEvents;
+                var finalHit = _this.hitDragging.finalHit;
+                _this.clearDrag(); // must happen after revert animation
+                initialCalendar_1.publiclyTrigger('eventDragStop', [
+                    {
+                        el: _this.subjectSeg.el,
+                        event: eventApi,
+                        jsEvent: ev.origEvent,
+                        view: initialView
+                    }
+                ]);
+                if (validMutation) {
+                    // dropped within same calendar
+                    if (receivingCalendar === initialCalendar_1) {
+                        initialCalendar_1.dispatch({
+                            type: 'MERGE_EVENTS',
+                            eventStore: mutatedRelevantEvents
+                        });
+                        var transformed = {};
+                        for (var _i = 0, _b = initialCalendar_1.pluginSystem.hooks.eventDropTransformers; _i < _b.length; _i++) {
+                            var transformer = _b[_i];
+                            __assign(transformed, transformer(validMutation, initialCalendar_1));
+                        }
+                        var eventDropArg = __assign({}, transformed, { el: ev.subjectEl, delta: validMutation.datesDelta, oldEvent: eventApi, event: new EventApi(// the data AFTER the mutation
+                            initialCalendar_1, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null), revert: function () {
+                                initialCalendar_1.dispatch({
+                                    type: 'MERGE_EVENTS',
+                                    eventStore: relevantEvents_1
+                                });
+                            }, jsEvent: ev.origEvent, view: initialView });
+                        initialCalendar_1.publiclyTrigger('eventDrop', [eventDropArg]);
+                        // dropped in different calendar
+                    }
+                    else if (receivingCalendar) {
+                        initialCalendar_1.publiclyTrigger('eventLeave', [
+                            {
+                                draggedEl: ev.subjectEl,
+                                event: eventApi,
+                                view: initialView
+                            }
+                        ]);
+                        initialCalendar_1.dispatch({
+                            type: 'REMOVE_EVENT_INSTANCES',
+                            instances: _this.mutatedRelevantEvents.instances
+                        });
+                        receivingCalendar.dispatch({
+                            type: 'MERGE_EVENTS',
+                            eventStore: _this.mutatedRelevantEvents
+                        });
+                        if (ev.isTouch) {
+                            receivingCalendar.dispatch({
+                                type: 'SELECT_EVENT',
+                                eventInstanceId: eventInstance.instanceId
+                            });
+                        }
+                        var dropArg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.component // should this be finalHit.component.view? See #4644
+                         });
+                        receivingCalendar.publiclyTrigger('drop', [dropArg]);
+                        receivingCalendar.publiclyTrigger('eventReceive', [
+                            {
+                                draggedEl: ev.subjectEl,
+                                event: new EventApi(// the data AFTER the mutation
+                                receivingCalendar, mutatedRelevantEvents.defs[eventDef.defId], mutatedRelevantEvents.instances[eventInstance.instanceId]),
+                                view: finalHit.component // should this be finalHit.component.view? See #4644
+                            }
+                        ]);
+                    }
+                }
+                else {
+                    initialCalendar_1.publiclyTrigger('_noEventDrop');
+                }
+            }
+            _this.cleanup();
+        };
+        var component = _this.component;
+        var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
+        dragging.pointer.selector = EventDragging.SELECTOR;
+        dragging.touchScrollAllowed = false;
+        dragging.autoScroller.isEnabled = component.opt('dragScroll');
+        var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsStore);
+        hitDragging.useSubjectCenter = settings.useEventCenter;
+        hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+        hitDragging.emitter.on('dragstart', _this.handleDragStart);
+        hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+        hitDragging.emitter.on('pointerup', _this.handlePointerUp);
+        hitDragging.emitter.on('dragend', _this.handleDragEnd);
+        return _this;
+    }
+    EventDragging.prototype.destroy = function () {
+        this.dragging.destroy();
+    };
+    // render a drag state on the next receivingCalendar
+    EventDragging.prototype.displayDrag = function (nextCalendar, state) {
+        var initialCalendar = this.component.calendar;
+        var prevCalendar = this.receivingCalendar;
+        // does the previous calendar need to be cleared?
+        if (prevCalendar && prevCalendar !== nextCalendar) {
+            // does the initial calendar need to be cleared?
+            // if so, don't clear all the way. we still need to to hide the affectedEvents
+            if (prevCalendar === initialCalendar) {
+                prevCalendar.dispatch({
+                    type: 'SET_EVENT_DRAG',
+                    state: {
+                        affectedEvents: state.affectedEvents,
+                        mutatedEvents: createEmptyEventStore(),
+                        isEvent: true,
+                        origSeg: state.origSeg
+                    }
+                });
+                // completely clear the old calendar if it wasn't the initial
+            }
+            else {
+                prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+            }
+        }
+        if (nextCalendar) {
+            nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state });
+        }
+    };
+    EventDragging.prototype.clearDrag = function () {
+        var initialCalendar = this.component.calendar;
+        var receivingCalendar = this.receivingCalendar;
+        if (receivingCalendar) {
+            receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+        }
+        // the initial calendar might have an dummy drag state from displayDrag
+        if (initialCalendar !== receivingCalendar) {
+            initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+        }
+    };
+    EventDragging.prototype.cleanup = function () {
+        this.subjectSeg = null;
+        this.isDragging = false;
+        this.eventRange = null;
+        this.relevantEvents = null;
+        this.receivingCalendar = null;
+        this.validMutation = null;
+        this.mutatedRelevantEvents = null;
+    };
+    EventDragging.SELECTOR = '.fc-draggable, .fc-resizable'; // TODO: test this in IE11
+    return EventDragging;
+}(Interaction));
+function computeEventMutation(hit0, hit1, massagers) {
+    var dateSpan0 = hit0.dateSpan;
+    var dateSpan1 = hit1.dateSpan;
+    var date0 = dateSpan0.range.start;
+    var date1 = dateSpan1.range.start;
+    var standardProps = {};
+    if (dateSpan0.allDay !== dateSpan1.allDay) {
+        standardProps.allDay = dateSpan1.allDay;
+        standardProps.hasEnd = hit1.component.opt('allDayMaintainDuration');
+        if (dateSpan1.allDay) {
+            // means date1 is already start-of-day,
+            // but date0 needs to be converted
+            date0 = startOfDay(date0);
+        }
+    }
+    var delta = diffDates(date0, date1, hit0.component.dateEnv, hit0.component === hit1.component ?
+        hit0.component.largeUnit :
+        null);
+    if (delta.milliseconds) { // has hours/minutes/seconds
+        standardProps.allDay = false;
+    }
+    var mutation = {
+        datesDelta: delta,
+        standardProps: standardProps
+    };
+    for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) {
+        var massager = massagers_1[_i];
+        massager(mutation, hit0, hit1);
+    }
+    return mutation;
+}
+function getComponentTouchDelay$1(component) {
+    var delay = component.opt('eventLongPressDelay');
+    if (delay == null) {
+        delay = component.opt('longPressDelay');
+    }
+    return delay;
+}
+
+var EventDragging$1 = /** @class */ (function (_super) {
+    __extends(EventDragging, _super);
+    function EventDragging(settings) {
+        var _this = _super.call(this, settings) || this;
+        // internal state
+        _this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg?
+        _this.eventRange = null;
+        _this.relevantEvents = null;
+        _this.validMutation = null;
+        _this.mutatedRelevantEvents = null;
+        _this.handlePointerDown = function (ev) {
+            var component = _this.component;
+            var seg = _this.querySeg(ev);
+            var eventRange = _this.eventRange = seg.eventRange;
+            _this.dragging.minDistance = component.opt('eventDragMinDistance');
+            // if touch, need to be working with a selected event
+            _this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) ||
+                (ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId));
+        };
+        _this.handleDragStart = function (ev) {
+            var calendar = _this.component.calendar;
+            var eventRange = _this.eventRange;
+            _this.relevantEvents = getRelevantEvents(calendar.state.eventStore, _this.eventRange.instance.instanceId);
+            _this.draggingSeg = _this.querySeg(ev);
+            calendar.unselect();
+            calendar.publiclyTrigger('eventResizeStart', [
+                {
+                    el: _this.draggingSeg.el,
+                    event: new EventApi(calendar, eventRange.def, eventRange.instance),
+                    jsEvent: ev.origEvent,
+                    view: _this.component.view
+                }
+            ]);
+        };
+        _this.handleHitUpdate = function (hit, isFinal, ev) {
+            var calendar = _this.component.calendar;
+            var relevantEvents = _this.relevantEvents;
+            var initialHit = _this.hitDragging.initialHit;
+            var eventInstance = _this.eventRange.instance;
+            var mutation = null;
+            var mutatedRelevantEvents = null;
+            var isInvalid = false;
+            var interaction = {
+                affectedEvents: relevantEvents,
+                mutatedEvents: createEmptyEventStore(),
+                isEvent: true,
+                origSeg: _this.draggingSeg
+            };
+            if (hit) {
+                mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-start-resizer'), eventInstance.range, calendar.pluginSystem.hooks.eventResizeJoinTransforms);
+            }
+            if (mutation) {
+                mutatedRelevantEvents = applyMutationToEventStore(relevantEvents, calendar.eventUiBases, mutation, calendar);
+                interaction.mutatedEvents = mutatedRelevantEvents;
+                if (!_this.component.isInteractionValid(interaction)) {
+                    isInvalid = true;
+                    mutation = null;
+                    mutatedRelevantEvents = null;
+                    interaction.mutatedEvents = null;
+                }
+            }
+            if (mutatedRelevantEvents) {
+                calendar.dispatch({
+                    type: 'SET_EVENT_RESIZE',
+                    state: interaction
+                });
+            }
+            else {
+                calendar.dispatch({ type: 'UNSET_EVENT_RESIZE' });
+            }
+            if (!isInvalid) {
+                enableCursor();
+            }
+            else {
+                disableCursor();
+            }
+            if (!isFinal) {
+                if (mutation && isHitsEqual(initialHit, hit)) {
+                    mutation = null;
+                }
+                _this.validMutation = mutation;
+                _this.mutatedRelevantEvents = mutatedRelevantEvents;
+            }
+        };
+        _this.handleDragEnd = function (ev) {
+            var calendar = _this.component.calendar;
+            var view = _this.component.view;
+            var eventDef = _this.eventRange.def;
+            var eventInstance = _this.eventRange.instance;
+            var eventApi = new EventApi(calendar, eventDef, eventInstance);
+            var relevantEvents = _this.relevantEvents;
+            var mutatedRelevantEvents = _this.mutatedRelevantEvents;
+            calendar.publiclyTrigger('eventResizeStop', [
+                {
+                    el: _this.draggingSeg.el,
+                    event: eventApi,
+                    jsEvent: ev.origEvent,
+                    view: view
+                }
+            ]);
+            if (_this.validMutation) {
+                calendar.dispatch({
+                    type: 'MERGE_EVENTS',
+                    eventStore: mutatedRelevantEvents
+                });
+                calendar.publiclyTrigger('eventResize', [
+                    {
+                        el: _this.draggingSeg.el,
+                        startDelta: _this.validMutation.startDelta || createDuration(0),
+                        endDelta: _this.validMutation.endDelta || createDuration(0),
+                        prevEvent: eventApi,
+                        event: new EventApi(// the data AFTER the mutation
+                        calendar, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null),
+                        revert: function () {
+                            calendar.dispatch({
+                                type: 'MERGE_EVENTS',
+                                eventStore: relevantEvents
+                            });
+                        },
+                        jsEvent: ev.origEvent,
+                        view: view
+                    }
+                ]);
+            }
+            else {
+                calendar.publiclyTrigger('_noEventResize');
+            }
+            // reset all internal state
+            _this.draggingSeg = null;
+            _this.relevantEvents = null;
+            _this.validMutation = null;
+            // okay to keep eventInstance around. useful to set it in handlePointerDown
+        };
+        var component = settings.component;
+        var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
+        dragging.pointer.selector = '.fc-resizer';
+        dragging.touchScrollAllowed = false;
+        dragging.autoScroller.isEnabled = component.opt('dragScroll');
+        var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, interactionSettingsToStore(settings));
+        hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+        hitDragging.emitter.on('dragstart', _this.handleDragStart);
+        hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+        hitDragging.emitter.on('dragend', _this.handleDragEnd);
+        return _this;
+    }
+    EventDragging.prototype.destroy = function () {
+        this.dragging.destroy();
+    };
+    EventDragging.prototype.querySeg = function (ev) {
+        return getElSeg(elementClosest(ev.subjectEl, this.component.fgSegSelector));
+    };
+    return EventDragging;
+}(Interaction));
+function computeMutation(hit0, hit1, isFromStart, instanceRange, transforms) {
+    var dateEnv = hit0.component.dateEnv;
+    var date0 = hit0.dateSpan.range.start;
+    var date1 = hit1.dateSpan.range.start;
+    var delta = diffDates(date0, date1, dateEnv, hit0.component.largeUnit);
+    var props = {};
+    for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) {
+        var transform = transforms_1[_i];
+        var res = transform(hit0, hit1);
+        if (res === false) {
+            return null;
+        }
+        else if (res) {
+            __assign(props, res);
+        }
+    }
+    if (isFromStart) {
+        if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) {
+            props.startDelta = delta;
+            return props;
+        }
+    }
+    else {
+        if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
+            props.endDelta = delta;
+            return props;
+        }
+    }
+    return null;
+}
+
+var UnselectAuto = /** @class */ (function () {
+    function UnselectAuto(calendar) {
+        var _this = this;
+        this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system
+        this.onSelect = function (selectInfo) {
+            if (selectInfo.jsEvent) {
+                _this.isRecentPointerDateSelect = true;
+            }
+        };
+        this.onDocumentPointerUp = function (pev) {
+            var _a = _this, calendar = _a.calendar, documentPointer = _a.documentPointer;
+            var state = calendar.state;
+            // touch-scrolling should never unfocus any type of selection
+            if (!documentPointer.wasTouchScroll) {
+                if (state.dateSelection && // an existing date selection?
+                    !_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp?
+                ) {
+                    var unselectAuto = calendar.viewOpt('unselectAuto');
+                    var unselectCancel = calendar.viewOpt('unselectCancel');
+                    if (unselectAuto && (!unselectAuto || !elementClosest(documentPointer.downEl, unselectCancel))) {
+                        calendar.unselect(pev);
+                    }
+                }
+                if (state.eventSelection && // an existing event selected?
+                    !elementClosest(documentPointer.downEl, EventDragging.SELECTOR) // interaction DIDN'T start on an event
+                ) {
+                    calendar.dispatch({ type: 'UNSELECT_EVENT' });
+                }
+            }
+            _this.isRecentPointerDateSelect = false;
+        };
+        this.calendar = calendar;
+        var documentPointer = this.documentPointer = new PointerDragging(document);
+        documentPointer.shouldIgnoreMove = true;
+        documentPointer.shouldWatchScroll = false;
+        documentPointer.emitter.on('pointerup', this.onDocumentPointerUp);
+        /*
+        TODO: better way to know about whether there was a selection with the pointer
+        */
+        calendar.on('select', this.onSelect);
+    }
+    UnselectAuto.prototype.destroy = function () {
+        this.calendar.off('select', this.onSelect);
+        this.documentPointer.destroy();
+    };
+    return UnselectAuto;
+}());
+
+/*
+Given an already instantiated draggable object for one-or-more elements,
+Interprets any dragging as an attempt to drag an events that lives outside
+of a calendar onto a calendar.
+*/
+var ExternalElementDragging = /** @class */ (function () {
+    function ExternalElementDragging(dragging, suppliedDragMeta) {
+        var _this = this;
+        this.receivingCalendar = null;
+        this.droppableEvent = null; // will exist for all drags, even if create:false
+        this.suppliedDragMeta = null;
+        this.dragMeta = null;
+        this.handleDragStart = function (ev) {
+            _this.dragMeta = _this.buildDragMeta(ev.subjectEl);
+        };
+        this.handleHitUpdate = function (hit, isFinal, ev) {
+            var dragging = _this.hitDragging.dragging;
+            var receivingCalendar = null;
+            var droppableEvent = null;
+            var isInvalid = false;
+            var interaction = {
+                affectedEvents: createEmptyEventStore(),
+                mutatedEvents: createEmptyEventStore(),
+                isEvent: _this.dragMeta.create,
+                origSeg: null
+            };
+            if (hit) {
+                receivingCalendar = hit.component.calendar;
+                if (_this.canDropElOnCalendar(ev.subjectEl, receivingCalendar)) {
+                    droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingCalendar);
+                    interaction.mutatedEvents = eventTupleToStore(droppableEvent);
+                    isInvalid = !isInteractionValid(interaction, receivingCalendar);
+                    if (isInvalid) {
+                        interaction.mutatedEvents = createEmptyEventStore();
+                        droppableEvent = null;
+                    }
+                }
+            }
+            _this.displayDrag(receivingCalendar, interaction);
+            // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?)
+            // TODO: wish we could somehow wait for dispatch to guarantee render
+            dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-mirror'));
+            if (!isInvalid) {
+                enableCursor();
+            }
+            else {
+                disableCursor();
+            }
+            if (!isFinal) {
+                dragging.setMirrorNeedsRevert(!droppableEvent);
+                _this.receivingCalendar = receivingCalendar;
+                _this.droppableEvent = droppableEvent;
+            }
+        };
+        this.handleDragEnd = function (pev) {
+            var _a = _this, receivingCalendar = _a.receivingCalendar, droppableEvent = _a.droppableEvent;
+            _this.clearDrag();
+            if (receivingCalendar && droppableEvent) {
+                var finalHit = _this.hitDragging.finalHit;
+                var finalView = finalHit.component.view;
+                var dragMeta = _this.dragMeta;
+                var arg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView });
+                receivingCalendar.publiclyTrigger('drop', [arg]);
+                if (dragMeta.create) {
+                    receivingCalendar.dispatch({
+                        type: 'MERGE_EVENTS',
+                        eventStore: eventTupleToStore(droppableEvent)
+                    });
+                    if (pev.isTouch) {
+                        receivingCalendar.dispatch({
+                            type: 'SELECT_EVENT',
+                            eventInstanceId: droppableEvent.instance.instanceId
+                        });
+                    }
+                    // signal that an external event landed
+                    receivingCalendar.publiclyTrigger('eventReceive', [
+                        {
+                            draggedEl: pev.subjectEl,
+                            event: new EventApi(receivingCalendar, droppableEvent.def, droppableEvent.instance),
+                            view: finalView
+                        }
+                    ]);
+                }
+            }
+            _this.receivingCalendar = null;
+            _this.droppableEvent = null;
+        };
+        var hitDragging = this.hitDragging = new HitDragging(dragging, interactionSettingsStore);
+        hitDragging.requireInitial = false; // will start outside of a component
+        hitDragging.emitter.on('dragstart', this.handleDragStart);
+        hitDragging.emitter.on('hitupdate', this.handleHitUpdate);
+        hitDragging.emitter.on('dragend', this.handleDragEnd);
+        this.suppliedDragMeta = suppliedDragMeta;
+    }
+    ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) {
+        if (typeof this.suppliedDragMeta === 'object') {
+            return parseDragMeta(this.suppliedDragMeta);
+        }
+        else if (typeof this.suppliedDragMeta === 'function') {
+            return parseDragMeta(this.suppliedDragMeta(subjectEl));
+        }
+        else {
+            return getDragMetaFromEl(subjectEl);
+        }
+    };
+    ExternalElementDragging.prototype.displayDrag = function (nextCalendar, state) {
+        var prevCalendar = this.receivingCalendar;
+        if (prevCalendar && prevCalendar !== nextCalendar) {
+            prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+        }
+        if (nextCalendar) {
+            nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state });
+        }
+    };
+    ExternalElementDragging.prototype.clearDrag = function () {
+        if (this.receivingCalendar) {
+            this.receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+        }
+    };
+    ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingCalendar) {
+        var dropAccept = receivingCalendar.opt('dropAccept');
+        if (typeof dropAccept === 'function') {
+            return dropAccept(el);
+        }
+        else if (typeof dropAccept === 'string' && dropAccept) {
+            return Boolean(elementMatches(el, dropAccept));
+        }
+        return true;
+    };
+    return ExternalElementDragging;
+}());
+// Utils for computing event store from the DragMeta
+// ----------------------------------------------------------------------------------------------------
+function computeEventForDateSpan(dateSpan, dragMeta, calendar) {
+    var defProps = __assign({}, dragMeta.leftoverProps);
+    for (var _i = 0, _a = calendar.pluginSystem.hooks.externalDefTransforms; _i < _a.length; _i++) {
+        var transform = _a[_i];
+        __assign(defProps, transform(dateSpan, dragMeta));
+    }
+    var def = parseEventDef(defProps, dragMeta.sourceId, dateSpan.allDay, calendar.opt('forceEventDuration') || Boolean(dragMeta.duration), // hasEnd
+    calendar);
+    var start = dateSpan.range.start;
+    // only rely on time info if drop zone is all-day,
+    // otherwise, we already know the time
+    if (dateSpan.allDay && dragMeta.startTime) {
+        start = calendar.dateEnv.add(start, dragMeta.startTime);
+    }
+    var end = dragMeta.duration ?
+        calendar.dateEnv.add(start, dragMeta.duration) :
+        calendar.getDefaultEventEnd(dateSpan.allDay, start);
+    var instance = createEventInstance(def.defId, { start: start, end: end });
+    return { def: def, instance: instance };
+}
+// Utils for extracting data from element
+// ----------------------------------------------------------------------------------------------------
+function getDragMetaFromEl(el) {
+    var str = getEmbeddedElData(el, 'event');
+    var obj = str ?
+        JSON.parse(str) :
+        { create: false }; // if no embedded data, assume no event creation
+    return parseDragMeta(obj);
+}
+config.dataAttrPrefix = '';
+function getEmbeddedElData(el, name) {
+    var prefix = config.dataAttrPrefix;
+    var prefixedName = (prefix ? prefix + '-' : '') + name;
+    return el.getAttribute('data-' + prefixedName) || '';
+}
+
+/*
+Makes an element (that is *external* to any calendar) draggable.
+Can pass in data that determines how an event will be created when dropped onto a calendar.
+Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system.
+*/
+var ExternalDraggable = /** @class */ (function () {
+    function ExternalDraggable(el, settings) {
+        var _this = this;
+        if (settings === void 0) { settings = {}; }
+        this.handlePointerDown = function (ev) {
+            var dragging = _this.dragging;
+            var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay;
+            dragging.minDistance =
+                minDistance != null ?
+                    minDistance :
+                    (ev.isTouch ? 0 : globalDefaults.eventDragMinDistance);
+            dragging.delay =
+                ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
+                    (longPressDelay != null ? longPressDelay : globalDefaults.longPressDelay) :
+                    0;
+        };
+        this.handleDragStart = function (ev) {
+            if (ev.isTouch &&
+                _this.dragging.delay &&
+                ev.subjectEl.classList.contains('fc-event')) {
+                _this.dragging.mirror.getMirrorEl().classList.add('fc-selected');
+            }
+        };
+        this.settings = settings;
+        var dragging = this.dragging = new FeaturefulElementDragging(el);
+        dragging.touchScrollAllowed = false;
+        if (settings.itemSelector != null) {
+            dragging.pointer.selector = settings.itemSelector;
+        }
+        if (settings.appendTo != null) {
+            dragging.mirror.parentNode = settings.appendTo; // TODO: write tests
+        }
+        dragging.emitter.on('pointerdown', this.handlePointerDown);
+        dragging.emitter.on('dragstart', this.handleDragStart);
+        new ExternalElementDragging(dragging, settings.eventData);
+    }
+    ExternalDraggable.prototype.destroy = function () {
+        this.dragging.destroy();
+    };
+    return ExternalDraggable;
+}());
+
+/*
+Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements.
+The third-party system is responsible for drawing the visuals effects of the drag.
+This class simply monitors for pointer movements and fires events.
+It also has the ability to hide the moving element (the "mirror") during the drag.
+*/
+var InferredElementDragging = /** @class */ (function (_super) {
+    __extends(InferredElementDragging, _super);
+    function InferredElementDragging(containerEl) {
+        var _this = _super.call(this, containerEl) || this;
+        _this.shouldIgnoreMove = false;
+        _this.mirrorSelector = '';
+        _this.currentMirrorEl = null;
+        _this.handlePointerDown = function (ev) {
+            _this.emitter.trigger('pointerdown', ev);
+            if (!_this.shouldIgnoreMove) {
+                // fire dragstart right away. does not support delay or min-distance
+                _this.emitter.trigger('dragstart', ev);
+            }
+        };
+        _this.handlePointerMove = function (ev) {
+            if (!_this.shouldIgnoreMove) {
+                _this.emitter.trigger('dragmove', ev);
+            }
+        };
+        _this.handlePointerUp = function (ev) {
+            _this.emitter.trigger('pointerup', ev);
+            if (!_this.shouldIgnoreMove) {
+                // fire dragend right away. does not support a revert animation
+                _this.emitter.trigger('dragend', ev);
+            }
+        };
+        var pointer = _this.pointer = new PointerDragging(containerEl);
+        pointer.emitter.on('pointerdown', _this.handlePointerDown);
+        pointer.emitter.on('pointermove', _this.handlePointerMove);
+        pointer.emitter.on('pointerup', _this.handlePointerUp);
+        return _this;
+    }
+    InferredElementDragging.prototype.destroy = function () {
+        this.pointer.destroy();
+    };
+    InferredElementDragging.prototype.setIgnoreMove = function (bool) {
+        this.shouldIgnoreMove = bool;
+    };
+    InferredElementDragging.prototype.setMirrorIsVisible = function (bool) {
+        if (bool) {
+            // restore a previously hidden element.
+            // use the reference in case the selector class has already been removed.
+            if (this.currentMirrorEl) {
+                this.currentMirrorEl.style.visibility = '';
+                this.currentMirrorEl = null;
+            }
+        }
+        else {
+            var mirrorEl = this.mirrorSelector ?
+                document.querySelector(this.mirrorSelector) :
+                null;
+            if (mirrorEl) {
+                this.currentMirrorEl = mirrorEl;
+                mirrorEl.style.visibility = 'hidden';
+            }
+        }
+    };
+    return InferredElementDragging;
+}(ElementDragging));
+
+/*
+Bridges third-party drag-n-drop systems with FullCalendar.
+Must be instantiated and destroyed by caller.
+*/
+var ThirdPartyDraggable = /** @class */ (function () {
+    function ThirdPartyDraggable(containerOrSettings, settings) {
+        var containerEl = document;
+        if (
+        // wish we could just test instanceof EventTarget, but doesn't work in IE11
+        containerOrSettings === document ||
+            containerOrSettings instanceof Element) {
+            containerEl = containerOrSettings;
+            settings = settings || {};
+        }
+        else {
+            settings = (containerOrSettings || {});
+        }
+        var dragging = this.dragging = new InferredElementDragging(containerEl);
+        if (typeof settings.itemSelector === 'string') {
+            dragging.pointer.selector = settings.itemSelector;
+        }
+        else if (containerEl === document) {
+            dragging.pointer.selector = '[data-event]';
+        }
+        if (typeof settings.mirrorSelector === 'string') {
+            dragging.mirrorSelector = settings.mirrorSelector;
+        }
+        new ExternalElementDragging(dragging, settings.eventData);
+    }
+    ThirdPartyDraggable.prototype.destroy = function () {
+        this.dragging.destroy();
+    };
+    return ThirdPartyDraggable;
+}());
+
+var main = createPlugin({
+    componentInteractions: [DateClicking, DateSelecting, EventDragging, EventDragging$1],
+    calendarInteractions: [UnselectAuto],
+    elementDraggingImpl: FeaturefulElementDragging
+});
+
+export default main;
+export { ExternalDraggable as Draggable, FeaturefulElementDragging, PointerDragging, ThirdPartyDraggable };
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/main.js b/AKPlan/static/AKPlan/fullcalendar/interaction/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..10589f28e354060df8481d16dd181830c1ebe787
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/main.js
@@ -0,0 +1,2143 @@
+/*!
+FullCalendar Interaction Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarInteraction = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    core.config.touchMouseIgnoreWait = 500;
+    var ignoreMouseDepth = 0;
+    var listenerCnt = 0;
+    var isWindowTouchMoveCancelled = false;
+    /*
+    Uses a "pointer" abstraction, which monitors UI events for both mouse and touch.
+    Tracks when the pointer "drags" on a certain element, meaning down+move+up.
+
+    Also, tracks if there was touch-scrolling.
+    Also, can prevent touch-scrolling from happening.
+    Also, can fire pointermove events when scrolling happens underneath, even when no real pointer movement.
+
+    emits:
+    - pointerdown
+    - pointermove
+    - pointerup
+    */
+    var PointerDragging = /** @class */ (function () {
+        function PointerDragging(containerEl) {
+            var _this = this;
+            this.subjectEl = null;
+            this.downEl = null;
+            // options that can be directly assigned by caller
+            this.selector = ''; // will cause subjectEl in all emitted events to be this element
+            this.handleSelector = '';
+            this.shouldIgnoreMove = false;
+            this.shouldWatchScroll = true; // for simulating pointermove on scroll
+            // internal states
+            this.isDragging = false;
+            this.isTouchDragging = false;
+            this.wasTouchScroll = false;
+            // Mouse
+            // ----------------------------------------------------------------------------------------------------
+            this.handleMouseDown = function (ev) {
+                if (!_this.shouldIgnoreMouse() &&
+                    isPrimaryMouseButton(ev) &&
+                    _this.tryStart(ev)) {
+                    var pev = _this.createEventFromMouse(ev, true);
+                    _this.emitter.trigger('pointerdown', pev);
+                    _this.initScrollWatch(pev);
+                    if (!_this.shouldIgnoreMove) {
+                        document.addEventListener('mousemove', _this.handleMouseMove);
+                    }
+                    document.addEventListener('mouseup', _this.handleMouseUp);
+                }
+            };
+            this.handleMouseMove = function (ev) {
+                var pev = _this.createEventFromMouse(ev);
+                _this.recordCoords(pev);
+                _this.emitter.trigger('pointermove', pev);
+            };
+            this.handleMouseUp = function (ev) {
+                document.removeEventListener('mousemove', _this.handleMouseMove);
+                document.removeEventListener('mouseup', _this.handleMouseUp);
+                _this.emitter.trigger('pointerup', _this.createEventFromMouse(ev));
+                _this.cleanup(); // call last so that pointerup has access to props
+            };
+            // Touch
+            // ----------------------------------------------------------------------------------------------------
+            this.handleTouchStart = function (ev) {
+                if (_this.tryStart(ev)) {
+                    _this.isTouchDragging = true;
+                    var pev = _this.createEventFromTouch(ev, true);
+                    _this.emitter.trigger('pointerdown', pev);
+                    _this.initScrollWatch(pev);
+                    // unlike mouse, need to attach to target, not document
+                    // https://stackoverflow.com/a/45760014
+                    var target = ev.target;
+                    if (!_this.shouldIgnoreMove) {
+                        target.addEventListener('touchmove', _this.handleTouchMove);
+                    }
+                    target.addEventListener('touchend', _this.handleTouchEnd);
+                    target.addEventListener('touchcancel', _this.handleTouchEnd); // treat it as a touch end
+                    // attach a handler to get called when ANY scroll action happens on the page.
+                    // this was impossible to do with normal on/off because 'scroll' doesn't bubble.
+                    // http://stackoverflow.com/a/32954565/96342
+                    window.addEventListener('scroll', _this.handleTouchScroll, true // useCapture
+                    );
+                }
+            };
+            this.handleTouchMove = function (ev) {
+                var pev = _this.createEventFromTouch(ev);
+                _this.recordCoords(pev);
+                _this.emitter.trigger('pointermove', pev);
+            };
+            this.handleTouchEnd = function (ev) {
+                if (_this.isDragging) { // done to guard against touchend followed by touchcancel
+                    var target = ev.target;
+                    target.removeEventListener('touchmove', _this.handleTouchMove);
+                    target.removeEventListener('touchend', _this.handleTouchEnd);
+                    target.removeEventListener('touchcancel', _this.handleTouchEnd);
+                    window.removeEventListener('scroll', _this.handleTouchScroll, true); // useCaptured=true
+                    _this.emitter.trigger('pointerup', _this.createEventFromTouch(ev));
+                    _this.cleanup(); // call last so that pointerup has access to props
+                    _this.isTouchDragging = false;
+                    startIgnoringMouse();
+                }
+            };
+            this.handleTouchScroll = function () {
+                _this.wasTouchScroll = true;
+            };
+            this.handleScroll = function (ev) {
+                if (!_this.shouldIgnoreMove) {
+                    var pageX = (window.pageXOffset - _this.prevScrollX) + _this.prevPageX;
+                    var pageY = (window.pageYOffset - _this.prevScrollY) + _this.prevPageY;
+                    _this.emitter.trigger('pointermove', {
+                        origEvent: ev,
+                        isTouch: _this.isTouchDragging,
+                        subjectEl: _this.subjectEl,
+                        pageX: pageX,
+                        pageY: pageY,
+                        deltaX: pageX - _this.origPageX,
+                        deltaY: pageY - _this.origPageY
+                    });
+                }
+            };
+            this.containerEl = containerEl;
+            this.emitter = new core.EmitterMixin();
+            containerEl.addEventListener('mousedown', this.handleMouseDown);
+            containerEl.addEventListener('touchstart', this.handleTouchStart, { passive: true });
+            listenerCreated();
+        }
+        PointerDragging.prototype.destroy = function () {
+            this.containerEl.removeEventListener('mousedown', this.handleMouseDown);
+            this.containerEl.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
+            listenerDestroyed();
+        };
+        PointerDragging.prototype.tryStart = function (ev) {
+            var subjectEl = this.querySubjectEl(ev);
+            var downEl = ev.target;
+            if (subjectEl &&
+                (!this.handleSelector || core.elementClosest(downEl, this.handleSelector))) {
+                this.subjectEl = subjectEl;
+                this.downEl = downEl;
+                this.isDragging = true; // do this first so cancelTouchScroll will work
+                this.wasTouchScroll = false;
+                return true;
+            }
+            return false;
+        };
+        PointerDragging.prototype.cleanup = function () {
+            isWindowTouchMoveCancelled = false;
+            this.isDragging = false;
+            this.subjectEl = null;
+            this.downEl = null;
+            // keep wasTouchScroll around for later access
+            this.destroyScrollWatch();
+        };
+        PointerDragging.prototype.querySubjectEl = function (ev) {
+            if (this.selector) {
+                return core.elementClosest(ev.target, this.selector);
+            }
+            else {
+                return this.containerEl;
+            }
+        };
+        PointerDragging.prototype.shouldIgnoreMouse = function () {
+            return ignoreMouseDepth || this.isTouchDragging;
+        };
+        // can be called by user of this class, to cancel touch-based scrolling for the current drag
+        PointerDragging.prototype.cancelTouchScroll = function () {
+            if (this.isDragging) {
+                isWindowTouchMoveCancelled = true;
+            }
+        };
+        // Scrolling that simulates pointermoves
+        // ----------------------------------------------------------------------------------------------------
+        PointerDragging.prototype.initScrollWatch = function (ev) {
+            if (this.shouldWatchScroll) {
+                this.recordCoords(ev);
+                window.addEventListener('scroll', this.handleScroll, true); // useCapture=true
+            }
+        };
+        PointerDragging.prototype.recordCoords = function (ev) {
+            if (this.shouldWatchScroll) {
+                this.prevPageX = ev.pageX;
+                this.prevPageY = ev.pageY;
+                this.prevScrollX = window.pageXOffset;
+                this.prevScrollY = window.pageYOffset;
+            }
+        };
+        PointerDragging.prototype.destroyScrollWatch = function () {
+            if (this.shouldWatchScroll) {
+                window.removeEventListener('scroll', this.handleScroll, true); // useCaptured=true
+            }
+        };
+        // Event Normalization
+        // ----------------------------------------------------------------------------------------------------
+        PointerDragging.prototype.createEventFromMouse = function (ev, isFirst) {
+            var deltaX = 0;
+            var deltaY = 0;
+            // TODO: repeat code
+            if (isFirst) {
+                this.origPageX = ev.pageX;
+                this.origPageY = ev.pageY;
+            }
+            else {
+                deltaX = ev.pageX - this.origPageX;
+                deltaY = ev.pageY - this.origPageY;
+            }
+            return {
+                origEvent: ev,
+                isTouch: false,
+                subjectEl: this.subjectEl,
+                pageX: ev.pageX,
+                pageY: ev.pageY,
+                deltaX: deltaX,
+                deltaY: deltaY
+            };
+        };
+        PointerDragging.prototype.createEventFromTouch = function (ev, isFirst) {
+            var touches = ev.touches;
+            var pageX;
+            var pageY;
+            var deltaX = 0;
+            var deltaY = 0;
+            // if touch coords available, prefer,
+            // because FF would give bad ev.pageX ev.pageY
+            if (touches && touches.length) {
+                pageX = touches[0].pageX;
+                pageY = touches[0].pageY;
+            }
+            else {
+                pageX = ev.pageX;
+                pageY = ev.pageY;
+            }
+            // TODO: repeat code
+            if (isFirst) {
+                this.origPageX = pageX;
+                this.origPageY = pageY;
+            }
+            else {
+                deltaX = pageX - this.origPageX;
+                deltaY = pageY - this.origPageY;
+            }
+            return {
+                origEvent: ev,
+                isTouch: true,
+                subjectEl: this.subjectEl,
+                pageX: pageX,
+                pageY: pageY,
+                deltaX: deltaX,
+                deltaY: deltaY
+            };
+        };
+        return PointerDragging;
+    }());
+    // Returns a boolean whether this was a left mouse click and no ctrl key (which means right click on Mac)
+    function isPrimaryMouseButton(ev) {
+        return ev.button === 0 && !ev.ctrlKey;
+    }
+    // Ignoring fake mouse events generated by touch
+    // ----------------------------------------------------------------------------------------------------
+    function startIgnoringMouse() {
+        ignoreMouseDepth++;
+        setTimeout(function () {
+            ignoreMouseDepth--;
+        }, core.config.touchMouseIgnoreWait);
+    }
+    // We want to attach touchmove as early as possible for Safari
+    // ----------------------------------------------------------------------------------------------------
+    function listenerCreated() {
+        if (!(listenerCnt++)) {
+            window.addEventListener('touchmove', onWindowTouchMove, { passive: false });
+        }
+    }
+    function listenerDestroyed() {
+        if (!(--listenerCnt)) {
+            window.removeEventListener('touchmove', onWindowTouchMove, { passive: false });
+        }
+    }
+    function onWindowTouchMove(ev) {
+        if (isWindowTouchMoveCancelled) {
+            ev.preventDefault();
+        }
+    }
+
+    /*
+    An effect in which an element follows the movement of a pointer across the screen.
+    The moving element is a clone of some other element.
+    Must call start + handleMove + stop.
+    */
+    var ElementMirror = /** @class */ (function () {
+        function ElementMirror() {
+            this.isVisible = false; // must be explicitly enabled
+            this.sourceEl = null;
+            this.mirrorEl = null;
+            this.sourceElRect = null; // screen coords relative to viewport
+            // options that can be set directly by caller
+            this.parentNode = document.body;
+            this.zIndex = 9999;
+            this.revertDuration = 0;
+        }
+        ElementMirror.prototype.start = function (sourceEl, pageX, pageY) {
+            this.sourceEl = sourceEl;
+            this.sourceElRect = this.sourceEl.getBoundingClientRect();
+            this.origScreenX = pageX - window.pageXOffset;
+            this.origScreenY = pageY - window.pageYOffset;
+            this.deltaX = 0;
+            this.deltaY = 0;
+            this.updateElPosition();
+        };
+        ElementMirror.prototype.handleMove = function (pageX, pageY) {
+            this.deltaX = (pageX - window.pageXOffset) - this.origScreenX;
+            this.deltaY = (pageY - window.pageYOffset) - this.origScreenY;
+            this.updateElPosition();
+        };
+        // can be called before start
+        ElementMirror.prototype.setIsVisible = function (bool) {
+            if (bool) {
+                if (!this.isVisible) {
+                    if (this.mirrorEl) {
+                        this.mirrorEl.style.display = '';
+                    }
+                    this.isVisible = bool; // needs to happen before updateElPosition
+                    this.updateElPosition(); // because was not updating the position while invisible
+                }
+            }
+            else {
+                if (this.isVisible) {
+                    if (this.mirrorEl) {
+                        this.mirrorEl.style.display = 'none';
+                    }
+                    this.isVisible = bool;
+                }
+            }
+        };
+        // always async
+        ElementMirror.prototype.stop = function (needsRevertAnimation, callback) {
+            var _this = this;
+            var done = function () {
+                _this.cleanup();
+                callback();
+            };
+            if (needsRevertAnimation &&
+                this.mirrorEl &&
+                this.isVisible &&
+                this.revertDuration && // if 0, transition won't work
+                (this.deltaX || this.deltaY) // if same coords, transition won't work
+            ) {
+                this.doRevertAnimation(done, this.revertDuration);
+            }
+            else {
+                setTimeout(done, 0);
+            }
+        };
+        ElementMirror.prototype.doRevertAnimation = function (callback, revertDuration) {
+            var mirrorEl = this.mirrorEl;
+            var finalSourceElRect = this.sourceEl.getBoundingClientRect(); // because autoscrolling might have happened
+            mirrorEl.style.transition =
+                'top ' + revertDuration + 'ms,' +
+                    'left ' + revertDuration + 'ms';
+            core.applyStyle(mirrorEl, {
+                left: finalSourceElRect.left,
+                top: finalSourceElRect.top
+            });
+            core.whenTransitionDone(mirrorEl, function () {
+                mirrorEl.style.transition = '';
+                callback();
+            });
+        };
+        ElementMirror.prototype.cleanup = function () {
+            if (this.mirrorEl) {
+                core.removeElement(this.mirrorEl);
+                this.mirrorEl = null;
+            }
+            this.sourceEl = null;
+        };
+        ElementMirror.prototype.updateElPosition = function () {
+            if (this.sourceEl && this.isVisible) {
+                core.applyStyle(this.getMirrorEl(), {
+                    left: this.sourceElRect.left + this.deltaX,
+                    top: this.sourceElRect.top + this.deltaY
+                });
+            }
+        };
+        ElementMirror.prototype.getMirrorEl = function () {
+            var sourceElRect = this.sourceElRect;
+            var mirrorEl = this.mirrorEl;
+            if (!mirrorEl) {
+                mirrorEl = this.mirrorEl = this.sourceEl.cloneNode(true); // cloneChildren=true
+                // we don't want long taps or any mouse interaction causing selection/menus.
+                // would use preventSelection(), but that prevents selectstart, causing problems.
+                mirrorEl.classList.add('fc-unselectable');
+                mirrorEl.classList.add('fc-dragging');
+                core.applyStyle(mirrorEl, {
+                    position: 'fixed',
+                    zIndex: this.zIndex,
+                    visibility: '',
+                    boxSizing: 'border-box',
+                    width: sourceElRect.right - sourceElRect.left,
+                    height: sourceElRect.bottom - sourceElRect.top,
+                    right: 'auto',
+                    bottom: 'auto',
+                    margin: 0
+                });
+                this.parentNode.appendChild(mirrorEl);
+            }
+            return mirrorEl;
+        };
+        return ElementMirror;
+    }());
+
+    /*
+    Is a cache for a given element's scroll information (all the info that ScrollController stores)
+    in addition the "client rectangle" of the element.. the area within the scrollbars.
+
+    The cache can be in one of two modes:
+    - doesListening:false - ignores when the container is scrolled by someone else
+    - doesListening:true - watch for scrolling and update the cache
+    */
+    var ScrollGeomCache = /** @class */ (function (_super) {
+        __extends(ScrollGeomCache, _super);
+        function ScrollGeomCache(scrollController, doesListening) {
+            var _this = _super.call(this) || this;
+            _this.handleScroll = function () {
+                _this.scrollTop = _this.scrollController.getScrollTop();
+                _this.scrollLeft = _this.scrollController.getScrollLeft();
+                _this.handleScrollChange();
+            };
+            _this.scrollController = scrollController;
+            _this.doesListening = doesListening;
+            _this.scrollTop = _this.origScrollTop = scrollController.getScrollTop();
+            _this.scrollLeft = _this.origScrollLeft = scrollController.getScrollLeft();
+            _this.scrollWidth = scrollController.getScrollWidth();
+            _this.scrollHeight = scrollController.getScrollHeight();
+            _this.clientWidth = scrollController.getClientWidth();
+            _this.clientHeight = scrollController.getClientHeight();
+            _this.clientRect = _this.computeClientRect(); // do last in case it needs cached values
+            if (_this.doesListening) {
+                _this.getEventTarget().addEventListener('scroll', _this.handleScroll);
+            }
+            return _this;
+        }
+        ScrollGeomCache.prototype.destroy = function () {
+            if (this.doesListening) {
+                this.getEventTarget().removeEventListener('scroll', this.handleScroll);
+            }
+        };
+        ScrollGeomCache.prototype.getScrollTop = function () {
+            return this.scrollTop;
+        };
+        ScrollGeomCache.prototype.getScrollLeft = function () {
+            return this.scrollLeft;
+        };
+        ScrollGeomCache.prototype.setScrollTop = function (top) {
+            this.scrollController.setScrollTop(top);
+            if (!this.doesListening) {
+                // we are not relying on the element to normalize out-of-bounds scroll values
+                // so we need to sanitize ourselves
+                this.scrollTop = Math.max(Math.min(top, this.getMaxScrollTop()), 0);
+                this.handleScrollChange();
+            }
+        };
+        ScrollGeomCache.prototype.setScrollLeft = function (top) {
+            this.scrollController.setScrollLeft(top);
+            if (!this.doesListening) {
+                // we are not relying on the element to normalize out-of-bounds scroll values
+                // so we need to sanitize ourselves
+                this.scrollLeft = Math.max(Math.min(top, this.getMaxScrollLeft()), 0);
+                this.handleScrollChange();
+            }
+        };
+        ScrollGeomCache.prototype.getClientWidth = function () {
+            return this.clientWidth;
+        };
+        ScrollGeomCache.prototype.getClientHeight = function () {
+            return this.clientHeight;
+        };
+        ScrollGeomCache.prototype.getScrollWidth = function () {
+            return this.scrollWidth;
+        };
+        ScrollGeomCache.prototype.getScrollHeight = function () {
+            return this.scrollHeight;
+        };
+        ScrollGeomCache.prototype.handleScrollChange = function () {
+        };
+        return ScrollGeomCache;
+    }(core.ScrollController));
+    var ElementScrollGeomCache = /** @class */ (function (_super) {
+        __extends(ElementScrollGeomCache, _super);
+        function ElementScrollGeomCache(el, doesListening) {
+            return _super.call(this, new core.ElementScrollController(el), doesListening) || this;
+        }
+        ElementScrollGeomCache.prototype.getEventTarget = function () {
+            return this.scrollController.el;
+        };
+        ElementScrollGeomCache.prototype.computeClientRect = function () {
+            return core.computeInnerRect(this.scrollController.el);
+        };
+        return ElementScrollGeomCache;
+    }(ScrollGeomCache));
+    var WindowScrollGeomCache = /** @class */ (function (_super) {
+        __extends(WindowScrollGeomCache, _super);
+        function WindowScrollGeomCache(doesListening) {
+            return _super.call(this, new core.WindowScrollController(), doesListening) || this;
+        }
+        WindowScrollGeomCache.prototype.getEventTarget = function () {
+            return window;
+        };
+        WindowScrollGeomCache.prototype.computeClientRect = function () {
+            return {
+                left: this.scrollLeft,
+                right: this.scrollLeft + this.clientWidth,
+                top: this.scrollTop,
+                bottom: this.scrollTop + this.clientHeight
+            };
+        };
+        // the window is the only scroll object that changes it's rectangle relative
+        // to the document's topleft as it scrolls
+        WindowScrollGeomCache.prototype.handleScrollChange = function () {
+            this.clientRect = this.computeClientRect();
+        };
+        return WindowScrollGeomCache;
+    }(ScrollGeomCache));
+
+    // If available we are using native "performance" API instead of "Date"
+    // Read more about it on MDN:
+    // https://developer.mozilla.org/en-US/docs/Web/API/Performance
+    var getTime = typeof performance === 'function' ? performance.now : Date.now;
+    /*
+    For a pointer interaction, automatically scrolls certain scroll containers when the pointer
+    approaches the edge.
+
+    The caller must call start + handleMove + stop.
+    */
+    var AutoScroller = /** @class */ (function () {
+        function AutoScroller() {
+            var _this = this;
+            // options that can be set by caller
+            this.isEnabled = true;
+            this.scrollQuery = [window, '.fc-scroller'];
+            this.edgeThreshold = 50; // pixels
+            this.maxVelocity = 300; // pixels per second
+            // internal state
+            this.pointerScreenX = null;
+            this.pointerScreenY = null;
+            this.isAnimating = false;
+            this.scrollCaches = null;
+            // protect against the initial pointerdown being too close to an edge and starting the scroll
+            this.everMovedUp = false;
+            this.everMovedDown = false;
+            this.everMovedLeft = false;
+            this.everMovedRight = false;
+            this.animate = function () {
+                if (_this.isAnimating) { // wasn't cancelled between animation calls
+                    var edge = _this.computeBestEdge(_this.pointerScreenX + window.pageXOffset, _this.pointerScreenY + window.pageYOffset);
+                    if (edge) {
+                        var now = getTime();
+                        _this.handleSide(edge, (now - _this.msSinceRequest) / 1000);
+                        _this.requestAnimation(now);
+                    }
+                    else {
+                        _this.isAnimating = false; // will stop animation
+                    }
+                }
+            };
+        }
+        AutoScroller.prototype.start = function (pageX, pageY) {
+            if (this.isEnabled) {
+                this.scrollCaches = this.buildCaches();
+                this.pointerScreenX = null;
+                this.pointerScreenY = null;
+                this.everMovedUp = false;
+                this.everMovedDown = false;
+                this.everMovedLeft = false;
+                this.everMovedRight = false;
+                this.handleMove(pageX, pageY);
+            }
+        };
+        AutoScroller.prototype.handleMove = function (pageX, pageY) {
+            if (this.isEnabled) {
+                var pointerScreenX = pageX - window.pageXOffset;
+                var pointerScreenY = pageY - window.pageYOffset;
+                var yDelta = this.pointerScreenY === null ? 0 : pointerScreenY - this.pointerScreenY;
+                var xDelta = this.pointerScreenX === null ? 0 : pointerScreenX - this.pointerScreenX;
+                if (yDelta < 0) {
+                    this.everMovedUp = true;
+                }
+                else if (yDelta > 0) {
+                    this.everMovedDown = true;
+                }
+                if (xDelta < 0) {
+                    this.everMovedLeft = true;
+                }
+                else if (xDelta > 0) {
+                    this.everMovedRight = true;
+                }
+                this.pointerScreenX = pointerScreenX;
+                this.pointerScreenY = pointerScreenY;
+                if (!this.isAnimating) {
+                    this.isAnimating = true;
+                    this.requestAnimation(getTime());
+                }
+            }
+        };
+        AutoScroller.prototype.stop = function () {
+            if (this.isEnabled) {
+                this.isAnimating = false; // will stop animation
+                for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                    var scrollCache = _a[_i];
+                    scrollCache.destroy();
+                }
+                this.scrollCaches = null;
+            }
+        };
+        AutoScroller.prototype.requestAnimation = function (now) {
+            this.msSinceRequest = now;
+            requestAnimationFrame(this.animate);
+        };
+        AutoScroller.prototype.handleSide = function (edge, seconds) {
+            var scrollCache = edge.scrollCache;
+            var edgeThreshold = this.edgeThreshold;
+            var invDistance = edgeThreshold - edge.distance;
+            var velocity = // the closer to the edge, the faster we scroll
+             (invDistance * invDistance) / (edgeThreshold * edgeThreshold) * // quadratic
+                this.maxVelocity * seconds;
+            var sign = 1;
+            switch (edge.name) {
+                case 'left':
+                    sign = -1;
+                // falls through
+                case 'right':
+                    scrollCache.setScrollLeft(scrollCache.getScrollLeft() + velocity * sign);
+                    break;
+                case 'top':
+                    sign = -1;
+                // falls through
+                case 'bottom':
+                    scrollCache.setScrollTop(scrollCache.getScrollTop() + velocity * sign);
+                    break;
+            }
+        };
+        // left/top are relative to document topleft
+        AutoScroller.prototype.computeBestEdge = function (left, top) {
+            var edgeThreshold = this.edgeThreshold;
+            var bestSide = null;
+            for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                var scrollCache = _a[_i];
+                var rect = scrollCache.clientRect;
+                var leftDist = left - rect.left;
+                var rightDist = rect.right - left;
+                var topDist = top - rect.top;
+                var bottomDist = rect.bottom - top;
+                // completely within the rect?
+                if (leftDist >= 0 && rightDist >= 0 && topDist >= 0 && bottomDist >= 0) {
+                    if (topDist <= edgeThreshold && this.everMovedUp && scrollCache.canScrollUp() &&
+                        (!bestSide || bestSide.distance > topDist)) {
+                        bestSide = { scrollCache: scrollCache, name: 'top', distance: topDist };
+                    }
+                    if (bottomDist <= edgeThreshold && this.everMovedDown && scrollCache.canScrollDown() &&
+                        (!bestSide || bestSide.distance > bottomDist)) {
+                        bestSide = { scrollCache: scrollCache, name: 'bottom', distance: bottomDist };
+                    }
+                    if (leftDist <= edgeThreshold && this.everMovedLeft && scrollCache.canScrollLeft() &&
+                        (!bestSide || bestSide.distance > leftDist)) {
+                        bestSide = { scrollCache: scrollCache, name: 'left', distance: leftDist };
+                    }
+                    if (rightDist <= edgeThreshold && this.everMovedRight && scrollCache.canScrollRight() &&
+                        (!bestSide || bestSide.distance > rightDist)) {
+                        bestSide = { scrollCache: scrollCache, name: 'right', distance: rightDist };
+                    }
+                }
+            }
+            return bestSide;
+        };
+        AutoScroller.prototype.buildCaches = function () {
+            return this.queryScrollEls().map(function (el) {
+                if (el === window) {
+                    return new WindowScrollGeomCache(false); // false = don't listen to user-generated scrolls
+                }
+                else {
+                    return new ElementScrollGeomCache(el, false); // false = don't listen to user-generated scrolls
+                }
+            });
+        };
+        AutoScroller.prototype.queryScrollEls = function () {
+            var els = [];
+            for (var _i = 0, _a = this.scrollQuery; _i < _a.length; _i++) {
+                var query = _a[_i];
+                if (typeof query === 'object') {
+                    els.push(query);
+                }
+                else {
+                    els.push.apply(els, Array.prototype.slice.call(document.querySelectorAll(query)));
+                }
+            }
+            return els;
+        };
+        return AutoScroller;
+    }());
+
+    /*
+    Monitors dragging on an element. Has a number of high-level features:
+    - minimum distance required before dragging
+    - minimum wait time ("delay") before dragging
+    - a mirror element that follows the pointer
+    */
+    var FeaturefulElementDragging = /** @class */ (function (_super) {
+        __extends(FeaturefulElementDragging, _super);
+        function FeaturefulElementDragging(containerEl) {
+            var _this = _super.call(this, containerEl) || this;
+            // options that can be directly set by caller
+            // the caller can also set the PointerDragging's options as well
+            _this.delay = null;
+            _this.minDistance = 0;
+            _this.touchScrollAllowed = true; // prevents drag from starting and blocks scrolling during drag
+            _this.mirrorNeedsRevert = false;
+            _this.isInteracting = false; // is the user validly moving the pointer? lasts until pointerup
+            _this.isDragging = false; // is it INTENTFULLY dragging? lasts until after revert animation
+            _this.isDelayEnded = false;
+            _this.isDistanceSurpassed = false;
+            _this.delayTimeoutId = null;
+            _this.onPointerDown = function (ev) {
+                if (!_this.isDragging) { // so new drag doesn't happen while revert animation is going
+                    _this.isInteracting = true;
+                    _this.isDelayEnded = false;
+                    _this.isDistanceSurpassed = false;
+                    core.preventSelection(document.body);
+                    core.preventContextMenu(document.body);
+                    // prevent links from being visited if there's an eventual drag.
+                    // also prevents selection in older browsers (maybe?).
+                    // not necessary for touch, besides, browser would complain about passiveness.
+                    if (!ev.isTouch) {
+                        ev.origEvent.preventDefault();
+                    }
+                    _this.emitter.trigger('pointerdown', ev);
+                    if (!_this.pointer.shouldIgnoreMove) {
+                        // actions related to initiating dragstart+dragmove+dragend...
+                        _this.mirror.setIsVisible(false); // reset. caller must set-visible
+                        _this.mirror.start(ev.subjectEl, ev.pageX, ev.pageY); // must happen on first pointer down
+                        _this.startDelay(ev);
+                        if (!_this.minDistance) {
+                            _this.handleDistanceSurpassed(ev);
+                        }
+                    }
+                }
+            };
+            _this.onPointerMove = function (ev) {
+                if (_this.isInteracting) { // if false, still waiting for previous drag's revert
+                    _this.emitter.trigger('pointermove', ev);
+                    if (!_this.isDistanceSurpassed) {
+                        var minDistance = _this.minDistance;
+                        var distanceSq = void 0; // current distance from the origin, squared
+                        var deltaX = ev.deltaX, deltaY = ev.deltaY;
+                        distanceSq = deltaX * deltaX + deltaY * deltaY;
+                        if (distanceSq >= minDistance * minDistance) { // use pythagorean theorem
+                            _this.handleDistanceSurpassed(ev);
+                        }
+                    }
+                    if (_this.isDragging) {
+                        // a real pointer move? (not one simulated by scrolling)
+                        if (ev.origEvent.type !== 'scroll') {
+                            _this.mirror.handleMove(ev.pageX, ev.pageY);
+                            _this.autoScroller.handleMove(ev.pageX, ev.pageY);
+                        }
+                        _this.emitter.trigger('dragmove', ev);
+                    }
+                }
+            };
+            _this.onPointerUp = function (ev) {
+                if (_this.isInteracting) { // if false, still waiting for previous drag's revert
+                    _this.isInteracting = false;
+                    core.allowSelection(document.body);
+                    core.allowContextMenu(document.body);
+                    _this.emitter.trigger('pointerup', ev); // can potentially set mirrorNeedsRevert
+                    if (_this.isDragging) {
+                        _this.autoScroller.stop();
+                        _this.tryStopDrag(ev); // which will stop the mirror
+                    }
+                    if (_this.delayTimeoutId) {
+                        clearTimeout(_this.delayTimeoutId);
+                        _this.delayTimeoutId = null;
+                    }
+                }
+            };
+            var pointer = _this.pointer = new PointerDragging(containerEl);
+            pointer.emitter.on('pointerdown', _this.onPointerDown);
+            pointer.emitter.on('pointermove', _this.onPointerMove);
+            pointer.emitter.on('pointerup', _this.onPointerUp);
+            _this.mirror = new ElementMirror();
+            _this.autoScroller = new AutoScroller();
+            return _this;
+        }
+        FeaturefulElementDragging.prototype.destroy = function () {
+            this.pointer.destroy();
+        };
+        FeaturefulElementDragging.prototype.startDelay = function (ev) {
+            var _this = this;
+            if (typeof this.delay === 'number') {
+                this.delayTimeoutId = setTimeout(function () {
+                    _this.delayTimeoutId = null;
+                    _this.handleDelayEnd(ev);
+                }, this.delay); // not assignable to number!
+            }
+            else {
+                this.handleDelayEnd(ev);
+            }
+        };
+        FeaturefulElementDragging.prototype.handleDelayEnd = function (ev) {
+            this.isDelayEnded = true;
+            this.tryStartDrag(ev);
+        };
+        FeaturefulElementDragging.prototype.handleDistanceSurpassed = function (ev) {
+            this.isDistanceSurpassed = true;
+            this.tryStartDrag(ev);
+        };
+        FeaturefulElementDragging.prototype.tryStartDrag = function (ev) {
+            if (this.isDelayEnded && this.isDistanceSurpassed) {
+                if (!this.pointer.wasTouchScroll || this.touchScrollAllowed) {
+                    this.isDragging = true;
+                    this.mirrorNeedsRevert = false;
+                    this.autoScroller.start(ev.pageX, ev.pageY);
+                    this.emitter.trigger('dragstart', ev);
+                    if (this.touchScrollAllowed === false) {
+                        this.pointer.cancelTouchScroll();
+                    }
+                }
+            }
+        };
+        FeaturefulElementDragging.prototype.tryStopDrag = function (ev) {
+            // .stop() is ALWAYS asynchronous, which we NEED because we want all pointerup events
+            // that come from the document to fire beforehand. much more convenient this way.
+            this.mirror.stop(this.mirrorNeedsRevert, this.stopDrag.bind(this, ev) // bound with args
+            );
+        };
+        FeaturefulElementDragging.prototype.stopDrag = function (ev) {
+            this.isDragging = false;
+            this.emitter.trigger('dragend', ev);
+        };
+        // fill in the implementations...
+        FeaturefulElementDragging.prototype.setIgnoreMove = function (bool) {
+            this.pointer.shouldIgnoreMove = bool;
+        };
+        FeaturefulElementDragging.prototype.setMirrorIsVisible = function (bool) {
+            this.mirror.setIsVisible(bool);
+        };
+        FeaturefulElementDragging.prototype.setMirrorNeedsRevert = function (bool) {
+            this.mirrorNeedsRevert = bool;
+        };
+        FeaturefulElementDragging.prototype.setAutoScrollEnabled = function (bool) {
+            this.autoScroller.isEnabled = bool;
+        };
+        return FeaturefulElementDragging;
+    }(core.ElementDragging));
+
+    /*
+    When this class is instantiated, it records the offset of an element (relative to the document topleft),
+    and continues to monitor scrolling, updating the cached coordinates if it needs to.
+    Does not access the DOM after instantiation, so highly performant.
+
+    Also keeps track of all scrolling/overflow:hidden containers that are parents of the given element
+    and an determine if a given point is inside the combined clipping rectangle.
+    */
+    var OffsetTracker = /** @class */ (function () {
+        function OffsetTracker(el) {
+            this.origRect = core.computeRect(el);
+            // will work fine for divs that have overflow:hidden
+            this.scrollCaches = core.getClippingParents(el).map(function (el) {
+                return new ElementScrollGeomCache(el, true); // listen=true
+            });
+        }
+        OffsetTracker.prototype.destroy = function () {
+            for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                var scrollCache = _a[_i];
+                scrollCache.destroy();
+            }
+        };
+        OffsetTracker.prototype.computeLeft = function () {
+            var left = this.origRect.left;
+            for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                var scrollCache = _a[_i];
+                left += scrollCache.origScrollLeft - scrollCache.getScrollLeft();
+            }
+            return left;
+        };
+        OffsetTracker.prototype.computeTop = function () {
+            var top = this.origRect.top;
+            for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                var scrollCache = _a[_i];
+                top += scrollCache.origScrollTop - scrollCache.getScrollTop();
+            }
+            return top;
+        };
+        OffsetTracker.prototype.isWithinClipping = function (pageX, pageY) {
+            var point = { left: pageX, top: pageY };
+            for (var _i = 0, _a = this.scrollCaches; _i < _a.length; _i++) {
+                var scrollCache = _a[_i];
+                if (!isIgnoredClipping(scrollCache.getEventTarget()) &&
+                    !core.pointInsideRect(point, scrollCache.clientRect)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+        return OffsetTracker;
+    }());
+    // certain clipping containers should never constrain interactions, like <html> and <body>
+    // https://github.com/fullcalendar/fullcalendar/issues/3615
+    function isIgnoredClipping(node) {
+        var tagName = node.tagName;
+        return tagName === 'HTML' || tagName === 'BODY';
+    }
+
+    /*
+    Tracks movement over multiple droppable areas (aka "hits")
+    that exist in one or more DateComponents.
+    Relies on an existing draggable.
+
+    emits:
+    - pointerdown
+    - dragstart
+    - hitchange - fires initially, even if not over a hit
+    - pointerup
+    - (hitchange - again, to null, if ended over a hit)
+    - dragend
+    */
+    var HitDragging = /** @class */ (function () {
+        function HitDragging(dragging, droppableStore) {
+            var _this = this;
+            // options that can be set by caller
+            this.useSubjectCenter = false;
+            this.requireInitial = true; // if doesn't start out on a hit, won't emit any events
+            this.initialHit = null;
+            this.movingHit = null;
+            this.finalHit = null; // won't ever be populated if shouldIgnoreMove
+            this.handlePointerDown = function (ev) {
+                var dragging = _this.dragging;
+                _this.initialHit = null;
+                _this.movingHit = null;
+                _this.finalHit = null;
+                _this.prepareHits();
+                _this.processFirstCoord(ev);
+                if (_this.initialHit || !_this.requireInitial) {
+                    dragging.setIgnoreMove(false);
+                    _this.emitter.trigger('pointerdown', ev); // TODO: fire this before computing processFirstCoord, so listeners can cancel. this gets fired by almost every handler :(
+                }
+                else {
+                    dragging.setIgnoreMove(true);
+                }
+            };
+            this.handleDragStart = function (ev) {
+                _this.emitter.trigger('dragstart', ev);
+                _this.handleMove(ev, true); // force = fire even if initially null
+            };
+            this.handleDragMove = function (ev) {
+                _this.emitter.trigger('dragmove', ev);
+                _this.handleMove(ev);
+            };
+            this.handlePointerUp = function (ev) {
+                _this.releaseHits();
+                _this.emitter.trigger('pointerup', ev);
+            };
+            this.handleDragEnd = function (ev) {
+                if (_this.movingHit) {
+                    _this.emitter.trigger('hitupdate', null, true, ev);
+                }
+                _this.finalHit = _this.movingHit;
+                _this.movingHit = null;
+                _this.emitter.trigger('dragend', ev);
+            };
+            this.droppableStore = droppableStore;
+            dragging.emitter.on('pointerdown', this.handlePointerDown);
+            dragging.emitter.on('dragstart', this.handleDragStart);
+            dragging.emitter.on('dragmove', this.handleDragMove);
+            dragging.emitter.on('pointerup', this.handlePointerUp);
+            dragging.emitter.on('dragend', this.handleDragEnd);
+            this.dragging = dragging;
+            this.emitter = new core.EmitterMixin();
+        }
+        // sets initialHit
+        // sets coordAdjust
+        HitDragging.prototype.processFirstCoord = function (ev) {
+            var origPoint = { left: ev.pageX, top: ev.pageY };
+            var adjustedPoint = origPoint;
+            var subjectEl = ev.subjectEl;
+            var subjectRect;
+            if (subjectEl !== document) {
+                subjectRect = core.computeRect(subjectEl);
+                adjustedPoint = core.constrainPoint(adjustedPoint, subjectRect);
+            }
+            var initialHit = this.initialHit = this.queryHitForOffset(adjustedPoint.left, adjustedPoint.top);
+            if (initialHit) {
+                if (this.useSubjectCenter && subjectRect) {
+                    var slicedSubjectRect = core.intersectRects(subjectRect, initialHit.rect);
+                    if (slicedSubjectRect) {
+                        adjustedPoint = core.getRectCenter(slicedSubjectRect);
+                    }
+                }
+                this.coordAdjust = core.diffPoints(adjustedPoint, origPoint);
+            }
+            else {
+                this.coordAdjust = { left: 0, top: 0 };
+            }
+        };
+        HitDragging.prototype.handleMove = function (ev, forceHandle) {
+            var hit = this.queryHitForOffset(ev.pageX + this.coordAdjust.left, ev.pageY + this.coordAdjust.top);
+            if (forceHandle || !isHitsEqual(this.movingHit, hit)) {
+                this.movingHit = hit;
+                this.emitter.trigger('hitupdate', hit, false, ev);
+            }
+        };
+        HitDragging.prototype.prepareHits = function () {
+            this.offsetTrackers = core.mapHash(this.droppableStore, function (interactionSettings) {
+                interactionSettings.component.buildPositionCaches();
+                return new OffsetTracker(interactionSettings.el);
+            });
+        };
+        HitDragging.prototype.releaseHits = function () {
+            var offsetTrackers = this.offsetTrackers;
+            for (var id in offsetTrackers) {
+                offsetTrackers[id].destroy();
+            }
+            this.offsetTrackers = {};
+        };
+        HitDragging.prototype.queryHitForOffset = function (offsetLeft, offsetTop) {
+            var _a = this, droppableStore = _a.droppableStore, offsetTrackers = _a.offsetTrackers;
+            var bestHit = null;
+            for (var id in droppableStore) {
+                var component = droppableStore[id].component;
+                var offsetTracker = offsetTrackers[id];
+                if (offsetTracker.isWithinClipping(offsetLeft, offsetTop)) {
+                    var originLeft = offsetTracker.computeLeft();
+                    var originTop = offsetTracker.computeTop();
+                    var positionLeft = offsetLeft - originLeft;
+                    var positionTop = offsetTop - originTop;
+                    var origRect = offsetTracker.origRect;
+                    var width = origRect.right - origRect.left;
+                    var height = origRect.bottom - origRect.top;
+                    if (
+                    // must be within the element's bounds
+                    positionLeft >= 0 && positionLeft < width &&
+                        positionTop >= 0 && positionTop < height) {
+                        var hit = component.queryHit(positionLeft, positionTop, width, height);
+                        if (hit &&
+                            (
+                            // make sure the hit is within activeRange, meaning it's not a deal cell
+                            !component.props.dateProfile || // hack for DayTile
+                                core.rangeContainsRange(component.props.dateProfile.activeRange, hit.dateSpan.range)) &&
+                            (!bestHit || hit.layer > bestHit.layer)) {
+                            // TODO: better way to re-orient rectangle
+                            hit.rect.left += originLeft;
+                            hit.rect.right += originLeft;
+                            hit.rect.top += originTop;
+                            hit.rect.bottom += originTop;
+                            bestHit = hit;
+                        }
+                    }
+                }
+            }
+            return bestHit;
+        };
+        return HitDragging;
+    }());
+    function isHitsEqual(hit0, hit1) {
+        if (!hit0 && !hit1) {
+            return true;
+        }
+        if (Boolean(hit0) !== Boolean(hit1)) {
+            return false;
+        }
+        return core.isDateSpansEqual(hit0.dateSpan, hit1.dateSpan);
+    }
+
+    /*
+    Monitors when the user clicks on a specific date/time of a component.
+    A pointerdown+pointerup on the same "hit" constitutes a click.
+    */
+    var DateClicking = /** @class */ (function (_super) {
+        __extends(DateClicking, _super);
+        function DateClicking(settings) {
+            var _this = _super.call(this, settings) || this;
+            _this.handlePointerDown = function (ev) {
+                var dragging = _this.dragging;
+                // do this in pointerdown (not dragend) because DOM might be mutated by the time dragend is fired
+                dragging.setIgnoreMove(!_this.component.isValidDateDownEl(dragging.pointer.downEl));
+            };
+            // won't even fire if moving was ignored
+            _this.handleDragEnd = function (ev) {
+                var component = _this.component;
+                var pointer = _this.dragging.pointer;
+                if (!pointer.wasTouchScroll) {
+                    var _a = _this.hitDragging, initialHit = _a.initialHit, finalHit = _a.finalHit;
+                    if (initialHit && finalHit && isHitsEqual(initialHit, finalHit)) {
+                        component.calendar.triggerDateClick(initialHit.dateSpan, initialHit.dayEl, component.view, ev.origEvent);
+                    }
+                }
+            };
+            var component = settings.component;
+            // we DO want to watch pointer moves because otherwise finalHit won't get populated
+            _this.dragging = new FeaturefulElementDragging(component.el);
+            _this.dragging.autoScroller.isEnabled = false;
+            var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings));
+            hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+            hitDragging.emitter.on('dragend', _this.handleDragEnd);
+            return _this;
+        }
+        DateClicking.prototype.destroy = function () {
+            this.dragging.destroy();
+        };
+        return DateClicking;
+    }(core.Interaction));
+
+    /*
+    Tracks when the user selects a portion of time of a component,
+    constituted by a drag over date cells, with a possible delay at the beginning of the drag.
+    */
+    var DateSelecting = /** @class */ (function (_super) {
+        __extends(DateSelecting, _super);
+        function DateSelecting(settings) {
+            var _this = _super.call(this, settings) || this;
+            _this.dragSelection = null;
+            _this.handlePointerDown = function (ev) {
+                var _a = _this, component = _a.component, dragging = _a.dragging;
+                var canSelect = component.opt('selectable') &&
+                    component.isValidDateDownEl(ev.origEvent.target);
+                // don't bother to watch expensive moves if component won't do selection
+                dragging.setIgnoreMove(!canSelect);
+                // if touch, require user to hold down
+                dragging.delay = ev.isTouch ? getComponentTouchDelay(component) : null;
+            };
+            _this.handleDragStart = function (ev) {
+                _this.component.calendar.unselect(ev); // unselect previous selections
+            };
+            _this.handleHitUpdate = function (hit, isFinal) {
+                var calendar = _this.component.calendar;
+                var dragSelection = null;
+                var isInvalid = false;
+                if (hit) {
+                    dragSelection = joinHitsIntoSelection(_this.hitDragging.initialHit, hit, calendar.pluginSystem.hooks.dateSelectionTransformers);
+                    if (!dragSelection || !_this.component.isDateSelectionValid(dragSelection)) {
+                        isInvalid = true;
+                        dragSelection = null;
+                    }
+                }
+                if (dragSelection) {
+                    calendar.dispatch({ type: 'SELECT_DATES', selection: dragSelection });
+                }
+                else if (!isFinal) { // only unselect if moved away while dragging
+                    calendar.dispatch({ type: 'UNSELECT_DATES' });
+                }
+                if (!isInvalid) {
+                    core.enableCursor();
+                }
+                else {
+                    core.disableCursor();
+                }
+                if (!isFinal) {
+                    _this.dragSelection = dragSelection; // only clear if moved away from all hits while dragging
+                }
+            };
+            _this.handlePointerUp = function (pev) {
+                if (_this.dragSelection) {
+                    // selection is already rendered, so just need to report selection
+                    _this.component.calendar.triggerDateSelect(_this.dragSelection, pev);
+                    _this.dragSelection = null;
+                }
+            };
+            var component = settings.component;
+            var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
+            dragging.touchScrollAllowed = false;
+            dragging.minDistance = component.opt('selectMinDistance') || 0;
+            dragging.autoScroller.isEnabled = component.opt('dragScroll');
+            var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings));
+            hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+            hitDragging.emitter.on('dragstart', _this.handleDragStart);
+            hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+            hitDragging.emitter.on('pointerup', _this.handlePointerUp);
+            return _this;
+        }
+        DateSelecting.prototype.destroy = function () {
+            this.dragging.destroy();
+        };
+        return DateSelecting;
+    }(core.Interaction));
+    function getComponentTouchDelay(component) {
+        var delay = component.opt('selectLongPressDelay');
+        if (delay == null) {
+            delay = component.opt('longPressDelay');
+        }
+        return delay;
+    }
+    function joinHitsIntoSelection(hit0, hit1, dateSelectionTransformers) {
+        var dateSpan0 = hit0.dateSpan;
+        var dateSpan1 = hit1.dateSpan;
+        var ms = [
+            dateSpan0.range.start,
+            dateSpan0.range.end,
+            dateSpan1.range.start,
+            dateSpan1.range.end
+        ];
+        ms.sort(core.compareNumbers);
+        var props = {};
+        for (var _i = 0, dateSelectionTransformers_1 = dateSelectionTransformers; _i < dateSelectionTransformers_1.length; _i++) {
+            var transformer = dateSelectionTransformers_1[_i];
+            var res = transformer(hit0, hit1);
+            if (res === false) {
+                return null;
+            }
+            else if (res) {
+                __assign(props, res);
+            }
+        }
+        props.range = { start: ms[0], end: ms[3] };
+        props.allDay = dateSpan0.allDay;
+        return props;
+    }
+
+    var EventDragging = /** @class */ (function (_super) {
+        __extends(EventDragging, _super);
+        function EventDragging(settings) {
+            var _this = _super.call(this, settings) || this;
+            // internal state
+            _this.subjectSeg = null; // the seg being selected/dragged
+            _this.isDragging = false;
+            _this.eventRange = null;
+            _this.relevantEvents = null; // the events being dragged
+            _this.receivingCalendar = null;
+            _this.validMutation = null;
+            _this.mutatedRelevantEvents = null;
+            _this.handlePointerDown = function (ev) {
+                var origTarget = ev.origEvent.target;
+                var _a = _this, component = _a.component, dragging = _a.dragging;
+                var mirror = dragging.mirror;
+                var initialCalendar = component.calendar;
+                var subjectSeg = _this.subjectSeg = core.getElSeg(ev.subjectEl);
+                var eventRange = _this.eventRange = subjectSeg.eventRange;
+                var eventInstanceId = eventRange.instance.instanceId;
+                _this.relevantEvents = core.getRelevantEvents(initialCalendar.state.eventStore, eventInstanceId);
+                dragging.minDistance = ev.isTouch ? 0 : component.opt('eventDragMinDistance');
+                dragging.delay =
+                    // only do a touch delay if touch and this event hasn't been selected yet
+                    (ev.isTouch && eventInstanceId !== component.props.eventSelection) ?
+                        getComponentTouchDelay$1(component) :
+                        null;
+                mirror.parentNode = initialCalendar.el;
+                mirror.revertDuration = component.opt('dragRevertDuration');
+                var isValid = component.isValidSegDownEl(origTarget) &&
+                    !core.elementClosest(origTarget, '.fc-resizer'); // NOT on a resizer
+                dragging.setIgnoreMove(!isValid);
+                // disable dragging for elements that are resizable (ie, selectable)
+                // but are not draggable
+                _this.isDragging = isValid &&
+                    ev.subjectEl.classList.contains('fc-draggable');
+            };
+            _this.handleDragStart = function (ev) {
+                var initialCalendar = _this.component.calendar;
+                var eventRange = _this.eventRange;
+                var eventInstanceId = eventRange.instance.instanceId;
+                if (ev.isTouch) {
+                    // need to select a different event?
+                    if (eventInstanceId !== _this.component.props.eventSelection) {
+                        initialCalendar.dispatch({ type: 'SELECT_EVENT', eventInstanceId: eventInstanceId });
+                    }
+                }
+                else {
+                    // if now using mouse, but was previous touch interaction, clear selected event
+                    initialCalendar.dispatch({ type: 'UNSELECT_EVENT' });
+                }
+                if (_this.isDragging) {
+                    initialCalendar.unselect(ev); // unselect *date* selection
+                    initialCalendar.publiclyTrigger('eventDragStart', [
+                        {
+                            el: _this.subjectSeg.el,
+                            event: new core.EventApi(initialCalendar, eventRange.def, eventRange.instance),
+                            jsEvent: ev.origEvent,
+                            view: _this.component.view
+                        }
+                    ]);
+                }
+            };
+            _this.handleHitUpdate = function (hit, isFinal) {
+                if (!_this.isDragging) {
+                    return;
+                }
+                var relevantEvents = _this.relevantEvents;
+                var initialHit = _this.hitDragging.initialHit;
+                var initialCalendar = _this.component.calendar;
+                // states based on new hit
+                var receivingCalendar = null;
+                var mutation = null;
+                var mutatedRelevantEvents = null;
+                var isInvalid = false;
+                var interaction = {
+                    affectedEvents: relevantEvents,
+                    mutatedEvents: core.createEmptyEventStore(),
+                    isEvent: true,
+                    origSeg: _this.subjectSeg
+                };
+                if (hit) {
+                    var receivingComponent = hit.component;
+                    receivingCalendar = receivingComponent.calendar;
+                    if (initialCalendar === receivingCalendar ||
+                        receivingComponent.opt('editable') && receivingComponent.opt('droppable')) {
+                        mutation = computeEventMutation(initialHit, hit, receivingCalendar.pluginSystem.hooks.eventDragMutationMassagers);
+                        if (mutation) {
+                            mutatedRelevantEvents = core.applyMutationToEventStore(relevantEvents, receivingCalendar.eventUiBases, mutation, receivingCalendar);
+                            interaction.mutatedEvents = mutatedRelevantEvents;
+                            if (!receivingComponent.isInteractionValid(interaction)) {
+                                isInvalid = true;
+                                mutation = null;
+                                mutatedRelevantEvents = null;
+                                interaction.mutatedEvents = core.createEmptyEventStore();
+                            }
+                        }
+                    }
+                    else {
+                        receivingCalendar = null;
+                    }
+                }
+                _this.displayDrag(receivingCalendar, interaction);
+                if (!isInvalid) {
+                    core.enableCursor();
+                }
+                else {
+                    core.disableCursor();
+                }
+                if (!isFinal) {
+                    if (initialCalendar === receivingCalendar && // TODO: write test for this
+                        isHitsEqual(initialHit, hit)) {
+                        mutation = null;
+                    }
+                    _this.dragging.setMirrorNeedsRevert(!mutation);
+                    // render the mirror if no already-rendered mirror
+                    // TODO: wish we could somehow wait for dispatch to guarantee render
+                    _this.dragging.setMirrorIsVisible(!hit || !document.querySelector('.fc-mirror'));
+                    // assign states based on new hit
+                    _this.receivingCalendar = receivingCalendar;
+                    _this.validMutation = mutation;
+                    _this.mutatedRelevantEvents = mutatedRelevantEvents;
+                }
+            };
+            _this.handlePointerUp = function () {
+                if (!_this.isDragging) {
+                    _this.cleanup(); // because handleDragEnd won't fire
+                }
+            };
+            _this.handleDragEnd = function (ev) {
+                if (_this.isDragging) {
+                    var initialCalendar_1 = _this.component.calendar;
+                    var initialView = _this.component.view;
+                    var _a = _this, receivingCalendar = _a.receivingCalendar, validMutation = _a.validMutation;
+                    var eventDef = _this.eventRange.def;
+                    var eventInstance = _this.eventRange.instance;
+                    var eventApi = new core.EventApi(initialCalendar_1, eventDef, eventInstance);
+                    var relevantEvents_1 = _this.relevantEvents;
+                    var mutatedRelevantEvents = _this.mutatedRelevantEvents;
+                    var finalHit = _this.hitDragging.finalHit;
+                    _this.clearDrag(); // must happen after revert animation
+                    initialCalendar_1.publiclyTrigger('eventDragStop', [
+                        {
+                            el: _this.subjectSeg.el,
+                            event: eventApi,
+                            jsEvent: ev.origEvent,
+                            view: initialView
+                        }
+                    ]);
+                    if (validMutation) {
+                        // dropped within same calendar
+                        if (receivingCalendar === initialCalendar_1) {
+                            initialCalendar_1.dispatch({
+                                type: 'MERGE_EVENTS',
+                                eventStore: mutatedRelevantEvents
+                            });
+                            var transformed = {};
+                            for (var _i = 0, _b = initialCalendar_1.pluginSystem.hooks.eventDropTransformers; _i < _b.length; _i++) {
+                                var transformer = _b[_i];
+                                __assign(transformed, transformer(validMutation, initialCalendar_1));
+                            }
+                            var eventDropArg = __assign({}, transformed, { el: ev.subjectEl, delta: validMutation.datesDelta, oldEvent: eventApi, event: new core.EventApi(// the data AFTER the mutation
+                                initialCalendar_1, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null), revert: function () {
+                                    initialCalendar_1.dispatch({
+                                        type: 'MERGE_EVENTS',
+                                        eventStore: relevantEvents_1
+                                    });
+                                }, jsEvent: ev.origEvent, view: initialView });
+                            initialCalendar_1.publiclyTrigger('eventDrop', [eventDropArg]);
+                            // dropped in different calendar
+                        }
+                        else if (receivingCalendar) {
+                            initialCalendar_1.publiclyTrigger('eventLeave', [
+                                {
+                                    draggedEl: ev.subjectEl,
+                                    event: eventApi,
+                                    view: initialView
+                                }
+                            ]);
+                            initialCalendar_1.dispatch({
+                                type: 'REMOVE_EVENT_INSTANCES',
+                                instances: _this.mutatedRelevantEvents.instances
+                            });
+                            receivingCalendar.dispatch({
+                                type: 'MERGE_EVENTS',
+                                eventStore: _this.mutatedRelevantEvents
+                            });
+                            if (ev.isTouch) {
+                                receivingCalendar.dispatch({
+                                    type: 'SELECT_EVENT',
+                                    eventInstanceId: eventInstance.instanceId
+                                });
+                            }
+                            var dropArg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: ev.subjectEl, jsEvent: ev.origEvent, view: finalHit.component // should this be finalHit.component.view? See #4644
+                             });
+                            receivingCalendar.publiclyTrigger('drop', [dropArg]);
+                            receivingCalendar.publiclyTrigger('eventReceive', [
+                                {
+                                    draggedEl: ev.subjectEl,
+                                    event: new core.EventApi(// the data AFTER the mutation
+                                    receivingCalendar, mutatedRelevantEvents.defs[eventDef.defId], mutatedRelevantEvents.instances[eventInstance.instanceId]),
+                                    view: finalHit.component // should this be finalHit.component.view? See #4644
+                                }
+                            ]);
+                        }
+                    }
+                    else {
+                        initialCalendar_1.publiclyTrigger('_noEventDrop');
+                    }
+                }
+                _this.cleanup();
+            };
+            var component = _this.component;
+            var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
+            dragging.pointer.selector = EventDragging.SELECTOR;
+            dragging.touchScrollAllowed = false;
+            dragging.autoScroller.isEnabled = component.opt('dragScroll');
+            var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsStore);
+            hitDragging.useSubjectCenter = settings.useEventCenter;
+            hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+            hitDragging.emitter.on('dragstart', _this.handleDragStart);
+            hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+            hitDragging.emitter.on('pointerup', _this.handlePointerUp);
+            hitDragging.emitter.on('dragend', _this.handleDragEnd);
+            return _this;
+        }
+        EventDragging.prototype.destroy = function () {
+            this.dragging.destroy();
+        };
+        // render a drag state on the next receivingCalendar
+        EventDragging.prototype.displayDrag = function (nextCalendar, state) {
+            var initialCalendar = this.component.calendar;
+            var prevCalendar = this.receivingCalendar;
+            // does the previous calendar need to be cleared?
+            if (prevCalendar && prevCalendar !== nextCalendar) {
+                // does the initial calendar need to be cleared?
+                // if so, don't clear all the way. we still need to to hide the affectedEvents
+                if (prevCalendar === initialCalendar) {
+                    prevCalendar.dispatch({
+                        type: 'SET_EVENT_DRAG',
+                        state: {
+                            affectedEvents: state.affectedEvents,
+                            mutatedEvents: core.createEmptyEventStore(),
+                            isEvent: true,
+                            origSeg: state.origSeg
+                        }
+                    });
+                    // completely clear the old calendar if it wasn't the initial
+                }
+                else {
+                    prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+                }
+            }
+            if (nextCalendar) {
+                nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state });
+            }
+        };
+        EventDragging.prototype.clearDrag = function () {
+            var initialCalendar = this.component.calendar;
+            var receivingCalendar = this.receivingCalendar;
+            if (receivingCalendar) {
+                receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+            }
+            // the initial calendar might have an dummy drag state from displayDrag
+            if (initialCalendar !== receivingCalendar) {
+                initialCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+            }
+        };
+        EventDragging.prototype.cleanup = function () {
+            this.subjectSeg = null;
+            this.isDragging = false;
+            this.eventRange = null;
+            this.relevantEvents = null;
+            this.receivingCalendar = null;
+            this.validMutation = null;
+            this.mutatedRelevantEvents = null;
+        };
+        EventDragging.SELECTOR = '.fc-draggable, .fc-resizable'; // TODO: test this in IE11
+        return EventDragging;
+    }(core.Interaction));
+    function computeEventMutation(hit0, hit1, massagers) {
+        var dateSpan0 = hit0.dateSpan;
+        var dateSpan1 = hit1.dateSpan;
+        var date0 = dateSpan0.range.start;
+        var date1 = dateSpan1.range.start;
+        var standardProps = {};
+        if (dateSpan0.allDay !== dateSpan1.allDay) {
+            standardProps.allDay = dateSpan1.allDay;
+            standardProps.hasEnd = hit1.component.opt('allDayMaintainDuration');
+            if (dateSpan1.allDay) {
+                // means date1 is already start-of-day,
+                // but date0 needs to be converted
+                date0 = core.startOfDay(date0);
+            }
+        }
+        var delta = core.diffDates(date0, date1, hit0.component.dateEnv, hit0.component === hit1.component ?
+            hit0.component.largeUnit :
+            null);
+        if (delta.milliseconds) { // has hours/minutes/seconds
+            standardProps.allDay = false;
+        }
+        var mutation = {
+            datesDelta: delta,
+            standardProps: standardProps
+        };
+        for (var _i = 0, massagers_1 = massagers; _i < massagers_1.length; _i++) {
+            var massager = massagers_1[_i];
+            massager(mutation, hit0, hit1);
+        }
+        return mutation;
+    }
+    function getComponentTouchDelay$1(component) {
+        var delay = component.opt('eventLongPressDelay');
+        if (delay == null) {
+            delay = component.opt('longPressDelay');
+        }
+        return delay;
+    }
+
+    var EventDragging$1 = /** @class */ (function (_super) {
+        __extends(EventDragging, _super);
+        function EventDragging(settings) {
+            var _this = _super.call(this, settings) || this;
+            // internal state
+            _this.draggingSeg = null; // TODO: rename to resizingSeg? subjectSeg?
+            _this.eventRange = null;
+            _this.relevantEvents = null;
+            _this.validMutation = null;
+            _this.mutatedRelevantEvents = null;
+            _this.handlePointerDown = function (ev) {
+                var component = _this.component;
+                var seg = _this.querySeg(ev);
+                var eventRange = _this.eventRange = seg.eventRange;
+                _this.dragging.minDistance = component.opt('eventDragMinDistance');
+                // if touch, need to be working with a selected event
+                _this.dragging.setIgnoreMove(!_this.component.isValidSegDownEl(ev.origEvent.target) ||
+                    (ev.isTouch && _this.component.props.eventSelection !== eventRange.instance.instanceId));
+            };
+            _this.handleDragStart = function (ev) {
+                var calendar = _this.component.calendar;
+                var eventRange = _this.eventRange;
+                _this.relevantEvents = core.getRelevantEvents(calendar.state.eventStore, _this.eventRange.instance.instanceId);
+                _this.draggingSeg = _this.querySeg(ev);
+                calendar.unselect();
+                calendar.publiclyTrigger('eventResizeStart', [
+                    {
+                        el: _this.draggingSeg.el,
+                        event: new core.EventApi(calendar, eventRange.def, eventRange.instance),
+                        jsEvent: ev.origEvent,
+                        view: _this.component.view
+                    }
+                ]);
+            };
+            _this.handleHitUpdate = function (hit, isFinal, ev) {
+                var calendar = _this.component.calendar;
+                var relevantEvents = _this.relevantEvents;
+                var initialHit = _this.hitDragging.initialHit;
+                var eventInstance = _this.eventRange.instance;
+                var mutation = null;
+                var mutatedRelevantEvents = null;
+                var isInvalid = false;
+                var interaction = {
+                    affectedEvents: relevantEvents,
+                    mutatedEvents: core.createEmptyEventStore(),
+                    isEvent: true,
+                    origSeg: _this.draggingSeg
+                };
+                if (hit) {
+                    mutation = computeMutation(initialHit, hit, ev.subjectEl.classList.contains('fc-start-resizer'), eventInstance.range, calendar.pluginSystem.hooks.eventResizeJoinTransforms);
+                }
+                if (mutation) {
+                    mutatedRelevantEvents = core.applyMutationToEventStore(relevantEvents, calendar.eventUiBases, mutation, calendar);
+                    interaction.mutatedEvents = mutatedRelevantEvents;
+                    if (!_this.component.isInteractionValid(interaction)) {
+                        isInvalid = true;
+                        mutation = null;
+                        mutatedRelevantEvents = null;
+                        interaction.mutatedEvents = null;
+                    }
+                }
+                if (mutatedRelevantEvents) {
+                    calendar.dispatch({
+                        type: 'SET_EVENT_RESIZE',
+                        state: interaction
+                    });
+                }
+                else {
+                    calendar.dispatch({ type: 'UNSET_EVENT_RESIZE' });
+                }
+                if (!isInvalid) {
+                    core.enableCursor();
+                }
+                else {
+                    core.disableCursor();
+                }
+                if (!isFinal) {
+                    if (mutation && isHitsEqual(initialHit, hit)) {
+                        mutation = null;
+                    }
+                    _this.validMutation = mutation;
+                    _this.mutatedRelevantEvents = mutatedRelevantEvents;
+                }
+            };
+            _this.handleDragEnd = function (ev) {
+                var calendar = _this.component.calendar;
+                var view = _this.component.view;
+                var eventDef = _this.eventRange.def;
+                var eventInstance = _this.eventRange.instance;
+                var eventApi = new core.EventApi(calendar, eventDef, eventInstance);
+                var relevantEvents = _this.relevantEvents;
+                var mutatedRelevantEvents = _this.mutatedRelevantEvents;
+                calendar.publiclyTrigger('eventResizeStop', [
+                    {
+                        el: _this.draggingSeg.el,
+                        event: eventApi,
+                        jsEvent: ev.origEvent,
+                        view: view
+                    }
+                ]);
+                if (_this.validMutation) {
+                    calendar.dispatch({
+                        type: 'MERGE_EVENTS',
+                        eventStore: mutatedRelevantEvents
+                    });
+                    calendar.publiclyTrigger('eventResize', [
+                        {
+                            el: _this.draggingSeg.el,
+                            startDelta: _this.validMutation.startDelta || core.createDuration(0),
+                            endDelta: _this.validMutation.endDelta || core.createDuration(0),
+                            prevEvent: eventApi,
+                            event: new core.EventApi(// the data AFTER the mutation
+                            calendar, mutatedRelevantEvents.defs[eventDef.defId], eventInstance ? mutatedRelevantEvents.instances[eventInstance.instanceId] : null),
+                            revert: function () {
+                                calendar.dispatch({
+                                    type: 'MERGE_EVENTS',
+                                    eventStore: relevantEvents
+                                });
+                            },
+                            jsEvent: ev.origEvent,
+                            view: view
+                        }
+                    ]);
+                }
+                else {
+                    calendar.publiclyTrigger('_noEventResize');
+                }
+                // reset all internal state
+                _this.draggingSeg = null;
+                _this.relevantEvents = null;
+                _this.validMutation = null;
+                // okay to keep eventInstance around. useful to set it in handlePointerDown
+            };
+            var component = settings.component;
+            var dragging = _this.dragging = new FeaturefulElementDragging(component.el);
+            dragging.pointer.selector = '.fc-resizer';
+            dragging.touchScrollAllowed = false;
+            dragging.autoScroller.isEnabled = component.opt('dragScroll');
+            var hitDragging = _this.hitDragging = new HitDragging(_this.dragging, core.interactionSettingsToStore(settings));
+            hitDragging.emitter.on('pointerdown', _this.handlePointerDown);
+            hitDragging.emitter.on('dragstart', _this.handleDragStart);
+            hitDragging.emitter.on('hitupdate', _this.handleHitUpdate);
+            hitDragging.emitter.on('dragend', _this.handleDragEnd);
+            return _this;
+        }
+        EventDragging.prototype.destroy = function () {
+            this.dragging.destroy();
+        };
+        EventDragging.prototype.querySeg = function (ev) {
+            return core.getElSeg(core.elementClosest(ev.subjectEl, this.component.fgSegSelector));
+        };
+        return EventDragging;
+    }(core.Interaction));
+    function computeMutation(hit0, hit1, isFromStart, instanceRange, transforms) {
+        var dateEnv = hit0.component.dateEnv;
+        var date0 = hit0.dateSpan.range.start;
+        var date1 = hit1.dateSpan.range.start;
+        var delta = core.diffDates(date0, date1, dateEnv, hit0.component.largeUnit);
+        var props = {};
+        for (var _i = 0, transforms_1 = transforms; _i < transforms_1.length; _i++) {
+            var transform = transforms_1[_i];
+            var res = transform(hit0, hit1);
+            if (res === false) {
+                return null;
+            }
+            else if (res) {
+                __assign(props, res);
+            }
+        }
+        if (isFromStart) {
+            if (dateEnv.add(instanceRange.start, delta) < instanceRange.end) {
+                props.startDelta = delta;
+                return props;
+            }
+        }
+        else {
+            if (dateEnv.add(instanceRange.end, delta) > instanceRange.start) {
+                props.endDelta = delta;
+                return props;
+            }
+        }
+        return null;
+    }
+
+    var UnselectAuto = /** @class */ (function () {
+        function UnselectAuto(calendar) {
+            var _this = this;
+            this.isRecentPointerDateSelect = false; // wish we could use a selector to detect date selection, but uses hit system
+            this.onSelect = function (selectInfo) {
+                if (selectInfo.jsEvent) {
+                    _this.isRecentPointerDateSelect = true;
+                }
+            };
+            this.onDocumentPointerUp = function (pev) {
+                var _a = _this, calendar = _a.calendar, documentPointer = _a.documentPointer;
+                var state = calendar.state;
+                // touch-scrolling should never unfocus any type of selection
+                if (!documentPointer.wasTouchScroll) {
+                    if (state.dateSelection && // an existing date selection?
+                        !_this.isRecentPointerDateSelect // a new pointer-initiated date selection since last onDocumentPointerUp?
+                    ) {
+                        var unselectAuto = calendar.viewOpt('unselectAuto');
+                        var unselectCancel = calendar.viewOpt('unselectCancel');
+                        if (unselectAuto && (!unselectAuto || !core.elementClosest(documentPointer.downEl, unselectCancel))) {
+                            calendar.unselect(pev);
+                        }
+                    }
+                    if (state.eventSelection && // an existing event selected?
+                        !core.elementClosest(documentPointer.downEl, EventDragging.SELECTOR) // interaction DIDN'T start on an event
+                    ) {
+                        calendar.dispatch({ type: 'UNSELECT_EVENT' });
+                    }
+                }
+                _this.isRecentPointerDateSelect = false;
+            };
+            this.calendar = calendar;
+            var documentPointer = this.documentPointer = new PointerDragging(document);
+            documentPointer.shouldIgnoreMove = true;
+            documentPointer.shouldWatchScroll = false;
+            documentPointer.emitter.on('pointerup', this.onDocumentPointerUp);
+            /*
+            TODO: better way to know about whether there was a selection with the pointer
+            */
+            calendar.on('select', this.onSelect);
+        }
+        UnselectAuto.prototype.destroy = function () {
+            this.calendar.off('select', this.onSelect);
+            this.documentPointer.destroy();
+        };
+        return UnselectAuto;
+    }());
+
+    /*
+    Given an already instantiated draggable object for one-or-more elements,
+    Interprets any dragging as an attempt to drag an events that lives outside
+    of a calendar onto a calendar.
+    */
+    var ExternalElementDragging = /** @class */ (function () {
+        function ExternalElementDragging(dragging, suppliedDragMeta) {
+            var _this = this;
+            this.receivingCalendar = null;
+            this.droppableEvent = null; // will exist for all drags, even if create:false
+            this.suppliedDragMeta = null;
+            this.dragMeta = null;
+            this.handleDragStart = function (ev) {
+                _this.dragMeta = _this.buildDragMeta(ev.subjectEl);
+            };
+            this.handleHitUpdate = function (hit, isFinal, ev) {
+                var dragging = _this.hitDragging.dragging;
+                var receivingCalendar = null;
+                var droppableEvent = null;
+                var isInvalid = false;
+                var interaction = {
+                    affectedEvents: core.createEmptyEventStore(),
+                    mutatedEvents: core.createEmptyEventStore(),
+                    isEvent: _this.dragMeta.create,
+                    origSeg: null
+                };
+                if (hit) {
+                    receivingCalendar = hit.component.calendar;
+                    if (_this.canDropElOnCalendar(ev.subjectEl, receivingCalendar)) {
+                        droppableEvent = computeEventForDateSpan(hit.dateSpan, _this.dragMeta, receivingCalendar);
+                        interaction.mutatedEvents = core.eventTupleToStore(droppableEvent);
+                        isInvalid = !core.isInteractionValid(interaction, receivingCalendar);
+                        if (isInvalid) {
+                            interaction.mutatedEvents = core.createEmptyEventStore();
+                            droppableEvent = null;
+                        }
+                    }
+                }
+                _this.displayDrag(receivingCalendar, interaction);
+                // show mirror if no already-rendered mirror element OR if we are shutting down the mirror (?)
+                // TODO: wish we could somehow wait for dispatch to guarantee render
+                dragging.setMirrorIsVisible(isFinal || !droppableEvent || !document.querySelector('.fc-mirror'));
+                if (!isInvalid) {
+                    core.enableCursor();
+                }
+                else {
+                    core.disableCursor();
+                }
+                if (!isFinal) {
+                    dragging.setMirrorNeedsRevert(!droppableEvent);
+                    _this.receivingCalendar = receivingCalendar;
+                    _this.droppableEvent = droppableEvent;
+                }
+            };
+            this.handleDragEnd = function (pev) {
+                var _a = _this, receivingCalendar = _a.receivingCalendar, droppableEvent = _a.droppableEvent;
+                _this.clearDrag();
+                if (receivingCalendar && droppableEvent) {
+                    var finalHit = _this.hitDragging.finalHit;
+                    var finalView = finalHit.component.view;
+                    var dragMeta = _this.dragMeta;
+                    var arg = __assign({}, receivingCalendar.buildDatePointApi(finalHit.dateSpan), { draggedEl: pev.subjectEl, jsEvent: pev.origEvent, view: finalView });
+                    receivingCalendar.publiclyTrigger('drop', [arg]);
+                    if (dragMeta.create) {
+                        receivingCalendar.dispatch({
+                            type: 'MERGE_EVENTS',
+                            eventStore: core.eventTupleToStore(droppableEvent)
+                        });
+                        if (pev.isTouch) {
+                            receivingCalendar.dispatch({
+                                type: 'SELECT_EVENT',
+                                eventInstanceId: droppableEvent.instance.instanceId
+                            });
+                        }
+                        // signal that an external event landed
+                        receivingCalendar.publiclyTrigger('eventReceive', [
+                            {
+                                draggedEl: pev.subjectEl,
+                                event: new core.EventApi(receivingCalendar, droppableEvent.def, droppableEvent.instance),
+                                view: finalView
+                            }
+                        ]);
+                    }
+                }
+                _this.receivingCalendar = null;
+                _this.droppableEvent = null;
+            };
+            var hitDragging = this.hitDragging = new HitDragging(dragging, core.interactionSettingsStore);
+            hitDragging.requireInitial = false; // will start outside of a component
+            hitDragging.emitter.on('dragstart', this.handleDragStart);
+            hitDragging.emitter.on('hitupdate', this.handleHitUpdate);
+            hitDragging.emitter.on('dragend', this.handleDragEnd);
+            this.suppliedDragMeta = suppliedDragMeta;
+        }
+        ExternalElementDragging.prototype.buildDragMeta = function (subjectEl) {
+            if (typeof this.suppliedDragMeta === 'object') {
+                return core.parseDragMeta(this.suppliedDragMeta);
+            }
+            else if (typeof this.suppliedDragMeta === 'function') {
+                return core.parseDragMeta(this.suppliedDragMeta(subjectEl));
+            }
+            else {
+                return getDragMetaFromEl(subjectEl);
+            }
+        };
+        ExternalElementDragging.prototype.displayDrag = function (nextCalendar, state) {
+            var prevCalendar = this.receivingCalendar;
+            if (prevCalendar && prevCalendar !== nextCalendar) {
+                prevCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+            }
+            if (nextCalendar) {
+                nextCalendar.dispatch({ type: 'SET_EVENT_DRAG', state: state });
+            }
+        };
+        ExternalElementDragging.prototype.clearDrag = function () {
+            if (this.receivingCalendar) {
+                this.receivingCalendar.dispatch({ type: 'UNSET_EVENT_DRAG' });
+            }
+        };
+        ExternalElementDragging.prototype.canDropElOnCalendar = function (el, receivingCalendar) {
+            var dropAccept = receivingCalendar.opt('dropAccept');
+            if (typeof dropAccept === 'function') {
+                return dropAccept(el);
+            }
+            else if (typeof dropAccept === 'string' && dropAccept) {
+                return Boolean(core.elementMatches(el, dropAccept));
+            }
+            return true;
+        };
+        return ExternalElementDragging;
+    }());
+    // Utils for computing event store from the DragMeta
+    // ----------------------------------------------------------------------------------------------------
+    function computeEventForDateSpan(dateSpan, dragMeta, calendar) {
+        var defProps = __assign({}, dragMeta.leftoverProps);
+        for (var _i = 0, _a = calendar.pluginSystem.hooks.externalDefTransforms; _i < _a.length; _i++) {
+            var transform = _a[_i];
+            __assign(defProps, transform(dateSpan, dragMeta));
+        }
+        var def = core.parseEventDef(defProps, dragMeta.sourceId, dateSpan.allDay, calendar.opt('forceEventDuration') || Boolean(dragMeta.duration), // hasEnd
+        calendar);
+        var start = dateSpan.range.start;
+        // only rely on time info if drop zone is all-day,
+        // otherwise, we already know the time
+        if (dateSpan.allDay && dragMeta.startTime) {
+            start = calendar.dateEnv.add(start, dragMeta.startTime);
+        }
+        var end = dragMeta.duration ?
+            calendar.dateEnv.add(start, dragMeta.duration) :
+            calendar.getDefaultEventEnd(dateSpan.allDay, start);
+        var instance = core.createEventInstance(def.defId, { start: start, end: end });
+        return { def: def, instance: instance };
+    }
+    // Utils for extracting data from element
+    // ----------------------------------------------------------------------------------------------------
+    function getDragMetaFromEl(el) {
+        var str = getEmbeddedElData(el, 'event');
+        var obj = str ?
+            JSON.parse(str) :
+            { create: false }; // if no embedded data, assume no event creation
+        return core.parseDragMeta(obj);
+    }
+    core.config.dataAttrPrefix = '';
+    function getEmbeddedElData(el, name) {
+        var prefix = core.config.dataAttrPrefix;
+        var prefixedName = (prefix ? prefix + '-' : '') + name;
+        return el.getAttribute('data-' + prefixedName) || '';
+    }
+
+    /*
+    Makes an element (that is *external* to any calendar) draggable.
+    Can pass in data that determines how an event will be created when dropped onto a calendar.
+    Leverages FullCalendar's internal drag-n-drop functionality WITHOUT a third-party drag system.
+    */
+    var ExternalDraggable = /** @class */ (function () {
+        function ExternalDraggable(el, settings) {
+            var _this = this;
+            if (settings === void 0) { settings = {}; }
+            this.handlePointerDown = function (ev) {
+                var dragging = _this.dragging;
+                var _a = _this.settings, minDistance = _a.minDistance, longPressDelay = _a.longPressDelay;
+                dragging.minDistance =
+                    minDistance != null ?
+                        minDistance :
+                        (ev.isTouch ? 0 : core.globalDefaults.eventDragMinDistance);
+                dragging.delay =
+                    ev.isTouch ? // TODO: eventually read eventLongPressDelay instead vvv
+                        (longPressDelay != null ? longPressDelay : core.globalDefaults.longPressDelay) :
+                        0;
+            };
+            this.handleDragStart = function (ev) {
+                if (ev.isTouch &&
+                    _this.dragging.delay &&
+                    ev.subjectEl.classList.contains('fc-event')) {
+                    _this.dragging.mirror.getMirrorEl().classList.add('fc-selected');
+                }
+            };
+            this.settings = settings;
+            var dragging = this.dragging = new FeaturefulElementDragging(el);
+            dragging.touchScrollAllowed = false;
+            if (settings.itemSelector != null) {
+                dragging.pointer.selector = settings.itemSelector;
+            }
+            if (settings.appendTo != null) {
+                dragging.mirror.parentNode = settings.appendTo; // TODO: write tests
+            }
+            dragging.emitter.on('pointerdown', this.handlePointerDown);
+            dragging.emitter.on('dragstart', this.handleDragStart);
+            new ExternalElementDragging(dragging, settings.eventData);
+        }
+        ExternalDraggable.prototype.destroy = function () {
+            this.dragging.destroy();
+        };
+        return ExternalDraggable;
+    }());
+
+    /*
+    Detects when a *THIRD-PARTY* drag-n-drop system interacts with elements.
+    The third-party system is responsible for drawing the visuals effects of the drag.
+    This class simply monitors for pointer movements and fires events.
+    It also has the ability to hide the moving element (the "mirror") during the drag.
+    */
+    var InferredElementDragging = /** @class */ (function (_super) {
+        __extends(InferredElementDragging, _super);
+        function InferredElementDragging(containerEl) {
+            var _this = _super.call(this, containerEl) || this;
+            _this.shouldIgnoreMove = false;
+            _this.mirrorSelector = '';
+            _this.currentMirrorEl = null;
+            _this.handlePointerDown = function (ev) {
+                _this.emitter.trigger('pointerdown', ev);
+                if (!_this.shouldIgnoreMove) {
+                    // fire dragstart right away. does not support delay or min-distance
+                    _this.emitter.trigger('dragstart', ev);
+                }
+            };
+            _this.handlePointerMove = function (ev) {
+                if (!_this.shouldIgnoreMove) {
+                    _this.emitter.trigger('dragmove', ev);
+                }
+            };
+            _this.handlePointerUp = function (ev) {
+                _this.emitter.trigger('pointerup', ev);
+                if (!_this.shouldIgnoreMove) {
+                    // fire dragend right away. does not support a revert animation
+                    _this.emitter.trigger('dragend', ev);
+                }
+            };
+            var pointer = _this.pointer = new PointerDragging(containerEl);
+            pointer.emitter.on('pointerdown', _this.handlePointerDown);
+            pointer.emitter.on('pointermove', _this.handlePointerMove);
+            pointer.emitter.on('pointerup', _this.handlePointerUp);
+            return _this;
+        }
+        InferredElementDragging.prototype.destroy = function () {
+            this.pointer.destroy();
+        };
+        InferredElementDragging.prototype.setIgnoreMove = function (bool) {
+            this.shouldIgnoreMove = bool;
+        };
+        InferredElementDragging.prototype.setMirrorIsVisible = function (bool) {
+            if (bool) {
+                // restore a previously hidden element.
+                // use the reference in case the selector class has already been removed.
+                if (this.currentMirrorEl) {
+                    this.currentMirrorEl.style.visibility = '';
+                    this.currentMirrorEl = null;
+                }
+            }
+            else {
+                var mirrorEl = this.mirrorSelector ?
+                    document.querySelector(this.mirrorSelector) :
+                    null;
+                if (mirrorEl) {
+                    this.currentMirrorEl = mirrorEl;
+                    mirrorEl.style.visibility = 'hidden';
+                }
+            }
+        };
+        return InferredElementDragging;
+    }(core.ElementDragging));
+
+    /*
+    Bridges third-party drag-n-drop systems with FullCalendar.
+    Must be instantiated and destroyed by caller.
+    */
+    var ThirdPartyDraggable = /** @class */ (function () {
+        function ThirdPartyDraggable(containerOrSettings, settings) {
+            var containerEl = document;
+            if (
+            // wish we could just test instanceof EventTarget, but doesn't work in IE11
+            containerOrSettings === document ||
+                containerOrSettings instanceof Element) {
+                containerEl = containerOrSettings;
+                settings = settings || {};
+            }
+            else {
+                settings = (containerOrSettings || {});
+            }
+            var dragging = this.dragging = new InferredElementDragging(containerEl);
+            if (typeof settings.itemSelector === 'string') {
+                dragging.pointer.selector = settings.itemSelector;
+            }
+            else if (containerEl === document) {
+                dragging.pointer.selector = '[data-event]';
+            }
+            if (typeof settings.mirrorSelector === 'string') {
+                dragging.mirrorSelector = settings.mirrorSelector;
+            }
+            new ExternalElementDragging(dragging, settings.eventData);
+        }
+        ThirdPartyDraggable.prototype.destroy = function () {
+            this.dragging.destroy();
+        };
+        return ThirdPartyDraggable;
+    }());
+
+    var main = core.createPlugin({
+        componentInteractions: [DateClicking, DateSelecting, EventDragging, EventDragging$1],
+        calendarInteractions: [UnselectAuto],
+        elementDraggingImpl: FeaturefulElementDragging
+    });
+
+    exports.Draggable = ExternalDraggable;
+    exports.FeaturefulElementDragging = FeaturefulElementDragging;
+    exports.PointerDragging = PointerDragging;
+    exports.ThirdPartyDraggable = ThirdPartyDraggable;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/main.min.js b/AKPlan/static/AKPlan/fullcalendar/interaction/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..7a36731f3e7281da1e3d396cfbaef58c3573f703
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Interaction Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarInteraction={},e.FullCalendar)}(this,function(e,t){"use strict";var n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function r(e,t){function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var i=function(){return(i=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var i in t=arguments[n])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)};t.config.touchMouseIgnoreWait=500;var o=0,a=0,l=!1,s=function(){function e(e){var n=this;this.subjectEl=null,this.downEl=null,this.selector="",this.handleSelector="",this.shouldIgnoreMove=!1,this.shouldWatchScroll=!0,this.isDragging=!1,this.isTouchDragging=!1,this.wasTouchScroll=!1,this.handleMouseDown=function(e){if(!n.shouldIgnoreMouse()&&function(e){return 0===e.button&&!e.ctrlKey}(e)&&n.tryStart(e)){var t=n.createEventFromMouse(e,!0);n.emitter.trigger("pointerdown",t),n.initScrollWatch(t),n.shouldIgnoreMove||document.addEventListener("mousemove",n.handleMouseMove),document.addEventListener("mouseup",n.handleMouseUp)}},this.handleMouseMove=function(e){var t=n.createEventFromMouse(e);n.recordCoords(t),n.emitter.trigger("pointermove",t)},this.handleMouseUp=function(e){document.removeEventListener("mousemove",n.handleMouseMove),document.removeEventListener("mouseup",n.handleMouseUp),n.emitter.trigger("pointerup",n.createEventFromMouse(e)),n.cleanup()},this.handleTouchStart=function(e){if(n.tryStart(e)){n.isTouchDragging=!0;var t=n.createEventFromTouch(e,!0);n.emitter.trigger("pointerdown",t),n.initScrollWatch(t);var r=e.target;n.shouldIgnoreMove||r.addEventListener("touchmove",n.handleTouchMove),r.addEventListener("touchend",n.handleTouchEnd),r.addEventListener("touchcancel",n.handleTouchEnd),window.addEventListener("scroll",n.handleTouchScroll,!0)}},this.handleTouchMove=function(e){var t=n.createEventFromTouch(e);n.recordCoords(t),n.emitter.trigger("pointermove",t)},this.handleTouchEnd=function(e){if(n.isDragging){var r=e.target;r.removeEventListener("touchmove",n.handleTouchMove),r.removeEventListener("touchend",n.handleTouchEnd),r.removeEventListener("touchcancel",n.handleTouchEnd),window.removeEventListener("scroll",n.handleTouchScroll,!0),n.emitter.trigger("pointerup",n.createEventFromTouch(e)),n.cleanup(),n.isTouchDragging=!1,o++,setTimeout(function(){o--},t.config.touchMouseIgnoreWait)}},this.handleTouchScroll=function(){n.wasTouchScroll=!0},this.handleScroll=function(e){if(!n.shouldIgnoreMove){var t=window.pageXOffset-n.prevScrollX+n.prevPageX,r=window.pageYOffset-n.prevScrollY+n.prevPageY;n.emitter.trigger("pointermove",{origEvent:e,isTouch:n.isTouchDragging,subjectEl:n.subjectEl,pageX:t,pageY:r,deltaX:t-n.origPageX,deltaY:r-n.origPageY})}},this.containerEl=e,this.emitter=new t.EmitterMixin,e.addEventListener("mousedown",this.handleMouseDown),e.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),a++||window.addEventListener("touchmove",c,{passive:!1})}return e.prototype.destroy=function(){this.containerEl.removeEventListener("mousedown",this.handleMouseDown),this.containerEl.removeEventListener("touchstart",this.handleTouchStart,{passive:!0}),--a||window.removeEventListener("touchmove",c,{passive:!1})},e.prototype.tryStart=function(e){var n=this.querySubjectEl(e),r=e.target;return!(!n||this.handleSelector&&!t.elementClosest(r,this.handleSelector))&&(this.subjectEl=n,this.downEl=r,this.isDragging=!0,this.wasTouchScroll=!1,!0)},e.prototype.cleanup=function(){l=!1,this.isDragging=!1,this.subjectEl=null,this.downEl=null,this.destroyScrollWatch()},e.prototype.querySubjectEl=function(e){return this.selector?t.elementClosest(e.target,this.selector):this.containerEl},e.prototype.shouldIgnoreMouse=function(){return o||this.isTouchDragging},e.prototype.cancelTouchScroll=function(){this.isDragging&&(l=!0)},e.prototype.initScrollWatch=function(e){this.shouldWatchScroll&&(this.recordCoords(e),window.addEventListener("scroll",this.handleScroll,!0))},e.prototype.recordCoords=function(e){this.shouldWatchScroll&&(this.prevPageX=e.pageX,this.prevPageY=e.pageY,this.prevScrollX=window.pageXOffset,this.prevScrollY=window.pageYOffset)},e.prototype.destroyScrollWatch=function(){this.shouldWatchScroll&&window.removeEventListener("scroll",this.handleScroll,!0)},e.prototype.createEventFromMouse=function(e,t){var n=0,r=0;return t?(this.origPageX=e.pageX,this.origPageY=e.pageY):(n=e.pageX-this.origPageX,r=e.pageY-this.origPageY),{origEvent:e,isTouch:!1,subjectEl:this.subjectEl,pageX:e.pageX,pageY:e.pageY,deltaX:n,deltaY:r}},e.prototype.createEventFromTouch=function(e,t){var n,r,i=e.touches,o=0,a=0;return i&&i.length?(n=i[0].pageX,r=i[0].pageY):(n=e.pageX,r=e.pageY),t?(this.origPageX=n,this.origPageY=r):(o=n-this.origPageX,a=r-this.origPageY),{origEvent:e,isTouch:!0,subjectEl:this.subjectEl,pageX:n,pageY:r,deltaX:o,deltaY:a}},e}();function c(e){l&&e.preventDefault()}var d=function(){function e(){this.isVisible=!1,this.sourceEl=null,this.mirrorEl=null,this.sourceElRect=null,this.parentNode=document.body,this.zIndex=9999,this.revertDuration=0}return e.prototype.start=function(e,t,n){this.sourceEl=e,this.sourceElRect=this.sourceEl.getBoundingClientRect(),this.origScreenX=t-window.pageXOffset,this.origScreenY=n-window.pageYOffset,this.deltaX=0,this.deltaY=0,this.updateElPosition()},e.prototype.handleMove=function(e,t){this.deltaX=e-window.pageXOffset-this.origScreenX,this.deltaY=t-window.pageYOffset-this.origScreenY,this.updateElPosition()},e.prototype.setIsVisible=function(e){e?this.isVisible||(this.mirrorEl&&(this.mirrorEl.style.display=""),this.isVisible=e,this.updateElPosition()):this.isVisible&&(this.mirrorEl&&(this.mirrorEl.style.display="none"),this.isVisible=e)},e.prototype.stop=function(e,t){var n=this,r=function(){n.cleanup(),t()};e&&this.mirrorEl&&this.isVisible&&this.revertDuration&&(this.deltaX||this.deltaY)?this.doRevertAnimation(r,this.revertDuration):setTimeout(r,0)},e.prototype.doRevertAnimation=function(e,n){var r=this.mirrorEl,i=this.sourceEl.getBoundingClientRect();r.style.transition="top "+n+"ms,left "+n+"ms",t.applyStyle(r,{left:i.left,top:i.top}),t.whenTransitionDone(r,function(){r.style.transition="",e()})},e.prototype.cleanup=function(){this.mirrorEl&&(t.removeElement(this.mirrorEl),this.mirrorEl=null),this.sourceEl=null},e.prototype.updateElPosition=function(){this.sourceEl&&this.isVisible&&t.applyStyle(this.getMirrorEl(),{left:this.sourceElRect.left+this.deltaX,top:this.sourceElRect.top+this.deltaY})},e.prototype.getMirrorEl=function(){var e=this.sourceElRect,n=this.mirrorEl;return n||((n=this.mirrorEl=this.sourceEl.cloneNode(!0)).classList.add("fc-unselectable"),n.classList.add("fc-dragging"),t.applyStyle(n,{position:"fixed",zIndex:this.zIndex,visibility:"",boxSizing:"border-box",width:e.right-e.left,height:e.bottom-e.top,right:"auto",bottom:"auto",margin:0}),this.parentNode.appendChild(n)),n},e}(),g=function(e){function t(t,n){var r=e.call(this)||this;return r.handleScroll=function(){r.scrollTop=r.scrollController.getScrollTop(),r.scrollLeft=r.scrollController.getScrollLeft(),r.handleScrollChange()},r.scrollController=t,r.doesListening=n,r.scrollTop=r.origScrollTop=t.getScrollTop(),r.scrollLeft=r.origScrollLeft=t.getScrollLeft(),r.scrollWidth=t.getScrollWidth(),r.scrollHeight=t.getScrollHeight(),r.clientWidth=t.getClientWidth(),r.clientHeight=t.getClientHeight(),r.clientRect=r.computeClientRect(),r.doesListening&&r.getEventTarget().addEventListener("scroll",r.handleScroll),r}return r(t,e),t.prototype.destroy=function(){this.doesListening&&this.getEventTarget().removeEventListener("scroll",this.handleScroll)},t.prototype.getScrollTop=function(){return this.scrollTop},t.prototype.getScrollLeft=function(){return this.scrollLeft},t.prototype.setScrollTop=function(e){this.scrollController.setScrollTop(e),this.doesListening||(this.scrollTop=Math.max(Math.min(e,this.getMaxScrollTop()),0),this.handleScrollChange())},t.prototype.setScrollLeft=function(e){this.scrollController.setScrollLeft(e),this.doesListening||(this.scrollLeft=Math.max(Math.min(e,this.getMaxScrollLeft()),0),this.handleScrollChange())},t.prototype.getClientWidth=function(){return this.clientWidth},t.prototype.getClientHeight=function(){return this.clientHeight},t.prototype.getScrollWidth=function(){return this.scrollWidth},t.prototype.getScrollHeight=function(){return this.scrollHeight},t.prototype.handleScrollChange=function(){},t}(t.ScrollController),u=function(e){function n(n,r){return e.call(this,new t.ElementScrollController(n),r)||this}return r(n,e),n.prototype.getEventTarget=function(){return this.scrollController.el},n.prototype.computeClientRect=function(){return t.computeInnerRect(this.scrollController.el)},n}(g),h=function(e){function n(n){return e.call(this,new t.WindowScrollController,n)||this}return r(n,e),n.prototype.getEventTarget=function(){return window},n.prototype.computeClientRect=function(){return{left:this.scrollLeft,right:this.scrollLeft+this.clientWidth,top:this.scrollTop,bottom:this.scrollTop+this.clientHeight}},n.prototype.handleScrollChange=function(){this.clientRect=this.computeClientRect()},n}(g),p="function"==typeof performance?performance.now:Date.now,v=function(){function e(){var e=this;this.isEnabled=!0,this.scrollQuery=[window,".fc-scroller"],this.edgeThreshold=50,this.maxVelocity=300,this.pointerScreenX=null,this.pointerScreenY=null,this.isAnimating=!1,this.scrollCaches=null,this.everMovedUp=!1,this.everMovedDown=!1,this.everMovedLeft=!1,this.everMovedRight=!1,this.animate=function(){if(e.isAnimating){var t=e.computeBestEdge(e.pointerScreenX+window.pageXOffset,e.pointerScreenY+window.pageYOffset);if(t){var n=p();e.handleSide(t,(n-e.msSinceRequest)/1e3),e.requestAnimation(n)}else e.isAnimating=!1}}}return e.prototype.start=function(e,t){this.isEnabled&&(this.scrollCaches=this.buildCaches(),this.pointerScreenX=null,this.pointerScreenY=null,this.everMovedUp=!1,this.everMovedDown=!1,this.everMovedLeft=!1,this.everMovedRight=!1,this.handleMove(e,t))},e.prototype.handleMove=function(e,t){if(this.isEnabled){var n=e-window.pageXOffset,r=t-window.pageYOffset,i=null===this.pointerScreenY?0:r-this.pointerScreenY,o=null===this.pointerScreenX?0:n-this.pointerScreenX;i<0?this.everMovedUp=!0:i>0&&(this.everMovedDown=!0),o<0?this.everMovedLeft=!0:o>0&&(this.everMovedRight=!0),this.pointerScreenX=n,this.pointerScreenY=r,this.isAnimating||(this.isAnimating=!0,this.requestAnimation(p()))}},e.prototype.stop=function(){if(this.isEnabled){this.isAnimating=!1;for(var e=0,t=this.scrollCaches;e<t.length;e++){t[e].destroy()}this.scrollCaches=null}},e.prototype.requestAnimation=function(e){this.msSinceRequest=e,requestAnimationFrame(this.animate)},e.prototype.handleSide=function(e,t){var n=e.scrollCache,r=this.edgeThreshold,i=r-e.distance,o=i*i/(r*r)*this.maxVelocity*t,a=1;switch(e.name){case"left":a=-1;case"right":n.setScrollLeft(n.getScrollLeft()+o*a);break;case"top":a=-1;case"bottom":n.setScrollTop(n.getScrollTop()+o*a)}},e.prototype.computeBestEdge=function(e,t){for(var n=this.edgeThreshold,r=null,i=0,o=this.scrollCaches;i<o.length;i++){var a=o[i],l=a.clientRect,s=e-l.left,c=l.right-e,d=t-l.top,g=l.bottom-t;s>=0&&c>=0&&d>=0&&g>=0&&(d<=n&&this.everMovedUp&&a.canScrollUp()&&(!r||r.distance>d)&&(r={scrollCache:a,name:"top",distance:d}),g<=n&&this.everMovedDown&&a.canScrollDown()&&(!r||r.distance>g)&&(r={scrollCache:a,name:"bottom",distance:g}),s<=n&&this.everMovedLeft&&a.canScrollLeft()&&(!r||r.distance>s)&&(r={scrollCache:a,name:"left",distance:s}),c<=n&&this.everMovedRight&&a.canScrollRight()&&(!r||r.distance>c)&&(r={scrollCache:a,name:"right",distance:c}))}return r},e.prototype.buildCaches=function(){return this.queryScrollEls().map(function(e){return e===window?new h(!1):new u(e,!1)})},e.prototype.queryScrollEls=function(){for(var e=[],t=0,n=this.scrollQuery;t<n.length;t++){var r=n[t];"object"==typeof r?e.push(r):e.push.apply(e,Array.prototype.slice.call(document.querySelectorAll(r)))}return e},e}(),f=function(e){function n(n){var r=e.call(this,n)||this;r.delay=null,r.minDistance=0,r.touchScrollAllowed=!0,r.mirrorNeedsRevert=!1,r.isInteracting=!1,r.isDragging=!1,r.isDelayEnded=!1,r.isDistanceSurpassed=!1,r.delayTimeoutId=null,r.onPointerDown=function(e){r.isDragging||(r.isInteracting=!0,r.isDelayEnded=!1,r.isDistanceSurpassed=!1,t.preventSelection(document.body),t.preventContextMenu(document.body),e.isTouch||e.origEvent.preventDefault(),r.emitter.trigger("pointerdown",e),r.pointer.shouldIgnoreMove||(r.mirror.setIsVisible(!1),r.mirror.start(e.subjectEl,e.pageX,e.pageY),r.startDelay(e),r.minDistance||r.handleDistanceSurpassed(e)))},r.onPointerMove=function(e){if(r.isInteracting){if(r.emitter.trigger("pointermove",e),!r.isDistanceSurpassed){var t=r.minDistance,n=e.deltaX,i=e.deltaY;n*n+i*i>=t*t&&r.handleDistanceSurpassed(e)}r.isDragging&&("scroll"!==e.origEvent.type&&(r.mirror.handleMove(e.pageX,e.pageY),r.autoScroller.handleMove(e.pageX,e.pageY)),r.emitter.trigger("dragmove",e))}},r.onPointerUp=function(e){r.isInteracting&&(r.isInteracting=!1,t.allowSelection(document.body),t.allowContextMenu(document.body),r.emitter.trigger("pointerup",e),r.isDragging&&(r.autoScroller.stop(),r.tryStopDrag(e)),r.delayTimeoutId&&(clearTimeout(r.delayTimeoutId),r.delayTimeoutId=null))};var i=r.pointer=new s(n);return i.emitter.on("pointerdown",r.onPointerDown),i.emitter.on("pointermove",r.onPointerMove),i.emitter.on("pointerup",r.onPointerUp),r.mirror=new d,r.autoScroller=new v,r}return r(n,e),n.prototype.destroy=function(){this.pointer.destroy()},n.prototype.startDelay=function(e){var t=this;"number"==typeof this.delay?this.delayTimeoutId=setTimeout(function(){t.delayTimeoutId=null,t.handleDelayEnd(e)},this.delay):this.handleDelayEnd(e)},n.prototype.handleDelayEnd=function(e){this.isDelayEnded=!0,this.tryStartDrag(e)},n.prototype.handleDistanceSurpassed=function(e){this.isDistanceSurpassed=!0,this.tryStartDrag(e)},n.prototype.tryStartDrag=function(e){this.isDelayEnded&&this.isDistanceSurpassed&&(this.pointer.wasTouchScroll&&!this.touchScrollAllowed||(this.isDragging=!0,this.mirrorNeedsRevert=!1,this.autoScroller.start(e.pageX,e.pageY),this.emitter.trigger("dragstart",e),!1===this.touchScrollAllowed&&this.pointer.cancelTouchScroll()))},n.prototype.tryStopDrag=function(e){this.mirror.stop(this.mirrorNeedsRevert,this.stopDrag.bind(this,e))},n.prototype.stopDrag=function(e){this.isDragging=!1,this.emitter.trigger("dragend",e)},n.prototype.setIgnoreMove=function(e){this.pointer.shouldIgnoreMove=e},n.prototype.setMirrorIsVisible=function(e){this.mirror.setIsVisible(e)},n.prototype.setMirrorNeedsRevert=function(e){this.mirrorNeedsRevert=e},n.prototype.setAutoScrollEnabled=function(e){this.autoScroller.isEnabled=e},n}(t.ElementDragging),E=function(){function e(e){this.origRect=t.computeRect(e),this.scrollCaches=t.getClippingParents(e).map(function(e){return new u(e,!0)})}return e.prototype.destroy=function(){for(var e=0,t=this.scrollCaches;e<t.length;e++){t[e].destroy()}},e.prototype.computeLeft=function(){for(var e=this.origRect.left,t=0,n=this.scrollCaches;t<n.length;t++){var r=n[t];e+=r.origScrollLeft-r.getScrollLeft()}return e},e.prototype.computeTop=function(){for(var e=this.origRect.top,t=0,n=this.scrollCaches;t<n.length;t++){var r=n[t];e+=r.origScrollTop-r.getScrollTop()}return e},e.prototype.isWithinClipping=function(e,n){for(var r,i,o={left:e,top:n},a=0,l=this.scrollCaches;a<l.length;a++){var s=l[a];if(r=s.getEventTarget(),i=void 0,"HTML"!==(i=r.tagName)&&"BODY"!==i&&!t.pointInsideRect(o,s.clientRect))return!1}return!0},e}();var m=function(){function e(e,n){var r=this;this.useSubjectCenter=!1,this.requireInitial=!0,this.initialHit=null,this.movingHit=null,this.finalHit=null,this.handlePointerDown=function(e){var t=r.dragging;r.initialHit=null,r.movingHit=null,r.finalHit=null,r.prepareHits(),r.processFirstCoord(e),r.initialHit||!r.requireInitial?(t.setIgnoreMove(!1),r.emitter.trigger("pointerdown",e)):t.setIgnoreMove(!0)},this.handleDragStart=function(e){r.emitter.trigger("dragstart",e),r.handleMove(e,!0)},this.handleDragMove=function(e){r.emitter.trigger("dragmove",e),r.handleMove(e)},this.handlePointerUp=function(e){r.releaseHits(),r.emitter.trigger("pointerup",e)},this.handleDragEnd=function(e){r.movingHit&&r.emitter.trigger("hitupdate",null,!0,e),r.finalHit=r.movingHit,r.movingHit=null,r.emitter.trigger("dragend",e)},this.droppableStore=n,e.emitter.on("pointerdown",this.handlePointerDown),e.emitter.on("dragstart",this.handleDragStart),e.emitter.on("dragmove",this.handleDragMove),e.emitter.on("pointerup",this.handlePointerUp),e.emitter.on("dragend",this.handleDragEnd),this.dragging=e,this.emitter=new t.EmitterMixin}return e.prototype.processFirstCoord=function(e){var n,r={left:e.pageX,top:e.pageY},i=r,o=e.subjectEl;o!==document&&(n=t.computeRect(o),i=t.constrainPoint(i,n));var a=this.initialHit=this.queryHitForOffset(i.left,i.top);if(a){if(this.useSubjectCenter&&n){var l=t.intersectRects(n,a.rect);l&&(i=t.getRectCenter(l))}this.coordAdjust=t.diffPoints(i,r)}else this.coordAdjust={left:0,top:0}},e.prototype.handleMove=function(e,t){var n=this.queryHitForOffset(e.pageX+this.coordAdjust.left,e.pageY+this.coordAdjust.top);!t&&S(this.movingHit,n)||(this.movingHit=n,this.emitter.trigger("hitupdate",n,!1,e))},e.prototype.prepareHits=function(){this.offsetTrackers=t.mapHash(this.droppableStore,function(e){return e.component.buildPositionCaches(),new E(e.el)})},e.prototype.releaseHits=function(){var e=this.offsetTrackers;for(var t in e)e[t].destroy();this.offsetTrackers={}},e.prototype.queryHitForOffset=function(e,n){var r=this.droppableStore,i=this.offsetTrackers,o=null;for(var a in r){var l=r[a].component,s=i[a];if(s.isWithinClipping(e,n)){var c=s.computeLeft(),d=s.computeTop(),g=e-c,u=n-d,h=s.origRect,p=h.right-h.left,v=h.bottom-h.top;if(g>=0&&g<p&&u>=0&&u<v){var f=l.queryHit(g,u,p,v);!f||l.props.dateProfile&&!t.rangeContainsRange(l.props.dateProfile.activeRange,f.dateSpan.range)||o&&!(f.layer>o.layer)||(f.rect.left+=c,f.rect.right+=c,f.rect.top+=d,f.rect.bottom+=d,o=f)}}}return o},e}();function S(e,n){return!e&&!n||Boolean(e)===Boolean(n)&&t.isDateSpansEqual(e.dateSpan,n.dateSpan)}var y=function(e){function n(n){var r=e.call(this,n)||this;r.handlePointerDown=function(e){var t=r.dragging;t.setIgnoreMove(!r.component.isValidDateDownEl(t.pointer.downEl))},r.handleDragEnd=function(e){var t=r.component;if(!r.dragging.pointer.wasTouchScroll){var n=r.hitDragging,i=n.initialHit,o=n.finalHit;i&&o&&S(i,o)&&t.calendar.triggerDateClick(i.dateSpan,i.dayEl,t.view,e.origEvent)}};var i=n.component;r.dragging=new f(i.el),r.dragging.autoScroller.isEnabled=!1;var o=r.hitDragging=new m(r.dragging,t.interactionSettingsToStore(n));return o.emitter.on("pointerdown",r.handlePointerDown),o.emitter.on("dragend",r.handleDragEnd),r}return r(n,e),n.prototype.destroy=function(){this.dragging.destroy()},n}(t.Interaction),D=function(e){function n(n){var r=e.call(this,n)||this;r.dragSelection=null,r.handlePointerDown=function(e){var t=r,n=t.component,i=t.dragging,o=n.opt("selectable")&&n.isValidDateDownEl(e.origEvent.target);i.setIgnoreMove(!o),i.delay=e.isTouch?function(e){var t=e.opt("selectLongPressDelay");null==t&&(t=e.opt("longPressDelay"));return t}(n):null},r.handleDragStart=function(e){r.component.calendar.unselect(e)},r.handleHitUpdate=function(e,n){var o=r.component.calendar,a=null,l=!1;e&&((a=function(e,n,r){var o=e.dateSpan,a=n.dateSpan,l=[o.range.start,o.range.end,a.range.start,a.range.end];l.sort(t.compareNumbers);for(var s={},c=0,d=r;c<d.length;c++){var g=d[c],u=g(e,n);if(!1===u)return null;u&&i(s,u)}return s.range={start:l[0],end:l[3]},s.allDay=o.allDay,s}(r.hitDragging.initialHit,e,o.pluginSystem.hooks.dateSelectionTransformers))&&r.component.isDateSelectionValid(a)||(l=!0,a=null)),a?o.dispatch({type:"SELECT_DATES",selection:a}):n||o.dispatch({type:"UNSELECT_DATES"}),l?t.disableCursor():t.enableCursor(),n||(r.dragSelection=a)},r.handlePointerUp=function(e){r.dragSelection&&(r.component.calendar.triggerDateSelect(r.dragSelection,e),r.dragSelection=null)};var o=n.component,a=r.dragging=new f(o.el);a.touchScrollAllowed=!1,a.minDistance=o.opt("selectMinDistance")||0,a.autoScroller.isEnabled=o.opt("dragScroll");var l=r.hitDragging=new m(r.dragging,t.interactionSettingsToStore(n));return l.emitter.on("pointerdown",r.handlePointerDown),l.emitter.on("dragstart",r.handleDragStart),l.emitter.on("hitupdate",r.handleHitUpdate),l.emitter.on("pointerup",r.handlePointerUp),r}return r(n,e),n.prototype.destroy=function(){this.dragging.destroy()},n}(t.Interaction);var w=function(e){function n(r){var o=e.call(this,r)||this;o.subjectSeg=null,o.isDragging=!1,o.eventRange=null,o.relevantEvents=null,o.receivingCalendar=null,o.validMutation=null,o.mutatedRelevantEvents=null,o.handlePointerDown=function(e){var n=e.origEvent.target,r=o,i=r.component,a=r.dragging,l=a.mirror,s=i.calendar,c=o.subjectSeg=t.getElSeg(e.subjectEl),d=(o.eventRange=c.eventRange).instance.instanceId;o.relevantEvents=t.getRelevantEvents(s.state.eventStore,d),a.minDistance=e.isTouch?0:i.opt("eventDragMinDistance"),a.delay=e.isTouch&&d!==i.props.eventSelection?function(e){var t=e.opt("eventLongPressDelay");null==t&&(t=e.opt("longPressDelay"));return t}(i):null,l.parentNode=s.el,l.revertDuration=i.opt("dragRevertDuration");var g=i.isValidSegDownEl(n)&&!t.elementClosest(n,".fc-resizer");a.setIgnoreMove(!g),o.isDragging=g&&e.subjectEl.classList.contains("fc-draggable")},o.handleDragStart=function(e){var n=o.component.calendar,r=o.eventRange,i=r.instance.instanceId;e.isTouch?i!==o.component.props.eventSelection&&n.dispatch({type:"SELECT_EVENT",eventInstanceId:i}):n.dispatch({type:"UNSELECT_EVENT"}),o.isDragging&&(n.unselect(e),n.publiclyTrigger("eventDragStart",[{el:o.subjectSeg.el,event:new t.EventApi(n,r.def,r.instance),jsEvent:e.origEvent,view:o.component.view}]))},o.handleHitUpdate=function(e,n){if(o.isDragging){var r=o.relevantEvents,i=o.hitDragging.initialHit,a=o.component.calendar,l=null,s=null,c=null,d=!1,g={affectedEvents:r,mutatedEvents:t.createEmptyEventStore(),isEvent:!0,origSeg:o.subjectSeg};if(e){var u=e.component;a===(l=u.calendar)||u.opt("editable")&&u.opt("droppable")?(s=function(e,n,r){var i=e.dateSpan,o=n.dateSpan,a=i.range.start,l=o.range.start,s={};i.allDay!==o.allDay&&(s.allDay=o.allDay,s.hasEnd=n.component.opt("allDayMaintainDuration"),o.allDay&&(a=t.startOfDay(a)));var c=t.diffDates(a,l,e.component.dateEnv,e.component===n.component?e.component.largeUnit:null);c.milliseconds&&(s.allDay=!1);for(var d={datesDelta:c,standardProps:s},g=0,u=r;g<u.length;g++){var h=u[g];h(d,e,n)}return d}(i,e,l.pluginSystem.hooks.eventDragMutationMassagers))&&(c=t.applyMutationToEventStore(r,l.eventUiBases,s,l),g.mutatedEvents=c,u.isInteractionValid(g)||(d=!0,s=null,c=null,g.mutatedEvents=t.createEmptyEventStore())):l=null}o.displayDrag(l,g),d?t.disableCursor():t.enableCursor(),n||(a===l&&S(i,e)&&(s=null),o.dragging.setMirrorNeedsRevert(!s),o.dragging.setMirrorIsVisible(!e||!document.querySelector(".fc-mirror")),o.receivingCalendar=l,o.validMutation=s,o.mutatedRelevantEvents=c)}},o.handlePointerUp=function(){o.isDragging||o.cleanup()},o.handleDragEnd=function(e){if(o.isDragging){var n=o.component.calendar,r=o.component.view,a=o,l=a.receivingCalendar,s=a.validMutation,c=o.eventRange.def,d=o.eventRange.instance,g=new t.EventApi(n,c,d),u=o.relevantEvents,h=o.mutatedRelevantEvents,p=o.hitDragging.finalHit;if(o.clearDrag(),n.publiclyTrigger("eventDragStop",[{el:o.subjectSeg.el,event:g,jsEvent:e.origEvent,view:r}]),s){if(l===n){n.dispatch({type:"MERGE_EVENTS",eventStore:h});for(var v={},f=0,E=n.pluginSystem.hooks.eventDropTransformers;f<E.length;f++){var m=E[f];i(v,m(s,n))}var S=i({},v,{el:e.subjectEl,delta:s.datesDelta,oldEvent:g,event:new t.EventApi(n,h.defs[c.defId],d?h.instances[d.instanceId]:null),revert:function(){n.dispatch({type:"MERGE_EVENTS",eventStore:u})},jsEvent:e.origEvent,view:r});n.publiclyTrigger("eventDrop",[S])}else if(l){n.publiclyTrigger("eventLeave",[{draggedEl:e.subjectEl,event:g,view:r}]),n.dispatch({type:"REMOVE_EVENT_INSTANCES",instances:o.mutatedRelevantEvents.instances}),l.dispatch({type:"MERGE_EVENTS",eventStore:o.mutatedRelevantEvents}),e.isTouch&&l.dispatch({type:"SELECT_EVENT",eventInstanceId:d.instanceId});var y=i({},l.buildDatePointApi(p.dateSpan),{draggedEl:e.subjectEl,jsEvent:e.origEvent,view:p.component});l.publiclyTrigger("drop",[y]),l.publiclyTrigger("eventReceive",[{draggedEl:e.subjectEl,event:new t.EventApi(l,h.defs[c.defId],h.instances[d.instanceId]),view:p.component}])}}else n.publiclyTrigger("_noEventDrop")}o.cleanup()};var a=o.component,l=o.dragging=new f(a.el);l.pointer.selector=n.SELECTOR,l.touchScrollAllowed=!1,l.autoScroller.isEnabled=a.opt("dragScroll");var s=o.hitDragging=new m(o.dragging,t.interactionSettingsStore);return s.useSubjectCenter=r.useEventCenter,s.emitter.on("pointerdown",o.handlePointerDown),s.emitter.on("dragstart",o.handleDragStart),s.emitter.on("hitupdate",o.handleHitUpdate),s.emitter.on("pointerup",o.handlePointerUp),s.emitter.on("dragend",o.handleDragEnd),o}return r(n,e),n.prototype.destroy=function(){this.dragging.destroy()},n.prototype.displayDrag=function(e,n){var r=this.component.calendar,i=this.receivingCalendar;i&&i!==e&&(i===r?i.dispatch({type:"SET_EVENT_DRAG",state:{affectedEvents:n.affectedEvents,mutatedEvents:t.createEmptyEventStore(),isEvent:!0,origSeg:n.origSeg}}):i.dispatch({type:"UNSET_EVENT_DRAG"})),e&&e.dispatch({type:"SET_EVENT_DRAG",state:n})},n.prototype.clearDrag=function(){var e=this.component.calendar,t=this.receivingCalendar;t&&t.dispatch({type:"UNSET_EVENT_DRAG"}),e!==t&&e.dispatch({type:"UNSET_EVENT_DRAG"})},n.prototype.cleanup=function(){this.subjectSeg=null,this.isDragging=!1,this.eventRange=null,this.relevantEvents=null,this.receivingCalendar=null,this.validMutation=null,this.mutatedRelevantEvents=null},n.SELECTOR=".fc-draggable, .fc-resizable",n}(t.Interaction);var T=function(e){function n(n){var r=e.call(this,n)||this;r.draggingSeg=null,r.eventRange=null,r.relevantEvents=null,r.validMutation=null,r.mutatedRelevantEvents=null,r.handlePointerDown=function(e){var t=r.component,n=r.querySeg(e),i=r.eventRange=n.eventRange;r.dragging.minDistance=t.opt("eventDragMinDistance"),r.dragging.setIgnoreMove(!r.component.isValidSegDownEl(e.origEvent.target)||e.isTouch&&r.component.props.eventSelection!==i.instance.instanceId)},r.handleDragStart=function(e){var n=r.component.calendar,i=r.eventRange;r.relevantEvents=t.getRelevantEvents(n.state.eventStore,r.eventRange.instance.instanceId),r.draggingSeg=r.querySeg(e),n.unselect(),n.publiclyTrigger("eventResizeStart",[{el:r.draggingSeg.el,event:new t.EventApi(n,i.def,i.instance),jsEvent:e.origEvent,view:r.component.view}])},r.handleHitUpdate=function(e,n,o){var a=r.component.calendar,l=r.relevantEvents,s=r.hitDragging.initialHit,c=r.eventRange.instance,d=null,g=null,u=!1,h={affectedEvents:l,mutatedEvents:t.createEmptyEventStore(),isEvent:!0,origSeg:r.draggingSeg};e&&(d=function(e,n,r,o,a){for(var l=e.component.dateEnv,s=e.dateSpan.range.start,c=n.dateSpan.range.start,d=t.diffDates(s,c,l,e.component.largeUnit),g={},u=0,h=a;u<h.length;u++){var p=h[u],v=p(e,n);if(!1===v)return null;v&&i(g,v)}if(r){if(l.add(o.start,d)<o.end)return g.startDelta=d,g}else if(l.add(o.end,d)>o.start)return g.endDelta=d,g;return null}(s,e,o.subjectEl.classList.contains("fc-start-resizer"),c.range,a.pluginSystem.hooks.eventResizeJoinTransforms)),d&&(g=t.applyMutationToEventStore(l,a.eventUiBases,d,a),h.mutatedEvents=g,r.component.isInteractionValid(h)||(u=!0,d=null,g=null,h.mutatedEvents=null)),g?a.dispatch({type:"SET_EVENT_RESIZE",state:h}):a.dispatch({type:"UNSET_EVENT_RESIZE"}),u?t.disableCursor():t.enableCursor(),n||(d&&S(s,e)&&(d=null),r.validMutation=d,r.mutatedRelevantEvents=g)},r.handleDragEnd=function(e){var n=r.component.calendar,i=r.component.view,o=r.eventRange.def,a=r.eventRange.instance,l=new t.EventApi(n,o,a),s=r.relevantEvents,c=r.mutatedRelevantEvents;n.publiclyTrigger("eventResizeStop",[{el:r.draggingSeg.el,event:l,jsEvent:e.origEvent,view:i}]),r.validMutation?(n.dispatch({type:"MERGE_EVENTS",eventStore:c}),n.publiclyTrigger("eventResize",[{el:r.draggingSeg.el,startDelta:r.validMutation.startDelta||t.createDuration(0),endDelta:r.validMutation.endDelta||t.createDuration(0),prevEvent:l,event:new t.EventApi(n,c.defs[o.defId],a?c.instances[a.instanceId]:null),revert:function(){n.dispatch({type:"MERGE_EVENTS",eventStore:s})},jsEvent:e.origEvent,view:i}])):n.publiclyTrigger("_noEventResize"),r.draggingSeg=null,r.relevantEvents=null,r.validMutation=null};var o=n.component,a=r.dragging=new f(o.el);a.pointer.selector=".fc-resizer",a.touchScrollAllowed=!1,a.autoScroller.isEnabled=o.opt("dragScroll");var l=r.hitDragging=new m(r.dragging,t.interactionSettingsToStore(n));return l.emitter.on("pointerdown",r.handlePointerDown),l.emitter.on("dragstart",r.handleDragStart),l.emitter.on("hitupdate",r.handleHitUpdate),l.emitter.on("dragend",r.handleDragEnd),r}return r(n,e),n.prototype.destroy=function(){this.dragging.destroy()},n.prototype.querySeg=function(e){return t.getElSeg(t.elementClosest(e.subjectEl,this.component.fgSegSelector))},n}(t.Interaction);var M=function(){function e(e){var n=this;this.isRecentPointerDateSelect=!1,this.onSelect=function(e){e.jsEvent&&(n.isRecentPointerDateSelect=!0)},this.onDocumentPointerUp=function(e){var r=n,i=r.calendar,o=r.documentPointer,a=i.state;if(!o.wasTouchScroll){if(a.dateSelection&&!n.isRecentPointerDateSelect){var l=i.viewOpt("unselectAuto"),s=i.viewOpt("unselectCancel");!l||l&&t.elementClosest(o.downEl,s)||i.unselect(e)}a.eventSelection&&!t.elementClosest(o.downEl,w.SELECTOR)&&i.dispatch({type:"UNSELECT_EVENT"})}n.isRecentPointerDateSelect=!1},this.calendar=e;var r=this.documentPointer=new s(document);r.shouldIgnoreMove=!0,r.shouldWatchScroll=!1,r.emitter.on("pointerup",this.onDocumentPointerUp),e.on("select",this.onSelect)}return e.prototype.destroy=function(){this.calendar.off("select",this.onSelect),this.documentPointer.destroy()},e}(),b=function(){function e(e,n){var r=this;this.receivingCalendar=null,this.droppableEvent=null,this.suppliedDragMeta=null,this.dragMeta=null,this.handleDragStart=function(e){r.dragMeta=r.buildDragMeta(e.subjectEl)},this.handleHitUpdate=function(e,n,o){var a=r.hitDragging.dragging,l=null,s=null,c=!1,d={affectedEvents:t.createEmptyEventStore(),mutatedEvents:t.createEmptyEventStore(),isEvent:r.dragMeta.create,origSeg:null};e&&(l=e.component.calendar,r.canDropElOnCalendar(o.subjectEl,l)&&(s=function(e,n,r){for(var o=i({},n.leftoverProps),a=0,l=r.pluginSystem.hooks.externalDefTransforms;a<l.length;a++){var s=l[a];i(o,s(e,n))}var c=t.parseEventDef(o,n.sourceId,e.allDay,r.opt("forceEventDuration")||Boolean(n.duration),r),d=e.range.start;e.allDay&&n.startTime&&(d=r.dateEnv.add(d,n.startTime));var g=n.duration?r.dateEnv.add(d,n.duration):r.getDefaultEventEnd(e.allDay,d),u=t.createEventInstance(c.defId,{start:d,end:g});return{def:c,instance:u}}(e.dateSpan,r.dragMeta,l),d.mutatedEvents=t.eventTupleToStore(s),(c=!t.isInteractionValid(d,l))&&(d.mutatedEvents=t.createEmptyEventStore(),s=null))),r.displayDrag(l,d),a.setMirrorIsVisible(n||!s||!document.querySelector(".fc-mirror")),c?t.disableCursor():t.enableCursor(),n||(a.setMirrorNeedsRevert(!s),r.receivingCalendar=l,r.droppableEvent=s)},this.handleDragEnd=function(e){var n=r,o=n.receivingCalendar,a=n.droppableEvent;if(r.clearDrag(),o&&a){var l=r.hitDragging.finalHit,s=l.component.view,c=r.dragMeta,d=i({},o.buildDatePointApi(l.dateSpan),{draggedEl:e.subjectEl,jsEvent:e.origEvent,view:s});o.publiclyTrigger("drop",[d]),c.create&&(o.dispatch({type:"MERGE_EVENTS",eventStore:t.eventTupleToStore(a)}),e.isTouch&&o.dispatch({type:"SELECT_EVENT",eventInstanceId:a.instance.instanceId}),o.publiclyTrigger("eventReceive",[{draggedEl:e.subjectEl,event:new t.EventApi(o,a.def,a.instance),view:s}]))}r.receivingCalendar=null,r.droppableEvent=null};var o=this.hitDragging=new m(e,t.interactionSettingsStore);o.requireInitial=!1,o.emitter.on("dragstart",this.handleDragStart),o.emitter.on("hitupdate",this.handleHitUpdate),o.emitter.on("dragend",this.handleDragEnd),this.suppliedDragMeta=n}return e.prototype.buildDragMeta=function(e){return"object"==typeof this.suppliedDragMeta?t.parseDragMeta(this.suppliedDragMeta):"function"==typeof this.suppliedDragMeta?t.parseDragMeta(this.suppliedDragMeta(e)):(n=function(e,n){var r=t.config.dataAttrPrefix,i=(r?r+"-":"")+n;return e.getAttribute("data-"+i)||""}(e,"event"),r=n?JSON.parse(n):{create:!1},t.parseDragMeta(r));var n,r},e.prototype.displayDrag=function(e,t){var n=this.receivingCalendar;n&&n!==e&&n.dispatch({type:"UNSET_EVENT_DRAG"}),e&&e.dispatch({type:"SET_EVENT_DRAG",state:t})},e.prototype.clearDrag=function(){this.receivingCalendar&&this.receivingCalendar.dispatch({type:"UNSET_EVENT_DRAG"})},e.prototype.canDropElOnCalendar=function(e,n){var r=n.opt("dropAccept");return"function"==typeof r?r(e):"string"!=typeof r||!r||Boolean(t.elementMatches(e,r))},e}();t.config.dataAttrPrefix="";var C=function(){function e(e,n){var r=this;void 0===n&&(n={}),this.handlePointerDown=function(e){var n=r.dragging,i=r.settings,o=i.minDistance,a=i.longPressDelay;n.minDistance=null!=o?o:e.isTouch?0:t.globalDefaults.eventDragMinDistance,n.delay=e.isTouch?null!=a?a:t.globalDefaults.longPressDelay:0},this.handleDragStart=function(e){e.isTouch&&r.dragging.delay&&e.subjectEl.classList.contains("fc-event")&&r.dragging.mirror.getMirrorEl().classList.add("fc-selected")},this.settings=n;var i=this.dragging=new f(e);i.touchScrollAllowed=!1,null!=n.itemSelector&&(i.pointer.selector=n.itemSelector),null!=n.appendTo&&(i.mirror.parentNode=n.appendTo),i.emitter.on("pointerdown",this.handlePointerDown),i.emitter.on("dragstart",this.handleDragStart),new b(i,n.eventData)}return e.prototype.destroy=function(){this.dragging.destroy()},e}(),R=function(e){function t(t){var n=e.call(this,t)||this;n.shouldIgnoreMove=!1,n.mirrorSelector="",n.currentMirrorEl=null,n.handlePointerDown=function(e){n.emitter.trigger("pointerdown",e),n.shouldIgnoreMove||n.emitter.trigger("dragstart",e)},n.handlePointerMove=function(e){n.shouldIgnoreMove||n.emitter.trigger("dragmove",e)},n.handlePointerUp=function(e){n.emitter.trigger("pointerup",e),n.shouldIgnoreMove||n.emitter.trigger("dragend",e)};var r=n.pointer=new s(t);return r.emitter.on("pointerdown",n.handlePointerDown),r.emitter.on("pointermove",n.handlePointerMove),r.emitter.on("pointerup",n.handlePointerUp),n}return r(t,e),t.prototype.destroy=function(){this.pointer.destroy()},t.prototype.setIgnoreMove=function(e){this.shouldIgnoreMove=e},t.prototype.setMirrorIsVisible=function(e){if(e)this.currentMirrorEl&&(this.currentMirrorEl.style.visibility="",this.currentMirrorEl=null);else{var t=this.mirrorSelector?document.querySelector(this.mirrorSelector):null;t&&(this.currentMirrorEl=t,t.style.visibility="hidden")}},t}(t.ElementDragging),I=function(){function e(e,t){var n=document;e===document||e instanceof Element?(n=e,t=t||{}):t=e||{};var r=this.dragging=new R(n);"string"==typeof t.itemSelector?r.pointer.selector=t.itemSelector:n===document&&(r.pointer.selector="[data-event]"),"string"==typeof t.mirrorSelector&&(r.mirrorSelector=t.mirrorSelector),new b(r,t.eventData)}return e.prototype.destroy=function(){this.dragging.destroy()},e}(),P=t.createPlugin({componentInteractions:[y,D,w,T],calendarInteractions:[M],elementDraggingImpl:f});e.Draggable=C,e.FeaturefulElementDragging=f,e.PointerDragging=s,e.ThirdPartyDraggable=I,e.default=P,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/interaction/package.json b/AKPlan/static/AKPlan/fullcalendar/interaction/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..812a7b4e6bdd0e8cf752cc34863dc998c5b92f9c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/interaction/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/interaction",
+  "version": "4.3.0",
+  "title": "FullCalendar Interaction Plugin",
+  "description": "Provides functionality for event drag-n-drop, resizing, dateClick, and selectable actions",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/editable",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/list/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/README.md b/AKPlan/static/AKPlan/fullcalendar/list/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1122d7f9db8b210f0d7c3440898b3ea1e5e4c7c1
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar List View Plugin
+
+View your events as a bulleted list
+
+[View the docs &raquo;](https://fullcalendar.io/docs/list-view)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/main.css b/AKPlan/static/AKPlan/fullcalendar/list/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..6af9bb70c3dbc67755c085bfb5a3013759218f83
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/main.css
@@ -0,0 +1,118 @@
+/* List View
+--------------------------------------------------------------------------------------------------*/
+/* possibly reusable */
+.fc-event-dot {
+  display: inline-block;
+  width: 10px;
+  height: 10px;
+  border-radius: 5px;
+}
+
+/* view wrapper */
+.fc-rtl .fc-list-view {
+  direction: rtl;
+  /* unlike core views, leverage browser RTL */
+}
+
+.fc-list-view {
+  border-width: 1px;
+  border-style: solid;
+}
+
+/* table resets */
+.fc .fc-list-table {
+  table-layout: auto;
+  /* for shrinkwrapping cell content */
+}
+
+.fc-list-table td {
+  border-width: 1px 0 0;
+  padding: 8px 14px;
+}
+
+.fc-list-table tr:first-child td {
+  border-top-width: 0;
+}
+
+/* day headings with the list */
+.fc-list-heading {
+  border-bottom-width: 1px;
+}
+
+.fc-list-heading td {
+  font-weight: bold;
+}
+
+.fc-ltr .fc-list-heading-main {
+  float: left;
+}
+
+.fc-ltr .fc-list-heading-alt {
+  float: right;
+}
+
+.fc-rtl .fc-list-heading-main {
+  float: right;
+}
+
+.fc-rtl .fc-list-heading-alt {
+  float: left;
+}
+
+/* event list items */
+.fc-list-item.fc-has-url {
+  cursor: pointer;
+  /* whole row will be clickable */
+}
+
+.fc-list-item-marker,
+.fc-list-item-time {
+  white-space: nowrap;
+  width: 1px;
+}
+
+/* make the dot closer to the event title */
+.fc-ltr .fc-list-item-marker {
+  padding-right: 0;
+}
+
+.fc-rtl .fc-list-item-marker {
+  padding-left: 0;
+}
+
+.fc-list-item-title a {
+  /* every event title cell has an <a> tag */
+  text-decoration: none;
+  color: inherit;
+}
+
+.fc-list-item-title a[href]:hover {
+  /* hover effect only on titles with hrefs */
+  text-decoration: underline;
+}
+
+/* message when no events */
+.fc-list-empty-wrap2 {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.fc-list-empty-wrap1 {
+  width: 100%;
+  height: 100%;
+  display: table;
+}
+
+.fc-list-empty {
+  display: table-cell;
+  vertical-align: middle;
+  text-align: center;
+}
+
+.fc-unthemed .fc-list-empty {
+  /* theme will provide own background */
+  background-color: #eee;
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/list/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..78aeb2e1195cba65bb0d445dc176c6e0ede59ef9
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/main.d.ts
@@ -0,0 +1,33 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/list' {
+    import ListView from '@fullcalendar/list/ListView';
+    export { ListView };
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/list/ListView' {
+    import { View, ViewProps, ScrollComponent, DateMarker, DateRange, DateProfileGenerator, ComponentContext, ViewSpec, EventUiHash, EventRenderRange, EventStore, Seg } from '@fullcalendar/core';
+    export { ListView as default, ListView };
+    class ListView extends View {
+        scroller: ScrollComponent;
+        contentEl: HTMLElement;
+        dayDates: DateMarker[];
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        render(props: ViewProps): void;
+        destroy(): void;
+        updateSize(isResize: any, viewHeight: any, isAuto: any): void;
+        computeScrollerHeight(viewHeight: any): number;
+        _eventStoreToSegs(eventStore: EventStore, eventUiBases: EventUiHash, dayRanges: DateRange[]): Seg[];
+        eventRangesToSegs(eventRanges: EventRenderRange[], dayRanges: DateRange[]): any[];
+        eventRangeToSegs(eventRange: EventRenderRange, dayRanges: DateRange[]): any[];
+        renderEmptyMessage(): void;
+        renderSegList(allSegs: any): void;
+        groupSegsByDay(segs: any): any[];
+        buildDayHeaderRow(dayDate: any): HTMLTableRowElement;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/list/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..87b9eb2e4932bd07d3b36ef951c259438bd128ba
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/main.esm.js
@@ -0,0 +1,335 @@
+/*!
+FullCalendar List View Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { getAllDayHtml, isMultiDayRange, htmlEscape, FgEventRenderer, memoize, memoizeRendering, ScrollComponent, subtractInnerElHeight, sliceEventStore, intersectRanges, htmlToElement, createFormatter, createElement, buildGotoAnchorHtml, View, startOfDay, addDays, createPlugin } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var ListEventRenderer = /** @class */ (function (_super) {
+    __extends(ListEventRenderer, _super);
+    function ListEventRenderer(listView) {
+        var _this = _super.call(this, listView.context) || this;
+        _this.listView = listView;
+        return _this;
+    }
+    ListEventRenderer.prototype.attachSegs = function (segs) {
+        if (!segs.length) {
+            this.listView.renderEmptyMessage();
+        }
+        else {
+            this.listView.renderSegList(segs);
+        }
+    };
+    ListEventRenderer.prototype.detachSegs = function () {
+    };
+    // generates the HTML for a single event row
+    ListEventRenderer.prototype.renderSegHtml = function (seg) {
+        var _a = this.context, view = _a.view, theme = _a.theme;
+        var eventRange = seg.eventRange;
+        var eventDef = eventRange.def;
+        var eventInstance = eventRange.instance;
+        var eventUi = eventRange.ui;
+        var url = eventDef.url;
+        var classes = ['fc-list-item'].concat(eventUi.classNames);
+        var bgColor = eventUi.backgroundColor;
+        var timeHtml;
+        if (eventDef.allDay) {
+            timeHtml = getAllDayHtml(view);
+        }
+        else if (isMultiDayRange(eventRange.range)) {
+            if (seg.isStart) {
+                timeHtml = htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay
+                ));
+            }
+            else if (seg.isEnd) {
+                timeHtml = htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay
+                ));
+            }
+            else { // inner segment that lasts the whole day
+                timeHtml = getAllDayHtml(view);
+            }
+        }
+        else {
+            // Display the normal time text for the *event's* times
+            timeHtml = htmlEscape(this.getTimeText(eventRange));
+        }
+        if (url) {
+            classes.push('fc-has-url');
+        }
+        return '<tr class="' + classes.join(' ') + '">' +
+            (this.displayEventTime ?
+                '<td class="fc-list-item-time ' + theme.getClass('widgetContent') + '">' +
+                    (timeHtml || '') +
+                    '</td>' :
+                '') +
+            '<td class="fc-list-item-marker ' + theme.getClass('widgetContent') + '">' +
+            '<span class="fc-event-dot"' +
+            (bgColor ?
+                ' style="background-color:' + bgColor + '"' :
+                '') +
+            '></span>' +
+            '</td>' +
+            '<td class="fc-list-item-title ' + theme.getClass('widgetContent') + '">' +
+            '<a' + (url ? ' href="' + htmlEscape(url) + '"' : '') + '>' +
+            htmlEscape(eventDef.title || '') +
+            '</a>' +
+            '</td>' +
+            '</tr>';
+    };
+    // like "4:00am"
+    ListEventRenderer.prototype.computeEventTimeFormat = function () {
+        return {
+            hour: 'numeric',
+            minute: '2-digit',
+            meridiem: 'short'
+        };
+    };
+    return ListEventRenderer;
+}(FgEventRenderer));
+
+/*
+Responsible for the scroller, and forwarding event-related actions into the "grid".
+*/
+var ListView = /** @class */ (function (_super) {
+    __extends(ListView, _super);
+    function ListView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.computeDateVars = memoize(computeDateVars);
+        _this.eventStoreToSegs = memoize(_this._eventStoreToSegs);
+        var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this);
+        _this.renderContent = memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer));
+        _this.el.classList.add('fc-list-view');
+        var listViewClassNames = (_this.theme.getClass('listView') || '').split(' '); // wish we didn't have to do this
+        for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) {
+            var listViewClassName = listViewClassNames_1[_i];
+            if (listViewClassName) { // in case input was empty string
+                _this.el.classList.add(listViewClassName);
+            }
+        }
+        _this.scroller = new ScrollComponent('hidden', // overflow x
+        'auto' // overflow y
+        );
+        _this.el.appendChild(_this.scroller.el);
+        _this.contentEl = _this.scroller.el; // shortcut
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.el
+            // TODO: make aware that it doesn't do Hits
+        });
+        return _this;
+    }
+    ListView.prototype.render = function (props) {
+        var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges;
+        this.dayDates = dayDates;
+        this.renderContent(this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges));
+    };
+    ListView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.renderContent.unrender();
+        this.scroller.destroy(); // will remove the Grid too
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+        _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto);
+        this.eventRenderer.computeSizes(isResize);
+        this.eventRenderer.assignSizes(isResize);
+        this.scroller.clear(); // sets height to 'auto' and clears overflow
+        if (!isAuto) {
+            this.scroller.setHeight(this.computeScrollerHeight(viewHeight));
+        }
+    };
+    ListView.prototype.computeScrollerHeight = function (viewHeight) {
+        return viewHeight -
+            subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
+    };
+    ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) {
+        return this.eventRangesToSegs(sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.nextDayThreshold).fg, dayRanges);
+    };
+    ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) {
+        var segs = [];
+        for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
+            var eventRange = eventRanges_1[_i];
+            segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges));
+        }
+        return segs;
+    };
+    ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) {
+        var _a = this, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold;
+        var range = eventRange.range;
+        var allDay = eventRange.def.allDay;
+        var dayIndex;
+        var segRange;
+        var seg;
+        var segs = [];
+        for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) {
+            segRange = intersectRanges(range, dayRanges[dayIndex]);
+            if (segRange) {
+                seg = {
+                    component: this,
+                    eventRange: eventRange,
+                    start: segRange.start,
+                    end: segRange.end,
+                    isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(),
+                    isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(),
+                    dayIndex: dayIndex
+                };
+                segs.push(seg);
+                // detect when range won't go fully into the next day,
+                // and mutate the latest seg to the be the end.
+                if (!seg.isEnd && !allDay &&
+                    dayIndex + 1 < dayRanges.length &&
+                    range.end <
+                        dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) {
+                    seg.end = range.end;
+                    seg.isEnd = true;
+                    break;
+                }
+            }
+        }
+        return segs;
+    };
+    ListView.prototype.renderEmptyMessage = function () {
+        this.contentEl.innerHTML =
+            '<div class="fc-list-empty-wrap2">' + // TODO: try less wraps
+                '<div class="fc-list-empty-wrap1">' +
+                '<div class="fc-list-empty">' +
+                htmlEscape(this.opt('noEventsMessage')) +
+                '</div>' +
+                '</div>' +
+                '</div>';
+    };
+    // called by ListEventRenderer
+    ListView.prototype.renderSegList = function (allSegs) {
+        var segsByDay = this.groupSegsByDay(allSegs); // sparse array
+        var dayIndex;
+        var daySegs;
+        var i;
+        var tableEl = htmlToElement('<table class="fc-list-table ' + this.calendar.theme.getClass('tableList') + '"><tbody></tbody></table>');
+        var tbodyEl = tableEl.querySelector('tbody');
+        for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) {
+            daySegs = segsByDay[dayIndex];
+            if (daySegs) { // sparse array, so might be undefined
+                // append a day header
+                tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex]));
+                daySegs = this.eventRenderer.sortEventSegs(daySegs);
+                for (i = 0; i < daySegs.length; i++) {
+                    tbodyEl.appendChild(daySegs[i].el); // append event row
+                }
+            }
+        }
+        this.contentEl.innerHTML = '';
+        this.contentEl.appendChild(tableEl);
+    };
+    // Returns a sparse array of arrays, segs grouped by their dayIndex
+    ListView.prototype.groupSegsByDay = function (segs) {
+        var segsByDay = []; // sparse array
+        var i;
+        var seg;
+        for (i = 0; i < segs.length; i++) {
+            seg = segs[i];
+            (segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = []))
+                .push(seg);
+        }
+        return segsByDay;
+    };
+    // generates the HTML for the day headers that live amongst the event rows
+    ListView.prototype.buildDayHeaderRow = function (dayDate) {
+        var dateEnv = this.dateEnv;
+        var mainFormat = createFormatter(this.opt('listDayFormat')); // TODO: cache
+        var altFormat = createFormatter(this.opt('listDayAltFormat')); // TODO: cache
+        return createElement('tr', {
+            className: 'fc-list-heading',
+            'data-date': dateEnv.formatIso(dayDate, { omitTime: true })
+        }, '<td class="' + (this.calendar.theme.getClass('tableListHeading') ||
+            this.calendar.theme.getClass('widgetHeader')) + '" colspan="3">' +
+            (mainFormat ?
+                buildGotoAnchorHtml(this, dayDate, { 'class': 'fc-list-heading-main' }, htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML
+                ) :
+                '') +
+            (altFormat ?
+                buildGotoAnchorHtml(this, dayDate, { 'class': 'fc-list-heading-alt' }, htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML
+                ) :
+                '') +
+            '</td>');
+    };
+    return ListView;
+}(View));
+ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions
+function computeDateVars(dateProfile) {
+    var dayStart = startOfDay(dateProfile.renderRange.start);
+    var viewEnd = dateProfile.renderRange.end;
+    var dayDates = [];
+    var dayRanges = [];
+    while (dayStart < viewEnd) {
+        dayDates.push(dayStart);
+        dayRanges.push({
+            start: dayStart,
+            end: addDays(dayStart, 1)
+        });
+        dayStart = addDays(dayStart, 1);
+    }
+    return { dayDates: dayDates, dayRanges: dayRanges };
+}
+
+var main = createPlugin({
+    views: {
+        list: {
+            class: ListView,
+            buttonTextKey: 'list',
+            listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016"
+        },
+        listDay: {
+            type: 'list',
+            duration: { days: 1 },
+            listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header
+        },
+        listWeek: {
+            type: 'list',
+            duration: { weeks: 1 },
+            listDayFormat: { weekday: 'long' },
+            listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' }
+        },
+        listMonth: {
+            type: 'list',
+            duration: { month: 1 },
+            listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
+        },
+        listYear: {
+            type: 'list',
+            duration: { year: 1 },
+            listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
+        }
+    }
+});
+
+export default main;
+export { ListView };
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/main.js b/AKPlan/static/AKPlan/fullcalendar/list/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..b855e4e0e6a36b27e28a2e73e013b814a81d567b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/main.js
@@ -0,0 +1,343 @@
+/*!
+FullCalendar List View Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarList = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var ListEventRenderer = /** @class */ (function (_super) {
+        __extends(ListEventRenderer, _super);
+        function ListEventRenderer(listView) {
+            var _this = _super.call(this, listView.context) || this;
+            _this.listView = listView;
+            return _this;
+        }
+        ListEventRenderer.prototype.attachSegs = function (segs) {
+            if (!segs.length) {
+                this.listView.renderEmptyMessage();
+            }
+            else {
+                this.listView.renderSegList(segs);
+            }
+        };
+        ListEventRenderer.prototype.detachSegs = function () {
+        };
+        // generates the HTML for a single event row
+        ListEventRenderer.prototype.renderSegHtml = function (seg) {
+            var _a = this.context, view = _a.view, theme = _a.theme;
+            var eventRange = seg.eventRange;
+            var eventDef = eventRange.def;
+            var eventInstance = eventRange.instance;
+            var eventUi = eventRange.ui;
+            var url = eventDef.url;
+            var classes = ['fc-list-item'].concat(eventUi.classNames);
+            var bgColor = eventUi.backgroundColor;
+            var timeHtml;
+            if (eventDef.allDay) {
+                timeHtml = core.getAllDayHtml(view);
+            }
+            else if (core.isMultiDayRange(eventRange.range)) {
+                if (seg.isStart) {
+                    timeHtml = core.htmlEscape(this._getTimeText(eventInstance.range.start, seg.end, false // allDay
+                    ));
+                }
+                else if (seg.isEnd) {
+                    timeHtml = core.htmlEscape(this._getTimeText(seg.start, eventInstance.range.end, false // allDay
+                    ));
+                }
+                else { // inner segment that lasts the whole day
+                    timeHtml = core.getAllDayHtml(view);
+                }
+            }
+            else {
+                // Display the normal time text for the *event's* times
+                timeHtml = core.htmlEscape(this.getTimeText(eventRange));
+            }
+            if (url) {
+                classes.push('fc-has-url');
+            }
+            return '<tr class="' + classes.join(' ') + '">' +
+                (this.displayEventTime ?
+                    '<td class="fc-list-item-time ' + theme.getClass('widgetContent') + '">' +
+                        (timeHtml || '') +
+                        '</td>' :
+                    '') +
+                '<td class="fc-list-item-marker ' + theme.getClass('widgetContent') + '">' +
+                '<span class="fc-event-dot"' +
+                (bgColor ?
+                    ' style="background-color:' + bgColor + '"' :
+                    '') +
+                '></span>' +
+                '</td>' +
+                '<td class="fc-list-item-title ' + theme.getClass('widgetContent') + '">' +
+                '<a' + (url ? ' href="' + core.htmlEscape(url) + '"' : '') + '>' +
+                core.htmlEscape(eventDef.title || '') +
+                '</a>' +
+                '</td>' +
+                '</tr>';
+        };
+        // like "4:00am"
+        ListEventRenderer.prototype.computeEventTimeFormat = function () {
+            return {
+                hour: 'numeric',
+                minute: '2-digit',
+                meridiem: 'short'
+            };
+        };
+        return ListEventRenderer;
+    }(core.FgEventRenderer));
+
+    /*
+    Responsible for the scroller, and forwarding event-related actions into the "grid".
+    */
+    var ListView = /** @class */ (function (_super) {
+        __extends(ListView, _super);
+        function ListView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.computeDateVars = core.memoize(computeDateVars);
+            _this.eventStoreToSegs = core.memoize(_this._eventStoreToSegs);
+            var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this);
+            _this.renderContent = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer));
+            _this.el.classList.add('fc-list-view');
+            var listViewClassNames = (_this.theme.getClass('listView') || '').split(' '); // wish we didn't have to do this
+            for (var _i = 0, listViewClassNames_1 = listViewClassNames; _i < listViewClassNames_1.length; _i++) {
+                var listViewClassName = listViewClassNames_1[_i];
+                if (listViewClassName) { // in case input was empty string
+                    _this.el.classList.add(listViewClassName);
+                }
+            }
+            _this.scroller = new core.ScrollComponent('hidden', // overflow x
+            'auto' // overflow y
+            );
+            _this.el.appendChild(_this.scroller.el);
+            _this.contentEl = _this.scroller.el; // shortcut
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.el
+                // TODO: make aware that it doesn't do Hits
+            });
+            return _this;
+        }
+        ListView.prototype.render = function (props) {
+            var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges;
+            this.dayDates = dayDates;
+            this.renderContent(this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges));
+        };
+        ListView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.renderContent.unrender();
+            this.scroller.destroy(); // will remove the Grid too
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        ListView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+            _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto);
+            this.eventRenderer.computeSizes(isResize);
+            this.eventRenderer.assignSizes(isResize);
+            this.scroller.clear(); // sets height to 'auto' and clears overflow
+            if (!isAuto) {
+                this.scroller.setHeight(this.computeScrollerHeight(viewHeight));
+            }
+        };
+        ListView.prototype.computeScrollerHeight = function (viewHeight) {
+            return viewHeight -
+                core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
+        };
+        ListView.prototype._eventStoreToSegs = function (eventStore, eventUiBases, dayRanges) {
+            return this.eventRangesToSegs(core.sliceEventStore(eventStore, eventUiBases, this.props.dateProfile.activeRange, this.nextDayThreshold).fg, dayRanges);
+        };
+        ListView.prototype.eventRangesToSegs = function (eventRanges, dayRanges) {
+            var segs = [];
+            for (var _i = 0, eventRanges_1 = eventRanges; _i < eventRanges_1.length; _i++) {
+                var eventRange = eventRanges_1[_i];
+                segs.push.apply(segs, this.eventRangeToSegs(eventRange, dayRanges));
+            }
+            return segs;
+        };
+        ListView.prototype.eventRangeToSegs = function (eventRange, dayRanges) {
+            var _a = this, dateEnv = _a.dateEnv, nextDayThreshold = _a.nextDayThreshold;
+            var range = eventRange.range;
+            var allDay = eventRange.def.allDay;
+            var dayIndex;
+            var segRange;
+            var seg;
+            var segs = [];
+            for (dayIndex = 0; dayIndex < dayRanges.length; dayIndex++) {
+                segRange = core.intersectRanges(range, dayRanges[dayIndex]);
+                if (segRange) {
+                    seg = {
+                        component: this,
+                        eventRange: eventRange,
+                        start: segRange.start,
+                        end: segRange.end,
+                        isStart: eventRange.isStart && segRange.start.valueOf() === range.start.valueOf(),
+                        isEnd: eventRange.isEnd && segRange.end.valueOf() === range.end.valueOf(),
+                        dayIndex: dayIndex
+                    };
+                    segs.push(seg);
+                    // detect when range won't go fully into the next day,
+                    // and mutate the latest seg to the be the end.
+                    if (!seg.isEnd && !allDay &&
+                        dayIndex + 1 < dayRanges.length &&
+                        range.end <
+                            dateEnv.add(dayRanges[dayIndex + 1].start, nextDayThreshold)) {
+                        seg.end = range.end;
+                        seg.isEnd = true;
+                        break;
+                    }
+                }
+            }
+            return segs;
+        };
+        ListView.prototype.renderEmptyMessage = function () {
+            this.contentEl.innerHTML =
+                '<div class="fc-list-empty-wrap2">' + // TODO: try less wraps
+                    '<div class="fc-list-empty-wrap1">' +
+                    '<div class="fc-list-empty">' +
+                    core.htmlEscape(this.opt('noEventsMessage')) +
+                    '</div>' +
+                    '</div>' +
+                    '</div>';
+        };
+        // called by ListEventRenderer
+        ListView.prototype.renderSegList = function (allSegs) {
+            var segsByDay = this.groupSegsByDay(allSegs); // sparse array
+            var dayIndex;
+            var daySegs;
+            var i;
+            var tableEl = core.htmlToElement('<table class="fc-list-table ' + this.calendar.theme.getClass('tableList') + '"><tbody></tbody></table>');
+            var tbodyEl = tableEl.querySelector('tbody');
+            for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) {
+                daySegs = segsByDay[dayIndex];
+                if (daySegs) { // sparse array, so might be undefined
+                    // append a day header
+                    tbodyEl.appendChild(this.buildDayHeaderRow(this.dayDates[dayIndex]));
+                    daySegs = this.eventRenderer.sortEventSegs(daySegs);
+                    for (i = 0; i < daySegs.length; i++) {
+                        tbodyEl.appendChild(daySegs[i].el); // append event row
+                    }
+                }
+            }
+            this.contentEl.innerHTML = '';
+            this.contentEl.appendChild(tableEl);
+        };
+        // Returns a sparse array of arrays, segs grouped by their dayIndex
+        ListView.prototype.groupSegsByDay = function (segs) {
+            var segsByDay = []; // sparse array
+            var i;
+            var seg;
+            for (i = 0; i < segs.length; i++) {
+                seg = segs[i];
+                (segsByDay[seg.dayIndex] || (segsByDay[seg.dayIndex] = []))
+                    .push(seg);
+            }
+            return segsByDay;
+        };
+        // generates the HTML for the day headers that live amongst the event rows
+        ListView.prototype.buildDayHeaderRow = function (dayDate) {
+            var dateEnv = this.dateEnv;
+            var mainFormat = core.createFormatter(this.opt('listDayFormat')); // TODO: cache
+            var altFormat = core.createFormatter(this.opt('listDayAltFormat')); // TODO: cache
+            return core.createElement('tr', {
+                className: 'fc-list-heading',
+                'data-date': dateEnv.formatIso(dayDate, { omitTime: true })
+            }, '<td class="' + (this.calendar.theme.getClass('tableListHeading') ||
+                this.calendar.theme.getClass('widgetHeader')) + '" colspan="3">' +
+                (mainFormat ?
+                    core.buildGotoAnchorHtml(this, dayDate, { 'class': 'fc-list-heading-main' }, core.htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML
+                    ) :
+                    '') +
+                (altFormat ?
+                    core.buildGotoAnchorHtml(this, dayDate, { 'class': 'fc-list-heading-alt' }, core.htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML
+                    ) :
+                    '') +
+                '</td>');
+        };
+        return ListView;
+    }(core.View));
+    ListView.prototype.fgSegSelector = '.fc-list-item'; // which elements accept event actions
+    function computeDateVars(dateProfile) {
+        var dayStart = core.startOfDay(dateProfile.renderRange.start);
+        var viewEnd = dateProfile.renderRange.end;
+        var dayDates = [];
+        var dayRanges = [];
+        while (dayStart < viewEnd) {
+            dayDates.push(dayStart);
+            dayRanges.push({
+                start: dayStart,
+                end: core.addDays(dayStart, 1)
+            });
+            dayStart = core.addDays(dayStart, 1);
+        }
+        return { dayDates: dayDates, dayRanges: dayRanges };
+    }
+
+    var main = core.createPlugin({
+        views: {
+            list: {
+                class: ListView,
+                buttonTextKey: 'list',
+                listDayFormat: { month: 'long', day: 'numeric', year: 'numeric' } // like "January 1, 2016"
+            },
+            listDay: {
+                type: 'list',
+                duration: { days: 1 },
+                listDayFormat: { weekday: 'long' } // day-of-week is all we need. full date is probably in header
+            },
+            listWeek: {
+                type: 'list',
+                duration: { weeks: 1 },
+                listDayFormat: { weekday: 'long' },
+                listDayAltFormat: { month: 'long', day: 'numeric', year: 'numeric' }
+            },
+            listMonth: {
+                type: 'list',
+                duration: { month: 1 },
+                listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
+            },
+            listYear: {
+                type: 'list',
+                duration: { year: 1 },
+                listDayAltFormat: { weekday: 'long' } // day-of-week is nice-to-have
+            }
+        }
+    });
+
+    exports.ListView = ListView;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/main.min.css b/AKPlan/static/AKPlan/fullcalendar/list/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..18446f442da015ae8fdc3c91c8f10685cf3353f2
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/main.min.css
@@ -0,0 +1 @@
+.fc-event-dot{display:inline-block;width:10px;height:10px;border-radius:5px}.fc-rtl .fc-list-view{direction:rtl}.fc-list-view{border-width:1px;border-style:solid}.fc .fc-list-table{table-layout:auto}.fc-list-table td{border-width:1px 0 0;padding:8px 14px}.fc-list-table tr:first-child td{border-top-width:0}.fc-list-heading{border-bottom-width:1px}.fc-list-heading td{font-weight:700}.fc-ltr .fc-list-heading-main{float:left}.fc-ltr .fc-list-heading-alt,.fc-rtl .fc-list-heading-main{float:right}.fc-rtl .fc-list-heading-alt{float:left}.fc-list-item.fc-has-url{cursor:pointer}.fc-list-item-marker,.fc-list-item-time{white-space:nowrap;width:1px}.fc-ltr .fc-list-item-marker{padding-right:0}.fc-rtl .fc-list-item-marker{padding-left:0}.fc-list-item-title a{text-decoration:none;color:inherit}.fc-list-item-title a[href]:hover{text-decoration:underline}.fc-list-empty-wrap2{position:absolute;top:0;left:0;right:0;bottom:0}.fc-list-empty-wrap1{width:100%;height:100%;display:table}.fc-list-empty{display:table-cell;vertical-align:middle;text-align:center}.fc-unthemed .fc-list-empty{background-color:#eee}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/main.min.js b/AKPlan/static/AKPlan/fullcalendar/list/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..f954aac89a8a85462e31747bd7943b94311183c7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar List View Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarList={},e.FullCalendar)}(this,function(e,t){"use strict";var n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function r(e,t){function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var s=function(e){function n(t){var n=e.call(this,t.context)||this;return n.listView=t,n}return r(n,e),n.prototype.attachSegs=function(e){e.length?this.listView.renderSegList(e):this.listView.renderEmptyMessage()},n.prototype.detachSegs=function(){},n.prototype.renderSegHtml=function(e){var n,r=this.context,s=r.view,a=r.theme,i=e.eventRange,o=i.def,l=i.instance,d=i.ui,c=o.url,p=["fc-list-item"].concat(d.classNames),h=d.backgroundColor;return n=o.allDay?t.getAllDayHtml(s):t.isMultiDayRange(i.range)?e.isStart?t.htmlEscape(this._getTimeText(l.range.start,e.end,!1)):e.isEnd?t.htmlEscape(this._getTimeText(e.start,l.range.end,!1)):t.getAllDayHtml(s):t.htmlEscape(this.getTimeText(i)),c&&p.push("fc-has-url"),'<tr class="'+p.join(" ")+'">'+(this.displayEventTime?'<td class="fc-list-item-time '+a.getClass("widgetContent")+'">'+(n||"")+"</td>":"")+'<td class="fc-list-item-marker '+a.getClass("widgetContent")+'"><span class="fc-event-dot"'+(h?' style="background-color:'+h+'"':"")+'></span></td><td class="fc-list-item-title '+a.getClass("widgetContent")+'"><a'+(c?' href="'+t.htmlEscape(c)+'"':"")+">"+t.htmlEscape(o.title||"")+"</a></td></tr>"},n.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",meridiem:"short"}},n}(t.FgEventRenderer),a=function(e){function n(n,r,a,o){var l=e.call(this,n,r,a,o)||this;l.computeDateVars=t.memoize(i),l.eventStoreToSegs=t.memoize(l._eventStoreToSegs);var d=l.eventRenderer=new s(l);l.renderContent=t.memoizeRendering(d.renderSegs.bind(d),d.unrender.bind(d)),l.el.classList.add("fc-list-view");for(var c=0,p=(l.theme.getClass("listView")||"").split(" ");c<p.length;c++){var h=p[c];h&&l.el.classList.add(h)}return l.scroller=new t.ScrollComponent("hidden","auto"),l.el.appendChild(l.scroller.el),l.contentEl=l.scroller.el,n.calendar.registerInteractiveComponent(l,{el:l.el}),l}return r(n,e),n.prototype.render=function(e){var t=this.computeDateVars(e.dateProfile),n=t.dayDates,r=t.dayRanges;this.dayDates=n,this.renderContent(this.eventStoreToSegs(e.eventStore,e.eventUiBases,r))},n.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderContent.unrender(),this.scroller.destroy(),this.calendar.unregisterInteractiveComponent(this)},n.prototype.updateSize=function(t,n,r){e.prototype.updateSize.call(this,t,n,r),this.eventRenderer.computeSizes(t),this.eventRenderer.assignSizes(t),this.scroller.clear(),r||this.scroller.setHeight(this.computeScrollerHeight(n))},n.prototype.computeScrollerHeight=function(e){return e-t.subtractInnerElHeight(this.el,this.scroller.el)},n.prototype._eventStoreToSegs=function(e,n,r){return this.eventRangesToSegs(t.sliceEventStore(e,n,this.props.dateProfile.activeRange,this.nextDayThreshold).fg,r)},n.prototype.eventRangesToSegs=function(e,t){for(var n=[],r=0,s=e;r<s.length;r++){var a=s[r];n.push.apply(n,this.eventRangeToSegs(a,t))}return n},n.prototype.eventRangeToSegs=function(e,n){var r,s,a,i=this.dateEnv,o=this.nextDayThreshold,l=e.range,d=e.def.allDay,c=[];for(r=0;r<n.length;r++)if((s=t.intersectRanges(l,n[r]))&&(a={component:this,eventRange:e,start:s.start,end:s.end,isStart:e.isStart&&s.start.valueOf()===l.start.valueOf(),isEnd:e.isEnd&&s.end.valueOf()===l.end.valueOf(),dayIndex:r},c.push(a),!a.isEnd&&!d&&r+1<n.length&&l.end<i.add(n[r+1].start,o))){a.end=l.end,a.isEnd=!0;break}return c},n.prototype.renderEmptyMessage=function(){this.contentEl.innerHTML='<div class="fc-list-empty-wrap2"><div class="fc-list-empty-wrap1"><div class="fc-list-empty">'+t.htmlEscape(this.opt("noEventsMessage"))+"</div></div></div>"},n.prototype.renderSegList=function(e){var n,r,s,a=this.groupSegsByDay(e),i=t.htmlToElement('<table class="fc-list-table '+this.calendar.theme.getClass("tableList")+'"><tbody></tbody></table>'),o=i.querySelector("tbody");for(n=0;n<a.length;n++)if(r=a[n])for(o.appendChild(this.buildDayHeaderRow(this.dayDates[n])),r=this.eventRenderer.sortEventSegs(r),s=0;s<r.length;s++)o.appendChild(r[s].el);this.contentEl.innerHTML="",this.contentEl.appendChild(i)},n.prototype.groupSegsByDay=function(e){var t,n,r=[];for(t=0;t<e.length;t++)(r[(n=e[t]).dayIndex]||(r[n.dayIndex]=[])).push(n);return r},n.prototype.buildDayHeaderRow=function(e){var n=this.dateEnv,r=t.createFormatter(this.opt("listDayFormat")),s=t.createFormatter(this.opt("listDayAltFormat"));return t.createElement("tr",{className:"fc-list-heading","data-date":n.formatIso(e,{omitTime:!0})},'<td class="'+(this.calendar.theme.getClass("tableListHeading")||this.calendar.theme.getClass("widgetHeader"))+'" colspan="3">'+(r?t.buildGotoAnchorHtml(this,e,{class:"fc-list-heading-main"},t.htmlEscape(n.format(e,r))):"")+(s?t.buildGotoAnchorHtml(this,e,{class:"fc-list-heading-alt"},t.htmlEscape(n.format(e,s))):"")+"</td>")},n}(t.View);function i(e){for(var n=t.startOfDay(e.renderRange.start),r=e.renderRange.end,s=[],a=[];n<r;)s.push(n),a.push({start:n,end:t.addDays(n,1)}),n=t.addDays(n,1);return{dayDates:s,dayRanges:a}}a.prototype.fgSegSelector=".fc-list-item";var o=t.createPlugin({views:{list:{class:a,buttonTextKey:"list",listDayFormat:{month:"long",day:"numeric",year:"numeric"}},listDay:{type:"list",duration:{days:1},listDayFormat:{weekday:"long"}},listWeek:{type:"list",duration:{weeks:1},listDayFormat:{weekday:"long"},listDayAltFormat:{month:"long",day:"numeric",year:"numeric"}},listMonth:{type:"list",duration:{month:1},listDayAltFormat:{weekday:"long"}},listYear:{type:"list",duration:{year:1},listDayAltFormat:{weekday:"long"}}}});e.ListView=a,e.default=o,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/list/package.json b/AKPlan/static/AKPlan/fullcalendar/list/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..83cc13e4039680aba1f2a96f8f27b1abd30831ab
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/list/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/list",
+  "version": "4.3.0",
+  "title": "FullCalendar List View Plugin",
+  "description": "View your events as a bulleted list",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/list-view",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/luxon/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/README.md b/AKPlan/static/AKPlan/fullcalendar/luxon/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4c42b505ec1c8a0c9569cef26e153e116665d479
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Luxon Plugin
+
+A connector to the Luxon date library
+
+[View the docs &raquo;](https://fullcalendar.io/docs/luxon-plugin)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/luxon/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54f016ddd129150132b82901d044639f684d744c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/main.d.ts
@@ -0,0 +1,14 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../luxon
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/luxon' {
+    import { DateTime as LuxonDateTime, Duration as LuxonDuration } from 'luxon';
+    import { Calendar, Duration } from '@fullcalendar/core';
+    export function toDateTime(date: Date, calendar: Calendar): LuxonDateTime;
+    export function toDuration(duration: Duration, calendar: Calendar): LuxonDuration;
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/luxon/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..f41c464f848d2f428cc25b88a81a0ba504d45bed
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/main.esm.js
@@ -0,0 +1,162 @@
+/*!
+FullCalendar Luxon Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { DateTime, Duration } from 'luxon';
+import { createPlugin, Calendar, NamedTimeZoneImpl } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+function toDateTime(date, calendar) {
+    if (!(calendar instanceof Calendar)) {
+        throw new Error('must supply a Calendar instance');
+    }
+    return DateTime.fromJSDate(date, {
+        zone: calendar.dateEnv.timeZone,
+        locale: calendar.dateEnv.locale.codes[0]
+    });
+}
+function toDuration(duration, calendar) {
+    if (!(calendar instanceof Calendar)) {
+        throw new Error('must supply a Calendar instance');
+    }
+    return Duration.fromObject(__assign({}, duration, { locale: calendar.dateEnv.locale.codes[0] }));
+}
+var LuxonNamedTimeZone = /** @class */ (function (_super) {
+    __extends(LuxonNamedTimeZone, _super);
+    function LuxonNamedTimeZone() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    LuxonNamedTimeZone.prototype.offsetForArray = function (a) {
+        return arrayToLuxon(a, this.timeZoneName).offset;
+    };
+    LuxonNamedTimeZone.prototype.timestampToArray = function (ms) {
+        return luxonToArray(DateTime.fromMillis(ms, {
+            zone: this.timeZoneName
+        }));
+    };
+    return LuxonNamedTimeZone;
+}(NamedTimeZoneImpl));
+function formatWithCmdStr(cmdStr, arg) {
+    var cmd = parseCmdStr(cmdStr);
+    if (arg.end) {
+        var start = arrayToLuxon(arg.start.array, arg.timeZone, arg.localeCodes[0]);
+        var end = arrayToLuxon(arg.end.array, arg.timeZone, arg.localeCodes[0]);
+        return formatRange(cmd, start.toFormat.bind(start), end.toFormat.bind(end), arg.separator);
+    }
+    return arrayToLuxon(arg.date.array, arg.timeZone, arg.localeCodes[0]).toFormat(cmd.whole);
+}
+var main = createPlugin({
+    cmdFormatter: formatWithCmdStr,
+    namedTimeZonedImpl: LuxonNamedTimeZone
+});
+function luxonToArray(datetime) {
+    return [
+        datetime.year,
+        datetime.month - 1,
+        datetime.day,
+        datetime.hour,
+        datetime.minute,
+        datetime.second,
+        datetime.millisecond
+    ];
+}
+function arrayToLuxon(arr, timeZone, locale) {
+    return DateTime.fromObject({
+        zone: timeZone,
+        locale: locale,
+        year: arr[0],
+        month: arr[1] + 1,
+        day: arr[2],
+        hour: arr[3],
+        minute: arr[4],
+        second: arr[5],
+        millisecond: arr[6]
+    });
+}
+function parseCmdStr(cmdStr) {
+    var parts = cmdStr.match(/^(.*?)\{(.*)\}(.*)$/); // TODO: lookbehinds for escape characters
+    if (parts) {
+        var middle = parseCmdStr(parts[2]);
+        return {
+            head: parts[1],
+            middle: middle,
+            tail: parts[3],
+            whole: parts[1] + middle.whole + parts[3]
+        };
+    }
+    else {
+        return {
+            head: null,
+            middle: null,
+            tail: null,
+            whole: cmdStr
+        };
+    }
+}
+function formatRange(cmd, formatStart, formatEnd, separator) {
+    if (cmd.middle) {
+        var startHead = formatStart(cmd.head);
+        var startMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+        var startTail = formatStart(cmd.tail);
+        var endHead = formatEnd(cmd.head);
+        var endMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+        var endTail = formatEnd(cmd.tail);
+        if (startHead === endHead && startTail === endTail) {
+            return startHead +
+                (startMiddle === endMiddle ? startMiddle : startMiddle + separator + endMiddle) +
+                startTail;
+        }
+    }
+    var startWhole = formatStart(cmd.whole);
+    var endWhole = formatEnd(cmd.whole);
+    if (startWhole === endWhole) {
+        return startWhole;
+    }
+    else {
+        return startWhole + separator + endWhole;
+    }
+}
+
+export default main;
+export { toDateTime, toDuration };
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/main.js b/AKPlan/static/AKPlan/fullcalendar/luxon/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..000c77211e35b2b4c602c12f53ce47344f49b0bc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/main.js
@@ -0,0 +1,170 @@
+/*!
+FullCalendar Luxon Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('luxon'), require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', 'luxon', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarLuxon = {}, global.luxon, global.FullCalendar));
+}(this, function (exports, luxon, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    function toDateTime(date, calendar) {
+        if (!(calendar instanceof core.Calendar)) {
+            throw new Error('must supply a Calendar instance');
+        }
+        return luxon.DateTime.fromJSDate(date, {
+            zone: calendar.dateEnv.timeZone,
+            locale: calendar.dateEnv.locale.codes[0]
+        });
+    }
+    function toDuration(duration, calendar) {
+        if (!(calendar instanceof core.Calendar)) {
+            throw new Error('must supply a Calendar instance');
+        }
+        return luxon.Duration.fromObject(__assign({}, duration, { locale: calendar.dateEnv.locale.codes[0] }));
+    }
+    var LuxonNamedTimeZone = /** @class */ (function (_super) {
+        __extends(LuxonNamedTimeZone, _super);
+        function LuxonNamedTimeZone() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        LuxonNamedTimeZone.prototype.offsetForArray = function (a) {
+            return arrayToLuxon(a, this.timeZoneName).offset;
+        };
+        LuxonNamedTimeZone.prototype.timestampToArray = function (ms) {
+            return luxonToArray(luxon.DateTime.fromMillis(ms, {
+                zone: this.timeZoneName
+            }));
+        };
+        return LuxonNamedTimeZone;
+    }(core.NamedTimeZoneImpl));
+    function formatWithCmdStr(cmdStr, arg) {
+        var cmd = parseCmdStr(cmdStr);
+        if (arg.end) {
+            var start = arrayToLuxon(arg.start.array, arg.timeZone, arg.localeCodes[0]);
+            var end = arrayToLuxon(arg.end.array, arg.timeZone, arg.localeCodes[0]);
+            return formatRange(cmd, start.toFormat.bind(start), end.toFormat.bind(end), arg.separator);
+        }
+        return arrayToLuxon(arg.date.array, arg.timeZone, arg.localeCodes[0]).toFormat(cmd.whole);
+    }
+    var main = core.createPlugin({
+        cmdFormatter: formatWithCmdStr,
+        namedTimeZonedImpl: LuxonNamedTimeZone
+    });
+    function luxonToArray(datetime) {
+        return [
+            datetime.year,
+            datetime.month - 1,
+            datetime.day,
+            datetime.hour,
+            datetime.minute,
+            datetime.second,
+            datetime.millisecond
+        ];
+    }
+    function arrayToLuxon(arr, timeZone, locale) {
+        return luxon.DateTime.fromObject({
+            zone: timeZone,
+            locale: locale,
+            year: arr[0],
+            month: arr[1] + 1,
+            day: arr[2],
+            hour: arr[3],
+            minute: arr[4],
+            second: arr[5],
+            millisecond: arr[6]
+        });
+    }
+    function parseCmdStr(cmdStr) {
+        var parts = cmdStr.match(/^(.*?)\{(.*)\}(.*)$/); // TODO: lookbehinds for escape characters
+        if (parts) {
+            var middle = parseCmdStr(parts[2]);
+            return {
+                head: parts[1],
+                middle: middle,
+                tail: parts[3],
+                whole: parts[1] + middle.whole + parts[3]
+            };
+        }
+        else {
+            return {
+                head: null,
+                middle: null,
+                tail: null,
+                whole: cmdStr
+            };
+        }
+    }
+    function formatRange(cmd, formatStart, formatEnd, separator) {
+        if (cmd.middle) {
+            var startHead = formatStart(cmd.head);
+            var startMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+            var startTail = formatStart(cmd.tail);
+            var endHead = formatEnd(cmd.head);
+            var endMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+            var endTail = formatEnd(cmd.tail);
+            if (startHead === endHead && startTail === endTail) {
+                return startHead +
+                    (startMiddle === endMiddle ? startMiddle : startMiddle + separator + endMiddle) +
+                    startTail;
+            }
+        }
+        var startWhole = formatStart(cmd.whole);
+        var endWhole = formatEnd(cmd.whole);
+        if (startWhole === endWhole) {
+            return startWhole;
+        }
+        else {
+            return startWhole + separator + endWhole;
+        }
+    }
+
+    exports.default = main;
+    exports.toDateTime = toDateTime;
+    exports.toDuration = toDuration;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/main.min.js b/AKPlan/static/AKPlan/fullcalendar/luxon/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..bc8b9f7c184b6a1ecf58e7c5579bb391623ba00d
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Luxon Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("luxon"),require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","luxon","@fullcalendar/core"],t):t((e=e||self).FullCalendarLuxon={},e.luxon,e.FullCalendar)}(this,function(e,t,n){"use strict";var o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};var r=function(){return(r=Object.assign||function(e){for(var t,n=1,o=arguments.length;n<o;n++)for(var r in t=arguments[n])Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e}).apply(this,arguments)};var a=function(e){function n(){return null!==e&&e.apply(this,arguments)||this}return function(e,t){function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}(n,e),n.prototype.offsetForArray=function(e){return l(e,this.timeZoneName).offset},n.prototype.timestampToArray=function(e){return[(n=t.DateTime.fromMillis(e,{zone:this.timeZoneName})).year,n.month-1,n.day,n.hour,n.minute,n.second,n.millisecond];var n},n}(n.NamedTimeZoneImpl);var i=n.createPlugin({cmdFormatter:function(e,t){var n=function e(t){var n=t.match(/^(.*?)\{(.*)\}(.*)$/);if(n){var o=e(n[2]);return{head:n[1],middle:o,tail:n[3],whole:n[1]+o.whole+n[3]}}return{head:null,middle:null,tail:null,whole:t}}(e);if(t.end){var o=l(t.start.array,t.timeZone,t.localeCodes[0]),r=l(t.end.array,t.timeZone,t.localeCodes[0]);return function e(t,n,o,r){if(t.middle){var a=n(t.head),i=e(t.middle,n,o,r),l=n(t.tail),u=o(t.head),c=e(t.middle,n,o,r),d=o(t.tail);if(a===u&&l===d)return a+(i===c?i:i+r+c)+l}var f=n(t.whole),m=o(t.whole);return f===m?f:f+r+m}(n,o.toFormat.bind(o),r.toFormat.bind(r),t.separator)}return l(t.date.array,t.timeZone,t.localeCodes[0]).toFormat(n.whole)},namedTimeZonedImpl:a});function l(e,n,o){return t.DateTime.fromObject({zone:n,locale:o,year:e[0],month:e[1]+1,day:e[2],hour:e[3],minute:e[4],second:e[5],millisecond:e[6]})}e.default=i,e.toDateTime=function(e,o){if(!(o instanceof n.Calendar))throw new Error("must supply a Calendar instance");return t.DateTime.fromJSDate(e,{zone:o.dateEnv.timeZone,locale:o.dateEnv.locale.codes[0]})},e.toDuration=function(e,o){if(!(o instanceof n.Calendar))throw new Error("must supply a Calendar instance");return t.Duration.fromObject(r({},e,{locale:o.dateEnv.locale.codes[0]}))},Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/luxon/package.json b/AKPlan/static/AKPlan/fullcalendar/luxon/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..4cf344fd8930fdcba3e8c8ac7367f697f60691e7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/luxon/package.json
@@ -0,0 +1,34 @@
+{
+  "name": "@fullcalendar/luxon",
+  "version": "4.3.0",
+  "title": "FullCalendar Luxon Plugin",
+  "description": "A connector to the Luxon date library",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/luxon-plugin",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0",
+    "luxon": "^1.12.1"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/README.md b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e84412a2acf9820fb431d8d28654666c60259e47
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Moment Timezone Plugin
+
+A connector to the moment-timezone library
+
+[View the docs &raquo;](https://fullcalendar.io/docs/moment-plugins#moment-timezone)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb0a165b1b5bb8ce16f6f74af84bd73cbbc477f7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.d.ts
@@ -0,0 +1,11 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../moment-timezone/builds/moment-timezone-with-data
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/moment-timezone' {
+    import 'moment-timezone/builds/moment-timezone-with-data';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..98e943cb13819597dbb01ef8aabdc55d81cdc23e
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.esm.js
@@ -0,0 +1,58 @@
+/*!
+FullCalendar Moment Timezone Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import * as momentNs from 'moment';
+import 'moment-timezone/builds/moment-timezone-with-data';
+import { createPlugin, NamedTimeZoneImpl } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var moment = momentNs; // the directly callable function
+var MomentNamedTimeZone = /** @class */ (function (_super) {
+    __extends(MomentNamedTimeZone, _super);
+    function MomentNamedTimeZone() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    MomentNamedTimeZone.prototype.offsetForArray = function (a) {
+        return moment.tz(a, this.timeZoneName).utcOffset();
+    };
+    MomentNamedTimeZone.prototype.timestampToArray = function (ms) {
+        return moment.tz(ms, this.timeZoneName).toArray();
+    };
+    return MomentNamedTimeZone;
+}(NamedTimeZoneImpl));
+var main = createPlugin({
+    namedTimeZonedImpl: MomentNamedTimeZone
+});
+
+export default main;
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.js b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..b5d2897c27c9836eaddf410cc88e34085eec4971
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.js
@@ -0,0 +1,64 @@
+/*!
+FullCalendar Moment Timezone Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('moment'), require('moment-timezone/builds/moment-timezone-with-data'), require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', 'moment', 'moment-timezone/builds/moment-timezone-with-data', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarMomentTimezone = {}, global.moment, global.moment, global.FullCalendar));
+}(this, function (exports, momentNs, momentTimezoneWithData, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var moment = momentNs; // the directly callable function
+    var MomentNamedTimeZone = /** @class */ (function (_super) {
+        __extends(MomentNamedTimeZone, _super);
+        function MomentNamedTimeZone() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        MomentNamedTimeZone.prototype.offsetForArray = function (a) {
+            return moment.tz(a, this.timeZoneName).utcOffset();
+        };
+        MomentNamedTimeZone.prototype.timestampToArray = function (ms) {
+            return moment.tz(ms, this.timeZoneName).toArray();
+        };
+        return MomentNamedTimeZone;
+    }(core.NamedTimeZoneImpl));
+    var main = core.createPlugin({
+        namedTimeZonedImpl: MomentNamedTimeZone
+    });
+
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.min.js b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9ee66c023275c07686668cf9b3f4f9b6c897aa7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Moment Timezone Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("moment"),require("moment-timezone/builds/moment-timezone-with-data"),require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","moment","moment-timezone/builds/moment-timezone-with-data","@fullcalendar/core"],t):t((e=e||self).FullCalendarMomentTimezone={},e.moment,e.moment,e.FullCalendar)}(this,function(e,t,n,o){"use strict";var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};var i=t,u=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}(t,e),t.prototype.offsetForArray=function(e){return i.tz(e,this.timeZoneName).utcOffset()},t.prototype.timestampToArray=function(e){return i.tz(e,this.timeZoneName).toArray()},t}(o.NamedTimeZoneImpl),m=o.createPlugin({namedTimeZonedImpl:u});e.default=m,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment-timezone/package.json b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..b875029cc4b7333d845dc19df4c9fea800fc41cf
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment-timezone/package.json
@@ -0,0 +1,35 @@
+{
+  "name": "@fullcalendar/moment-timezone",
+  "version": "4.3.0",
+  "title": "FullCalendar Moment Timezone Plugin",
+  "description": "A connector to the moment-timezone library",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/moment-plugins#moment-timezone",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0",
+    "moment": "^2.24.0",
+    "moment-timezone": "^0.5.25"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/moment/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/README.md b/AKPlan/static/AKPlan/fullcalendar/moment/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..854e4367f15a5928effd2c42c29fd1180975e05f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Moment Plugin
+
+A connector to the MomentJS date library
+
+[View the docs &raquo;](https://fullcalendar.io/docs/moment-plugins)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/moment/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc81dab62f2a1450d29b6199fb3168d1c9cb3ced
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/main.d.ts
@@ -0,0 +1,14 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../moment
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/moment' {
+    import * as momentNs from 'moment';
+    import { Calendar, Duration } from '@fullcalendar/core';
+    export function toMoment(date: Date, calendar: Calendar): momentNs.Moment;
+    export function toDuration(fcDuration: Duration): momentNs.Duration;
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/moment/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..f5addb725aa2753f8de50a9769feea84374be6fa
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/main.esm.js
@@ -0,0 +1,102 @@
+/*!
+FullCalendar Moment Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import * as momentNs from 'moment';
+import { createPlugin, Calendar } from '@fullcalendar/core';
+
+var moment = momentNs; // the directly callable function
+function toMoment(date, calendar) {
+    if (!(calendar instanceof Calendar)) {
+        throw new Error('must supply a Calendar instance');
+    }
+    return convertToMoment(date, calendar.dateEnv.timeZone, null, calendar.dateEnv.locale.codes[0]);
+}
+function toDuration(fcDuration) {
+    return moment.duration(fcDuration); // moment accepts all the props that fc.Duration already has!
+}
+function formatWithCmdStr(cmdStr, arg) {
+    var cmd = parseCmdStr(cmdStr);
+    if (arg.end) {
+        var startMom = convertToMoment(arg.start.array, arg.timeZone, arg.start.timeZoneOffset, arg.localeCodes[0]);
+        var endMom = convertToMoment(arg.end.array, arg.timeZone, arg.end.timeZoneOffset, arg.localeCodes[0]);
+        return formatRange(cmd, createMomentFormatFunc(startMom), createMomentFormatFunc(endMom), arg.separator);
+    }
+    return convertToMoment(arg.date.array, arg.timeZone, arg.date.timeZoneOffset, arg.localeCodes[0]).format(cmd.whole); // TODO: test for this
+}
+var main = createPlugin({
+    cmdFormatter: formatWithCmdStr
+});
+function createMomentFormatFunc(mom) {
+    return function (cmdStr) {
+        return cmdStr ? mom.format(cmdStr) : ''; // because calling with blank string results in ISO8601 :(
+    };
+}
+function convertToMoment(input, timeZone, timeZoneOffset, locale) {
+    var mom;
+    if (timeZone === 'local') {
+        mom = moment(input);
+    }
+    else if (timeZone === 'UTC') {
+        mom = moment.utc(input);
+    }
+    else if (moment.tz) {
+        mom = moment.tz(input, timeZone);
+    }
+    else {
+        mom = moment.utc(input);
+        if (timeZoneOffset != null) {
+            mom.utcOffset(timeZoneOffset);
+        }
+    }
+    mom.locale(locale);
+    return mom;
+}
+function parseCmdStr(cmdStr) {
+    var parts = cmdStr.match(/^(.*?)\{(.*)\}(.*)$/); // TODO: lookbehinds for escape characters
+    if (parts) {
+        var middle = parseCmdStr(parts[2]);
+        return {
+            head: parts[1],
+            middle: middle,
+            tail: parts[3],
+            whole: parts[1] + middle.whole + parts[3]
+        };
+    }
+    else {
+        return {
+            head: null,
+            middle: null,
+            tail: null,
+            whole: cmdStr
+        };
+    }
+}
+function formatRange(cmd, formatStart, formatEnd, separator) {
+    if (cmd.middle) {
+        var startHead = formatStart(cmd.head);
+        var startMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+        var startTail = formatStart(cmd.tail);
+        var endHead = formatEnd(cmd.head);
+        var endMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+        var endTail = formatEnd(cmd.tail);
+        if (startHead === endHead && startTail === endTail) {
+            return startHead +
+                (startMiddle === endMiddle ? startMiddle : startMiddle + separator + endMiddle) +
+                startTail;
+        }
+    }
+    var startWhole = formatStart(cmd.whole);
+    var endWhole = formatEnd(cmd.whole);
+    if (startWhole === endWhole) {
+        return startWhole;
+    }
+    else {
+        return startWhole + separator + endWhole;
+    }
+}
+
+export default main;
+export { toDuration, toMoment };
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/main.js b/AKPlan/static/AKPlan/fullcalendar/moment/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..aa8677c36404e4eaa1a75243610345c9a59c14d8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/main.js
@@ -0,0 +1,110 @@
+/*!
+FullCalendar Moment Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('moment'), require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', 'moment', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarMoment = {}, global.moment, global.FullCalendar));
+}(this, function (exports, momentNs, core) { 'use strict';
+
+    var moment = momentNs; // the directly callable function
+    function toMoment(date, calendar) {
+        if (!(calendar instanceof core.Calendar)) {
+            throw new Error('must supply a Calendar instance');
+        }
+        return convertToMoment(date, calendar.dateEnv.timeZone, null, calendar.dateEnv.locale.codes[0]);
+    }
+    function toDuration(fcDuration) {
+        return moment.duration(fcDuration); // moment accepts all the props that fc.Duration already has!
+    }
+    function formatWithCmdStr(cmdStr, arg) {
+        var cmd = parseCmdStr(cmdStr);
+        if (arg.end) {
+            var startMom = convertToMoment(arg.start.array, arg.timeZone, arg.start.timeZoneOffset, arg.localeCodes[0]);
+            var endMom = convertToMoment(arg.end.array, arg.timeZone, arg.end.timeZoneOffset, arg.localeCodes[0]);
+            return formatRange(cmd, createMomentFormatFunc(startMom), createMomentFormatFunc(endMom), arg.separator);
+        }
+        return convertToMoment(arg.date.array, arg.timeZone, arg.date.timeZoneOffset, arg.localeCodes[0]).format(cmd.whole); // TODO: test for this
+    }
+    var main = core.createPlugin({
+        cmdFormatter: formatWithCmdStr
+    });
+    function createMomentFormatFunc(mom) {
+        return function (cmdStr) {
+            return cmdStr ? mom.format(cmdStr) : ''; // because calling with blank string results in ISO8601 :(
+        };
+    }
+    function convertToMoment(input, timeZone, timeZoneOffset, locale) {
+        var mom;
+        if (timeZone === 'local') {
+            mom = moment(input);
+        }
+        else if (timeZone === 'UTC') {
+            mom = moment.utc(input);
+        }
+        else if (moment.tz) {
+            mom = moment.tz(input, timeZone);
+        }
+        else {
+            mom = moment.utc(input);
+            if (timeZoneOffset != null) {
+                mom.utcOffset(timeZoneOffset);
+            }
+        }
+        mom.locale(locale);
+        return mom;
+    }
+    function parseCmdStr(cmdStr) {
+        var parts = cmdStr.match(/^(.*?)\{(.*)\}(.*)$/); // TODO: lookbehinds for escape characters
+        if (parts) {
+            var middle = parseCmdStr(parts[2]);
+            return {
+                head: parts[1],
+                middle: middle,
+                tail: parts[3],
+                whole: parts[1] + middle.whole + parts[3]
+            };
+        }
+        else {
+            return {
+                head: null,
+                middle: null,
+                tail: null,
+                whole: cmdStr
+            };
+        }
+    }
+    function formatRange(cmd, formatStart, formatEnd, separator) {
+        if (cmd.middle) {
+            var startHead = formatStart(cmd.head);
+            var startMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+            var startTail = formatStart(cmd.tail);
+            var endHead = formatEnd(cmd.head);
+            var endMiddle = formatRange(cmd.middle, formatStart, formatEnd, separator);
+            var endTail = formatEnd(cmd.tail);
+            if (startHead === endHead && startTail === endTail) {
+                return startHead +
+                    (startMiddle === endMiddle ? startMiddle : startMiddle + separator + endMiddle) +
+                    startTail;
+            }
+        }
+        var startWhole = formatStart(cmd.whole);
+        var endWhole = formatEnd(cmd.whole);
+        if (startWhole === endWhole) {
+            return startWhole;
+        }
+        else {
+            return startWhole + separator + endWhole;
+        }
+    }
+
+    exports.default = main;
+    exports.toDuration = toDuration;
+    exports.toMoment = toMoment;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/main.min.js b/AKPlan/static/AKPlan/fullcalendar/moment/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..4dd2cadbc5f45f762293f71bc560a084a839b7d5
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Moment Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("moment"),require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","moment","@fullcalendar/core"],t):t((e=e||self).FullCalendarMoment={},e.moment,e.FullCalendar)}(this,function(e,t,n){"use strict";var r=t;var a=n.createPlugin({cmdFormatter:function(e,t){var n=function e(t){var n=t.match(/^(.*?)\{(.*)\}(.*)$/);if(n){var r=e(n[2]);return{head:n[1],middle:r,tail:n[3],whole:n[1]+r.whole+n[3]}}return{head:null,middle:null,tail:null,whole:t}}(e);if(t.end){var r=l(t.start.array,t.timeZone,t.start.timeZoneOffset,t.localeCodes[0]),a=l(t.end.array,t.timeZone,t.end.timeZoneOffset,t.localeCodes[0]);return function e(t,n,r,a){if(t.middle){var o=n(t.head),l=e(t.middle,n,r,a),u=n(t.tail),i=r(t.head),d=e(t.middle,n,r,a),f=r(t.tail);if(o===i&&u===f)return o+(l===d?l:l+a+d)+u}var c=n(t.whole),m=r(t.whole);return c===m?c:c+a+m}(n,o(r),o(a),t.separator)}return l(t.date.array,t.timeZone,t.date.timeZoneOffset,t.localeCodes[0]).format(n.whole)}});function o(e){return function(t){return t?e.format(t):""}}function l(e,t,n,a){var o;return"local"===t?o=r(e):"UTC"===t?o=r.utc(e):r.tz?o=r.tz(e,t):(o=r.utc(e),null!=n&&o.utcOffset(n)),o.locale(a),o}e.default=a,e.toDuration=function(e){return r.duration(e)},e.toMoment=function(e,t){if(!(t instanceof n.Calendar))throw new Error("must supply a Calendar instance");return l(e,t.dateEnv.timeZone,null,t.dateEnv.locale.codes[0])},Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/moment/package.json b/AKPlan/static/AKPlan/fullcalendar/moment/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..741d178d3c8aaeb0ad3096647245de475c9537e0
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/moment/package.json
@@ -0,0 +1,34 @@
+{
+  "name": "@fullcalendar/moment",
+  "version": "4.3.0",
+  "title": "FullCalendar Moment Plugin",
+  "description": "A connector to the MomentJS date library",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/moment-plugins",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0",
+    "moment": "^2.24.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/LICENSE.md b/AKPlan/static/AKPlan/fullcalendar/resource-common/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..52296639f8117dd1cd8c5a9e629fed87e5b32cfc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/LICENSE.md
@@ -0,0 +1,18 @@
+
+For complete licensing information, visit:
+http://fullcalendar.io/scheduler/license
+
+FullCalendar Scheduler is tri-licensed, meaning you must choose
+one of three licenses to use. Here is a summary of those licenses:
+
+- Commercial License
+  (a paid license, meant for commercial use)
+  http://fullcalendar.io/scheduler/license-details
+
+- Creative Commons Non-Commercial No-Derivatives
+  (meant for trial and non-commercial use)
+  https://creativecommons.org/licenses/by-nc-nd/4.0/
+
+- GPLv3 License
+  (meant for open-source projects)
+  http://www.gnu.org/licenses/gpl-3.0.en.html
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/README.md b/AKPlan/static/AKPlan/fullcalendar/resource-common/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ddc4db04731944dd9567f96eee144902be227a41
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Resources Common Plugin
+
+Offers base support for resources. Required for all resource-related plugins.
+
+[View the docs &raquo;](https://fullcalendar.io/docs/scheduler)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar-scheduler)
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14ed2f1d628975bbe845235b88cf77846aaae5b7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.d.ts
@@ -0,0 +1,487 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ambient.d.ts
+
+declare module '@fullcalendar/resource-common' {
+    import '@fullcalendar/resource-common/ambient';
+    import '@fullcalendar/resource-common/api/EventApi';
+    import '@fullcalendar/resource-common/resource-sources/resource-array';
+    import '@fullcalendar/resource-common/resource-sources/resource-func';
+    import '@fullcalendar/resource-common/resource-sources/resource-json-feed';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+    export { default as ResourceDayHeader } from '@fullcalendar/resource-common/common/ResourceDayHeader';
+    export { VResourceJoiner, AbstractResourceDayTable, ResourceDayTable, DayResourceTable, VResourceSplitter } from '@fullcalendar/resource-common/common/resource-day-table';
+    export { Resource, ResourceHash } from '@fullcalendar/resource-common/structs/resource';
+    export { ResourceViewProps } from '@fullcalendar/resource-common/View';
+    export { flattenResources, Group, isGroupsEqual, GroupNode, ResourceNode, buildRowNodes, buildResourceFields } from '@fullcalendar/resource-common/common/resource-hierarchy';
+    export { buildResourceTextFunc } from '@fullcalendar/resource-common/common/resource-rendering';
+    export { default as ResourceApi } from '@fullcalendar/resource-common/api/ResourceApi';
+    export { default as ResourceSplitter } from '@fullcalendar/resource-common/common/ResourceSplitter';
+}
+
+declare module '@fullcalendar/resource-common/ambient' {
+    import ResourceApi from '@fullcalendar/resource-common/api/ResourceApi';
+    import { ResourceSourceInput, ResourceSource } from '@fullcalendar/resource-common/structs/resource-source';
+    import { View } from '@fullcalendar/core';
+    import { ResourceHash } from '@fullcalendar/resource-common/structs/resource';
+    import { ResourceEntityExpansions } from '@fullcalendar/resource-common/reducers/resourceEntityExpansions';
+    import { ResourceAction } from '@fullcalendar/resource-common/reducers/resources';
+    module '@fullcalendar/core' {
+        interface Calendar {
+            addResource(input: ResourceSourceInput): ResourceApi;
+            getResourceById(id: string): ResourceApi | null;
+            getResources(): ResourceApi[];
+            getTopLevelResources(): ResourceApi[];
+            rerenderResources(): void;
+            refetchResources(): void;
+        }
+        interface Calendar {
+            dispatch(action: ResourceAction): any;
+        }
+        interface CalendarState {
+            resourceSource?: ResourceSource | null;
+            resourceStore?: ResourceHash;
+            resourceEntityExpansions?: ResourceEntityExpansions;
+        }
+        interface OptionsInput {
+            schedulerLicenseKey?: string;
+            resources?: ResourceSourceInput;
+            resourceLabelText?: string;
+            resourceOrder?: any;
+            filterResourcesWithEvents?: any;
+            resourceText?: any;
+            resourceGroupField?: any;
+            resourceGroupText?: any;
+            resourceAreaWidth?: any;
+            resourceColumns?: any;
+            resourcesInitiallyExpanded?: any;
+            slotWidth?: any;
+            datesAboveResources?: any;
+            eventResourceEditable?: boolean;
+            refetchResourcesOnNavigate?: boolean;
+            resourceRender?(arg: {
+                resource: ResourceApi;
+                el: HTMLElement;
+                view: View;
+            }): void;
+        }
+        interface DatePointApi {
+            resource?: ResourceApi;
+        }
+        interface DateSpanApi {
+            resource?: ResourceApi;
+        }
+        interface EventMutation {
+            resourceMutation?: {
+                matchResourceId: string;
+                setResourceId: string;
+            };
+        }
+        interface EventApi {
+            getResources: () => ResourceApi[];
+            setResources: (resources: (string | ResourceApi)[]) => void;
+        }
+        interface EventDef {
+            resourceIds: string[];
+            resourceEditable: boolean;
+        }
+    }
+}
+
+declare module '@fullcalendar/resource-common/api/EventApi' {
+    import '@fullcalendar/resource-common/ambient';
+}
+
+declare module '@fullcalendar/resource-common/resource-sources/resource-array' {
+    export {};
+}
+
+declare module '@fullcalendar/resource-common/resource-sources/resource-func' {
+    import { ResourceSourceError } from '@fullcalendar/resource-common/structs/resource-source';
+    import { ResourceInput } from '@fullcalendar/resource-common/structs/resource';
+    export type ResourceFunc = (arg: {
+        start: Date;
+        end: Date;
+        timeZone: string;
+    }, successCallback: (events: ResourceInput[]) => void, failureCallback: (errorObj: ResourceSourceError) => void) => any;
+}
+
+declare module '@fullcalendar/resource-common/resource-sources/resource-json-feed' {
+    export {};
+}
+
+declare module '@fullcalendar/resource-common/common/ResourceDayHeader' {
+    import { Component, ComponentContext, DateMarker, DateProfile, DateFormatter } from '@fullcalendar/core';
+    import { Resource } from '@fullcalendar/resource-common/structs/resource';
+    export interface ResourceDayHeaderProps {
+        dates: DateMarker[];
+        dateProfile: DateProfile;
+        datesRepDistinctDays: boolean;
+        resources: Resource[];
+        renderIntroHtml?: () => string;
+    }
+    export { ResourceDayHeader as default, ResourceDayHeader };
+    class ResourceDayHeader extends Component<ResourceDayHeaderProps> {
+        datesAboveResources: boolean;
+        resourceTextFunc: (resource: Resource) => string;
+        dateFormat: DateFormatter;
+        el: HTMLElement;
+        thead: HTMLElement;
+        constructor(context: ComponentContext, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: ResourceDayHeaderProps): void;
+        renderResourceRow(resources: Resource[]): string;
+        renderDayAndResourceRows(dates: DateMarker[], resources: Resource[]): string;
+        renderResourceAndDayRows(resources: Resource[], dates: DateMarker[]): string;
+        renderResourceCell(resource: Resource, colspan: number, date?: DateMarker): string;
+        renderDateCell(date: DateMarker, colspan: number, resource?: Resource): string;
+        buildTr(cellHtmls: string[]): string;
+        processResourceEls(resources: Resource[]): void;
+    }
+}
+
+declare module '@fullcalendar/resource-common/common/resource-day-table' {
+    import { SlicedProps, EventDef, Splitter, DayTable, DayTableCell, SplittableProps, DateSpan, Seg, EventSegUiInteractionState } from '@fullcalendar/core';
+    import { Resource } from '@fullcalendar/resource-common/structs/resource';
+    export interface ResourceDayTableCell extends DayTableCell {
+        resource: Resource;
+    }
+    export abstract class AbstractResourceDayTable {
+        cells: ResourceDayTableCell[][];
+        rowCnt: number;
+        colCnt: number;
+        dayTable: DayTable;
+        resources: Resource[];
+        resourceIndex: ResourceIndex;
+        constructor(dayTable: DayTable, resources: Resource[]);
+        abstract computeCol(dateI: any, resourceI: any): number;
+        abstract computeColRanges(dateStartI: any, dateEndI: any, resourceI: any): {
+            firstCol: number;
+            lastCol: number;
+            isStart: boolean;
+            isEnd: boolean;
+        }[];
+        buildCells(): ResourceDayTableCell[][];
+    }
+    export class ResourceDayTable extends AbstractResourceDayTable {
+        computeCol(dateI: any, resourceI: any): any;
+        computeColRanges(dateStartI: any, dateEndI: any, resourceI: any): {
+            firstCol: any;
+            lastCol: any;
+            isStart: boolean;
+            isEnd: boolean;
+        }[];
+    }
+    export class DayResourceTable extends AbstractResourceDayTable {
+        computeCol(dateI: any, resourceI: any): any;
+        computeColRanges(dateStartI: any, dateEndI: any, resourceI: any): any[];
+    }
+    export class ResourceIndex {
+        indicesById: {
+            [resourceId: string]: number;
+        };
+        ids: string[];
+        length: number;
+        constructor(resources: Resource[]);
+    }
+    export interface VResourceProps extends SplittableProps {
+        resourceDayTable: AbstractResourceDayTable;
+    }
+    export class VResourceSplitter extends Splitter<VResourceProps> {
+        getKeyInfo(props: VResourceProps): any;
+        getKeysForDateSpan(dateSpan: DateSpan): string[];
+        getKeysForEventDef(eventDef: EventDef): string[];
+    }
+    export abstract class VResourceJoiner<SegType extends Seg> {
+        joinProps(propSets: {
+            [resourceId: string]: SlicedProps<SegType>;
+        }, resourceDayTable: AbstractResourceDayTable): SlicedProps<SegType>;
+        joinSegs(resourceDayTable: AbstractResourceDayTable, ...segGroups: SegType[][]): SegType[];
+        expandSegs(resourceDayTable: AbstractResourceDayTable, segs: SegType[]): any[];
+        joinInteractions(resourceDayTable: AbstractResourceDayTable, ...interactions: EventSegUiInteractionState[]): EventSegUiInteractionState;
+        abstract transformSeg(seg: SegType, resourceDayTable: AbstractResourceDayTable, resourceI: number): SegType[];
+    }
+}
+
+declare module '@fullcalendar/resource-common/structs/resource' {
+    import { ConstraintInput, AllowFunc, EventStore, Calendar, EventUi, BusinessHoursInput } from '@fullcalendar/core';
+    export interface ResourceInput {
+        id?: string;
+        parentId?: string;
+        children?: ResourceInput[];
+        title?: string;
+        businessHours?: BusinessHoursInput;
+        eventEditable?: boolean;
+        eventStartEditable?: boolean;
+        eventDurationEditable?: boolean;
+        eventConstraint?: ConstraintInput;
+        eventOverlap?: boolean;
+        eventAllow?: AllowFunc;
+        eventClassName?: string[] | string;
+        eventClassNames?: string[] | string;
+        eventBackgroundColor?: string;
+        eventBorderColor?: string;
+        eventTextColor?: string;
+        eventColor?: string;
+        extendedProps?: {
+            [extendedProp: string]: any;
+        };
+        [otherProp: string]: any;
+    }
+    export interface Resource {
+        id: string;
+        parentId: string;
+        title: string;
+        businessHours: EventStore | null;
+        ui: EventUi;
+        extendedProps: {
+            [extendedProp: string]: any;
+        };
+    }
+    export type ResourceHash = {
+        [resourceId: string]: Resource;
+    };
+    export function parseResource(input: ResourceInput, parentId: string, store: ResourceHash, calendar: Calendar): Resource;
+    export function getPublicId(id: string): string;
+}
+
+declare module '@fullcalendar/resource-common/View' {
+    import { View, ViewProps, ViewSpec, ViewPropsTransformer, CalendarComponentProps, EventUi, EventUiHash, EventDefHash, EventDef, EventStore, DateRange } from '@fullcalendar/core';
+    import { ResourceHash } from '@fullcalendar/resource-common/structs/resource';
+    import { ResourceEntityExpansions } from '@fullcalendar/resource-common/reducers/resourceEntityExpansions';
+    export interface ResourceViewProps extends ViewProps {
+        resourceStore: ResourceHash;
+        resourceEntityExpansions: ResourceEntityExpansions;
+    }
+    export class ResourceDataAdder implements ViewPropsTransformer {
+        filterResources: typeof filterResources;
+        transform(viewProps: ViewProps, viewSpec: ViewSpec, calendarProps: CalendarComponentProps, view: View): {
+            resourceStore: ResourceHash;
+            resourceEntityExpansions: ResourceEntityExpansions;
+        };
+    }
+    function filterResources(resourceStore: ResourceHash, doFilterResourcesWithEvents: boolean, eventStore: EventStore, activeRange: DateRange): ResourceHash;
+    export class ResourceEventConfigAdder implements ViewPropsTransformer {
+        buildResourceEventUis: typeof buildResourceEventUis;
+        injectResourceEventUis: typeof injectResourceEventUis;
+        transform(viewProps: ViewProps, viewSpec: ViewSpec, calendarProps: CalendarComponentProps): {
+            eventUiBases: {
+                [key: string]: EventUi;
+            };
+        };
+    }
+    function buildResourceEventUis(resourceStore: ResourceHash): {
+        [key: string]: EventUi;
+    };
+    function injectResourceEventUis(eventUiBases: EventUiHash, eventDefs: EventDefHash, resourceEventUis: EventUiHash): {
+        [key: string]: EventUi;
+    };
+    export function transformIsDraggable(val: boolean, eventDef: EventDef, eventUi: EventUi, view: View): boolean;
+    export {};
+}
+
+declare module '@fullcalendar/resource-common/common/resource-hierarchy' {
+    import { ResourceHash, Resource } from '@fullcalendar/resource-common/structs/resource';
+    import { ResourceEntityExpansions } from '@fullcalendar/resource-common/reducers/resourceEntityExpansions';
+    export interface Group {
+        value: any;
+        spec: any;
+    }
+    export interface GroupNode {
+        id: string;
+        isExpanded: boolean;
+        group: Group;
+    }
+    export interface ResourceNode {
+        id: string;
+        rowSpans: number[];
+        depth: number;
+        isExpanded: boolean;
+        hasChildren: boolean;
+        resource: Resource;
+        resourceFields: any;
+    }
+    export function flattenResources(resourceStore: ResourceHash, orderSpecs: any): Resource[];
+    export function buildRowNodes(resourceStore: ResourceHash, groupSpecs: any, orderSpecs: any, isVGrouping: boolean, expansions: ResourceEntityExpansions, expansionDefault: boolean): (GroupNode | ResourceNode)[];
+    export function buildResourceFields(resource: Resource): {
+        id: string;
+        parentId: string;
+        title: string;
+        businessHours: import("@fullcalendar/core").EventStore;
+        ui: import("@fullcalendar/core").EventUi;
+        extendedProps: {
+            [extendedProp: string]: any;
+        };
+        startEditable: boolean;
+        durationEditable: boolean;
+        constraints: import("@fullcalendar/core").Constraint[];
+        overlap: boolean;
+        allows: import("@fullcalendar/core").AllowFunc[];
+        backgroundColor: string;
+        borderColor: string;
+        textColor: string;
+        classNames: string[];
+    };
+    export function isGroupsEqual(group0: Group, group1: Group): boolean;
+}
+
+declare module '@fullcalendar/resource-common/common/resource-rendering' {
+    import { Resource } from '@fullcalendar/resource-common/structs/resource';
+    export function buildResourceTextFunc(resourceTextSetting: any, calendar: any): (resource: Resource) => any;
+}
+
+declare module '@fullcalendar/resource-common/api/ResourceApi' {
+    import { Calendar, EventApi } from '@fullcalendar/core';
+    import { Resource } from '@fullcalendar/resource-common/structs/resource';
+    export { ResourceApi as default, ResourceApi };
+    class ResourceApi {
+        _calendar: Calendar;
+        _resource: Resource;
+        constructor(calendar: Calendar, rawResource: Resource);
+        setProp(name: string, value: any): void;
+        remove(): void;
+        getParent(): ResourceApi | null;
+        getChildren(): ResourceApi[];
+        getEvents(): EventApi[];
+        readonly id: string;
+        readonly title: string;
+        readonly eventConstraint: any;
+        readonly eventOverlap: any;
+        readonly eventAllow: any;
+        readonly eventBackgroundColor: string;
+        readonly eventBorderColor: string;
+        readonly eventTextColor: string;
+        readonly eventClassNames: string[];
+        readonly extendedProps: any;
+    }
+}
+
+declare module '@fullcalendar/resource-common/common/ResourceSplitter' {
+    import { Splitter, SplittableProps, DateSpan, EventDef } from '@fullcalendar/core';
+    import { ResourceHash } from '@fullcalendar/resource-common/structs/resource';
+    export interface SplittableResourceProps extends SplittableProps {
+        resourceStore: ResourceHash;
+    }
+    export { ResourceSplitter as default, ResourceSplitter };
+    class ResourceSplitter extends Splitter<SplittableResourceProps> {
+        getKeyInfo(props: SplittableResourceProps): {
+            '': {};
+        };
+        getKeysForDateSpan(dateSpan: DateSpan): string[];
+        getKeysForEventDef(eventDef: EventDef): string[];
+    }
+}
+
+declare module '@fullcalendar/resource-common/structs/resource-source' {
+    import { DateRange, Calendar } from '@fullcalendar/core';
+    import { ResourceInput } from '@fullcalendar/resource-common/structs/resource';
+    import { ResourceFunc } from '@fullcalendar/resource-common/resource-sources/resource-func';
+    export type ResourceSourceError = {
+        message: string;
+        xhr?: XMLHttpRequest;
+        [otherProp: string]: any;
+    };
+    export type ResourceFetcher = (arg: {
+        resourceSource: ResourceSource;
+        calendar: Calendar;
+        range: DateRange | null;
+    }, success: (res: {
+        rawResources: ResourceInput[];
+        xhr?: XMLHttpRequest;
+    }) => void, failure: (error: ResourceSourceError) => void) => void;
+    export interface ExtendedResourceSourceInput {
+        id?: string;
+        resources?: ResourceInput[];
+        url?: string;
+        method?: string;
+        extraParams?: object | (() => object);
+    }
+    export type ResourceSourceInput = ResourceInput[] | ExtendedResourceSourceInput | ResourceFunc | string;
+    export interface ResourceSource {
+        _raw: any;
+        sourceId: string;
+        sourceDefId: number;
+        meta: any;
+        publicId: string;
+        isFetching: boolean;
+        latestFetchId: string;
+        fetchRange: DateRange | null;
+    }
+    export interface ResourceSourceDef {
+        ignoreRange?: boolean;
+        parseMeta: (raw: ResourceSourceInput) => object | null;
+        fetch: ResourceFetcher;
+    }
+    export function registerResourceSourceDef(def: ResourceSourceDef): void;
+    export function getResourceSourceDef(id: number): ResourceSourceDef;
+    export function doesSourceIgnoreRange(source: ResourceSource): boolean;
+    export function parseResourceSource(input: ResourceSourceInput): ResourceSource;
+}
+
+declare module '@fullcalendar/resource-common/reducers/resourceEntityExpansions' {
+    import { ResourceAction } from '@fullcalendar/resource-common/reducers/resources';
+    export type ResourceEntityExpansions = {
+        [id: string]: boolean;
+    };
+    export function reduceResourceEntityExpansions(expansions: ResourceEntityExpansions, action: ResourceAction): ResourceEntityExpansions;
+}
+
+declare module '@fullcalendar/resource-common/reducers/resources' {
+    import { Calendar, CalendarState, Action, DateRange } from '@fullcalendar/core';
+    import { ResourceSourceError } from '@fullcalendar/resource-common/structs/resource-source';
+    import { ResourceHash, ResourceInput } from '@fullcalendar/resource-common/structs/resource';
+    export type ResourceAction = Action | {
+        type: 'FETCH_RESOURCE';
+    } | {
+        type: 'RECEIVE_RESOURCES';
+        rawResources: ResourceInput[];
+        fetchId: string;
+        fetchRange: DateRange | null;
+    } | {
+        type: 'RECEIVE_RESOURCE_ERROR';
+        error: ResourceSourceError;
+        fetchId: string;
+        fetchRange: DateRange | null;
+    } | {
+        type: 'ADD_RESOURCE';
+        resourceHash: ResourceHash;
+    } | // use a hash because needs to accept children
+    {
+        type: 'REMOVE_RESOURCE';
+        resourceId: string;
+    } | {
+        type: 'SET_RESOURCE_PROP';
+        resourceId: string;
+        propName: string;
+        propValue: any;
+    } | {
+        type: 'SET_RESOURCE_ENTITY_EXPANDED';
+        id: string;
+        isExpanded: boolean;
+    } | {
+        type: 'RESET_RESOURCES';
+    } | {
+        type: 'RESET_RESOURCE_SOURCE';
+        resourceSourceInput: any;
+    } | {
+        type: 'REFETCH_RESOURCES';
+    };
+    export default function (state: CalendarState, action: ResourceAction, calendar: Calendar): {
+        resourceSource: import("@fullcalendar/resource-common/structs/resource-source").ResourceSource;
+        resourceStore: ResourceHash;
+        resourceEntityExpansions: import("@fullcalendar/resource-common/reducers/resourceEntityExpansions").ResourceEntityExpansions;
+        eventSources: import("@fullcalendar/core").EventSourceHash;
+        eventSourceLoadingLevel: number;
+        loadingLevel: number;
+        viewType: string;
+        currentDate: Date;
+        dateProfile: import("@fullcalendar/core").DateProfile;
+        eventStore: import("@fullcalendar/core").EventStore;
+        dateSelection: import("@fullcalendar/core").DateSpan;
+        eventSelection: string;
+        eventDrag: import("@fullcalendar/core").EventInteractionState;
+        eventResize: import("@fullcalendar/core").EventInteractionState;
+    };
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..36f3dede8f5b6945a2806b6e4dc73abedd8a4aab
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.esm.js
@@ -0,0 +1,1562 @@
+/*!
+FullCalendar Resources Common Plugin v4.3.1
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+import { memoize, filterHash, rangesIntersect, memoizeOutput, isPropsEqual, mapHash, combineEventUis, refineProps, rangesEqual, processScopedUiProps, parseBusinessHours, EventApi, Calendar, Splitter, mergeEventStores, isPropsValid, appendToElement, htmlEscape, cssToStr, config, isValidDate, addDays, unpromisify, requestJson, htmlToElement, removeElement, createFormatter, computeFallbackHeaderFormat, renderDateCell, findElements, Component, flexibleCompare, compareByFieldSpecs, createPlugin } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+function massageEventDragMutation(eventMutation, hit0, hit1) {
+    var resource0 = hit0.dateSpan.resourceId;
+    var resource1 = hit1.dateSpan.resourceId;
+    if (resource0 && resource1 &&
+        resource0 !== resource1) {
+        eventMutation.resourceMutation = {
+            matchResourceId: resource0,
+            setResourceId: resource1
+        };
+    }
+}
+/*
+TODO: all this would be much easier if we were using a hash!
+*/
+function applyEventDefMutation(eventDef, mutation, calendar) {
+    var resourceMutation = mutation.resourceMutation;
+    if (resourceMutation && computeResourceEditable(eventDef, calendar)) {
+        var index = eventDef.resourceIds.indexOf(resourceMutation.matchResourceId);
+        if (index !== -1) {
+            var resourceIds = eventDef.resourceIds.slice(); // copy
+            resourceIds.splice(index, 1); // remove
+            if (resourceIds.indexOf(resourceMutation.setResourceId) === -1) { // not already in there
+                resourceIds.push(resourceMutation.setResourceId); // add
+            }
+            eventDef.resourceIds = resourceIds;
+        }
+    }
+}
+/*
+HACK
+TODO: use EventUi system instead of this
+*/
+function computeResourceEditable(eventDef, calendar) {
+    var resourceEditable = eventDef.resourceEditable;
+    if (resourceEditable == null) {
+        var source = eventDef.sourceId && calendar.state.eventSources[eventDef.sourceId];
+        if (source) {
+            resourceEditable = source.extendedProps.resourceEditable; // used the Source::extendedProps hack
+        }
+        if (resourceEditable == null) {
+            resourceEditable = calendar.opt('eventResourceEditable');
+            if (resourceEditable == null) {
+                resourceEditable = calendar.opt('editable'); // TODO: use defaults system instead
+            }
+        }
+    }
+    return resourceEditable;
+}
+function transformEventDrop(mutation, calendar) {
+    var resourceMutation = mutation.resourceMutation;
+    if (resourceMutation) {
+        return {
+            oldResource: calendar.getResourceById(resourceMutation.matchResourceId),
+            newResource: calendar.getResourceById(resourceMutation.setResourceId)
+        };
+    }
+    else {
+        return {
+            oldResource: null,
+            newResource: null
+        };
+    }
+}
+
+var ResourceDataAdder = /** @class */ (function () {
+    function ResourceDataAdder() {
+        this.filterResources = memoize(filterResources);
+    }
+    ResourceDataAdder.prototype.transform = function (viewProps, viewSpec, calendarProps, view) {
+        if (viewSpec.class.needsResourceData) {
+            return {
+                resourceStore: this.filterResources(calendarProps.resourceStore, view.opt('filterResourcesWithEvents'), calendarProps.eventStore, calendarProps.dateProfile.activeRange),
+                resourceEntityExpansions: calendarProps.resourceEntityExpansions
+            };
+        }
+    };
+    return ResourceDataAdder;
+}());
+function filterResources(resourceStore, doFilterResourcesWithEvents, eventStore, activeRange) {
+    if (doFilterResourcesWithEvents) {
+        var instancesInRange = filterEventInstancesInRange(eventStore.instances, activeRange);
+        var hasEvents_1 = computeHasEvents(instancesInRange, eventStore.defs);
+        __assign(hasEvents_1, computeAncestorHasEvents(hasEvents_1, resourceStore));
+        return filterHash(resourceStore, function (resource, resourceId) {
+            return hasEvents_1[resourceId];
+        });
+    }
+    else {
+        return resourceStore;
+    }
+}
+function filterEventInstancesInRange(eventInstances, activeRange) {
+    return filterHash(eventInstances, function (eventInstance) {
+        return rangesIntersect(eventInstance.range, activeRange);
+    });
+}
+function computeHasEvents(eventInstances, eventDefs) {
+    var hasEvents = {};
+    for (var instanceId in eventInstances) {
+        var instance = eventInstances[instanceId];
+        for (var _i = 0, _a = eventDefs[instance.defId].resourceIds; _i < _a.length; _i++) {
+            var resourceId = _a[_i];
+            hasEvents[resourceId] = true;
+        }
+    }
+    return hasEvents;
+}
+/*
+mark resources as having events if any of their ancestors have them
+NOTE: resourceStore might not have all the resources that hasEvents{} has keyed
+*/
+function computeAncestorHasEvents(hasEvents, resourceStore) {
+    var res = {};
+    for (var resourceId in hasEvents) {
+        var resource = void 0;
+        while ((resource = resourceStore[resourceId])) {
+            resourceId = resource.parentId; // now functioning as the parentId
+            if (resourceId) {
+                res[resourceId] = true;
+            }
+            else {
+                break;
+            }
+        }
+    }
+    return res;
+}
+// for when non-resource view should be given EventUi info (for event coloring/constraints based off of resource data)
+var ResourceEventConfigAdder = /** @class */ (function () {
+    function ResourceEventConfigAdder() {
+        this.buildResourceEventUis = memoizeOutput(buildResourceEventUis, isPropsEqual);
+        this.injectResourceEventUis = memoize(injectResourceEventUis);
+    }
+    ResourceEventConfigAdder.prototype.transform = function (viewProps, viewSpec, calendarProps) {
+        if (!viewSpec.class.needsResourceData) { // is a non-resource view?
+            return {
+                eventUiBases: this.injectResourceEventUis(viewProps.eventUiBases, viewProps.eventStore.defs, this.buildResourceEventUis(calendarProps.resourceStore))
+            };
+        }
+    };
+    return ResourceEventConfigAdder;
+}());
+function buildResourceEventUis(resourceStore) {
+    return mapHash(resourceStore, function (resource) {
+        return resource.ui;
+    });
+}
+function injectResourceEventUis(eventUiBases, eventDefs, resourceEventUis) {
+    return mapHash(eventUiBases, function (eventUi, defId) {
+        if (defId) { // not the '' key
+            return injectResourceEventUi(eventUi, eventDefs[defId], resourceEventUis);
+        }
+        else {
+            return eventUi;
+        }
+    });
+}
+function injectResourceEventUi(origEventUi, eventDef, resourceEventUis) {
+    var parts = [];
+    // first resource takes precedence, which fights with the ordering of combineEventUis, thus the unshifts
+    for (var _i = 0, _a = eventDef.resourceIds; _i < _a.length; _i++) {
+        var resourceId = _a[_i];
+        if (resourceEventUis[resourceId]) {
+            parts.unshift(resourceEventUis[resourceId]);
+        }
+    }
+    parts.unshift(origEventUi);
+    return combineEventUis(parts);
+}
+// for making sure events that have editable resources are always draggable in resource views
+function transformIsDraggable(val, eventDef, eventUi, view) {
+    if (!val) {
+        if (view.viewSpec.class.needsResourceData) {
+            if (computeResourceEditable(eventDef, view.calendar)) {
+                return true;
+            }
+        }
+    }
+    return val;
+}
+
+var RESOURCE_SOURCE_PROPS = {
+    id: String
+};
+var defs = [];
+var uid = 0;
+function registerResourceSourceDef(def) {
+    defs.push(def);
+}
+function getResourceSourceDef(id) {
+    return defs[id];
+}
+function doesSourceIgnoreRange(source) {
+    return Boolean(defs[source.sourceDefId].ignoreRange);
+}
+function parseResourceSource(input) {
+    for (var i = defs.length - 1; i >= 0; i--) { // later-added plugins take precedence
+        var def = defs[i];
+        var meta = def.parseMeta(input);
+        if (meta) {
+            var res = parseResourceSourceProps((typeof input === 'object' && input) ? input : {}, meta, i);
+            res._raw = input;
+            return res;
+        }
+    }
+    return null;
+}
+function parseResourceSourceProps(input, meta, sourceDefId) {
+    var props = refineProps(input, RESOURCE_SOURCE_PROPS);
+    props.sourceId = String(uid++);
+    props.sourceDefId = sourceDefId;
+    props.meta = meta;
+    props.publicId = props.id;
+    props.isFetching = false;
+    props.latestFetchId = '';
+    props.fetchRange = null;
+    delete props.id;
+    return props;
+}
+
+function reduceResourceSource (source, action, dateProfile, calendar) {
+    switch (action.type) {
+        case 'INIT':
+            return createSource(calendar.opt('resources'), calendar);
+        case 'RESET_RESOURCE_SOURCE':
+            return createSource(action.resourceSourceInput, calendar, true);
+        case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
+        case 'NEXT':
+        case 'SET_DATE':
+        case 'SET_VIEW_TYPE':
+            return handleRange(source, dateProfile.activeRange, calendar);
+        case 'RECEIVE_RESOURCES':
+        case 'RECEIVE_RESOURCE_ERROR':
+            return receiveResponse(source, action.fetchId, action.fetchRange);
+        case 'REFETCH_RESOURCES':
+            return fetchSource(source, dateProfile.activeRange, calendar);
+        default:
+            return source;
+    }
+}
+var uid$1 = 0;
+function createSource(input, calendar, forceFetch) {
+    if (input) {
+        var source = parseResourceSource(input);
+        if (forceFetch || !calendar.opt('refetchResourcesOnNavigate')) { // because assumes handleRange will do it later
+            source = fetchSource(source, null, calendar);
+        }
+        return source;
+    }
+    return null;
+}
+function handleRange(source, activeRange, calendar) {
+    if (calendar.opt('refetchResourcesOnNavigate') &&
+        !doesSourceIgnoreRange(source) &&
+        (!source.fetchRange || !rangesEqual(source.fetchRange, activeRange))) {
+        return fetchSource(source, activeRange, calendar);
+    }
+    else {
+        return source;
+    }
+}
+function fetchSource(source, fetchRange, calendar) {
+    var sourceDef = getResourceSourceDef(source.sourceDefId);
+    var fetchId = String(uid$1++);
+    sourceDef.fetch({
+        resourceSource: source,
+        calendar: calendar,
+        range: fetchRange
+    }, function (res) {
+        // HACK
+        // do before calling dispatch in case dispatch renders synchronously
+        calendar.afterSizingTriggers._resourcesRendered = [null]; // fire once
+        calendar.dispatch({
+            type: 'RECEIVE_RESOURCES',
+            fetchId: fetchId,
+            fetchRange: fetchRange,
+            rawResources: res.rawResources
+        });
+    }, function (error) {
+        calendar.dispatch({
+            type: 'RECEIVE_RESOURCE_ERROR',
+            fetchId: fetchId,
+            fetchRange: fetchRange,
+            error: error
+        });
+    });
+    return __assign({}, source, { isFetching: true, latestFetchId: fetchId });
+}
+function receiveResponse(source, fetchId, fetchRange) {
+    if (fetchId === source.latestFetchId) {
+        return __assign({}, source, { isFetching: false, fetchRange: fetchRange });
+    }
+    return source;
+}
+
+var RESOURCE_PROPS = {
+    id: String,
+    title: String,
+    parentId: String,
+    businessHours: null,
+    children: null,
+    extendedProps: null
+};
+var PRIVATE_ID_PREFIX = '_fc:';
+var uid$2 = 0;
+/*
+needs a full store so that it can populate children too
+*/
+function parseResource(input, parentId, store, calendar) {
+    if (parentId === void 0) { parentId = ''; }
+    var leftovers0 = {};
+    var props = refineProps(input, RESOURCE_PROPS, {}, leftovers0);
+    var leftovers1 = {};
+    var ui = processScopedUiProps('event', leftovers0, calendar, leftovers1);
+    if (!props.id) {
+        props.id = PRIVATE_ID_PREFIX + (uid$2++);
+    }
+    if (!props.parentId) { // give precedence to the parentId property
+        props.parentId = parentId;
+    }
+    props.businessHours = props.businessHours ? parseBusinessHours(props.businessHours, calendar) : null;
+    props.ui = ui;
+    props.extendedProps = __assign({}, leftovers1, props.extendedProps);
+    // help out ResourceApi from having user modify props
+    Object.freeze(ui.classNames);
+    Object.freeze(props.extendedProps);
+    if (store[props.id]) ;
+    else {
+        store[props.id] = props;
+        if (props.children) {
+            for (var _i = 0, _a = props.children; _i < _a.length; _i++) {
+                var childInput = _a[_i];
+                parseResource(childInput, props.id, store, calendar);
+            }
+            delete props.children;
+        }
+    }
+    return props;
+}
+/*
+TODO: use this in more places
+*/
+function getPublicId(id) {
+    if (id.indexOf(PRIVATE_ID_PREFIX) === 0) {
+        return '';
+    }
+    return id;
+}
+
+function reduceResourceStore (store, action, source, calendar) {
+    switch (action.type) {
+        case 'INIT':
+            return {};
+        case 'RECEIVE_RESOURCES':
+            return receiveRawResources(store, action.rawResources, action.fetchId, source, calendar);
+        case 'ADD_RESOURCE':
+            return addResource(store, action.resourceHash);
+        case 'REMOVE_RESOURCE':
+            return removeResource(store, action.resourceId);
+        case 'SET_RESOURCE_PROP':
+            return setResourceProp(store, action.resourceId, action.propName, action.propValue);
+        case 'RESET_RESOURCES':
+            // must make the calendar think each resource is a new object :/
+            return mapHash(store, function (resource) {
+                return __assign({}, resource);
+            });
+        default:
+            return store;
+    }
+}
+function receiveRawResources(existingStore, inputs, fetchId, source, calendar) {
+    if (source.latestFetchId === fetchId) {
+        var nextStore = {};
+        for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) {
+            var input = inputs_1[_i];
+            parseResource(input, '', nextStore, calendar);
+        }
+        return nextStore;
+    }
+    else {
+        return existingStore;
+    }
+}
+function addResource(existingStore, additions) {
+    // TODO: warn about duplicate IDs
+    return __assign({}, existingStore, additions);
+}
+function removeResource(existingStore, resourceId) {
+    var newStore = __assign({}, existingStore);
+    delete newStore[resourceId];
+    // promote children
+    for (var childResourceId in newStore) { // a child, *maybe* but probably not
+        if (newStore[childResourceId].parentId === resourceId) {
+            newStore[childResourceId] = __assign({}, newStore[childResourceId], { parentId: '' });
+        }
+    }
+    return newStore;
+}
+function setResourceProp(existingStore, resourceId, name, value) {
+    var _a, _b;
+    var existingResource = existingStore[resourceId];
+    // TODO: sanitization
+    if (existingResource) {
+        return __assign({}, existingStore, (_a = {}, _a[resourceId] = __assign({}, existingResource, (_b = {}, _b[name] = value, _b)), _a));
+    }
+    else {
+        return existingStore;
+    }
+}
+
+function reduceResourceEntityExpansions(expansions, action) {
+    var _a;
+    switch (action.type) {
+        case 'INIT':
+            return {};
+        case 'SET_RESOURCE_ENTITY_EXPANDED':
+            return __assign({}, expansions, (_a = {}, _a[action.id] = action.isExpanded, _a));
+        default:
+            return expansions;
+    }
+}
+
+function resourcesReducers (state, action, calendar) {
+    var resourceSource = reduceResourceSource(state.resourceSource, action, state.dateProfile, calendar);
+    var resourceStore = reduceResourceStore(state.resourceStore, action, resourceSource, calendar);
+    var resourceEntityExpansions = reduceResourceEntityExpansions(state.resourceEntityExpansions, action);
+    return __assign({}, state, { resourceSource: resourceSource,
+        resourceStore: resourceStore,
+        resourceEntityExpansions: resourceEntityExpansions });
+}
+
+var RESOURCE_RELATED_PROPS = {
+    resourceId: String,
+    resourceIds: function (items) {
+        return (items || []).map(function (item) {
+            return String(item);
+        });
+    },
+    resourceEditable: Boolean
+};
+function parseEventDef(def, props, leftovers) {
+    var resourceRelatedProps = refineProps(props, RESOURCE_RELATED_PROPS, {}, leftovers);
+    var resourceIds = resourceRelatedProps.resourceIds;
+    if (resourceRelatedProps.resourceId) {
+        resourceIds.push(resourceRelatedProps.resourceId);
+    }
+    def.resourceIds = resourceIds;
+    def.resourceEditable = resourceRelatedProps.resourceEditable;
+}
+
+function transformDateSelectionJoin(hit0, hit1) {
+    var resourceId0 = hit0.dateSpan.resourceId;
+    var resourceId1 = hit1.dateSpan.resourceId;
+    if (resourceId0 && resourceId1) {
+        if (hit0.component.allowAcrossResources === false &&
+            resourceId0 !== resourceId1) {
+            return false;
+        }
+        else {
+            return { resourceId: resourceId0 };
+        }
+    }
+}
+
+var ResourceApi = /** @class */ (function () {
+    function ResourceApi(calendar, rawResource) {
+        this._calendar = calendar;
+        this._resource = rawResource;
+    }
+    ResourceApi.prototype.setProp = function (name, value) {
+        this._calendar.dispatch({
+            type: 'SET_RESOURCE_PROP',
+            resourceId: this._resource.id,
+            propName: name,
+            propValue: value
+        });
+    };
+    ResourceApi.prototype.remove = function () {
+        this._calendar.dispatch({
+            type: 'REMOVE_RESOURCE',
+            resourceId: this._resource.id
+        });
+    };
+    ResourceApi.prototype.getParent = function () {
+        var calendar = this._calendar;
+        var parentId = this._resource.parentId;
+        if (parentId) {
+            return new ResourceApi(calendar, calendar.state.resourceSource[parentId]);
+        }
+        else {
+            return null;
+        }
+    };
+    ResourceApi.prototype.getChildren = function () {
+        var thisResourceId = this._resource.id;
+        var calendar = this._calendar;
+        var resourceStore = calendar.state.resourceStore;
+        var childApis = [];
+        for (var resourceId in resourceStore) {
+            if (resourceStore[resourceId].parentId === thisResourceId) {
+                childApis.push(new ResourceApi(calendar, resourceStore[resourceId]));
+            }
+        }
+        return childApis;
+    };
+    /*
+    this is really inefficient!
+    TODO: make EventApi::resourceIds a hash or keep an index in the Calendar's state
+    */
+    ResourceApi.prototype.getEvents = function () {
+        var thisResourceId = this._resource.id;
+        var calendar = this._calendar;
+        var _a = calendar.state.eventStore, defs = _a.defs, instances = _a.instances;
+        var eventApis = [];
+        for (var instanceId in instances) {
+            var instance = instances[instanceId];
+            var def = defs[instance.defId];
+            if (def.resourceIds.indexOf(thisResourceId) !== -1) { // inefficient!!!
+                eventApis.push(new EventApi(calendar, def, instance));
+            }
+        }
+        return eventApis;
+    };
+    Object.defineProperty(ResourceApi.prototype, "id", {
+        get: function () { return this._resource.id; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "title", {
+        get: function () { return this._resource.title; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventConstraint", {
+        get: function () { return this._resource.ui.constraints[0] || null; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventOverlap", {
+        get: function () { return this._resource.ui.overlap; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventAllow", {
+        get: function () { return this._resource.ui.allows[0] || null; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventBackgroundColor", {
+        get: function () { return this._resource.ui.backgroundColor; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventBorderColor", {
+        get: function () { return this._resource.ui.borderColor; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventTextColor", {
+        get: function () { return this._resource.ui.textColor; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "eventClassNames", {
+        // NOTE: user can't modify these because Object.freeze was called in event-def parsing
+        get: function () { return this._resource.ui.classNames; },
+        enumerable: true,
+        configurable: true
+    });
+    Object.defineProperty(ResourceApi.prototype, "extendedProps", {
+        get: function () { return this._resource.extendedProps; },
+        enumerable: true,
+        configurable: true
+    });
+    return ResourceApi;
+}());
+
+Calendar.prototype.addResource = function (input, scrollTo) {
+    var _a;
+    if (scrollTo === void 0) { scrollTo = true; }
+    var resourceHash;
+    var resource;
+    if (input instanceof ResourceApi) {
+        resource = input._resource;
+        resourceHash = (_a = {}, _a[resource.id] = resource, _a);
+    }
+    else {
+        resourceHash = {};
+        resource = parseResource(input, '', resourceHash, this);
+    }
+    // HACK
+    if (scrollTo) {
+        this.component.view.addScroll({ forcedRowId: resource.id });
+    }
+    this.dispatch({
+        type: 'ADD_RESOURCE',
+        resourceHash: resourceHash
+    });
+    return new ResourceApi(this, resource);
+};
+Calendar.prototype.getResourceById = function (id) {
+    id = String(id);
+    if (this.state.resourceStore) { // guard against calendar with no resource functionality
+        var rawResource = this.state.resourceStore[id];
+        if (rawResource) {
+            return new ResourceApi(this, rawResource);
+        }
+    }
+    return null;
+};
+Calendar.prototype.getResources = function () {
+    var resourceStore = this.state.resourceStore;
+    var resourceApis = [];
+    if (resourceStore) { // guard against calendar with no resource functionality
+        for (var resourceId in resourceStore) {
+            resourceApis.push(new ResourceApi(this, resourceStore[resourceId]));
+        }
+    }
+    return resourceApis;
+};
+Calendar.prototype.getTopLevelResources = function () {
+    var resourceStore = this.state.resourceStore;
+    var resourceApis = [];
+    if (resourceStore) { // guard against calendar with no resource functionality
+        for (var resourceId in resourceStore) {
+            if (!resourceStore[resourceId].parentId) {
+                resourceApis.push(new ResourceApi(this, resourceStore[resourceId]));
+            }
+        }
+    }
+    return resourceApis;
+};
+Calendar.prototype.rerenderResources = function () {
+    this.dispatch({
+        type: 'RESET_RESOURCES'
+    });
+};
+Calendar.prototype.refetchResources = function () {
+    this.dispatch({
+        type: 'REFETCH_RESOURCES'
+    });
+};
+function transformDatePoint(dateSpan, calendar) {
+    return dateSpan.resourceId ?
+        { resource: calendar.getResourceById(dateSpan.resourceId) } :
+        {};
+}
+function transformDateSpan(dateSpan, calendar) {
+    return dateSpan.resourceId ?
+        { resource: calendar.getResourceById(dateSpan.resourceId) } :
+        {};
+}
+
+/*
+splits things BASED OFF OF which resources they are associated with.
+creates a '' entry which is when something has NO resource.
+*/
+var ResourceSplitter = /** @class */ (function (_super) {
+    __extends(ResourceSplitter, _super);
+    function ResourceSplitter() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    ResourceSplitter.prototype.getKeyInfo = function (props) {
+        return __assign({ '': {} }, props.resourceStore // already has `ui` and `businessHours` keys!
+        );
+    };
+    ResourceSplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+        return [dateSpan.resourceId || ''];
+    };
+    ResourceSplitter.prototype.getKeysForEventDef = function (eventDef) {
+        var resourceIds = eventDef.resourceIds;
+        if (!resourceIds.length) {
+            return [''];
+        }
+        return resourceIds;
+    };
+    return ResourceSplitter;
+}(Splitter));
+
+function isPropsValidWithResources(props, calendar) {
+    var splitter = new ResourceSplitter();
+    var sets = splitter.splitProps(__assign({}, props, { resourceStore: calendar.state.resourceStore }));
+    for (var resourceId in sets) {
+        var props_1 = sets[resourceId];
+        // merge in event data from the non-resource segment
+        if (resourceId && sets['']) { // current segment is not the non-resource one, and there IS a non-resource one
+            props_1 = __assign({}, props_1, { eventStore: mergeEventStores(sets[''].eventStore, props_1.eventStore), eventUiBases: __assign({}, sets[''].eventUiBases, props_1.eventUiBases) });
+        }
+        if (!isPropsValid(props_1, calendar, { resourceId: resourceId }, filterConfig.bind(null, resourceId))) {
+            return false;
+        }
+    }
+    return true;
+}
+function filterConfig(resourceId, config) {
+    return __assign({}, config, { constraints: filterConstraints(resourceId, config.constraints) });
+}
+function filterConstraints(resourceId, constraints) {
+    return constraints.map(function (constraint) {
+        var defs = constraint.defs;
+        if (defs) { // we are dealing with an EventStore
+            // if any of the events define constraints to resources that are NOT this resource,
+            // then this resource is unconditionally prohibited, which is what a `false` value does.
+            for (var defId in defs) {
+                var resourceIds = defs[defId].resourceIds;
+                if (resourceIds.length && resourceIds.indexOf(resourceId) === -1) { // TODO: use a hash?!!! (for other reasons too)
+                    return false;
+                }
+            }
+        }
+        return constraint;
+    });
+}
+
+function transformExternalDef(dateSpan) {
+    return dateSpan.resourceId ?
+        { resourceId: dateSpan.resourceId } :
+        {};
+}
+
+function transformEventResizeJoin(hit0, hit1) {
+    var component = hit0.component;
+    if (component.allowAcrossResources === false &&
+        hit0.dateSpan.resourceId !== hit1.dateSpan.resourceId) {
+        return false;
+    }
+}
+
+EventApi.prototype.getResources = function () {
+    var calendar = this._calendar;
+    return this._def.resourceIds.map(function (resourceId) {
+        return calendar.getResourceById(resourceId);
+    });
+};
+EventApi.prototype.setResources = function (resources) {
+    var resourceIds = [];
+    // massage resources -> resourceIds
+    for (var _i = 0, resources_1 = resources; _i < resources_1.length; _i++) {
+        var resource = resources_1[_i];
+        var resourceId = null;
+        if (typeof resource === 'string') {
+            resourceId = resource;
+        }
+        else if (typeof resource === 'number') {
+            resourceId = String(resource);
+        }
+        else if (resource instanceof ResourceApi) {
+            resourceId = resource.id; // guaranteed to always have an ID. hmmm
+        }
+        else {
+            console.warn('unknown resource type: ' + resource);
+        }
+        if (resourceId) {
+            resourceIds.push(resourceId);
+        }
+    }
+    this.mutate({
+        standardProps: {
+            resourceIds: resourceIds
+        }
+    });
+};
+
+var RELEASE_DATE = '2019-08-10'; // for Scheduler
+var UPGRADE_WINDOW = 365 + 7; // days. 1 week leeway, for tz shift reasons too
+var LICENSE_INFO_URL = 'http://fullcalendar.io/scheduler/license/';
+var PRESET_LICENSE_KEYS = [
+    'GPL-My-Project-Is-Open-Source',
+    'CC-Attribution-NonCommercial-NoDerivatives'
+];
+var CSS = {
+    position: 'absolute',
+    'z-index': 99999,
+    bottom: '1px',
+    left: '1px',
+    background: '#eee',
+    'border-color': '#ddd',
+    'border-style': 'solid',
+    'border-width': '1px 1px 0 0',
+    padding: '2px 4px',
+    'font-size': '12px',
+    'border-top-right-radius': '3px'
+};
+function injectLicenseWarning(containerEl, calendar) {
+    var key = calendar.opt('schedulerLicenseKey');
+    if (!isImmuneUrl(window.location.href) && !isValidKey(key)) {
+        appendToElement(containerEl, '<div class="fc-license-message" style="' + htmlEscape(cssToStr(CSS)) + '">' +
+            'Please use a valid license key. <a href="' + LICENSE_INFO_URL + '">More Info</a>' +
+            '</div>');
+    }
+}
+/*
+This decryption is not meant to be bulletproof. Just a way to remind about an upgrade.
+*/
+function isValidKey(key) {
+    if (PRESET_LICENSE_KEYS.indexOf(key) !== -1) {
+        return true;
+    }
+    var parts = (key || '').match(/^(\d+)\-fcs\-(\d+)$/);
+    if (parts && (parts[1].length === 10)) {
+        var purchaseDate = new Date(parseInt(parts[2], 10) * 1000);
+        var releaseDate = new Date(config.mockSchedulerReleaseDate || RELEASE_DATE);
+        if (isValidDate(releaseDate)) { // token won't be replaced in dev mode
+            var minPurchaseDate = addDays(releaseDate, -UPGRADE_WINDOW);
+            if (minPurchaseDate < purchaseDate) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+function isImmuneUrl(url) {
+    return /\w+\:\/\/fullcalendar\.io\/|\/examples\/[\w-]+\.html$/.test(url);
+}
+
+var optionChangeHandlers = {
+    resources: handleResources
+};
+function handleResources(newSourceInput, calendar, deepEqual) {
+    var oldSourceInput = calendar.state.resourceSource._raw;
+    if (!deepEqual(oldSourceInput, newSourceInput)) {
+        calendar.dispatch({
+            type: 'RESET_RESOURCE_SOURCE',
+            resourceSourceInput: newSourceInput
+        });
+    }
+}
+
+registerResourceSourceDef({
+    ignoreRange: true,
+    parseMeta: function (raw) {
+        if (Array.isArray(raw)) {
+            return raw;
+        }
+        else if (Array.isArray(raw.resources)) {
+            return raw.resources;
+        }
+        return null;
+    },
+    fetch: function (arg, successCallback) {
+        successCallback({
+            rawResources: arg.resourceSource.meta
+        });
+    }
+});
+
+registerResourceSourceDef({
+    parseMeta: function (raw) {
+        if (typeof raw === 'function') {
+            return raw;
+        }
+        else if (typeof raw.resources === 'function') {
+            return raw.resources;
+        }
+        return null;
+    },
+    fetch: function (arg, success, failure) {
+        var dateEnv = arg.calendar.dateEnv;
+        var func = arg.resourceSource.meta;
+        var publicArg = {};
+        if (arg.range) {
+            publicArg = {
+                start: dateEnv.toDate(arg.range.start),
+                end: dateEnv.toDate(arg.range.end),
+                startStr: dateEnv.formatIso(arg.range.start),
+                endStr: dateEnv.formatIso(arg.range.end),
+                timeZone: dateEnv.timeZone
+            };
+        }
+        // TODO: make more dry with EventSourceFunc
+        // TODO: accept a response?
+        unpromisify(func.bind(null, publicArg), function (rawResources) {
+            success({ rawResources: rawResources }); // needs an object response
+        }, failure // send errorObj directly to failure callback
+        );
+    }
+});
+
+registerResourceSourceDef({
+    parseMeta: function (raw) {
+        if (typeof raw === 'string') {
+            raw = { url: raw };
+        }
+        else if (!raw || typeof raw !== 'object' || !raw.url) {
+            return null;
+        }
+        return {
+            url: raw.url,
+            method: (raw.method || 'GET').toUpperCase(),
+            extraParams: raw.extraParams
+        };
+    },
+    fetch: function (arg, successCallback, failureCallback) {
+        var meta = arg.resourceSource.meta;
+        var requestParams = buildRequestParams(meta, arg.range, arg.calendar);
+        requestJson(meta.method, meta.url, requestParams, function (rawResources, xhr) {
+            successCallback({ rawResources: rawResources, xhr: xhr });
+        }, function (message, xhr) {
+            failureCallback({ message: message, xhr: xhr });
+        });
+    }
+});
+// TODO: somehow consolidate with event json feed
+function buildRequestParams(meta, range, calendar) {
+    var dateEnv = calendar.dateEnv;
+    var startParam;
+    var endParam;
+    var timeZoneParam;
+    var customRequestParams;
+    var params = {};
+    if (range) {
+        // startParam = meta.startParam
+        // if (startParam == null) {
+        startParam = calendar.opt('startParam');
+        // }
+        // endParam = meta.endParam
+        // if (endParam == null) {
+        endParam = calendar.opt('endParam');
+        // }
+        // timeZoneParam = meta.timeZoneParam
+        // if (timeZoneParam == null) {
+        timeZoneParam = calendar.opt('timeZoneParam');
+        // }
+        params[startParam] = dateEnv.formatIso(range.start);
+        params[endParam] = dateEnv.formatIso(range.end);
+        if (dateEnv.timeZone !== 'local') {
+            params[timeZoneParam] = dateEnv.timeZone;
+        }
+    }
+    // retrieve any outbound GET/POST data from the options
+    if (typeof meta.extraParams === 'function') {
+        // supplied as a function that returns a key/value object
+        customRequestParams = meta.extraParams();
+    }
+    else {
+        // probably supplied as a straight key/value object
+        customRequestParams = meta.extraParams || {};
+    }
+    __assign(params, customRequestParams);
+    return params;
+}
+
+function buildResourceTextFunc(resourceTextSetting, calendar) {
+    if (typeof resourceTextSetting === 'function') {
+        return function (resource) {
+            return resourceTextSetting(new ResourceApi(calendar, resource));
+        };
+    }
+    else {
+        return function (resource) {
+            return resource.title || getPublicId(resource.id);
+        };
+    }
+}
+
+var ResourceDayHeader = /** @class */ (function (_super) {
+    __extends(ResourceDayHeader, _super);
+    function ResourceDayHeader(context, parentEl) {
+        var _this = _super.call(this, context) || this;
+        _this.datesAboveResources = _this.opt('datesAboveResources');
+        _this.resourceTextFunc = buildResourceTextFunc(_this.opt('resourceText'), _this.calendar);
+        parentEl.innerHTML = ''; // because might be nbsp
+        parentEl.appendChild(_this.el = htmlToElement('<div class="fc-row ' + _this.theme.getClass('headerRow') + '">' +
+            '<table class="' + _this.theme.getClass('tableGrid') + '">' +
+            '<thead></thead>' +
+            '</table>' +
+            '</div>'));
+        _this.thead = _this.el.querySelector('thead');
+        return _this;
+    }
+    ResourceDayHeader.prototype.destroy = function () {
+        removeElement(this.el);
+    };
+    ResourceDayHeader.prototype.render = function (props) {
+        var html;
+        this.dateFormat = createFormatter(this.opt('columnHeaderFormat') ||
+            computeFallbackHeaderFormat(props.datesRepDistinctDays, props.dates.length));
+        if (props.dates.length === 1) {
+            html = this.renderResourceRow(props.resources);
+        }
+        else {
+            if (this.datesAboveResources) {
+                html = this.renderDayAndResourceRows(props.dates, props.resources);
+            }
+            else {
+                html = this.renderResourceAndDayRows(props.resources, props.dates);
+            }
+        }
+        this.thead.innerHTML = html;
+        this.processResourceEls(props.resources);
+    };
+    ResourceDayHeader.prototype.renderResourceRow = function (resources) {
+        var _this = this;
+        var cellHtmls = resources.map(function (resource) {
+            return _this.renderResourceCell(resource, 1);
+        });
+        return this.buildTr(cellHtmls);
+    };
+    ResourceDayHeader.prototype.renderDayAndResourceRows = function (dates, resources) {
+        var dateHtmls = [];
+        var resourceHtmls = [];
+        for (var _i = 0, dates_1 = dates; _i < dates_1.length; _i++) {
+            var date = dates_1[_i];
+            dateHtmls.push(this.renderDateCell(date, resources.length));
+            for (var _a = 0, resources_1 = resources; _a < resources_1.length; _a++) {
+                var resource = resources_1[_a];
+                resourceHtmls.push(this.renderResourceCell(resource, 1, date));
+            }
+        }
+        return this.buildTr(dateHtmls) +
+            this.buildTr(resourceHtmls);
+    };
+    ResourceDayHeader.prototype.renderResourceAndDayRows = function (resources, dates) {
+        var resourceHtmls = [];
+        var dateHtmls = [];
+        for (var _i = 0, resources_2 = resources; _i < resources_2.length; _i++) {
+            var resource = resources_2[_i];
+            resourceHtmls.push(this.renderResourceCell(resource, dates.length));
+            for (var _a = 0, dates_2 = dates; _a < dates_2.length; _a++) {
+                var date = dates_2[_a];
+                dateHtmls.push(this.renderDateCell(date, 1, resource));
+            }
+        }
+        return this.buildTr(resourceHtmls) +
+            this.buildTr(dateHtmls);
+    };
+    // Cell Rendering Utils
+    // ----------------------------------------------------------------------------------------------
+    // a cell with the resource name. might be associated with a specific day
+    ResourceDayHeader.prototype.renderResourceCell = function (resource, colspan, date) {
+        var dateEnv = this.dateEnv;
+        return '<th class="fc-resource-cell"' +
+            ' data-resource-id="' + resource.id + '"' +
+            (date ?
+                ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' :
+                '') +
+            (colspan > 1 ?
+                ' colspan="' + colspan + '"' :
+                '') +
+            '>' +
+            htmlEscape(this.resourceTextFunc(resource)) +
+            '</th>';
+    };
+    // a cell with date text. might have a resource associated with it
+    ResourceDayHeader.prototype.renderDateCell = function (date, colspan, resource) {
+        var props = this.props;
+        return renderDateCell(date, props.dateProfile, props.datesRepDistinctDays, props.dates.length * props.resources.length, this.dateFormat, this.context, colspan, resource ? 'data-resource-id="' + resource.id + '"' : '');
+    };
+    ResourceDayHeader.prototype.buildTr = function (cellHtmls) {
+        if (!cellHtmls.length) {
+            cellHtmls = ['<td>&nbsp;</td>'];
+        }
+        if (this.props.renderIntroHtml) {
+            cellHtmls = [this.props.renderIntroHtml()].concat(cellHtmls);
+        }
+        if (this.isRtl) {
+            cellHtmls.reverse();
+        }
+        return '<tr>' +
+            cellHtmls.join('') +
+            '</tr>';
+    };
+    // Post-rendering
+    // ----------------------------------------------------------------------------------------------
+    // given a container with already rendered resource cells
+    ResourceDayHeader.prototype.processResourceEls = function (resources) {
+        var _this = this;
+        var view = this.view;
+        findElements(this.thead, '.fc-resource-cell').forEach(function (node, col) {
+            col = col % resources.length;
+            if (_this.isRtl) {
+                col = resources.length - 1 - col;
+            }
+            var resource = resources[col];
+            view.publiclyTrigger('resourceRender', [
+                {
+                    resource: new ResourceApi(_this.calendar, resource),
+                    el: node,
+                    view: view
+                }
+            ]);
+        });
+    };
+    return ResourceDayHeader;
+}(Component));
+
+var AbstractResourceDayTable = /** @class */ (function () {
+    function AbstractResourceDayTable(dayTable, resources) {
+        this.dayTable = dayTable;
+        this.resources = resources;
+        this.resourceIndex = new ResourceIndex(resources);
+        this.rowCnt = dayTable.rowCnt;
+        this.colCnt = dayTable.colCnt * resources.length;
+        this.cells = this.buildCells();
+    }
+    AbstractResourceDayTable.prototype.buildCells = function () {
+        var _a = this, rowCnt = _a.rowCnt, dayTable = _a.dayTable, resources = _a.resources;
+        var rows = [];
+        for (var row = 0; row < rowCnt; row++) {
+            var rowCells = [];
+            for (var dateCol = 0; dateCol < dayTable.colCnt; dateCol++) {
+                for (var resourceCol = 0; resourceCol < resources.length; resourceCol++) {
+                    var resource = resources[resourceCol];
+                    var htmlAttrs = 'data-resource-id="' + resource.id + '"';
+                    rowCells[this.computeCol(dateCol, resourceCol)] = {
+                        date: dayTable.cells[row][dateCol].date,
+                        resource: resource,
+                        htmlAttrs: htmlAttrs
+                    };
+                }
+            }
+            rows.push(rowCells);
+        }
+        return rows;
+    };
+    return AbstractResourceDayTable;
+}());
+/*
+resources over dates
+*/
+var ResourceDayTable = /** @class */ (function (_super) {
+    __extends(ResourceDayTable, _super);
+    function ResourceDayTable() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    ResourceDayTable.prototype.computeCol = function (dateI, resourceI) {
+        return resourceI * this.dayTable.colCnt + dateI;
+    };
+    /*
+    all date ranges are intact
+    */
+    ResourceDayTable.prototype.computeColRanges = function (dateStartI, dateEndI, resourceI) {
+        return [
+            {
+                firstCol: this.computeCol(dateStartI, resourceI),
+                lastCol: this.computeCol(dateEndI, resourceI),
+                isStart: true,
+                isEnd: true
+            }
+        ];
+    };
+    return ResourceDayTable;
+}(AbstractResourceDayTable));
+/*
+dates over resources
+*/
+var DayResourceTable = /** @class */ (function (_super) {
+    __extends(DayResourceTable, _super);
+    function DayResourceTable() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    DayResourceTable.prototype.computeCol = function (dateI, resourceI) {
+        return dateI * this.resources.length + resourceI;
+    };
+    /*
+    every single day is broken up
+    */
+    DayResourceTable.prototype.computeColRanges = function (dateStartI, dateEndI, resourceI) {
+        var segs = [];
+        for (var i = dateStartI; i <= dateEndI; i++) {
+            var col = this.computeCol(i, resourceI);
+            segs.push({
+                firstCol: col,
+                lastCol: col,
+                isStart: i === dateStartI,
+                isEnd: i === dateEndI
+            });
+        }
+        return segs;
+    };
+    return DayResourceTable;
+}(AbstractResourceDayTable));
+var ResourceIndex = /** @class */ (function () {
+    function ResourceIndex(resources) {
+        var indicesById = {};
+        var ids = [];
+        for (var i = 0; i < resources.length; i++) {
+            var id = resources[i].id;
+            ids.push(id);
+            indicesById[id] = i;
+        }
+        this.ids = ids;
+        this.indicesById = indicesById;
+        this.length = resources.length;
+    }
+    return ResourceIndex;
+}());
+/*
+TODO: just use ResourceHash somehow? could then use the generic ResourceSplitter
+*/
+var VResourceSplitter = /** @class */ (function (_super) {
+    __extends(VResourceSplitter, _super);
+    function VResourceSplitter() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    VResourceSplitter.prototype.getKeyInfo = function (props) {
+        var resourceDayTable = props.resourceDayTable;
+        var hash = mapHash(resourceDayTable.resourceIndex.indicesById, function (i) {
+            return resourceDayTable.resources[i]; // has `ui` AND `businessHours` keys!
+        }); // :(
+        hash[''] = {};
+        return hash;
+    };
+    VResourceSplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+        return [dateSpan.resourceId || ''];
+    };
+    VResourceSplitter.prototype.getKeysForEventDef = function (eventDef) {
+        var resourceIds = eventDef.resourceIds;
+        if (!resourceIds.length) {
+            return [''];
+        }
+        return resourceIds;
+    };
+    return VResourceSplitter;
+}(Splitter));
+// joiner
+var NO_SEGS = []; // for memoizing
+var VResourceJoiner = /** @class */ (function () {
+    function VResourceJoiner() {
+        this.joinDateSelection = memoize(this.joinSegs);
+        this.joinBusinessHours = memoize(this.joinSegs);
+        this.joinFgEvents = memoize(this.joinSegs);
+        this.joinBgEvents = memoize(this.joinSegs);
+        this.joinEventDrags = memoize(this.joinInteractions);
+        this.joinEventResizes = memoize(this.joinInteractions);
+    }
+    /*
+    propSets also has a '' key for things with no resource
+    */
+    VResourceJoiner.prototype.joinProps = function (propSets, resourceDayTable) {
+        var dateSelectionSets = [];
+        var businessHoursSets = [];
+        var fgEventSets = [];
+        var bgEventSets = [];
+        var eventDrags = [];
+        var eventResizes = [];
+        var eventSelection = '';
+        var keys = resourceDayTable.resourceIndex.ids.concat(['']); // add in the all-resource key
+        for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
+            var key = keys_1[_i];
+            var props = propSets[key];
+            dateSelectionSets.push(props.dateSelectionSegs);
+            businessHoursSets.push(key ? props.businessHourSegs : NO_SEGS); // don't include redundant all-resource businesshours
+            fgEventSets.push(key ? props.fgEventSegs : NO_SEGS); // don't include fg all-resource segs
+            bgEventSets.push(props.bgEventSegs);
+            eventDrags.push(props.eventDrag);
+            eventResizes.push(props.eventResize);
+            eventSelection = eventSelection || props.eventSelection;
+        }
+        return {
+            dateSelectionSegs: this.joinDateSelection.apply(this, [resourceDayTable].concat(dateSelectionSets)),
+            businessHourSegs: this.joinBusinessHours.apply(this, [resourceDayTable].concat(businessHoursSets)),
+            fgEventSegs: this.joinFgEvents.apply(this, [resourceDayTable].concat(fgEventSets)),
+            bgEventSegs: this.joinBgEvents.apply(this, [resourceDayTable].concat(bgEventSets)),
+            eventDrag: this.joinEventDrags.apply(this, [resourceDayTable].concat(eventDrags)),
+            eventResize: this.joinEventResizes.apply(this, [resourceDayTable].concat(eventResizes)),
+            eventSelection: eventSelection
+        };
+    };
+    VResourceJoiner.prototype.joinSegs = function (resourceDayTable) {
+        var segGroups = [];
+        for (var _i = 1; _i < arguments.length; _i++) {
+            segGroups[_i - 1] = arguments[_i];
+        }
+        var resourceCnt = resourceDayTable.resources.length;
+        var transformedSegs = [];
+        for (var i = 0; i < resourceCnt; i++) {
+            for (var _a = 0, _b = segGroups[i]; _a < _b.length; _a++) {
+                var seg = _b[_a];
+                transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i));
+            }
+            for (var _c = 0, _d = segGroups[resourceCnt]; _c < _d.length; _c++) { // one beyond. the all-resource
+                var seg = _d[_c];
+                transformedSegs.push.apply(// one beyond. the all-resource
+                transformedSegs, this.transformSeg(seg, resourceDayTable, i));
+            }
+        }
+        return transformedSegs;
+    };
+    /*
+    for expanding non-resource segs to all resources.
+    only for public use.
+    no memoizing.
+    */
+    VResourceJoiner.prototype.expandSegs = function (resourceDayTable, segs) {
+        var resourceCnt = resourceDayTable.resources.length;
+        var transformedSegs = [];
+        for (var i = 0; i < resourceCnt; i++) {
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i));
+            }
+        }
+        return transformedSegs;
+    };
+    VResourceJoiner.prototype.joinInteractions = function (resourceDayTable) {
+        var interactions = [];
+        for (var _i = 1; _i < arguments.length; _i++) {
+            interactions[_i - 1] = arguments[_i];
+        }
+        var resourceCnt = resourceDayTable.resources.length;
+        var affectedInstances = {};
+        var transformedSegs = [];
+        var isEvent = false;
+        var sourceSeg = null;
+        for (var i = 0; i < resourceCnt; i++) {
+            var interaction = interactions[i];
+            if (interaction) {
+                for (var _a = 0, _b = interaction.segs; _a < _b.length; _a++) {
+                    var seg = _b[_a];
+                    transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i) // TODO: templateify Interaction::segs
+                    );
+                }
+                __assign(affectedInstances, interaction.affectedInstances);
+                isEvent = isEvent || interaction.isEvent;
+                sourceSeg = sourceSeg || interaction.sourceSeg;
+            }
+            if (interactions[resourceCnt]) { // one beyond. the all-resource
+                for (var _c = 0, _d = interactions[resourceCnt].segs; _c < _d.length; _c++) {
+                    var seg = _d[_c];
+                    transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i) // TODO: templateify Interaction::segs
+                    );
+                }
+            }
+        }
+        return {
+            affectedInstances: affectedInstances,
+            segs: transformedSegs,
+            isEvent: isEvent,
+            sourceSeg: sourceSeg
+        };
+    };
+    return VResourceJoiner;
+}());
+
+/*
+doesn't accept grouping
+*/
+function flattenResources(resourceStore, orderSpecs) {
+    return buildRowNodes(resourceStore, [], orderSpecs, false, {}, true)
+        .map(function (node) {
+        return node.resource;
+    });
+}
+function buildRowNodes(resourceStore, groupSpecs, orderSpecs, isVGrouping, expansions, expansionDefault) {
+    var complexNodes = buildHierarchy(resourceStore, isVGrouping ? -1 : 1, groupSpecs, orderSpecs);
+    var flatNodes = [];
+    flattenNodes(complexNodes, flatNodes, isVGrouping, [], 0, expansions, expansionDefault);
+    return flatNodes;
+}
+function flattenNodes(complexNodes, res, isVGrouping, rowSpans, depth, expansions, expansionDefault) {
+    for (var i = 0; i < complexNodes.length; i++) {
+        var complexNode = complexNodes[i];
+        var group = complexNode.group;
+        if (group) {
+            if (isVGrouping) {
+                var firstRowIndex = res.length;
+                var rowSpanIndex = rowSpans.length;
+                flattenNodes(complexNode.children, res, isVGrouping, rowSpans.concat(0), depth, expansions, expansionDefault);
+                if (firstRowIndex < res.length) {
+                    var firstRow = res[firstRowIndex];
+                    var firstRowSpans = firstRow.rowSpans = firstRow.rowSpans.slice();
+                    firstRowSpans[rowSpanIndex] = res.length - firstRowIndex;
+                }
+            }
+            else {
+                var id = group.spec.field + ':' + group.value;
+                var isExpanded = expansions[id] != null ? expansions[id] : expansionDefault;
+                res.push({ id: id, group: group, isExpanded: isExpanded });
+                if (isExpanded) {
+                    flattenNodes(complexNode.children, res, isVGrouping, rowSpans, depth + 1, expansions, expansionDefault);
+                }
+            }
+        }
+        else if (complexNode.resource) {
+            var id = complexNode.resource.id;
+            var isExpanded = expansions[id] != null ? expansions[id] : expansionDefault;
+            res.push({
+                id: id,
+                rowSpans: rowSpans,
+                depth: depth,
+                isExpanded: isExpanded,
+                hasChildren: Boolean(complexNode.children.length),
+                resource: complexNode.resource,
+                resourceFields: complexNode.resourceFields
+            });
+            if (isExpanded) {
+                flattenNodes(complexNode.children, res, isVGrouping, rowSpans, depth + 1, expansions, expansionDefault);
+            }
+        }
+    }
+}
+function buildHierarchy(resourceStore, maxDepth, groupSpecs, orderSpecs) {
+    var resourceNodes = buildResourceNodes(resourceStore, orderSpecs);
+    var builtNodes = [];
+    for (var resourceId in resourceNodes) {
+        var resourceNode = resourceNodes[resourceId];
+        if (!resourceNode.resource.parentId) {
+            insertResourceNode(resourceNode, builtNodes, groupSpecs, 0, maxDepth, orderSpecs);
+        }
+    }
+    return builtNodes;
+}
+function buildResourceNodes(resourceStore, orderSpecs) {
+    var nodeHash = {};
+    for (var resourceId in resourceStore) {
+        var resource = resourceStore[resourceId];
+        nodeHash[resourceId] = {
+            resource: resource,
+            resourceFields: buildResourceFields(resource),
+            children: []
+        };
+    }
+    for (var resourceId in resourceStore) {
+        var resource = resourceStore[resourceId];
+        if (resource.parentId) {
+            var parentNode = nodeHash[resource.parentId];
+            if (parentNode) {
+                insertResourceNodeInSiblings(nodeHash[resourceId], parentNode.children, orderSpecs);
+            }
+        }
+    }
+    return nodeHash;
+}
+function insertResourceNode(resourceNode, nodes, groupSpecs, depth, maxDepth, orderSpecs) {
+    if (groupSpecs.length && (maxDepth === -1 || depth <= maxDepth)) {
+        var groupNode = ensureGroupNodes(resourceNode, nodes, groupSpecs[0]);
+        insertResourceNode(resourceNode, groupNode.children, groupSpecs.slice(1), depth + 1, maxDepth, orderSpecs);
+    }
+    else {
+        insertResourceNodeInSiblings(resourceNode, nodes, orderSpecs);
+    }
+}
+function ensureGroupNodes(resourceNode, nodes, groupSpec) {
+    var groupValue = resourceNode.resourceFields[groupSpec.field];
+    var groupNode;
+    var newGroupIndex;
+    // find an existing group that matches, or determine the position for a new group
+    if (groupSpec.order) {
+        for (newGroupIndex = 0; newGroupIndex < nodes.length; newGroupIndex++) {
+            var node = nodes[newGroupIndex];
+            if (node.group) {
+                var cmp = flexibleCompare(groupValue, node.group.value) * groupSpec.order;
+                if (cmp === 0) {
+                    groupNode = node;
+                    break;
+                }
+                else if (cmp < 0) {
+                    break;
+                }
+            }
+        }
+    }
+    else { // the groups are unordered
+        for (newGroupIndex = 0; newGroupIndex < nodes.length; newGroupIndex++) {
+            var node = nodes[newGroupIndex];
+            if (node.group && groupValue === node.group.value) {
+                groupNode = node;
+                break;
+            }
+        }
+    }
+    if (!groupNode) {
+        groupNode = {
+            group: {
+                value: groupValue,
+                spec: groupSpec
+            },
+            children: []
+        };
+        nodes.splice(newGroupIndex, 0, groupNode);
+    }
+    return groupNode;
+}
+function insertResourceNodeInSiblings(resourceNode, siblings, orderSpecs) {
+    var i;
+    for (i = 0; i < siblings.length; i++) {
+        var cmp = compareByFieldSpecs(siblings[i].resourceFields, resourceNode.resourceFields, orderSpecs);
+        if (cmp > 0) { // went 1 past. insert at i
+            break;
+        }
+    }
+    siblings.splice(i, 0, resourceNode);
+}
+function buildResourceFields(resource) {
+    var obj = __assign({}, resource.extendedProps, resource.ui, resource);
+    delete obj.ui;
+    delete obj.extendedProps;
+    return obj;
+}
+function isGroupsEqual(group0, group1) {
+    return group0.spec === group1.spec && group0.value === group1.value;
+}
+
+var main = createPlugin({
+    reducers: [resourcesReducers],
+    eventDefParsers: [parseEventDef],
+    isDraggableTransformers: [transformIsDraggable],
+    eventDragMutationMassagers: [massageEventDragMutation],
+    eventDefMutationAppliers: [applyEventDefMutation],
+    dateSelectionTransformers: [transformDateSelectionJoin],
+    datePointTransforms: [transformDatePoint],
+    dateSpanTransforms: [transformDateSpan],
+    viewPropsTransformers: [ResourceDataAdder, ResourceEventConfigAdder],
+    isPropsValid: isPropsValidWithResources,
+    externalDefTransforms: [transformExternalDef],
+    eventResizeJoinTransforms: [transformEventResizeJoin],
+    viewContainerModifiers: [injectLicenseWarning],
+    eventDropTransformers: [transformEventDrop],
+    optionChangeHandlers: optionChangeHandlers
+});
+
+export default main;
+export { AbstractResourceDayTable, DayResourceTable, ResourceApi, ResourceDayHeader, ResourceDayTable, ResourceSplitter, VResourceJoiner, VResourceSplitter, buildResourceFields, buildResourceTextFunc, buildRowNodes, flattenResources, isGroupsEqual };
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/main.js b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..eaa626d7265fcb438157d76df9a55e576a7c7764
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.js
@@ -0,0 +1,1582 @@
+/*!
+FullCalendar Resources Common Plugin v4.3.1
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarResourceCommon = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    function massageEventDragMutation(eventMutation, hit0, hit1) {
+        var resource0 = hit0.dateSpan.resourceId;
+        var resource1 = hit1.dateSpan.resourceId;
+        if (resource0 && resource1 &&
+            resource0 !== resource1) {
+            eventMutation.resourceMutation = {
+                matchResourceId: resource0,
+                setResourceId: resource1
+            };
+        }
+    }
+    /*
+    TODO: all this would be much easier if we were using a hash!
+    */
+    function applyEventDefMutation(eventDef, mutation, calendar) {
+        var resourceMutation = mutation.resourceMutation;
+        if (resourceMutation && computeResourceEditable(eventDef, calendar)) {
+            var index = eventDef.resourceIds.indexOf(resourceMutation.matchResourceId);
+            if (index !== -1) {
+                var resourceIds = eventDef.resourceIds.slice(); // copy
+                resourceIds.splice(index, 1); // remove
+                if (resourceIds.indexOf(resourceMutation.setResourceId) === -1) { // not already in there
+                    resourceIds.push(resourceMutation.setResourceId); // add
+                }
+                eventDef.resourceIds = resourceIds;
+            }
+        }
+    }
+    /*
+    HACK
+    TODO: use EventUi system instead of this
+    */
+    function computeResourceEditable(eventDef, calendar) {
+        var resourceEditable = eventDef.resourceEditable;
+        if (resourceEditable == null) {
+            var source = eventDef.sourceId && calendar.state.eventSources[eventDef.sourceId];
+            if (source) {
+                resourceEditable = source.extendedProps.resourceEditable; // used the Source::extendedProps hack
+            }
+            if (resourceEditable == null) {
+                resourceEditable = calendar.opt('eventResourceEditable');
+                if (resourceEditable == null) {
+                    resourceEditable = calendar.opt('editable'); // TODO: use defaults system instead
+                }
+            }
+        }
+        return resourceEditable;
+    }
+    function transformEventDrop(mutation, calendar) {
+        var resourceMutation = mutation.resourceMutation;
+        if (resourceMutation) {
+            return {
+                oldResource: calendar.getResourceById(resourceMutation.matchResourceId),
+                newResource: calendar.getResourceById(resourceMutation.setResourceId)
+            };
+        }
+        else {
+            return {
+                oldResource: null,
+                newResource: null
+            };
+        }
+    }
+
+    var ResourceDataAdder = /** @class */ (function () {
+        function ResourceDataAdder() {
+            this.filterResources = core.memoize(filterResources);
+        }
+        ResourceDataAdder.prototype.transform = function (viewProps, viewSpec, calendarProps, view) {
+            if (viewSpec.class.needsResourceData) {
+                return {
+                    resourceStore: this.filterResources(calendarProps.resourceStore, view.opt('filterResourcesWithEvents'), calendarProps.eventStore, calendarProps.dateProfile.activeRange),
+                    resourceEntityExpansions: calendarProps.resourceEntityExpansions
+                };
+            }
+        };
+        return ResourceDataAdder;
+    }());
+    function filterResources(resourceStore, doFilterResourcesWithEvents, eventStore, activeRange) {
+        if (doFilterResourcesWithEvents) {
+            var instancesInRange = filterEventInstancesInRange(eventStore.instances, activeRange);
+            var hasEvents_1 = computeHasEvents(instancesInRange, eventStore.defs);
+            __assign(hasEvents_1, computeAncestorHasEvents(hasEvents_1, resourceStore));
+            return core.filterHash(resourceStore, function (resource, resourceId) {
+                return hasEvents_1[resourceId];
+            });
+        }
+        else {
+            return resourceStore;
+        }
+    }
+    function filterEventInstancesInRange(eventInstances, activeRange) {
+        return core.filterHash(eventInstances, function (eventInstance) {
+            return core.rangesIntersect(eventInstance.range, activeRange);
+        });
+    }
+    function computeHasEvents(eventInstances, eventDefs) {
+        var hasEvents = {};
+        for (var instanceId in eventInstances) {
+            var instance = eventInstances[instanceId];
+            for (var _i = 0, _a = eventDefs[instance.defId].resourceIds; _i < _a.length; _i++) {
+                var resourceId = _a[_i];
+                hasEvents[resourceId] = true;
+            }
+        }
+        return hasEvents;
+    }
+    /*
+    mark resources as having events if any of their ancestors have them
+    NOTE: resourceStore might not have all the resources that hasEvents{} has keyed
+    */
+    function computeAncestorHasEvents(hasEvents, resourceStore) {
+        var res = {};
+        for (var resourceId in hasEvents) {
+            var resource = void 0;
+            while ((resource = resourceStore[resourceId])) {
+                resourceId = resource.parentId; // now functioning as the parentId
+                if (resourceId) {
+                    res[resourceId] = true;
+                }
+                else {
+                    break;
+                }
+            }
+        }
+        return res;
+    }
+    // for when non-resource view should be given EventUi info (for event coloring/constraints based off of resource data)
+    var ResourceEventConfigAdder = /** @class */ (function () {
+        function ResourceEventConfigAdder() {
+            this.buildResourceEventUis = core.memoizeOutput(buildResourceEventUis, core.isPropsEqual);
+            this.injectResourceEventUis = core.memoize(injectResourceEventUis);
+        }
+        ResourceEventConfigAdder.prototype.transform = function (viewProps, viewSpec, calendarProps) {
+            if (!viewSpec.class.needsResourceData) { // is a non-resource view?
+                return {
+                    eventUiBases: this.injectResourceEventUis(viewProps.eventUiBases, viewProps.eventStore.defs, this.buildResourceEventUis(calendarProps.resourceStore))
+                };
+            }
+        };
+        return ResourceEventConfigAdder;
+    }());
+    function buildResourceEventUis(resourceStore) {
+        return core.mapHash(resourceStore, function (resource) {
+            return resource.ui;
+        });
+    }
+    function injectResourceEventUis(eventUiBases, eventDefs, resourceEventUis) {
+        return core.mapHash(eventUiBases, function (eventUi, defId) {
+            if (defId) { // not the '' key
+                return injectResourceEventUi(eventUi, eventDefs[defId], resourceEventUis);
+            }
+            else {
+                return eventUi;
+            }
+        });
+    }
+    function injectResourceEventUi(origEventUi, eventDef, resourceEventUis) {
+        var parts = [];
+        // first resource takes precedence, which fights with the ordering of combineEventUis, thus the unshifts
+        for (var _i = 0, _a = eventDef.resourceIds; _i < _a.length; _i++) {
+            var resourceId = _a[_i];
+            if (resourceEventUis[resourceId]) {
+                parts.unshift(resourceEventUis[resourceId]);
+            }
+        }
+        parts.unshift(origEventUi);
+        return core.combineEventUis(parts);
+    }
+    // for making sure events that have editable resources are always draggable in resource views
+    function transformIsDraggable(val, eventDef, eventUi, view) {
+        if (!val) {
+            if (view.viewSpec.class.needsResourceData) {
+                if (computeResourceEditable(eventDef, view.calendar)) {
+                    return true;
+                }
+            }
+        }
+        return val;
+    }
+
+    var RESOURCE_SOURCE_PROPS = {
+        id: String
+    };
+    var defs = [];
+    var uid = 0;
+    function registerResourceSourceDef(def) {
+        defs.push(def);
+    }
+    function getResourceSourceDef(id) {
+        return defs[id];
+    }
+    function doesSourceIgnoreRange(source) {
+        return Boolean(defs[source.sourceDefId].ignoreRange);
+    }
+    function parseResourceSource(input) {
+        for (var i = defs.length - 1; i >= 0; i--) { // later-added plugins take precedence
+            var def = defs[i];
+            var meta = def.parseMeta(input);
+            if (meta) {
+                var res = parseResourceSourceProps((typeof input === 'object' && input) ? input : {}, meta, i);
+                res._raw = input;
+                return res;
+            }
+        }
+        return null;
+    }
+    function parseResourceSourceProps(input, meta, sourceDefId) {
+        var props = core.refineProps(input, RESOURCE_SOURCE_PROPS);
+        props.sourceId = String(uid++);
+        props.sourceDefId = sourceDefId;
+        props.meta = meta;
+        props.publicId = props.id;
+        props.isFetching = false;
+        props.latestFetchId = '';
+        props.fetchRange = null;
+        delete props.id;
+        return props;
+    }
+
+    function reduceResourceSource (source, action, dateProfile, calendar) {
+        switch (action.type) {
+            case 'INIT':
+                return createSource(calendar.opt('resources'), calendar);
+            case 'RESET_RESOURCE_SOURCE':
+                return createSource(action.resourceSourceInput, calendar, true);
+            case 'PREV': // TODO: how do we track all actions that affect dateProfile :(
+            case 'NEXT':
+            case 'SET_DATE':
+            case 'SET_VIEW_TYPE':
+                return handleRange(source, dateProfile.activeRange, calendar);
+            case 'RECEIVE_RESOURCES':
+            case 'RECEIVE_RESOURCE_ERROR':
+                return receiveResponse(source, action.fetchId, action.fetchRange);
+            case 'REFETCH_RESOURCES':
+                return fetchSource(source, dateProfile.activeRange, calendar);
+            default:
+                return source;
+        }
+    }
+    var uid$1 = 0;
+    function createSource(input, calendar, forceFetch) {
+        if (input) {
+            var source = parseResourceSource(input);
+            if (forceFetch || !calendar.opt('refetchResourcesOnNavigate')) { // because assumes handleRange will do it later
+                source = fetchSource(source, null, calendar);
+            }
+            return source;
+        }
+        return null;
+    }
+    function handleRange(source, activeRange, calendar) {
+        if (calendar.opt('refetchResourcesOnNavigate') &&
+            !doesSourceIgnoreRange(source) &&
+            (!source.fetchRange || !core.rangesEqual(source.fetchRange, activeRange))) {
+            return fetchSource(source, activeRange, calendar);
+        }
+        else {
+            return source;
+        }
+    }
+    function fetchSource(source, fetchRange, calendar) {
+        var sourceDef = getResourceSourceDef(source.sourceDefId);
+        var fetchId = String(uid$1++);
+        sourceDef.fetch({
+            resourceSource: source,
+            calendar: calendar,
+            range: fetchRange
+        }, function (res) {
+            // HACK
+            // do before calling dispatch in case dispatch renders synchronously
+            calendar.afterSizingTriggers._resourcesRendered = [null]; // fire once
+            calendar.dispatch({
+                type: 'RECEIVE_RESOURCES',
+                fetchId: fetchId,
+                fetchRange: fetchRange,
+                rawResources: res.rawResources
+            });
+        }, function (error) {
+            calendar.dispatch({
+                type: 'RECEIVE_RESOURCE_ERROR',
+                fetchId: fetchId,
+                fetchRange: fetchRange,
+                error: error
+            });
+        });
+        return __assign({}, source, { isFetching: true, latestFetchId: fetchId });
+    }
+    function receiveResponse(source, fetchId, fetchRange) {
+        if (fetchId === source.latestFetchId) {
+            return __assign({}, source, { isFetching: false, fetchRange: fetchRange });
+        }
+        return source;
+    }
+
+    var RESOURCE_PROPS = {
+        id: String,
+        title: String,
+        parentId: String,
+        businessHours: null,
+        children: null,
+        extendedProps: null
+    };
+    var PRIVATE_ID_PREFIX = '_fc:';
+    var uid$2 = 0;
+    /*
+    needs a full store so that it can populate children too
+    */
+    function parseResource(input, parentId, store, calendar) {
+        if (parentId === void 0) { parentId = ''; }
+        var leftovers0 = {};
+        var props = core.refineProps(input, RESOURCE_PROPS, {}, leftovers0);
+        var leftovers1 = {};
+        var ui = core.processScopedUiProps('event', leftovers0, calendar, leftovers1);
+        if (!props.id) {
+            props.id = PRIVATE_ID_PREFIX + (uid$2++);
+        }
+        if (!props.parentId) { // give precedence to the parentId property
+            props.parentId = parentId;
+        }
+        props.businessHours = props.businessHours ? core.parseBusinessHours(props.businessHours, calendar) : null;
+        props.ui = ui;
+        props.extendedProps = __assign({}, leftovers1, props.extendedProps);
+        // help out ResourceApi from having user modify props
+        Object.freeze(ui.classNames);
+        Object.freeze(props.extendedProps);
+        if (store[props.id]) ;
+        else {
+            store[props.id] = props;
+            if (props.children) {
+                for (var _i = 0, _a = props.children; _i < _a.length; _i++) {
+                    var childInput = _a[_i];
+                    parseResource(childInput, props.id, store, calendar);
+                }
+                delete props.children;
+            }
+        }
+        return props;
+    }
+    /*
+    TODO: use this in more places
+    */
+    function getPublicId(id) {
+        if (id.indexOf(PRIVATE_ID_PREFIX) === 0) {
+            return '';
+        }
+        return id;
+    }
+
+    function reduceResourceStore (store, action, source, calendar) {
+        switch (action.type) {
+            case 'INIT':
+                return {};
+            case 'RECEIVE_RESOURCES':
+                return receiveRawResources(store, action.rawResources, action.fetchId, source, calendar);
+            case 'ADD_RESOURCE':
+                return addResource(store, action.resourceHash);
+            case 'REMOVE_RESOURCE':
+                return removeResource(store, action.resourceId);
+            case 'SET_RESOURCE_PROP':
+                return setResourceProp(store, action.resourceId, action.propName, action.propValue);
+            case 'RESET_RESOURCES':
+                // must make the calendar think each resource is a new object :/
+                return core.mapHash(store, function (resource) {
+                    return __assign({}, resource);
+                });
+            default:
+                return store;
+        }
+    }
+    function receiveRawResources(existingStore, inputs, fetchId, source, calendar) {
+        if (source.latestFetchId === fetchId) {
+            var nextStore = {};
+            for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) {
+                var input = inputs_1[_i];
+                parseResource(input, '', nextStore, calendar);
+            }
+            return nextStore;
+        }
+        else {
+            return existingStore;
+        }
+    }
+    function addResource(existingStore, additions) {
+        // TODO: warn about duplicate IDs
+        return __assign({}, existingStore, additions);
+    }
+    function removeResource(existingStore, resourceId) {
+        var newStore = __assign({}, existingStore);
+        delete newStore[resourceId];
+        // promote children
+        for (var childResourceId in newStore) { // a child, *maybe* but probably not
+            if (newStore[childResourceId].parentId === resourceId) {
+                newStore[childResourceId] = __assign({}, newStore[childResourceId], { parentId: '' });
+            }
+        }
+        return newStore;
+    }
+    function setResourceProp(existingStore, resourceId, name, value) {
+        var _a, _b;
+        var existingResource = existingStore[resourceId];
+        // TODO: sanitization
+        if (existingResource) {
+            return __assign({}, existingStore, (_a = {}, _a[resourceId] = __assign({}, existingResource, (_b = {}, _b[name] = value, _b)), _a));
+        }
+        else {
+            return existingStore;
+        }
+    }
+
+    function reduceResourceEntityExpansions(expansions, action) {
+        var _a;
+        switch (action.type) {
+            case 'INIT':
+                return {};
+            case 'SET_RESOURCE_ENTITY_EXPANDED':
+                return __assign({}, expansions, (_a = {}, _a[action.id] = action.isExpanded, _a));
+            default:
+                return expansions;
+        }
+    }
+
+    function resourcesReducers (state, action, calendar) {
+        var resourceSource = reduceResourceSource(state.resourceSource, action, state.dateProfile, calendar);
+        var resourceStore = reduceResourceStore(state.resourceStore, action, resourceSource, calendar);
+        var resourceEntityExpansions = reduceResourceEntityExpansions(state.resourceEntityExpansions, action);
+        return __assign({}, state, { resourceSource: resourceSource,
+            resourceStore: resourceStore,
+            resourceEntityExpansions: resourceEntityExpansions });
+    }
+
+    var RESOURCE_RELATED_PROPS = {
+        resourceId: String,
+        resourceIds: function (items) {
+            return (items || []).map(function (item) {
+                return String(item);
+            });
+        },
+        resourceEditable: Boolean
+    };
+    function parseEventDef(def, props, leftovers) {
+        var resourceRelatedProps = core.refineProps(props, RESOURCE_RELATED_PROPS, {}, leftovers);
+        var resourceIds = resourceRelatedProps.resourceIds;
+        if (resourceRelatedProps.resourceId) {
+            resourceIds.push(resourceRelatedProps.resourceId);
+        }
+        def.resourceIds = resourceIds;
+        def.resourceEditable = resourceRelatedProps.resourceEditable;
+    }
+
+    function transformDateSelectionJoin(hit0, hit1) {
+        var resourceId0 = hit0.dateSpan.resourceId;
+        var resourceId1 = hit1.dateSpan.resourceId;
+        if (resourceId0 && resourceId1) {
+            if (hit0.component.allowAcrossResources === false &&
+                resourceId0 !== resourceId1) {
+                return false;
+            }
+            else {
+                return { resourceId: resourceId0 };
+            }
+        }
+    }
+
+    var ResourceApi = /** @class */ (function () {
+        function ResourceApi(calendar, rawResource) {
+            this._calendar = calendar;
+            this._resource = rawResource;
+        }
+        ResourceApi.prototype.setProp = function (name, value) {
+            this._calendar.dispatch({
+                type: 'SET_RESOURCE_PROP',
+                resourceId: this._resource.id,
+                propName: name,
+                propValue: value
+            });
+        };
+        ResourceApi.prototype.remove = function () {
+            this._calendar.dispatch({
+                type: 'REMOVE_RESOURCE',
+                resourceId: this._resource.id
+            });
+        };
+        ResourceApi.prototype.getParent = function () {
+            var calendar = this._calendar;
+            var parentId = this._resource.parentId;
+            if (parentId) {
+                return new ResourceApi(calendar, calendar.state.resourceSource[parentId]);
+            }
+            else {
+                return null;
+            }
+        };
+        ResourceApi.prototype.getChildren = function () {
+            var thisResourceId = this._resource.id;
+            var calendar = this._calendar;
+            var resourceStore = calendar.state.resourceStore;
+            var childApis = [];
+            for (var resourceId in resourceStore) {
+                if (resourceStore[resourceId].parentId === thisResourceId) {
+                    childApis.push(new ResourceApi(calendar, resourceStore[resourceId]));
+                }
+            }
+            return childApis;
+        };
+        /*
+        this is really inefficient!
+        TODO: make EventApi::resourceIds a hash or keep an index in the Calendar's state
+        */
+        ResourceApi.prototype.getEvents = function () {
+            var thisResourceId = this._resource.id;
+            var calendar = this._calendar;
+            var _a = calendar.state.eventStore, defs = _a.defs, instances = _a.instances;
+            var eventApis = [];
+            for (var instanceId in instances) {
+                var instance = instances[instanceId];
+                var def = defs[instance.defId];
+                if (def.resourceIds.indexOf(thisResourceId) !== -1) { // inefficient!!!
+                    eventApis.push(new core.EventApi(calendar, def, instance));
+                }
+            }
+            return eventApis;
+        };
+        Object.defineProperty(ResourceApi.prototype, "id", {
+            get: function () { return this._resource.id; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "title", {
+            get: function () { return this._resource.title; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventConstraint", {
+            get: function () { return this._resource.ui.constraints[0] || null; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventOverlap", {
+            get: function () { return this._resource.ui.overlap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventAllow", {
+            get: function () { return this._resource.ui.allows[0] || null; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventBackgroundColor", {
+            get: function () { return this._resource.ui.backgroundColor; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventBorderColor", {
+            get: function () { return this._resource.ui.borderColor; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventTextColor", {
+            get: function () { return this._resource.ui.textColor; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "eventClassNames", {
+            // NOTE: user can't modify these because Object.freeze was called in event-def parsing
+            get: function () { return this._resource.ui.classNames; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ResourceApi.prototype, "extendedProps", {
+            get: function () { return this._resource.extendedProps; },
+            enumerable: true,
+            configurable: true
+        });
+        return ResourceApi;
+    }());
+
+    core.Calendar.prototype.addResource = function (input, scrollTo) {
+        var _a;
+        if (scrollTo === void 0) { scrollTo = true; }
+        var resourceHash;
+        var resource;
+        if (input instanceof ResourceApi) {
+            resource = input._resource;
+            resourceHash = (_a = {}, _a[resource.id] = resource, _a);
+        }
+        else {
+            resourceHash = {};
+            resource = parseResource(input, '', resourceHash, this);
+        }
+        // HACK
+        if (scrollTo) {
+            this.component.view.addScroll({ forcedRowId: resource.id });
+        }
+        this.dispatch({
+            type: 'ADD_RESOURCE',
+            resourceHash: resourceHash
+        });
+        return new ResourceApi(this, resource);
+    };
+    core.Calendar.prototype.getResourceById = function (id) {
+        id = String(id);
+        if (this.state.resourceStore) { // guard against calendar with no resource functionality
+            var rawResource = this.state.resourceStore[id];
+            if (rawResource) {
+                return new ResourceApi(this, rawResource);
+            }
+        }
+        return null;
+    };
+    core.Calendar.prototype.getResources = function () {
+        var resourceStore = this.state.resourceStore;
+        var resourceApis = [];
+        if (resourceStore) { // guard against calendar with no resource functionality
+            for (var resourceId in resourceStore) {
+                resourceApis.push(new ResourceApi(this, resourceStore[resourceId]));
+            }
+        }
+        return resourceApis;
+    };
+    core.Calendar.prototype.getTopLevelResources = function () {
+        var resourceStore = this.state.resourceStore;
+        var resourceApis = [];
+        if (resourceStore) { // guard against calendar with no resource functionality
+            for (var resourceId in resourceStore) {
+                if (!resourceStore[resourceId].parentId) {
+                    resourceApis.push(new ResourceApi(this, resourceStore[resourceId]));
+                }
+            }
+        }
+        return resourceApis;
+    };
+    core.Calendar.prototype.rerenderResources = function () {
+        this.dispatch({
+            type: 'RESET_RESOURCES'
+        });
+    };
+    core.Calendar.prototype.refetchResources = function () {
+        this.dispatch({
+            type: 'REFETCH_RESOURCES'
+        });
+    };
+    function transformDatePoint(dateSpan, calendar) {
+        return dateSpan.resourceId ?
+            { resource: calendar.getResourceById(dateSpan.resourceId) } :
+            {};
+    }
+    function transformDateSpan(dateSpan, calendar) {
+        return dateSpan.resourceId ?
+            { resource: calendar.getResourceById(dateSpan.resourceId) } :
+            {};
+    }
+
+    /*
+    splits things BASED OFF OF which resources they are associated with.
+    creates a '' entry which is when something has NO resource.
+    */
+    var ResourceSplitter = /** @class */ (function (_super) {
+        __extends(ResourceSplitter, _super);
+        function ResourceSplitter() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        ResourceSplitter.prototype.getKeyInfo = function (props) {
+            return __assign({ '': {} }, props.resourceStore // already has `ui` and `businessHours` keys!
+            );
+        };
+        ResourceSplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+            return [dateSpan.resourceId || ''];
+        };
+        ResourceSplitter.prototype.getKeysForEventDef = function (eventDef) {
+            var resourceIds = eventDef.resourceIds;
+            if (!resourceIds.length) {
+                return [''];
+            }
+            return resourceIds;
+        };
+        return ResourceSplitter;
+    }(core.Splitter));
+
+    function isPropsValidWithResources(props, calendar) {
+        var splitter = new ResourceSplitter();
+        var sets = splitter.splitProps(__assign({}, props, { resourceStore: calendar.state.resourceStore }));
+        for (var resourceId in sets) {
+            var props_1 = sets[resourceId];
+            // merge in event data from the non-resource segment
+            if (resourceId && sets['']) { // current segment is not the non-resource one, and there IS a non-resource one
+                props_1 = __assign({}, props_1, { eventStore: core.mergeEventStores(sets[''].eventStore, props_1.eventStore), eventUiBases: __assign({}, sets[''].eventUiBases, props_1.eventUiBases) });
+            }
+            if (!core.isPropsValid(props_1, calendar, { resourceId: resourceId }, filterConfig.bind(null, resourceId))) {
+                return false;
+            }
+        }
+        return true;
+    }
+    function filterConfig(resourceId, config) {
+        return __assign({}, config, { constraints: filterConstraints(resourceId, config.constraints) });
+    }
+    function filterConstraints(resourceId, constraints) {
+        return constraints.map(function (constraint) {
+            var defs = constraint.defs;
+            if (defs) { // we are dealing with an EventStore
+                // if any of the events define constraints to resources that are NOT this resource,
+                // then this resource is unconditionally prohibited, which is what a `false` value does.
+                for (var defId in defs) {
+                    var resourceIds = defs[defId].resourceIds;
+                    if (resourceIds.length && resourceIds.indexOf(resourceId) === -1) { // TODO: use a hash?!!! (for other reasons too)
+                        return false;
+                    }
+                }
+            }
+            return constraint;
+        });
+    }
+
+    function transformExternalDef(dateSpan) {
+        return dateSpan.resourceId ?
+            { resourceId: dateSpan.resourceId } :
+            {};
+    }
+
+    function transformEventResizeJoin(hit0, hit1) {
+        var component = hit0.component;
+        if (component.allowAcrossResources === false &&
+            hit0.dateSpan.resourceId !== hit1.dateSpan.resourceId) {
+            return false;
+        }
+    }
+
+    core.EventApi.prototype.getResources = function () {
+        var calendar = this._calendar;
+        return this._def.resourceIds.map(function (resourceId) {
+            return calendar.getResourceById(resourceId);
+        });
+    };
+    core.EventApi.prototype.setResources = function (resources) {
+        var resourceIds = [];
+        // massage resources -> resourceIds
+        for (var _i = 0, resources_1 = resources; _i < resources_1.length; _i++) {
+            var resource = resources_1[_i];
+            var resourceId = null;
+            if (typeof resource === 'string') {
+                resourceId = resource;
+            }
+            else if (typeof resource === 'number') {
+                resourceId = String(resource);
+            }
+            else if (resource instanceof ResourceApi) {
+                resourceId = resource.id; // guaranteed to always have an ID. hmmm
+            }
+            else {
+                console.warn('unknown resource type: ' + resource);
+            }
+            if (resourceId) {
+                resourceIds.push(resourceId);
+            }
+        }
+        this.mutate({
+            standardProps: {
+                resourceIds: resourceIds
+            }
+        });
+    };
+
+    var RELEASE_DATE = '2019-08-10'; // for Scheduler
+    var UPGRADE_WINDOW = 365 + 7; // days. 1 week leeway, for tz shift reasons too
+    var LICENSE_INFO_URL = 'http://fullcalendar.io/scheduler/license/';
+    var PRESET_LICENSE_KEYS = [
+        'GPL-My-Project-Is-Open-Source',
+        'CC-Attribution-NonCommercial-NoDerivatives'
+    ];
+    var CSS = {
+        position: 'absolute',
+        'z-index': 99999,
+        bottom: '1px',
+        left: '1px',
+        background: '#eee',
+        'border-color': '#ddd',
+        'border-style': 'solid',
+        'border-width': '1px 1px 0 0',
+        padding: '2px 4px',
+        'font-size': '12px',
+        'border-top-right-radius': '3px'
+    };
+    function injectLicenseWarning(containerEl, calendar) {
+        var key = calendar.opt('schedulerLicenseKey');
+        if (!isImmuneUrl(window.location.href) && !isValidKey(key)) {
+            core.appendToElement(containerEl, '<div class="fc-license-message" style="' + core.htmlEscape(core.cssToStr(CSS)) + '">' +
+                'Please use a valid license key. <a href="' + LICENSE_INFO_URL + '">More Info</a>' +
+                '</div>');
+        }
+    }
+    /*
+    This decryption is not meant to be bulletproof. Just a way to remind about an upgrade.
+    */
+    function isValidKey(key) {
+        if (PRESET_LICENSE_KEYS.indexOf(key) !== -1) {
+            return true;
+        }
+        var parts = (key || '').match(/^(\d+)\-fcs\-(\d+)$/);
+        if (parts && (parts[1].length === 10)) {
+            var purchaseDate = new Date(parseInt(parts[2], 10) * 1000);
+            var releaseDate = new Date(core.config.mockSchedulerReleaseDate || RELEASE_DATE);
+            if (core.isValidDate(releaseDate)) { // token won't be replaced in dev mode
+                var minPurchaseDate = core.addDays(releaseDate, -UPGRADE_WINDOW);
+                if (minPurchaseDate < purchaseDate) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    function isImmuneUrl(url) {
+        return /\w+\:\/\/fullcalendar\.io\/|\/examples\/[\w-]+\.html$/.test(url);
+    }
+
+    var optionChangeHandlers = {
+        resources: handleResources
+    };
+    function handleResources(newSourceInput, calendar, deepEqual) {
+        var oldSourceInput = calendar.state.resourceSource._raw;
+        if (!deepEqual(oldSourceInput, newSourceInput)) {
+            calendar.dispatch({
+                type: 'RESET_RESOURCE_SOURCE',
+                resourceSourceInput: newSourceInput
+            });
+        }
+    }
+
+    registerResourceSourceDef({
+        ignoreRange: true,
+        parseMeta: function (raw) {
+            if (Array.isArray(raw)) {
+                return raw;
+            }
+            else if (Array.isArray(raw.resources)) {
+                return raw.resources;
+            }
+            return null;
+        },
+        fetch: function (arg, successCallback) {
+            successCallback({
+                rawResources: arg.resourceSource.meta
+            });
+        }
+    });
+
+    registerResourceSourceDef({
+        parseMeta: function (raw) {
+            if (typeof raw === 'function') {
+                return raw;
+            }
+            else if (typeof raw.resources === 'function') {
+                return raw.resources;
+            }
+            return null;
+        },
+        fetch: function (arg, success, failure) {
+            var dateEnv = arg.calendar.dateEnv;
+            var func = arg.resourceSource.meta;
+            var publicArg = {};
+            if (arg.range) {
+                publicArg = {
+                    start: dateEnv.toDate(arg.range.start),
+                    end: dateEnv.toDate(arg.range.end),
+                    startStr: dateEnv.formatIso(arg.range.start),
+                    endStr: dateEnv.formatIso(arg.range.end),
+                    timeZone: dateEnv.timeZone
+                };
+            }
+            // TODO: make more dry with EventSourceFunc
+            // TODO: accept a response?
+            core.unpromisify(func.bind(null, publicArg), function (rawResources) {
+                success({ rawResources: rawResources }); // needs an object response
+            }, failure // send errorObj directly to failure callback
+            );
+        }
+    });
+
+    registerResourceSourceDef({
+        parseMeta: function (raw) {
+            if (typeof raw === 'string') {
+                raw = { url: raw };
+            }
+            else if (!raw || typeof raw !== 'object' || !raw.url) {
+                return null;
+            }
+            return {
+                url: raw.url,
+                method: (raw.method || 'GET').toUpperCase(),
+                extraParams: raw.extraParams
+            };
+        },
+        fetch: function (arg, successCallback, failureCallback) {
+            var meta = arg.resourceSource.meta;
+            var requestParams = buildRequestParams(meta, arg.range, arg.calendar);
+            core.requestJson(meta.method, meta.url, requestParams, function (rawResources, xhr) {
+                successCallback({ rawResources: rawResources, xhr: xhr });
+            }, function (message, xhr) {
+                failureCallback({ message: message, xhr: xhr });
+            });
+        }
+    });
+    // TODO: somehow consolidate with event json feed
+    function buildRequestParams(meta, range, calendar) {
+        var dateEnv = calendar.dateEnv;
+        var startParam;
+        var endParam;
+        var timeZoneParam;
+        var customRequestParams;
+        var params = {};
+        if (range) {
+            // startParam = meta.startParam
+            // if (startParam == null) {
+            startParam = calendar.opt('startParam');
+            // }
+            // endParam = meta.endParam
+            // if (endParam == null) {
+            endParam = calendar.opt('endParam');
+            // }
+            // timeZoneParam = meta.timeZoneParam
+            // if (timeZoneParam == null) {
+            timeZoneParam = calendar.opt('timeZoneParam');
+            // }
+            params[startParam] = dateEnv.formatIso(range.start);
+            params[endParam] = dateEnv.formatIso(range.end);
+            if (dateEnv.timeZone !== 'local') {
+                params[timeZoneParam] = dateEnv.timeZone;
+            }
+        }
+        // retrieve any outbound GET/POST data from the options
+        if (typeof meta.extraParams === 'function') {
+            // supplied as a function that returns a key/value object
+            customRequestParams = meta.extraParams();
+        }
+        else {
+            // probably supplied as a straight key/value object
+            customRequestParams = meta.extraParams || {};
+        }
+        __assign(params, customRequestParams);
+        return params;
+    }
+
+    function buildResourceTextFunc(resourceTextSetting, calendar) {
+        if (typeof resourceTextSetting === 'function') {
+            return function (resource) {
+                return resourceTextSetting(new ResourceApi(calendar, resource));
+            };
+        }
+        else {
+            return function (resource) {
+                return resource.title || getPublicId(resource.id);
+            };
+        }
+    }
+
+    var ResourceDayHeader = /** @class */ (function (_super) {
+        __extends(ResourceDayHeader, _super);
+        function ResourceDayHeader(context, parentEl) {
+            var _this = _super.call(this, context) || this;
+            _this.datesAboveResources = _this.opt('datesAboveResources');
+            _this.resourceTextFunc = buildResourceTextFunc(_this.opt('resourceText'), _this.calendar);
+            parentEl.innerHTML = ''; // because might be nbsp
+            parentEl.appendChild(_this.el = core.htmlToElement('<div class="fc-row ' + _this.theme.getClass('headerRow') + '">' +
+                '<table class="' + _this.theme.getClass('tableGrid') + '">' +
+                '<thead></thead>' +
+                '</table>' +
+                '</div>'));
+            _this.thead = _this.el.querySelector('thead');
+            return _this;
+        }
+        ResourceDayHeader.prototype.destroy = function () {
+            core.removeElement(this.el);
+        };
+        ResourceDayHeader.prototype.render = function (props) {
+            var html;
+            this.dateFormat = core.createFormatter(this.opt('columnHeaderFormat') ||
+                core.computeFallbackHeaderFormat(props.datesRepDistinctDays, props.dates.length));
+            if (props.dates.length === 1) {
+                html = this.renderResourceRow(props.resources);
+            }
+            else {
+                if (this.datesAboveResources) {
+                    html = this.renderDayAndResourceRows(props.dates, props.resources);
+                }
+                else {
+                    html = this.renderResourceAndDayRows(props.resources, props.dates);
+                }
+            }
+            this.thead.innerHTML = html;
+            this.processResourceEls(props.resources);
+        };
+        ResourceDayHeader.prototype.renderResourceRow = function (resources) {
+            var _this = this;
+            var cellHtmls = resources.map(function (resource) {
+                return _this.renderResourceCell(resource, 1);
+            });
+            return this.buildTr(cellHtmls);
+        };
+        ResourceDayHeader.prototype.renderDayAndResourceRows = function (dates, resources) {
+            var dateHtmls = [];
+            var resourceHtmls = [];
+            for (var _i = 0, dates_1 = dates; _i < dates_1.length; _i++) {
+                var date = dates_1[_i];
+                dateHtmls.push(this.renderDateCell(date, resources.length));
+                for (var _a = 0, resources_1 = resources; _a < resources_1.length; _a++) {
+                    var resource = resources_1[_a];
+                    resourceHtmls.push(this.renderResourceCell(resource, 1, date));
+                }
+            }
+            return this.buildTr(dateHtmls) +
+                this.buildTr(resourceHtmls);
+        };
+        ResourceDayHeader.prototype.renderResourceAndDayRows = function (resources, dates) {
+            var resourceHtmls = [];
+            var dateHtmls = [];
+            for (var _i = 0, resources_2 = resources; _i < resources_2.length; _i++) {
+                var resource = resources_2[_i];
+                resourceHtmls.push(this.renderResourceCell(resource, dates.length));
+                for (var _a = 0, dates_2 = dates; _a < dates_2.length; _a++) {
+                    var date = dates_2[_a];
+                    dateHtmls.push(this.renderDateCell(date, 1, resource));
+                }
+            }
+            return this.buildTr(resourceHtmls) +
+                this.buildTr(dateHtmls);
+        };
+        // Cell Rendering Utils
+        // ----------------------------------------------------------------------------------------------
+        // a cell with the resource name. might be associated with a specific day
+        ResourceDayHeader.prototype.renderResourceCell = function (resource, colspan, date) {
+            var dateEnv = this.dateEnv;
+            return '<th class="fc-resource-cell"' +
+                ' data-resource-id="' + resource.id + '"' +
+                (date ?
+                    ' data-date="' + dateEnv.formatIso(date, { omitTime: true }) + '"' :
+                    '') +
+                (colspan > 1 ?
+                    ' colspan="' + colspan + '"' :
+                    '') +
+                '>' +
+                core.htmlEscape(this.resourceTextFunc(resource)) +
+                '</th>';
+        };
+        // a cell with date text. might have a resource associated with it
+        ResourceDayHeader.prototype.renderDateCell = function (date, colspan, resource) {
+            var props = this.props;
+            return core.renderDateCell(date, props.dateProfile, props.datesRepDistinctDays, props.dates.length * props.resources.length, this.dateFormat, this.context, colspan, resource ? 'data-resource-id="' + resource.id + '"' : '');
+        };
+        ResourceDayHeader.prototype.buildTr = function (cellHtmls) {
+            if (!cellHtmls.length) {
+                cellHtmls = ['<td>&nbsp;</td>'];
+            }
+            if (this.props.renderIntroHtml) {
+                cellHtmls = [this.props.renderIntroHtml()].concat(cellHtmls);
+            }
+            if (this.isRtl) {
+                cellHtmls.reverse();
+            }
+            return '<tr>' +
+                cellHtmls.join('') +
+                '</tr>';
+        };
+        // Post-rendering
+        // ----------------------------------------------------------------------------------------------
+        // given a container with already rendered resource cells
+        ResourceDayHeader.prototype.processResourceEls = function (resources) {
+            var _this = this;
+            var view = this.view;
+            core.findElements(this.thead, '.fc-resource-cell').forEach(function (node, col) {
+                col = col % resources.length;
+                if (_this.isRtl) {
+                    col = resources.length - 1 - col;
+                }
+                var resource = resources[col];
+                view.publiclyTrigger('resourceRender', [
+                    {
+                        resource: new ResourceApi(_this.calendar, resource),
+                        el: node,
+                        view: view
+                    }
+                ]);
+            });
+        };
+        return ResourceDayHeader;
+    }(core.Component));
+
+    var AbstractResourceDayTable = /** @class */ (function () {
+        function AbstractResourceDayTable(dayTable, resources) {
+            this.dayTable = dayTable;
+            this.resources = resources;
+            this.resourceIndex = new ResourceIndex(resources);
+            this.rowCnt = dayTable.rowCnt;
+            this.colCnt = dayTable.colCnt * resources.length;
+            this.cells = this.buildCells();
+        }
+        AbstractResourceDayTable.prototype.buildCells = function () {
+            var _a = this, rowCnt = _a.rowCnt, dayTable = _a.dayTable, resources = _a.resources;
+            var rows = [];
+            for (var row = 0; row < rowCnt; row++) {
+                var rowCells = [];
+                for (var dateCol = 0; dateCol < dayTable.colCnt; dateCol++) {
+                    for (var resourceCol = 0; resourceCol < resources.length; resourceCol++) {
+                        var resource = resources[resourceCol];
+                        var htmlAttrs = 'data-resource-id="' + resource.id + '"';
+                        rowCells[this.computeCol(dateCol, resourceCol)] = {
+                            date: dayTable.cells[row][dateCol].date,
+                            resource: resource,
+                            htmlAttrs: htmlAttrs
+                        };
+                    }
+                }
+                rows.push(rowCells);
+            }
+            return rows;
+        };
+        return AbstractResourceDayTable;
+    }());
+    /*
+    resources over dates
+    */
+    var ResourceDayTable = /** @class */ (function (_super) {
+        __extends(ResourceDayTable, _super);
+        function ResourceDayTable() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        ResourceDayTable.prototype.computeCol = function (dateI, resourceI) {
+            return resourceI * this.dayTable.colCnt + dateI;
+        };
+        /*
+        all date ranges are intact
+        */
+        ResourceDayTable.prototype.computeColRanges = function (dateStartI, dateEndI, resourceI) {
+            return [
+                {
+                    firstCol: this.computeCol(dateStartI, resourceI),
+                    lastCol: this.computeCol(dateEndI, resourceI),
+                    isStart: true,
+                    isEnd: true
+                }
+            ];
+        };
+        return ResourceDayTable;
+    }(AbstractResourceDayTable));
+    /*
+    dates over resources
+    */
+    var DayResourceTable = /** @class */ (function (_super) {
+        __extends(DayResourceTable, _super);
+        function DayResourceTable() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        DayResourceTable.prototype.computeCol = function (dateI, resourceI) {
+            return dateI * this.resources.length + resourceI;
+        };
+        /*
+        every single day is broken up
+        */
+        DayResourceTable.prototype.computeColRanges = function (dateStartI, dateEndI, resourceI) {
+            var segs = [];
+            for (var i = dateStartI; i <= dateEndI; i++) {
+                var col = this.computeCol(i, resourceI);
+                segs.push({
+                    firstCol: col,
+                    lastCol: col,
+                    isStart: i === dateStartI,
+                    isEnd: i === dateEndI
+                });
+            }
+            return segs;
+        };
+        return DayResourceTable;
+    }(AbstractResourceDayTable));
+    var ResourceIndex = /** @class */ (function () {
+        function ResourceIndex(resources) {
+            var indicesById = {};
+            var ids = [];
+            for (var i = 0; i < resources.length; i++) {
+                var id = resources[i].id;
+                ids.push(id);
+                indicesById[id] = i;
+            }
+            this.ids = ids;
+            this.indicesById = indicesById;
+            this.length = resources.length;
+        }
+        return ResourceIndex;
+    }());
+    /*
+    TODO: just use ResourceHash somehow? could then use the generic ResourceSplitter
+    */
+    var VResourceSplitter = /** @class */ (function (_super) {
+        __extends(VResourceSplitter, _super);
+        function VResourceSplitter() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        VResourceSplitter.prototype.getKeyInfo = function (props) {
+            var resourceDayTable = props.resourceDayTable;
+            var hash = core.mapHash(resourceDayTable.resourceIndex.indicesById, function (i) {
+                return resourceDayTable.resources[i]; // has `ui` AND `businessHours` keys!
+            }); // :(
+            hash[''] = {};
+            return hash;
+        };
+        VResourceSplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+            return [dateSpan.resourceId || ''];
+        };
+        VResourceSplitter.prototype.getKeysForEventDef = function (eventDef) {
+            var resourceIds = eventDef.resourceIds;
+            if (!resourceIds.length) {
+                return [''];
+            }
+            return resourceIds;
+        };
+        return VResourceSplitter;
+    }(core.Splitter));
+    // joiner
+    var NO_SEGS = []; // for memoizing
+    var VResourceJoiner = /** @class */ (function () {
+        function VResourceJoiner() {
+            this.joinDateSelection = core.memoize(this.joinSegs);
+            this.joinBusinessHours = core.memoize(this.joinSegs);
+            this.joinFgEvents = core.memoize(this.joinSegs);
+            this.joinBgEvents = core.memoize(this.joinSegs);
+            this.joinEventDrags = core.memoize(this.joinInteractions);
+            this.joinEventResizes = core.memoize(this.joinInteractions);
+        }
+        /*
+        propSets also has a '' key for things with no resource
+        */
+        VResourceJoiner.prototype.joinProps = function (propSets, resourceDayTable) {
+            var dateSelectionSets = [];
+            var businessHoursSets = [];
+            var fgEventSets = [];
+            var bgEventSets = [];
+            var eventDrags = [];
+            var eventResizes = [];
+            var eventSelection = '';
+            var keys = resourceDayTable.resourceIndex.ids.concat(['']); // add in the all-resource key
+            for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
+                var key = keys_1[_i];
+                var props = propSets[key];
+                dateSelectionSets.push(props.dateSelectionSegs);
+                businessHoursSets.push(key ? props.businessHourSegs : NO_SEGS); // don't include redundant all-resource businesshours
+                fgEventSets.push(key ? props.fgEventSegs : NO_SEGS); // don't include fg all-resource segs
+                bgEventSets.push(props.bgEventSegs);
+                eventDrags.push(props.eventDrag);
+                eventResizes.push(props.eventResize);
+                eventSelection = eventSelection || props.eventSelection;
+            }
+            return {
+                dateSelectionSegs: this.joinDateSelection.apply(this, [resourceDayTable].concat(dateSelectionSets)),
+                businessHourSegs: this.joinBusinessHours.apply(this, [resourceDayTable].concat(businessHoursSets)),
+                fgEventSegs: this.joinFgEvents.apply(this, [resourceDayTable].concat(fgEventSets)),
+                bgEventSegs: this.joinBgEvents.apply(this, [resourceDayTable].concat(bgEventSets)),
+                eventDrag: this.joinEventDrags.apply(this, [resourceDayTable].concat(eventDrags)),
+                eventResize: this.joinEventResizes.apply(this, [resourceDayTable].concat(eventResizes)),
+                eventSelection: eventSelection
+            };
+        };
+        VResourceJoiner.prototype.joinSegs = function (resourceDayTable) {
+            var segGroups = [];
+            for (var _i = 1; _i < arguments.length; _i++) {
+                segGroups[_i - 1] = arguments[_i];
+            }
+            var resourceCnt = resourceDayTable.resources.length;
+            var transformedSegs = [];
+            for (var i = 0; i < resourceCnt; i++) {
+                for (var _a = 0, _b = segGroups[i]; _a < _b.length; _a++) {
+                    var seg = _b[_a];
+                    transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i));
+                }
+                for (var _c = 0, _d = segGroups[resourceCnt]; _c < _d.length; _c++) { // one beyond. the all-resource
+                    var seg = _d[_c];
+                    transformedSegs.push.apply(// one beyond. the all-resource
+                    transformedSegs, this.transformSeg(seg, resourceDayTable, i));
+                }
+            }
+            return transformedSegs;
+        };
+        /*
+        for expanding non-resource segs to all resources.
+        only for public use.
+        no memoizing.
+        */
+        VResourceJoiner.prototype.expandSegs = function (resourceDayTable, segs) {
+            var resourceCnt = resourceDayTable.resources.length;
+            var transformedSegs = [];
+            for (var i = 0; i < resourceCnt; i++) {
+                for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                    var seg = segs_1[_i];
+                    transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i));
+                }
+            }
+            return transformedSegs;
+        };
+        VResourceJoiner.prototype.joinInteractions = function (resourceDayTable) {
+            var interactions = [];
+            for (var _i = 1; _i < arguments.length; _i++) {
+                interactions[_i - 1] = arguments[_i];
+            }
+            var resourceCnt = resourceDayTable.resources.length;
+            var affectedInstances = {};
+            var transformedSegs = [];
+            var isEvent = false;
+            var sourceSeg = null;
+            for (var i = 0; i < resourceCnt; i++) {
+                var interaction = interactions[i];
+                if (interaction) {
+                    for (var _a = 0, _b = interaction.segs; _a < _b.length; _a++) {
+                        var seg = _b[_a];
+                        transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i) // TODO: templateify Interaction::segs
+                        );
+                    }
+                    __assign(affectedInstances, interaction.affectedInstances);
+                    isEvent = isEvent || interaction.isEvent;
+                    sourceSeg = sourceSeg || interaction.sourceSeg;
+                }
+                if (interactions[resourceCnt]) { // one beyond. the all-resource
+                    for (var _c = 0, _d = interactions[resourceCnt].segs; _c < _d.length; _c++) {
+                        var seg = _d[_c];
+                        transformedSegs.push.apply(transformedSegs, this.transformSeg(seg, resourceDayTable, i) // TODO: templateify Interaction::segs
+                        );
+                    }
+                }
+            }
+            return {
+                affectedInstances: affectedInstances,
+                segs: transformedSegs,
+                isEvent: isEvent,
+                sourceSeg: sourceSeg
+            };
+        };
+        return VResourceJoiner;
+    }());
+
+    /*
+    doesn't accept grouping
+    */
+    function flattenResources(resourceStore, orderSpecs) {
+        return buildRowNodes(resourceStore, [], orderSpecs, false, {}, true)
+            .map(function (node) {
+            return node.resource;
+        });
+    }
+    function buildRowNodes(resourceStore, groupSpecs, orderSpecs, isVGrouping, expansions, expansionDefault) {
+        var complexNodes = buildHierarchy(resourceStore, isVGrouping ? -1 : 1, groupSpecs, orderSpecs);
+        var flatNodes = [];
+        flattenNodes(complexNodes, flatNodes, isVGrouping, [], 0, expansions, expansionDefault);
+        return flatNodes;
+    }
+    function flattenNodes(complexNodes, res, isVGrouping, rowSpans, depth, expansions, expansionDefault) {
+        for (var i = 0; i < complexNodes.length; i++) {
+            var complexNode = complexNodes[i];
+            var group = complexNode.group;
+            if (group) {
+                if (isVGrouping) {
+                    var firstRowIndex = res.length;
+                    var rowSpanIndex = rowSpans.length;
+                    flattenNodes(complexNode.children, res, isVGrouping, rowSpans.concat(0), depth, expansions, expansionDefault);
+                    if (firstRowIndex < res.length) {
+                        var firstRow = res[firstRowIndex];
+                        var firstRowSpans = firstRow.rowSpans = firstRow.rowSpans.slice();
+                        firstRowSpans[rowSpanIndex] = res.length - firstRowIndex;
+                    }
+                }
+                else {
+                    var id = group.spec.field + ':' + group.value;
+                    var isExpanded = expansions[id] != null ? expansions[id] : expansionDefault;
+                    res.push({ id: id, group: group, isExpanded: isExpanded });
+                    if (isExpanded) {
+                        flattenNodes(complexNode.children, res, isVGrouping, rowSpans, depth + 1, expansions, expansionDefault);
+                    }
+                }
+            }
+            else if (complexNode.resource) {
+                var id = complexNode.resource.id;
+                var isExpanded = expansions[id] != null ? expansions[id] : expansionDefault;
+                res.push({
+                    id: id,
+                    rowSpans: rowSpans,
+                    depth: depth,
+                    isExpanded: isExpanded,
+                    hasChildren: Boolean(complexNode.children.length),
+                    resource: complexNode.resource,
+                    resourceFields: complexNode.resourceFields
+                });
+                if (isExpanded) {
+                    flattenNodes(complexNode.children, res, isVGrouping, rowSpans, depth + 1, expansions, expansionDefault);
+                }
+            }
+        }
+    }
+    function buildHierarchy(resourceStore, maxDepth, groupSpecs, orderSpecs) {
+        var resourceNodes = buildResourceNodes(resourceStore, orderSpecs);
+        var builtNodes = [];
+        for (var resourceId in resourceNodes) {
+            var resourceNode = resourceNodes[resourceId];
+            if (!resourceNode.resource.parentId) {
+                insertResourceNode(resourceNode, builtNodes, groupSpecs, 0, maxDepth, orderSpecs);
+            }
+        }
+        return builtNodes;
+    }
+    function buildResourceNodes(resourceStore, orderSpecs) {
+        var nodeHash = {};
+        for (var resourceId in resourceStore) {
+            var resource = resourceStore[resourceId];
+            nodeHash[resourceId] = {
+                resource: resource,
+                resourceFields: buildResourceFields(resource),
+                children: []
+            };
+        }
+        for (var resourceId in resourceStore) {
+            var resource = resourceStore[resourceId];
+            if (resource.parentId) {
+                var parentNode = nodeHash[resource.parentId];
+                if (parentNode) {
+                    insertResourceNodeInSiblings(nodeHash[resourceId], parentNode.children, orderSpecs);
+                }
+            }
+        }
+        return nodeHash;
+    }
+    function insertResourceNode(resourceNode, nodes, groupSpecs, depth, maxDepth, orderSpecs) {
+        if (groupSpecs.length && (maxDepth === -1 || depth <= maxDepth)) {
+            var groupNode = ensureGroupNodes(resourceNode, nodes, groupSpecs[0]);
+            insertResourceNode(resourceNode, groupNode.children, groupSpecs.slice(1), depth + 1, maxDepth, orderSpecs);
+        }
+        else {
+            insertResourceNodeInSiblings(resourceNode, nodes, orderSpecs);
+        }
+    }
+    function ensureGroupNodes(resourceNode, nodes, groupSpec) {
+        var groupValue = resourceNode.resourceFields[groupSpec.field];
+        var groupNode;
+        var newGroupIndex;
+        // find an existing group that matches, or determine the position for a new group
+        if (groupSpec.order) {
+            for (newGroupIndex = 0; newGroupIndex < nodes.length; newGroupIndex++) {
+                var node = nodes[newGroupIndex];
+                if (node.group) {
+                    var cmp = core.flexibleCompare(groupValue, node.group.value) * groupSpec.order;
+                    if (cmp === 0) {
+                        groupNode = node;
+                        break;
+                    }
+                    else if (cmp < 0) {
+                        break;
+                    }
+                }
+            }
+        }
+        else { // the groups are unordered
+            for (newGroupIndex = 0; newGroupIndex < nodes.length; newGroupIndex++) {
+                var node = nodes[newGroupIndex];
+                if (node.group && groupValue === node.group.value) {
+                    groupNode = node;
+                    break;
+                }
+            }
+        }
+        if (!groupNode) {
+            groupNode = {
+                group: {
+                    value: groupValue,
+                    spec: groupSpec
+                },
+                children: []
+            };
+            nodes.splice(newGroupIndex, 0, groupNode);
+        }
+        return groupNode;
+    }
+    function insertResourceNodeInSiblings(resourceNode, siblings, orderSpecs) {
+        var i;
+        for (i = 0; i < siblings.length; i++) {
+            var cmp = core.compareByFieldSpecs(siblings[i].resourceFields, resourceNode.resourceFields, orderSpecs);
+            if (cmp > 0) { // went 1 past. insert at i
+                break;
+            }
+        }
+        siblings.splice(i, 0, resourceNode);
+    }
+    function buildResourceFields(resource) {
+        var obj = __assign({}, resource.extendedProps, resource.ui, resource);
+        delete obj.ui;
+        delete obj.extendedProps;
+        return obj;
+    }
+    function isGroupsEqual(group0, group1) {
+        return group0.spec === group1.spec && group0.value === group1.value;
+    }
+
+    var main = core.createPlugin({
+        reducers: [resourcesReducers],
+        eventDefParsers: [parseEventDef],
+        isDraggableTransformers: [transformIsDraggable],
+        eventDragMutationMassagers: [massageEventDragMutation],
+        eventDefMutationAppliers: [applyEventDefMutation],
+        dateSelectionTransformers: [transformDateSelectionJoin],
+        datePointTransforms: [transformDatePoint],
+        dateSpanTransforms: [transformDateSpan],
+        viewPropsTransformers: [ResourceDataAdder, ResourceEventConfigAdder],
+        isPropsValid: isPropsValidWithResources,
+        externalDefTransforms: [transformExternalDef],
+        eventResizeJoinTransforms: [transformEventResizeJoin],
+        viewContainerModifiers: [injectLicenseWarning],
+        eventDropTransformers: [transformEventDrop],
+        optionChangeHandlers: optionChangeHandlers
+    });
+
+    exports.AbstractResourceDayTable = AbstractResourceDayTable;
+    exports.DayResourceTable = DayResourceTable;
+    exports.ResourceApi = ResourceApi;
+    exports.ResourceDayHeader = ResourceDayHeader;
+    exports.ResourceDayTable = ResourceDayTable;
+    exports.ResourceSplitter = ResourceSplitter;
+    exports.VResourceJoiner = VResourceJoiner;
+    exports.VResourceSplitter = VResourceSplitter;
+    exports.buildResourceFields = buildResourceFields;
+    exports.buildResourceTextFunc = buildResourceTextFunc;
+    exports.buildRowNodes = buildRowNodes;
+    exports.default = main;
+    exports.flattenResources = flattenResources;
+    exports.isGroupsEqual = isGroupsEqual;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/main.min.js b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..a3a3cb8a49d5660cd5e245374f60d2b83e20f6fc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Resources Common Plugin v4.3.1
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],r):r((e=e||self).FullCalendarResourceCommon={},e.FullCalendar)}(this,function(e,r){"use strict";var t=function(e,r){return(t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,r){e.__proto__=r}||function(e,r){for(var t in r)r.hasOwnProperty(t)&&(e[t]=r[t])})(e,r)};function n(e,r){function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}var o=function(){return(o=Object.assign||function(e){for(var r,t=1,n=arguments.length;t<n;t++)for(var o in r=arguments[t])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e}).apply(this,arguments)};function s(e,r){var t=e.resourceEditable;if(null==t){var n=e.sourceId&&r.state.eventSources[e.sourceId];n&&(t=n.extendedProps.resourceEditable),null==t&&null==(t=r.opt("eventResourceEditable"))&&(t=r.opt("editable"))}return t}var u=function(){function e(){this.filterResources=r.memoize(i)}return e.prototype.transform=function(e,r,t,n){if(r.class.needsResourceData)return{resourceStore:this.filterResources(t.resourceStore,n.opt("filterResourcesWithEvents"),t.eventStore,t.dateProfile.activeRange),resourceEntityExpansions:t.resourceEntityExpansions}},e}();function i(e,t,n,s){if(t){var u=function(e,r){var t={};for(var n in e)for(var o=e[n],s=0,u=r[o.defId].resourceIds;s<u.length;s++){var i=u[s];t[i]=!0}return t}(function(e,t){return r.filterHash(e,function(e){return r.rangesIntersect(e.range,t)})}(n.instances,s),n.defs);return o(u,function(e,r){var t={};for(var n in e)for(var o=void 0;(o=r[n])&&(n=o.parentId);)t[n]=!0;return t}(u,e)),r.filterHash(e,function(e,r){return u[r]})}return e}var a=function(){function e(){this.buildResourceEventUis=r.memoizeOutput(c,r.isPropsEqual),this.injectResourceEventUis=r.memoize(l)}return e.prototype.transform=function(e,r,t){if(!r.class.needsResourceData)return{eventUiBases:this.injectResourceEventUis(e.eventUiBases,e.eventStore.defs,this.buildResourceEventUis(t.resourceStore))}},e}();function c(e){return r.mapHash(e,function(e){return e.ui})}function l(e,t,n){return r.mapHash(e,function(e,o){return o?function(e,t,n){for(var o=[],s=0,u=t.resourceIds;s<u.length;s++){var i=u[s];n[i]&&o.unshift(n[i])}return o.unshift(e),r.combineEventUis(o)}(e,t[o],n):e})}var d={id:String},f=[],p=0;function h(e){f.push(e)}function v(e,t,n){var o=r.refineProps(e,d);return o.sourceId=String(p++),o.sourceDefId=n,o.meta=t,o.publicId=o.id,o.isFetching=!1,o.latestFetchId="",o.fetchRange=null,delete o.id,o}function g(e,t,n,s){switch(t.type){case"INIT":return y(s.opt("resources"),s);case"RESET_RESOURCE_SOURCE":return y(t.resourceSourceInput,s,!0);case"PREV":case"NEXT":case"SET_DATE":case"SET_VIEW_TYPE":return function(e,t,n){return!n.opt("refetchResourcesOnNavigate")||function(e){return Boolean(f[e.sourceDefId].ignoreRange)}(e)||e.fetchRange&&r.rangesEqual(e.fetchRange,t)?e:E(e,t,n)}(e,n.activeRange,s);case"RECEIVE_RESOURCES":case"RECEIVE_RESOURCE_ERROR":return function(e,r,t){if(r===e.latestFetchId)return o({},e,{isFetching:!1,fetchRange:t});return e}(e,t.fetchId,t.fetchRange);case"REFETCH_RESOURCES":return E(e,n.activeRange,s);default:return e}}var R=0;function y(e,r,t){if(e){var n=function(e){for(var r=f.length-1;r>=0;r--){var t=f[r].parseMeta(e);if(t){var n=v("object"==typeof e&&e?e:{},t,r);return n._raw=e,n}}return null}(e);return!t&&r.opt("refetchResourcesOnNavigate")||(n=E(n,null,r)),n}return null}function E(e,r,t){var n,s=(n=e.sourceDefId,f[n]),u=String(R++);return s.fetch({resourceSource:e,calendar:t,range:r},function(e){t.afterSizingTriggers._resourcesRendered=[null],t.dispatch({type:"RECEIVE_RESOURCES",fetchId:u,fetchRange:r,rawResources:e.rawResources})},function(e){t.dispatch({type:"RECEIVE_RESOURCE_ERROR",fetchId:u,fetchRange:r,error:e})}),o({},e,{isFetching:!0,latestFetchId:u})}var m={id:String,title:String,parentId:String,businessHours:null,children:null,extendedProps:null},S="_fc:",I=0;function b(e,t,n,s){void 0===t&&(t="");var u={},i=r.refineProps(e,m,{},u),a={},c=r.processScopedUiProps("event",u,s,a);if(i.id||(i.id=S+I++),i.parentId||(i.parentId=t),i.businessHours=i.businessHours?r.parseBusinessHours(i.businessHours,s):null,i.ui=c,i.extendedProps=o({},a,i.extendedProps),Object.freeze(c.classNames),Object.freeze(i.extendedProps),n[i.id]);else if(n[i.id]=i,i.children){for(var l=0,d=i.children;l<d.length;l++){b(d[l],i.id,n,s)}delete i.children}return i}function C(e,t,n,s){switch(t.type){case"INIT":return{};case"RECEIVE_RESOURCES":return function(e,r,t,n,o){if(n.latestFetchId===t){for(var s={},u=0,i=r;u<i.length;u++){var a=i[u];b(a,"",s,o)}return s}return e}(e,t.rawResources,t.fetchId,n,s);case"ADD_RESOURCE":return u=e,i=t.resourceHash,o({},u,i);case"REMOVE_RESOURCE":return function(e,r){var t=o({},e);for(var n in delete t[r],t)t[n].parentId===r&&(t[n]=o({},t[n],{parentId:""}));return t}(e,t.resourceId);case"SET_RESOURCE_PROP":return function(e,r,t,n){var s,u,i=e[r];return i?o({},e,((s={})[r]=o({},i,((u={})[t]=n,u)),s)):e}(e,t.resourceId,t.propName,t.propValue);case"RESET_RESOURCES":return r.mapHash(e,function(e){return o({},e)});default:return e}var u,i}var _={resourceId:String,resourceIds:function(e){return(e||[]).map(function(e){return String(e)})},resourceEditable:Boolean};var w=function(){function e(e,r){this._calendar=e,this._resource=r}return e.prototype.setProp=function(e,r){this._calendar.dispatch({type:"SET_RESOURCE_PROP",resourceId:this._resource.id,propName:e,propValue:r})},e.prototype.remove=function(){this._calendar.dispatch({type:"REMOVE_RESOURCE",resourceId:this._resource.id})},e.prototype.getParent=function(){var r=this._calendar,t=this._resource.parentId;return t?new e(r,r.state.resourceSource[t]):null},e.prototype.getChildren=function(){var r=this._resource.id,t=this._calendar,n=t.state.resourceStore,o=[];for(var s in n)n[s].parentId===r&&o.push(new e(t,n[s]));return o},e.prototype.getEvents=function(){var e=this._resource.id,t=this._calendar,n=t.state.eventStore,o=n.defs,s=n.instances,u=[];for(var i in s){var a=s[i],c=o[a.defId];-1!==c.resourceIds.indexOf(e)&&u.push(new r.EventApi(t,c,a))}return u},Object.defineProperty(e.prototype,"id",{get:function(){return this._resource.id},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"title",{get:function(){return this._resource.title},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventConstraint",{get:function(){return this._resource.ui.constraints[0]||null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventOverlap",{get:function(){return this._resource.ui.overlap},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventAllow",{get:function(){return this._resource.ui.allows[0]||null},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventBackgroundColor",{get:function(){return this._resource.ui.backgroundColor},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventBorderColor",{get:function(){return this._resource.ui.borderColor},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventTextColor",{get:function(){return this._resource.ui.textColor},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"eventClassNames",{get:function(){return this._resource.ui.classNames},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"extendedProps",{get:function(){return this._resource.extendedProps},enumerable:!0,configurable:!0}),e}();r.Calendar.prototype.addResource=function(e,r){var t,n,o;return void 0===r&&(r=!0),e instanceof w?((t={})[(o=e._resource).id]=o,n=t):o=b(e,"",n={},this),r&&this.component.view.addScroll({forcedRowId:o.id}),this.dispatch({type:"ADD_RESOURCE",resourceHash:n}),new w(this,o)},r.Calendar.prototype.getResourceById=function(e){if(e=String(e),this.state.resourceStore){var r=this.state.resourceStore[e];if(r)return new w(this,r)}return null},r.Calendar.prototype.getResources=function(){var e=this.state.resourceStore,r=[];if(e)for(var t in e)r.push(new w(this,e[t]));return r},r.Calendar.prototype.getTopLevelResources=function(){var e=this.state.resourceStore,r=[];if(e)for(var t in e)e[t].parentId||r.push(new w(this,e[t]));return r},r.Calendar.prototype.rerenderResources=function(){this.dispatch({type:"RESET_RESOURCES"})},r.Calendar.prototype.refetchResources=function(){this.dispatch({type:"REFETCH_RESOURCES"})};var P=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.getKeyInfo=function(e){return o({"":{}},e.resourceStore)},r.prototype.getKeysForDateSpan=function(e){return[e.resourceId||""]},r.prototype.getKeysForEventDef=function(e){var r=e.resourceIds;return r.length?r:[""]},r}(r.Splitter);function O(e,r){return o({},r,{constraints:x(e,r.constraints)})}function x(e,r){return r.map(function(r){var t=r.defs;if(t)for(var n in t){var o=t[n].resourceIds;if(o.length&&-1===o.indexOf(e))return!1}return r})}r.EventApi.prototype.getResources=function(){var e=this._calendar;return this._def.resourceIds.map(function(r){return e.getResourceById(r)})},r.EventApi.prototype.setResources=function(e){for(var r=[],t=0,n=e;t<n.length;t++){var o=n[t],s=null;"string"==typeof o?s=o:"number"==typeof o?s=String(o):o instanceof w?s=o.id:console.warn("unknown resource type: "+o),s&&r.push(s)}this.mutate({standardProps:{resourceIds:r}})};var T="2019-08-10",D=372,j="http://fullcalendar.io/scheduler/license/",F=["GPL-My-Project-Is-Open-Source","CC-Attribution-NonCommercial-NoDerivatives"],U={position:"absolute","z-index":99999,bottom:"1px",left:"1px",background:"#eee","border-color":"#ddd","border-style":"solid","border-width":"1px 1px 0 0",padding:"2px 4px","font-size":"12px","border-top-right-radius":"3px"};var A={resources:function(e,r,t){var n=r.state.resourceSource._raw;t(n,e)||r.dispatch({type:"RESET_RESOURCE_SOURCE",resourceSourceInput:e})}};function H(e,r){return"function"==typeof e?function(t){return e(new w(r,t))}:function(e){return e.title||(0===(r=e.id).indexOf(S)?"":r);var r}}h({ignoreRange:!0,parseMeta:function(e){return Array.isArray(e)?e:Array.isArray(e.resources)?e.resources:null},fetch:function(e,r){r({rawResources:e.resourceSource.meta})}}),h({parseMeta:function(e){return"function"==typeof e?e:"function"==typeof e.resources?e.resources:null},fetch:function(e,t,n){var o=e.calendar.dateEnv,s=e.resourceSource.meta,u={};e.range&&(u={start:o.toDate(e.range.start),end:o.toDate(e.range.end),startStr:o.formatIso(e.range.start),endStr:o.formatIso(e.range.end),timeZone:o.timeZone}),r.unpromisify(s.bind(null,u),function(e){t({rawResources:e})},n)}}),h({parseMeta:function(e){if("string"==typeof e)e={url:e};else if(!e||"object"!=typeof e||!e.url)return null;return{url:e.url,method:(e.method||"GET").toUpperCase(),extraParams:e.extraParams}},fetch:function(e,t,n){var s=e.resourceSource.meta,u=function(e,r,t){var n,s,u,i,a=t.dateEnv,c={};r&&(n=t.opt("startParam"),s=t.opt("endParam"),u=t.opt("timeZoneParam"),c[n]=a.formatIso(r.start),c[s]=a.formatIso(r.end),"local"!==a.timeZone&&(c[u]=a.timeZone));i="function"==typeof e.extraParams?e.extraParams():e.extraParams||{};return o(c,i),c}(s,e.range,e.calendar);r.requestJson(s.method,s.url,u,function(e,r){t({rawResources:e,xhr:r})},function(e,r){n({message:e,xhr:r})})}});var B=function(e){function t(t,n){var o=e.call(this,t)||this;return o.datesAboveResources=o.opt("datesAboveResources"),o.resourceTextFunc=H(o.opt("resourceText"),o.calendar),n.innerHTML="",n.appendChild(o.el=r.htmlToElement('<div class="fc-row '+o.theme.getClass("headerRow")+'"><table class="'+o.theme.getClass("tableGrid")+'"><thead></thead></table></div>')),o.thead=o.el.querySelector("thead"),o}return n(t,e),t.prototype.destroy=function(){r.removeElement(this.el)},t.prototype.render=function(e){var t;this.dateFormat=r.createFormatter(this.opt("columnHeaderFormat")||r.computeFallbackHeaderFormat(e.datesRepDistinctDays,e.dates.length)),t=1===e.dates.length?this.renderResourceRow(e.resources):this.datesAboveResources?this.renderDayAndResourceRows(e.dates,e.resources):this.renderResourceAndDayRows(e.resources,e.dates),this.thead.innerHTML=t,this.processResourceEls(e.resources)},t.prototype.renderResourceRow=function(e){var r=this,t=e.map(function(e){return r.renderResourceCell(e,1)});return this.buildTr(t)},t.prototype.renderDayAndResourceRows=function(e,r){for(var t=[],n=[],o=0,s=e;o<s.length;o++){var u=s[o];t.push(this.renderDateCell(u,r.length));for(var i=0,a=r;i<a.length;i++){var c=a[i];n.push(this.renderResourceCell(c,1,u))}}return this.buildTr(t)+this.buildTr(n)},t.prototype.renderResourceAndDayRows=function(e,r){for(var t=[],n=[],o=0,s=e;o<s.length;o++){var u=s[o];t.push(this.renderResourceCell(u,r.length));for(var i=0,a=r;i<a.length;i++){var c=a[i];n.push(this.renderDateCell(c,1,u))}}return this.buildTr(t)+this.buildTr(n)},t.prototype.renderResourceCell=function(e,t,n){var o=this.dateEnv;return'<th class="fc-resource-cell" data-resource-id="'+e.id+'"'+(n?' data-date="'+o.formatIso(n,{omitTime:!0})+'"':"")+(t>1?' colspan="'+t+'"':"")+">"+r.htmlEscape(this.resourceTextFunc(e))+"</th>"},t.prototype.renderDateCell=function(e,t,n){var o=this.props;return r.renderDateCell(e,o.dateProfile,o.datesRepDistinctDays,o.dates.length*o.resources.length,this.dateFormat,this.context,t,n?'data-resource-id="'+n.id+'"':"")},t.prototype.buildTr=function(e){return e.length||(e=["<td>&nbsp;</td>"]),this.props.renderIntroHtml&&(e=[this.props.renderIntroHtml()].concat(e)),this.isRtl&&e.reverse(),"<tr>"+e.join("")+"</tr>"},t.prototype.processResourceEls=function(e){var t=this,n=this.view;r.findElements(this.thead,".fc-resource-cell").forEach(function(r,o){o%=e.length,t.isRtl&&(o=e.length-1-o);var s=e[o];n.publiclyTrigger("resourceRender",[{resource:new w(t.calendar,s),el:r,view:n}])})},t}(r.Component),z=function(){function e(e,r){this.dayTable=e,this.resources=r,this.resourceIndex=new V(r),this.rowCnt=e.rowCnt,this.colCnt=e.colCnt*r.length,this.cells=this.buildCells()}return e.prototype.buildCells=function(){for(var e=this.rowCnt,r=this.dayTable,t=this.resources,n=[],o=0;o<e;o++){for(var s=[],u=0;u<r.colCnt;u++)for(var i=0;i<t.length;i++){var a=t[i],c='data-resource-id="'+a.id+'"';s[this.computeCol(u,i)]={date:r.cells[o][u].date,resource:a,htmlAttrs:c}}n.push(s)}return n},e}(),M=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.computeCol=function(e,r){return r*this.dayTable.colCnt+e},r.prototype.computeColRanges=function(e,r,t){return[{firstCol:this.computeCol(e,t),lastCol:this.computeCol(r,t),isStart:!0,isEnd:!0}]},r}(z),N=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.computeCol=function(e,r){return e*this.resources.length+r},r.prototype.computeColRanges=function(e,r,t){for(var n=[],o=e;o<=r;o++){var s=this.computeCol(o,t);n.push({firstCol:s,lastCol:s,isStart:o===e,isEnd:o===r})}return n},r}(z),V=function(e){for(var r={},t=[],n=0;n<e.length;n++){var o=e[n].id;t.push(o),r[o]=n}this.ids=t,this.indicesById=r,this.length=e.length},k=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.getKeyInfo=function(e){var t=e.resourceDayTable,n=r.mapHash(t.resourceIndex.indicesById,function(e){return t.resources[e]});return n[""]={},n},t.prototype.getKeysForDateSpan=function(e){return[e.resourceId||""]},t.prototype.getKeysForEventDef=function(e){var r=e.resourceIds;return r.length?r:[""]},t}(r.Splitter),K=[],q=function(){function e(){this.joinDateSelection=r.memoize(this.joinSegs),this.joinBusinessHours=r.memoize(this.joinSegs),this.joinFgEvents=r.memoize(this.joinSegs),this.joinBgEvents=r.memoize(this.joinSegs),this.joinEventDrags=r.memoize(this.joinInteractions),this.joinEventResizes=r.memoize(this.joinInteractions)}return e.prototype.joinProps=function(e,r){for(var t=[],n=[],o=[],s=[],u=[],i=[],a="",c=0,l=r.resourceIndex.ids.concat([""]);c<l.length;c++){var d=l[c],f=e[d];t.push(f.dateSelectionSegs),n.push(d?f.businessHourSegs:K),o.push(d?f.fgEventSegs:K),s.push(f.bgEventSegs),u.push(f.eventDrag),i.push(f.eventResize),a=a||f.eventSelection}return{dateSelectionSegs:this.joinDateSelection.apply(this,[r].concat(t)),businessHourSegs:this.joinBusinessHours.apply(this,[r].concat(n)),fgEventSegs:this.joinFgEvents.apply(this,[r].concat(o)),bgEventSegs:this.joinBgEvents.apply(this,[r].concat(s)),eventDrag:this.joinEventDrags.apply(this,[r].concat(u)),eventResize:this.joinEventResizes.apply(this,[r].concat(i)),eventSelection:a}},e.prototype.joinSegs=function(e){for(var r=[],t=1;t<arguments.length;t++)r[t-1]=arguments[t];for(var n=e.resources.length,o=[],s=0;s<n;s++){for(var u=0,i=r[s];u<i.length;u++){var a=i[u];o.push.apply(o,this.transformSeg(a,e,s))}for(var c=0,l=r[n];c<l.length;c++){a=l[c];o.push.apply(o,this.transformSeg(a,e,s))}}return o},e.prototype.expandSegs=function(e,r){for(var t=e.resources.length,n=[],o=0;o<t;o++)for(var s=0,u=r;s<u.length;s++){var i=u[s];n.push.apply(n,this.transformSeg(i,e,o))}return n},e.prototype.joinInteractions=function(e){for(var r=[],t=1;t<arguments.length;t++)r[t-1]=arguments[t];for(var n=e.resources.length,s={},u=[],i=!1,a=null,c=0;c<n;c++){var l=r[c];if(l){for(var d=0,f=l.segs;d<f.length;d++){var p=f[d];u.push.apply(u,this.transformSeg(p,e,c))}o(s,l.affectedInstances),i=i||l.isEvent,a=a||l.sourceSeg}if(r[n])for(var h=0,v=r[n].segs;h<v.length;h++){p=v[h];u.push.apply(u,this.transformSeg(p,e,c))}}return{affectedInstances:s,segs:u,isEvent:i,sourceSeg:a}},e}();function L(e,r,t,n,o,s){var u=[];return function e(r,t,n,o,s,u,i){for(var a=0;a<r.length;a++){var c=r[a],l=c.group;if(l)if(n){var d=t.length,f=o.length;if(e(c.children,t,n,o.concat(0),s,u,i),d<t.length){var p=t[d],h=p.rowSpans=p.rowSpans.slice();h[f]=t.length-d}}else{var v=l.spec.field+":"+l.value,g=null!=u[v]?u[v]:i;t.push({id:v,group:l,isExpanded:g}),g&&e(c.children,t,n,o,s+1,u,i)}else if(c.resource){var v=c.resource.id,g=null!=u[v]?u[v]:i;t.push({id:v,rowSpans:o,depth:s,isExpanded:g,hasChildren:Boolean(c.children.length),resource:c.resource,resourceFields:c.resourceFields}),g&&e(c.children,t,n,o,s+1,u,i)}}}(function(e,r,t,n){var o=function(e,r){var t={};for(var n in e){var o=e[n];t[n]={resource:o,resourceFields:J(o),children:[]}}for(var n in e){var o=e[n];if(o.parentId){var s=t[o.parentId];s&&G(t[n],s.children,r)}}return t}(e,n),s=[];for(var u in o){var i=o[u];i.resource.parentId||Z(i,s,t,0,r,n)}return s}(e,n?-1:1,r,t),u,n,[],0,o,s),u}function Z(e,t,n,o,s,u){n.length&&(-1===s||o<=s)?Z(e,function(e,t,n){var o,s,u=e.resourceFields[n.field];if(n.order)for(s=0;s<t.length;s++){var i=t[s];if(i.group){var a=r.flexibleCompare(u,i.group.value)*n.order;if(0===a){o=i;break}if(a<0)break}}else for(s=0;s<t.length;s++){var i=t[s];if(i.group&&u===i.group.value){o=i;break}}o||(o={group:{value:u,spec:n},children:[]},t.splice(s,0,o));return o}(e,t,n[0]).children,n.slice(1),o+1,s,u):G(e,t,u)}function G(e,t,n){var o;for(o=0;o<t.length;o++){if(r.compareByFieldSpecs(t[o].resourceFields,e.resourceFields,n)>0)break}t.splice(o,0,e)}function J(e){var r=o({},e.extendedProps,e.ui,e);return delete r.ui,delete r.extendedProps,r}var W=r.createPlugin({reducers:[function(e,r,t){var n=g(e.resourceSource,r,e.dateProfile,t),s=C(e.resourceStore,r,n,t),u=function(e,r){var t;switch(r.type){case"INIT":return{};case"SET_RESOURCE_ENTITY_EXPANDED":return o({},e,((t={})[r.id]=r.isExpanded,t));default:return e}}(e.resourceEntityExpansions,r);return o({},e,{resourceSource:n,resourceStore:s,resourceEntityExpansions:u})}],eventDefParsers:[function(e,t,n){var o=r.refineProps(t,_,{},n),s=o.resourceIds;o.resourceId&&s.push(o.resourceId),e.resourceIds=s,e.resourceEditable=o.resourceEditable}],isDraggableTransformers:[function(e,r,t,n){return!(e||!n.viewSpec.class.needsResourceData||!s(r,n.calendar))||e}],eventDragMutationMassagers:[function(e,r,t){var n=r.dateSpan.resourceId,o=t.dateSpan.resourceId;n&&o&&n!==o&&(e.resourceMutation={matchResourceId:n,setResourceId:o})}],eventDefMutationAppliers:[function(e,r,t){var n=r.resourceMutation;if(n&&s(e,t)){var o=e.resourceIds.indexOf(n.matchResourceId);if(-1!==o){var u=e.resourceIds.slice();u.splice(o,1),-1===u.indexOf(n.setResourceId)&&u.push(n.setResourceId),e.resourceIds=u}}}],dateSelectionTransformers:[function(e,r){var t=e.dateSpan.resourceId,n=r.dateSpan.resourceId;if(t&&n)return(!1!==e.component.allowAcrossResources||t===n)&&{resourceId:t}}],datePointTransforms:[function(e,r){return e.resourceId?{resource:r.getResourceById(e.resourceId)}:{}}],dateSpanTransforms:[function(e,r){return e.resourceId?{resource:r.getResourceById(e.resourceId)}:{}}],viewPropsTransformers:[u,a],isPropsValid:function(e,t){var n=(new P).splitProps(o({},e,{resourceStore:t.state.resourceStore}));for(var s in n){var u=n[s];if(s&&n[""]&&(u=o({},u,{eventStore:r.mergeEventStores(n[""].eventStore,u.eventStore),eventUiBases:o({},n[""].eventUiBases,u.eventUiBases)})),!r.isPropsValid(u,t,{resourceId:s},O.bind(null,s)))return!1}return!0},externalDefTransforms:[function(e){return e.resourceId?{resourceId:e.resourceId}:{}}],eventResizeJoinTransforms:[function(e,r){if(!1===e.component.allowAcrossResources&&e.dateSpan.resourceId!==r.dateSpan.resourceId)return!1}],viewContainerModifiers:[function(e,t){var n,o=t.opt("schedulerLicenseKey");n=window.location.href,/\w+\:\/\/fullcalendar\.io\/|\/examples\/[\w-]+\.html$/.test(n)||function(e){if(-1!==F.indexOf(e))return!0;var t=(e||"").match(/^(\d+)\-fcs\-(\d+)$/);if(t&&10===t[1].length){var n=new Date(1e3*parseInt(t[2],10)),o=new Date(r.config.mockSchedulerReleaseDate||T);if(r.isValidDate(o)){var s=r.addDays(o,-D);if(s<n)return!0}}return!1}(o)||r.appendToElement(e,'<div class="fc-license-message" style="'+r.htmlEscape(r.cssToStr(U))+'">Please use a valid license key. <a href="'+j+'">More Info</a></div>')}],eventDropTransformers:[function(e,r){var t=e.resourceMutation;return t?{oldResource:r.getResourceById(t.matchResourceId),newResource:r.getResourceById(t.setResourceId)}:{oldResource:null,newResource:null}}],optionChangeHandlers:A});e.AbstractResourceDayTable=z,e.DayResourceTable=N,e.ResourceApi=w,e.ResourceDayHeader=B,e.ResourceDayTable=M,e.ResourceSplitter=P,e.VResourceJoiner=q,e.VResourceSplitter=k,e.buildResourceFields=J,e.buildResourceTextFunc=H,e.buildRowNodes=L,e.default=W,e.flattenResources=function(e,r){return L(e,[],r,!1,{},!0).map(function(e){return e.resource})},e.isGroupsEqual=function(e,r){return e.spec===r.spec&&e.value===r.value},Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-common/package.json b/AKPlan/static/AKPlan/fullcalendar/resource-common/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..0af6409e12a51f30757f91596e4b8fb6f24a0cad
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-common/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/resource-common",
+  "version": "4.3.1",
+  "title": "FullCalendar Resources Common Plugin",
+  "description": "Offers base support for resources. Required for all resource-related plugins.",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/scheduler",
+  "docs": "https://fullcalendar.io/docs/scheduler",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar-scheduler.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar-scheduler"
+  },
+  "license": "SEE LICENSE IN LICENSE.md",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/LICENSE.md b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..52296639f8117dd1cd8c5a9e629fed87e5b32cfc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/LICENSE.md
@@ -0,0 +1,18 @@
+
+For complete licensing information, visit:
+http://fullcalendar.io/scheduler/license
+
+FullCalendar Scheduler is tri-licensed, meaning you must choose
+one of three licenses to use. Here is a summary of those licenses:
+
+- Commercial License
+  (a paid license, meant for commercial use)
+  http://fullcalendar.io/scheduler/license-details
+
+- Creative Commons Non-Commercial No-Derivatives
+  (meant for trial and non-commercial use)
+  https://creativecommons.org/licenses/by-nc-nd/4.0/
+
+- GPLv3 License
+  (meant for open-source projects)
+  http://www.gnu.org/licenses/gpl-3.0.en.html
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/README.md b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c56092c0a7fbc9633ccaf0ce857410adb5d2952d
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Resource Day Grid Plugin
+
+Displays events in individual columns for days and resources
+
+[View the docs &raquo;](https://fullcalendar.io/docs/resource-daygrid-view)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar-scheduler)
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e92d21b6ce2870f24f063c3589ce71d1d20e1cc3
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.d.ts
@@ -0,0 +1,59 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+//   ../../../../../@fullcalendar/daygrid
+//   ../../../../../@fullcalendar/resource-common
+
+declare module '@fullcalendar/resource-daygrid' {
+    import ResourceDayGridView from '@fullcalendar/resource-daygrid/ResourceDayGridView';
+    export { ResourceDayGridView };
+    export { default as ResourceDayGrid } from '@fullcalendar/resource-daygrid/ResourceDayGrid';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/resource-daygrid/ResourceDayGridView' {
+    import { ComponentContext, ViewSpec, DateProfileGenerator } from '@fullcalendar/core';
+    import { AbstractDayGridView } from '@fullcalendar/daygrid';
+    import { ResourceDayHeader, ResourceViewProps } from '@fullcalendar/resource-common';
+    import ResourceDayGrid from '@fullcalendar/resource-daygrid/ResourceDayGrid';
+    export { ResourceDayGridView as default, ResourceDayGridView };
+    class ResourceDayGridView extends AbstractDayGridView {
+        static needsResourceData: boolean;
+        props: ResourceViewProps;
+        header: ResourceDayHeader;
+        resourceDayGrid: ResourceDayGrid;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: ResourceViewProps): void;
+    }
+}
+
+declare module '@fullcalendar/resource-daygrid/ResourceDayGrid' {
+    import { Hit, DateSpan, DateComponent, DateProfile, EventStore, EventUiHash, EventInteractionState, ComponentContext, Duration } from '@fullcalendar/core';
+    import { DayGrid } from '@fullcalendar/daygrid';
+    import { AbstractResourceDayTable } from '@fullcalendar/resource-common';
+    export interface ResourceDayGridProps {
+        dateProfile: DateProfile | null;
+        resourceDayTable: AbstractResourceDayTable;
+        businessHours: EventStore;
+        eventStore: EventStore;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+        isRigid: boolean;
+        nextDayThreshold: Duration;
+    }
+    export { ResourceDayGrid as default, ResourceDayGrid };
+    class ResourceDayGrid extends DateComponent<ResourceDayGridProps> {
+        dayGrid: DayGrid;
+        constructor(context: ComponentContext, dayGrid: DayGrid);
+        destroy(): void;
+        render(props: ResourceDayGridProps): void;
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number): Hit;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..2c573004b3790752541ad83cfdd2b271f425b6a0
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.esm.js
@@ -0,0 +1,203 @@
+/*!
+FullCalendar Resource Day Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+import { mapHash, DateComponent, memoize, parseFieldSpecs, createPlugin } from '@fullcalendar/core';
+import ResourceCommonPlugin, { VResourceSplitter, VResourceJoiner, flattenResources, ResourceDayHeader, DayResourceTable, ResourceDayTable } from '@fullcalendar/resource-common';
+import DayGridPlugin, { DayGridSlicer, AbstractDayGridView, buildBasicDayTable } from '@fullcalendar/daygrid';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+var ResourceDayGrid = /** @class */ (function (_super) {
+    __extends(ResourceDayGrid, _super);
+    function ResourceDayGrid(context, dayGrid) {
+        var _this = _super.call(this, context, dayGrid.el) || this;
+        _this.splitter = new VResourceSplitter();
+        _this.slicers = {};
+        _this.joiner = new ResourceDayGridJoiner();
+        _this.dayGrid = dayGrid;
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.dayGrid.el
+        });
+        return _this;
+    }
+    ResourceDayGrid.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    ResourceDayGrid.prototype.render = function (props) {
+        var _this = this;
+        var dayGrid = this.dayGrid;
+        var dateProfile = props.dateProfile, resourceDayTable = props.resourceDayTable, nextDayThreshold = props.nextDayThreshold;
+        var splitProps = this.splitter.splitProps(props);
+        this.slicers = mapHash(splitProps, function (split, resourceId) {
+            return _this.slicers[resourceId] || new DayGridSlicer();
+        });
+        var slicedProps = mapHash(this.slicers, function (slicer, resourceId) {
+            return slicer.sliceProps(splitProps[resourceId], dateProfile, nextDayThreshold, dayGrid, resourceDayTable.dayTable);
+        });
+        dayGrid.allowAcrossResources = resourceDayTable.dayTable.colCnt === 1;
+        dayGrid.receiveProps(__assign({}, this.joiner.joinProps(slicedProps, resourceDayTable), { dateProfile: dateProfile, cells: resourceDayTable.cells, isRigid: props.isRigid }));
+    };
+    ResourceDayGrid.prototype.buildPositionCaches = function () {
+        this.dayGrid.buildPositionCaches();
+    };
+    ResourceDayGrid.prototype.queryHit = function (positionLeft, positionTop) {
+        var rawHit = this.dayGrid.positionToHit(positionLeft, positionTop);
+        if (rawHit) {
+            return {
+                component: this.dayGrid,
+                dateSpan: {
+                    range: rawHit.dateSpan.range,
+                    allDay: rawHit.dateSpan.allDay,
+                    resourceId: this.props.resourceDayTable.cells[rawHit.row][rawHit.col].resource.id
+                },
+                dayEl: rawHit.dayEl,
+                rect: {
+                    left: rawHit.relativeRect.left,
+                    right: rawHit.relativeRect.right,
+                    top: rawHit.relativeRect.top,
+                    bottom: rawHit.relativeRect.bottom
+                },
+                layer: 0
+            };
+        }
+    };
+    return ResourceDayGrid;
+}(DateComponent));
+var ResourceDayGridJoiner = /** @class */ (function (_super) {
+    __extends(ResourceDayGridJoiner, _super);
+    function ResourceDayGridJoiner() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    ResourceDayGridJoiner.prototype.transformSeg = function (seg, resourceDayTable, resourceI) {
+        var colRanges = resourceDayTable.computeColRanges(seg.firstCol, seg.lastCol, resourceI);
+        return colRanges.map(function (colRange) {
+            return __assign({}, seg, colRange, { isStart: seg.isStart && colRange.isStart, isEnd: seg.isEnd && colRange.isEnd });
+        });
+    };
+    return ResourceDayGridJoiner;
+}(VResourceJoiner));
+
+var ResourceDayGridView = /** @class */ (function (_super) {
+    __extends(ResourceDayGridView, _super);
+    function ResourceDayGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.flattenResources = memoize(flattenResources);
+        _this.buildResourceDayTable = memoize(buildResourceDayTable);
+        _this.resourceOrderSpecs = parseFieldSpecs(_this.opt('resourceOrder'));
+        if (_this.opt('columnHeader')) {
+            _this.header = new ResourceDayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+        }
+        _this.resourceDayGrid = new ResourceDayGrid(context, _this.dayGrid);
+        return _this;
+    }
+    ResourceDayGridView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        if (this.header) {
+            this.header.destroy();
+        }
+        this.resourceDayGrid.destroy();
+    };
+    ResourceDayGridView.prototype.render = function (props) {
+        _super.prototype.render.call(this, props); // for flags for updateSize
+        var resources = this.flattenResources(props.resourceStore, this.resourceOrderSpecs);
+        var resourceDayTable = this.buildResourceDayTable(this.props.dateProfile, this.dateProfileGenerator, resources, this.opt('datesAboveResources'));
+        if (this.header) {
+            this.header.receiveProps({
+                resources: resources,
+                dates: resourceDayTable.dayTable.headerDates,
+                dateProfile: props.dateProfile,
+                datesRepDistinctDays: true,
+                renderIntroHtml: this.renderHeadIntroHtml
+            });
+        }
+        this.resourceDayGrid.receiveProps({
+            dateProfile: props.dateProfile,
+            resourceDayTable: resourceDayTable,
+            businessHours: props.businessHours,
+            eventStore: props.eventStore,
+            eventUiBases: props.eventUiBases,
+            dateSelection: props.dateSelection,
+            eventSelection: props.eventSelection,
+            eventDrag: props.eventDrag,
+            eventResize: props.eventResize,
+            isRigid: this.hasRigidRows(),
+            nextDayThreshold: this.nextDayThreshold
+        });
+    };
+    ResourceDayGridView.needsResourceData = true; // for ResourceViewProps
+    return ResourceDayGridView;
+}(AbstractDayGridView));
+function buildResourceDayTable(dateProfile, dateProfileGenerator, resources, datesAboveResources) {
+    var dayTable = buildBasicDayTable(dateProfile, dateProfileGenerator);
+    return datesAboveResources ?
+        new DayResourceTable(dayTable, resources) :
+        new ResourceDayTable(dayTable, resources);
+}
+
+var main = createPlugin({
+    deps: [ResourceCommonPlugin, DayGridPlugin],
+    defaultView: 'resourceDayGridDay',
+    views: {
+        resourceDayGrid: ResourceDayGridView,
+        resourceDayGridDay: {
+            type: 'resourceDayGrid',
+            duration: { days: 1 }
+        },
+        resourceDayGridWeek: {
+            type: 'resourceDayGrid',
+            duration: { weeks: 1 }
+        },
+        resourceDayGridMonth: {
+            type: 'resourceDayGrid',
+            duration: { months: 1 },
+            // TODO: wish we didn't have to C&P from dayGrid's file
+            monthMode: true,
+            fixedWeekCount: true
+        }
+    }
+});
+
+export default main;
+export { ResourceDayGrid, ResourceDayGridView };
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.js b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..6b4464110aa4ebd5088d916e97071a4de76e2e6f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.js
@@ -0,0 +1,213 @@
+/*!
+FullCalendar Resource Day Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/resource-common'), require('@fullcalendar/daygrid')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/resource-common', '@fullcalendar/daygrid'], factory) :
+    (global = global || self, factory(global.FullCalendarResourceDayGrid = {}, global.FullCalendar, global.FullCalendarResourceCommon, global.FullCalendarDayGrid));
+}(this, function (exports, core, ResourceCommonPlugin, DayGridPlugin) { 'use strict';
+
+    var ResourceCommonPlugin__default = 'default' in ResourceCommonPlugin ? ResourceCommonPlugin['default'] : ResourceCommonPlugin;
+    var DayGridPlugin__default = 'default' in DayGridPlugin ? DayGridPlugin['default'] : DayGridPlugin;
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    var ResourceDayGrid = /** @class */ (function (_super) {
+        __extends(ResourceDayGrid, _super);
+        function ResourceDayGrid(context, dayGrid) {
+            var _this = _super.call(this, context, dayGrid.el) || this;
+            _this.splitter = new ResourceCommonPlugin.VResourceSplitter();
+            _this.slicers = {};
+            _this.joiner = new ResourceDayGridJoiner();
+            _this.dayGrid = dayGrid;
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.dayGrid.el
+            });
+            return _this;
+        }
+        ResourceDayGrid.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        ResourceDayGrid.prototype.render = function (props) {
+            var _this = this;
+            var dayGrid = this.dayGrid;
+            var dateProfile = props.dateProfile, resourceDayTable = props.resourceDayTable, nextDayThreshold = props.nextDayThreshold;
+            var splitProps = this.splitter.splitProps(props);
+            this.slicers = core.mapHash(splitProps, function (split, resourceId) {
+                return _this.slicers[resourceId] || new DayGridPlugin.DayGridSlicer();
+            });
+            var slicedProps = core.mapHash(this.slicers, function (slicer, resourceId) {
+                return slicer.sliceProps(splitProps[resourceId], dateProfile, nextDayThreshold, dayGrid, resourceDayTable.dayTable);
+            });
+            dayGrid.allowAcrossResources = resourceDayTable.dayTable.colCnt === 1;
+            dayGrid.receiveProps(__assign({}, this.joiner.joinProps(slicedProps, resourceDayTable), { dateProfile: dateProfile, cells: resourceDayTable.cells, isRigid: props.isRigid }));
+        };
+        ResourceDayGrid.prototype.buildPositionCaches = function () {
+            this.dayGrid.buildPositionCaches();
+        };
+        ResourceDayGrid.prototype.queryHit = function (positionLeft, positionTop) {
+            var rawHit = this.dayGrid.positionToHit(positionLeft, positionTop);
+            if (rawHit) {
+                return {
+                    component: this.dayGrid,
+                    dateSpan: {
+                        range: rawHit.dateSpan.range,
+                        allDay: rawHit.dateSpan.allDay,
+                        resourceId: this.props.resourceDayTable.cells[rawHit.row][rawHit.col].resource.id
+                    },
+                    dayEl: rawHit.dayEl,
+                    rect: {
+                        left: rawHit.relativeRect.left,
+                        right: rawHit.relativeRect.right,
+                        top: rawHit.relativeRect.top,
+                        bottom: rawHit.relativeRect.bottom
+                    },
+                    layer: 0
+                };
+            }
+        };
+        return ResourceDayGrid;
+    }(core.DateComponent));
+    var ResourceDayGridJoiner = /** @class */ (function (_super) {
+        __extends(ResourceDayGridJoiner, _super);
+        function ResourceDayGridJoiner() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        ResourceDayGridJoiner.prototype.transformSeg = function (seg, resourceDayTable, resourceI) {
+            var colRanges = resourceDayTable.computeColRanges(seg.firstCol, seg.lastCol, resourceI);
+            return colRanges.map(function (colRange) {
+                return __assign({}, seg, colRange, { isStart: seg.isStart && colRange.isStart, isEnd: seg.isEnd && colRange.isEnd });
+            });
+        };
+        return ResourceDayGridJoiner;
+    }(ResourceCommonPlugin.VResourceJoiner));
+
+    var ResourceDayGridView = /** @class */ (function (_super) {
+        __extends(ResourceDayGridView, _super);
+        function ResourceDayGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.flattenResources = core.memoize(ResourceCommonPlugin.flattenResources);
+            _this.buildResourceDayTable = core.memoize(buildResourceDayTable);
+            _this.resourceOrderSpecs = core.parseFieldSpecs(_this.opt('resourceOrder'));
+            if (_this.opt('columnHeader')) {
+                _this.header = new ResourceCommonPlugin.ResourceDayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+            }
+            _this.resourceDayGrid = new ResourceDayGrid(context, _this.dayGrid);
+            return _this;
+        }
+        ResourceDayGridView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            if (this.header) {
+                this.header.destroy();
+            }
+            this.resourceDayGrid.destroy();
+        };
+        ResourceDayGridView.prototype.render = function (props) {
+            _super.prototype.render.call(this, props); // for flags for updateSize
+            var resources = this.flattenResources(props.resourceStore, this.resourceOrderSpecs);
+            var resourceDayTable = this.buildResourceDayTable(this.props.dateProfile, this.dateProfileGenerator, resources, this.opt('datesAboveResources'));
+            if (this.header) {
+                this.header.receiveProps({
+                    resources: resources,
+                    dates: resourceDayTable.dayTable.headerDates,
+                    dateProfile: props.dateProfile,
+                    datesRepDistinctDays: true,
+                    renderIntroHtml: this.renderHeadIntroHtml
+                });
+            }
+            this.resourceDayGrid.receiveProps({
+                dateProfile: props.dateProfile,
+                resourceDayTable: resourceDayTable,
+                businessHours: props.businessHours,
+                eventStore: props.eventStore,
+                eventUiBases: props.eventUiBases,
+                dateSelection: props.dateSelection,
+                eventSelection: props.eventSelection,
+                eventDrag: props.eventDrag,
+                eventResize: props.eventResize,
+                isRigid: this.hasRigidRows(),
+                nextDayThreshold: this.nextDayThreshold
+            });
+        };
+        ResourceDayGridView.needsResourceData = true; // for ResourceViewProps
+        return ResourceDayGridView;
+    }(DayGridPlugin.AbstractDayGridView));
+    function buildResourceDayTable(dateProfile, dateProfileGenerator, resources, datesAboveResources) {
+        var dayTable = DayGridPlugin.buildBasicDayTable(dateProfile, dateProfileGenerator);
+        return datesAboveResources ?
+            new ResourceCommonPlugin.DayResourceTable(dayTable, resources) :
+            new ResourceCommonPlugin.ResourceDayTable(dayTable, resources);
+    }
+
+    var main = core.createPlugin({
+        deps: [ResourceCommonPlugin__default, DayGridPlugin__default],
+        defaultView: 'resourceDayGridDay',
+        views: {
+            resourceDayGrid: ResourceDayGridView,
+            resourceDayGridDay: {
+                type: 'resourceDayGrid',
+                duration: { days: 1 }
+            },
+            resourceDayGridWeek: {
+                type: 'resourceDayGrid',
+                duration: { weeks: 1 }
+            },
+            resourceDayGridMonth: {
+                type: 'resourceDayGrid',
+                duration: { months: 1 },
+                // TODO: wish we didn't have to C&P from dayGrid's file
+                monthMode: true,
+                fixedWeekCount: true
+            }
+        }
+    });
+
+    exports.ResourceDayGrid = ResourceDayGrid;
+    exports.ResourceDayGridView = ResourceDayGridView;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.min.js b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..cfc82d0c9ab4ab023b2755ab30d4cb6fd9891c1a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Resource Day Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@fullcalendar/core"),require("@fullcalendar/resource-common"),require("@fullcalendar/daygrid")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core","@fullcalendar/resource-common","@fullcalendar/daygrid"],r):r((e=e||self).FullCalendarResourceDayGrid={},e.FullCalendar,e.FullCalendarResourceCommon,e.FullCalendarDayGrid)}(this,function(e,r,t,o){"use strict";var i="default"in t?t.default:t,a="default"in o?o.default:o,s=function(e,r){return(s=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,r){e.__proto__=r}||function(e,r){for(var t in r)r.hasOwnProperty(t)&&(e[t]=r[t])})(e,r)};function n(e,r){function t(){this.constructor=e}s(e,r),e.prototype=null===r?Object.create(r):(t.prototype=r.prototype,new t)}var l=function(){return(l=Object.assign||function(e){for(var r,t=1,o=arguments.length;t<o;t++)for(var i in r=arguments[t])Object.prototype.hasOwnProperty.call(r,i)&&(e[i]=r[i]);return e}).apply(this,arguments)},c=function(e){function i(r,o){var i=e.call(this,r,o.el)||this;return i.splitter=new t.VResourceSplitter,i.slicers={},i.joiner=new d,i.dayGrid=o,r.calendar.registerInteractiveComponent(i,{el:i.dayGrid.el}),i}return n(i,e),i.prototype.destroy=function(){e.prototype.destroy.call(this),this.calendar.unregisterInteractiveComponent(this)},i.prototype.render=function(e){var t=this,i=this.dayGrid,a=e.dateProfile,s=e.resourceDayTable,n=e.nextDayThreshold,c=this.splitter.splitProps(e);this.slicers=r.mapHash(c,function(e,r){return t.slicers[r]||new o.DayGridSlicer});var d=r.mapHash(this.slicers,function(e,r){return e.sliceProps(c[r],a,n,i,s.dayTable)});i.allowAcrossResources=1===s.dayTable.colCnt,i.receiveProps(l({},this.joiner.joinProps(d,s),{dateProfile:a,cells:s.cells,isRigid:e.isRigid}))},i.prototype.buildPositionCaches=function(){this.dayGrid.buildPositionCaches()},i.prototype.queryHit=function(e,r){var t=this.dayGrid.positionToHit(e,r);if(t)return{component:this.dayGrid,dateSpan:{range:t.dateSpan.range,allDay:t.dateSpan.allDay,resourceId:this.props.resourceDayTable.cells[t.row][t.col].resource.id},dayEl:t.dayEl,rect:{left:t.relativeRect.left,right:t.relativeRect.right,top:t.relativeRect.top,bottom:t.relativeRect.bottom},layer:0}},i}(r.DateComponent),d=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.transformSeg=function(e,r,t){return r.computeColRanges(e.firstCol,e.lastCol,t).map(function(r){return l({},e,r,{isStart:e.isStart&&r.isStart,isEnd:e.isEnd&&r.isEnd})})},r}(t.VResourceJoiner),u=function(e){function o(o,i,a,s){var n=e.call(this,o,i,a,s)||this;return n.flattenResources=r.memoize(t.flattenResources),n.buildResourceDayTable=r.memoize(y),n.resourceOrderSpecs=r.parseFieldSpecs(n.opt("resourceOrder")),n.opt("columnHeader")&&(n.header=new t.ResourceDayHeader(n.context,n.el.querySelector(".fc-head-container"))),n.resourceDayGrid=new c(o,n.dayGrid),n}return n(o,e),o.prototype.destroy=function(){e.prototype.destroy.call(this),this.header&&this.header.destroy(),this.resourceDayGrid.destroy()},o.prototype.render=function(r){e.prototype.render.call(this,r);var t=this.flattenResources(r.resourceStore,this.resourceOrderSpecs),o=this.buildResourceDayTable(this.props.dateProfile,this.dateProfileGenerator,t,this.opt("datesAboveResources"));this.header&&this.header.receiveProps({resources:t,dates:o.dayTable.headerDates,dateProfile:r.dateProfile,datesRepDistinctDays:!0,renderIntroHtml:this.renderHeadIntroHtml}),this.resourceDayGrid.receiveProps({dateProfile:r.dateProfile,resourceDayTable:o,businessHours:r.businessHours,eventStore:r.eventStore,eventUiBases:r.eventUiBases,dateSelection:r.dateSelection,eventSelection:r.eventSelection,eventDrag:r.eventDrag,eventResize:r.eventResize,isRigid:this.hasRigidRows(),nextDayThreshold:this.nextDayThreshold})},o.needsResourceData=!0,o}(o.AbstractDayGridView);function y(e,r,i,a){var s=o.buildBasicDayTable(e,r);return a?new t.DayResourceTable(s,i):new t.ResourceDayTable(s,i)}var p=r.createPlugin({deps:[i,a],defaultView:"resourceDayGridDay",views:{resourceDayGrid:u,resourceDayGridDay:{type:"resourceDayGrid",duration:{days:1}},resourceDayGridWeek:{type:"resourceDayGrid",duration:{weeks:1}},resourceDayGridMonth:{type:"resourceDayGrid",duration:{months:1},monthMode:!0,fixedWeekCount:!0}}});e.ResourceDayGrid=c,e.ResourceDayGridView=u,e.default=p,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/package.json b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..4c9676d7a3d1a5b21160109b4f135a07eccef1b8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-daygrid/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "@fullcalendar/resource-daygrid",
+  "version": "4.3.0",
+  "title": "FullCalendar Resource Day Grid Plugin",
+  "description": "Displays events in individual columns for days and resources",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/scheduler",
+  "docs": "https://fullcalendar.io/docs/resource-daygrid-view",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar-scheduler.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar-scheduler"
+  },
+  "license": "SEE LICENSE IN LICENSE.md",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "dependencies": {
+    "@fullcalendar/resource-common": "~4.3.0",
+    "@fullcalendar/daygrid": "~4.3.0"
+  },
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/LICENSE.md b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..52296639f8117dd1cd8c5a9e629fed87e5b32cfc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/LICENSE.md
@@ -0,0 +1,18 @@
+
+For complete licensing information, visit:
+http://fullcalendar.io/scheduler/license
+
+FullCalendar Scheduler is tri-licensed, meaning you must choose
+one of three licenses to use. Here is a summary of those licenses:
+
+- Commercial License
+  (a paid license, meant for commercial use)
+  http://fullcalendar.io/scheduler/license-details
+
+- Creative Commons Non-Commercial No-Derivatives
+  (meant for trial and non-commercial use)
+  https://creativecommons.org/licenses/by-nc-nd/4.0/
+
+- GPLv3 License
+  (meant for open-source projects)
+  http://www.gnu.org/licenses/gpl-3.0.en.html
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/README.md b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f011ad540fc2b80784038dec7e50c5b4415953a4
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Resource Time Grid Plugin
+
+Displays events on a vertical resource view with time slots
+
+[View the docs &raquo;](https://fullcalendar.io/docs/vertical-resource-view)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar-scheduler)
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db710853cbbeb8d349a242d44ba16520181ff14c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.d.ts
@@ -0,0 +1,62 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+//   ../../../../../@fullcalendar/timegrid
+//   ../../../../../@fullcalendar/resource-common
+//   ../../../../../@fullcalendar/resource-daygrid
+
+declare module '@fullcalendar/resource-timegrid' {
+    import ResourceTimeGridView from '@fullcalendar/resource-timegrid/ResourceTimeGridView';
+    export { ResourceTimeGridView };
+    export { default as ResourceTimeGrid } from '@fullcalendar/resource-timegrid/ResourceTimeGrid';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/resource-timegrid/ResourceTimeGridView' {
+    import { ComponentContext, ViewSpec, DateProfileGenerator } from '@fullcalendar/core';
+    import { AbstractTimeGridView } from '@fullcalendar/timegrid';
+    import { ResourceDayHeader, ResourceViewProps } from '@fullcalendar/resource-common';
+    import { ResourceDayGrid } from '@fullcalendar/resource-daygrid';
+    import ResourceTimeGrid from '@fullcalendar/resource-timegrid/ResourceTimeGrid';
+    export { ResourceTimeGridView as default, ResourceTimeGridView };
+    class ResourceTimeGridView extends AbstractTimeGridView {
+        static needsResourceData: boolean;
+        props: ResourceViewProps;
+        header: ResourceDayHeader;
+        resourceTimeGrid: ResourceTimeGrid;
+        resourceDayGrid: ResourceDayGrid;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: ResourceViewProps): void;
+        renderNowIndicator(date: any): void;
+    }
+}
+
+declare module '@fullcalendar/resource-timegrid/ResourceTimeGrid' {
+    import { DateSpan, DateComponent, DateProfile, EventStore, EventUiHash, EventInteractionState, ComponentContext, DateMarker, Hit } from '@fullcalendar/core';
+    import { TimeGrid } from '@fullcalendar/timegrid';
+    import { AbstractResourceDayTable } from '@fullcalendar/resource-common';
+    export interface ResourceTimeGridProps {
+        dateProfile: DateProfile | null;
+        resourceDayTable: AbstractResourceDayTable;
+        businessHours: EventStore;
+        eventStore: EventStore;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+    }
+    export { ResourceTimeGrid as default, ResourceTimeGrid };
+    class ResourceTimeGrid extends DateComponent<ResourceTimeGridProps> {
+        timeGrid: TimeGrid;
+        constructor(context: ComponentContext, timeGrid: TimeGrid);
+        destroy(): void;
+        render(props: ResourceTimeGridProps): void;
+        renderNowIndicator(date: DateMarker): void;
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number): Hit;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..0e82575c2927031b02b31c4b7ced8cc55864153c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.esm.js
@@ -0,0 +1,211 @@
+/*!
+FullCalendar Resource Time Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+import { memoize, mapHash, DateComponent, parseFieldSpecs, createPlugin } from '@fullcalendar/core';
+import ResourceCommonPlugin, { VResourceSplitter, VResourceJoiner, flattenResources, ResourceDayHeader, DayResourceTable, ResourceDayTable } from '@fullcalendar/resource-common';
+import TimeGridPlugin, { buildDayRanges, TimeGridSlicer, AbstractTimeGridView, buildDayTable } from '@fullcalendar/timegrid';
+import { ResourceDayGrid } from '@fullcalendar/resource-daygrid';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+var ResourceTimeGrid = /** @class */ (function (_super) {
+    __extends(ResourceTimeGrid, _super);
+    function ResourceTimeGrid(context, timeGrid) {
+        var _this = _super.call(this, context, timeGrid.el) || this;
+        _this.buildDayRanges = memoize(buildDayRanges);
+        _this.splitter = new VResourceSplitter();
+        _this.slicers = {};
+        _this.joiner = new ResourceTimeGridJoiner();
+        _this.timeGrid = timeGrid;
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.timeGrid.el
+        });
+        return _this;
+    }
+    ResourceTimeGrid.prototype.destroy = function () {
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    ResourceTimeGrid.prototype.render = function (props) {
+        var _this = this;
+        var timeGrid = this.timeGrid;
+        var dateProfile = props.dateProfile, resourceDayTable = props.resourceDayTable;
+        var dayRanges = this.dayRanges = this.buildDayRanges(resourceDayTable.dayTable, dateProfile, this.dateEnv);
+        var splitProps = this.splitter.splitProps(props);
+        this.slicers = mapHash(splitProps, function (split, resourceId) {
+            return _this.slicers[resourceId] || new TimeGridSlicer();
+        });
+        var slicedProps = mapHash(this.slicers, function (slicer, resourceId) {
+            return slicer.sliceProps(splitProps[resourceId], dateProfile, null, timeGrid, dayRanges);
+        });
+        timeGrid.allowAcrossResources = dayRanges.length === 1;
+        timeGrid.receiveProps(__assign({}, this.joiner.joinProps(slicedProps, resourceDayTable), { dateProfile: dateProfile, cells: resourceDayTable.cells[0] }));
+    };
+    ResourceTimeGrid.prototype.renderNowIndicator = function (date) {
+        var timeGrid = this.timeGrid;
+        var resourceDayTable = this.props.resourceDayTable;
+        var nonResourceSegs = this.slicers[''].sliceNowDate(date, timeGrid, this.dayRanges);
+        var segs = this.joiner.expandSegs(resourceDayTable, nonResourceSegs);
+        timeGrid.renderNowIndicator(segs, date);
+    };
+    ResourceTimeGrid.prototype.buildPositionCaches = function () {
+        this.timeGrid.buildPositionCaches();
+    };
+    ResourceTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
+        var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
+        if (rawHit) {
+            return {
+                component: this.timeGrid,
+                dateSpan: {
+                    range: rawHit.dateSpan.range,
+                    allDay: rawHit.dateSpan.allDay,
+                    resourceId: this.props.resourceDayTable.cells[0][rawHit.col].resource.id
+                },
+                dayEl: rawHit.dayEl,
+                rect: {
+                    left: rawHit.relativeRect.left,
+                    right: rawHit.relativeRect.right,
+                    top: rawHit.relativeRect.top,
+                    bottom: rawHit.relativeRect.bottom
+                },
+                layer: 0
+            };
+        }
+    };
+    return ResourceTimeGrid;
+}(DateComponent));
+var ResourceTimeGridJoiner = /** @class */ (function (_super) {
+    __extends(ResourceTimeGridJoiner, _super);
+    function ResourceTimeGridJoiner() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    ResourceTimeGridJoiner.prototype.transformSeg = function (seg, resourceDayTable, resourceI) {
+        return [
+            __assign({}, seg, { col: resourceDayTable.computeCol(seg.col, resourceI) })
+        ];
+    };
+    return ResourceTimeGridJoiner;
+}(VResourceJoiner));
+
+var ResourceTimeGridView = /** @class */ (function (_super) {
+    __extends(ResourceTimeGridView, _super);
+    function ResourceTimeGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.flattenResources = memoize(flattenResources);
+        _this.buildResourceDayTable = memoize(buildResourceDayTable);
+        _this.resourceOrderSpecs = parseFieldSpecs(_this.opt('resourceOrder'));
+        if (_this.opt('columnHeader')) {
+            _this.header = new ResourceDayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+        }
+        _this.resourceTimeGrid = new ResourceTimeGrid(context, _this.timeGrid);
+        if (_this.dayGrid) {
+            _this.resourceDayGrid = new ResourceDayGrid(context, _this.dayGrid);
+        }
+        return _this;
+    }
+    ResourceTimeGridView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        if (this.header) {
+            this.header.destroy();
+        }
+        this.resourceTimeGrid.destroy();
+        if (this.resourceDayGrid) {
+            this.resourceDayGrid.destroy();
+        }
+    };
+    ResourceTimeGridView.prototype.render = function (props) {
+        _super.prototype.render.call(this, props); // for flags for updateSize
+        var splitProps = this.splitter.splitProps(props);
+        var resources = this.flattenResources(props.resourceStore, this.resourceOrderSpecs);
+        var resourceDayTable = this.buildResourceDayTable(this.props.dateProfile, this.dateProfileGenerator, resources, this.opt('datesAboveResources'));
+        if (this.header) {
+            this.header.receiveProps({
+                resources: resources,
+                dates: resourceDayTable.dayTable.headerDates,
+                dateProfile: props.dateProfile,
+                datesRepDistinctDays: true,
+                renderIntroHtml: this.renderHeadIntroHtml
+            });
+        }
+        this.resourceTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: props.dateProfile, resourceDayTable: resourceDayTable }));
+        if (this.resourceDayGrid) {
+            this.resourceDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: props.dateProfile, resourceDayTable: resourceDayTable, isRigid: false, nextDayThreshold: this.nextDayThreshold }));
+        }
+    };
+    ResourceTimeGridView.prototype.renderNowIndicator = function (date) {
+        this.resourceTimeGrid.renderNowIndicator(date);
+    };
+    ResourceTimeGridView.needsResourceData = true; // for ResourceViewProps
+    return ResourceTimeGridView;
+}(AbstractTimeGridView));
+function buildResourceDayTable(dateProfile, dateProfileGenerator, resources, datesAboveResources) {
+    var dayTable = buildDayTable(dateProfile, dateProfileGenerator);
+    return datesAboveResources ?
+        new DayResourceTable(dayTable, resources) :
+        new ResourceDayTable(dayTable, resources);
+}
+
+var main = createPlugin({
+    deps: [ResourceCommonPlugin, TimeGridPlugin],
+    defaultView: 'resourceTimeGridDay',
+    views: {
+        resourceTimeGrid: {
+            class: ResourceTimeGridView,
+            // TODO: wish we didn't have to C&P from timeGrid's file
+            allDaySlot: true,
+            slotDuration: '00:30:00',
+            slotEventOverlap: true // a bad name. confused with overlap/constraint system
+        },
+        resourceTimeGridDay: {
+            type: 'resourceTimeGrid',
+            duration: { days: 1 }
+        },
+        resourceTimeGridWeek: {
+            type: 'resourceTimeGrid',
+            duration: { weeks: 1 }
+        }
+    }
+});
+
+export default main;
+export { ResourceTimeGrid, ResourceTimeGridView };
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.js b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..c00b37eaa2d5e7c9b034932a4f94d90e56bb7593
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.js
@@ -0,0 +1,220 @@
+/*!
+FullCalendar Resource Time Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/resource-common'), require('@fullcalendar/timegrid'), require('@fullcalendar/resource-daygrid')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/resource-common', '@fullcalendar/timegrid', '@fullcalendar/resource-daygrid'], factory) :
+    (global = global || self, factory(global.FullCalendarResourceTimeGrid = {}, global.FullCalendar, global.FullCalendarResourceCommon, global.FullCalendarTimeGrid, global.FullCalendarResourceDayGrid));
+}(this, function (exports, core, ResourceCommonPlugin, TimeGridPlugin, resourceDaygrid) { 'use strict';
+
+    var ResourceCommonPlugin__default = 'default' in ResourceCommonPlugin ? ResourceCommonPlugin['default'] : ResourceCommonPlugin;
+    var TimeGridPlugin__default = 'default' in TimeGridPlugin ? TimeGridPlugin['default'] : TimeGridPlugin;
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    var ResourceTimeGrid = /** @class */ (function (_super) {
+        __extends(ResourceTimeGrid, _super);
+        function ResourceTimeGrid(context, timeGrid) {
+            var _this = _super.call(this, context, timeGrid.el) || this;
+            _this.buildDayRanges = core.memoize(TimeGridPlugin.buildDayRanges);
+            _this.splitter = new ResourceCommonPlugin.VResourceSplitter();
+            _this.slicers = {};
+            _this.joiner = new ResourceTimeGridJoiner();
+            _this.timeGrid = timeGrid;
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.timeGrid.el
+            });
+            return _this;
+        }
+        ResourceTimeGrid.prototype.destroy = function () {
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        ResourceTimeGrid.prototype.render = function (props) {
+            var _this = this;
+            var timeGrid = this.timeGrid;
+            var dateProfile = props.dateProfile, resourceDayTable = props.resourceDayTable;
+            var dayRanges = this.dayRanges = this.buildDayRanges(resourceDayTable.dayTable, dateProfile, this.dateEnv);
+            var splitProps = this.splitter.splitProps(props);
+            this.slicers = core.mapHash(splitProps, function (split, resourceId) {
+                return _this.slicers[resourceId] || new TimeGridPlugin.TimeGridSlicer();
+            });
+            var slicedProps = core.mapHash(this.slicers, function (slicer, resourceId) {
+                return slicer.sliceProps(splitProps[resourceId], dateProfile, null, timeGrid, dayRanges);
+            });
+            timeGrid.allowAcrossResources = dayRanges.length === 1;
+            timeGrid.receiveProps(__assign({}, this.joiner.joinProps(slicedProps, resourceDayTable), { dateProfile: dateProfile, cells: resourceDayTable.cells[0] }));
+        };
+        ResourceTimeGrid.prototype.renderNowIndicator = function (date) {
+            var timeGrid = this.timeGrid;
+            var resourceDayTable = this.props.resourceDayTable;
+            var nonResourceSegs = this.slicers[''].sliceNowDate(date, timeGrid, this.dayRanges);
+            var segs = this.joiner.expandSegs(resourceDayTable, nonResourceSegs);
+            timeGrid.renderNowIndicator(segs, date);
+        };
+        ResourceTimeGrid.prototype.buildPositionCaches = function () {
+            this.timeGrid.buildPositionCaches();
+        };
+        ResourceTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
+            var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
+            if (rawHit) {
+                return {
+                    component: this.timeGrid,
+                    dateSpan: {
+                        range: rawHit.dateSpan.range,
+                        allDay: rawHit.dateSpan.allDay,
+                        resourceId: this.props.resourceDayTable.cells[0][rawHit.col].resource.id
+                    },
+                    dayEl: rawHit.dayEl,
+                    rect: {
+                        left: rawHit.relativeRect.left,
+                        right: rawHit.relativeRect.right,
+                        top: rawHit.relativeRect.top,
+                        bottom: rawHit.relativeRect.bottom
+                    },
+                    layer: 0
+                };
+            }
+        };
+        return ResourceTimeGrid;
+    }(core.DateComponent));
+    var ResourceTimeGridJoiner = /** @class */ (function (_super) {
+        __extends(ResourceTimeGridJoiner, _super);
+        function ResourceTimeGridJoiner() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        ResourceTimeGridJoiner.prototype.transformSeg = function (seg, resourceDayTable, resourceI) {
+            return [
+                __assign({}, seg, { col: resourceDayTable.computeCol(seg.col, resourceI) })
+            ];
+        };
+        return ResourceTimeGridJoiner;
+    }(ResourceCommonPlugin.VResourceJoiner));
+
+    var ResourceTimeGridView = /** @class */ (function (_super) {
+        __extends(ResourceTimeGridView, _super);
+        function ResourceTimeGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.flattenResources = core.memoize(ResourceCommonPlugin.flattenResources);
+            _this.buildResourceDayTable = core.memoize(buildResourceDayTable);
+            _this.resourceOrderSpecs = core.parseFieldSpecs(_this.opt('resourceOrder'));
+            if (_this.opt('columnHeader')) {
+                _this.header = new ResourceCommonPlugin.ResourceDayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+            }
+            _this.resourceTimeGrid = new ResourceTimeGrid(context, _this.timeGrid);
+            if (_this.dayGrid) {
+                _this.resourceDayGrid = new resourceDaygrid.ResourceDayGrid(context, _this.dayGrid);
+            }
+            return _this;
+        }
+        ResourceTimeGridView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            if (this.header) {
+                this.header.destroy();
+            }
+            this.resourceTimeGrid.destroy();
+            if (this.resourceDayGrid) {
+                this.resourceDayGrid.destroy();
+            }
+        };
+        ResourceTimeGridView.prototype.render = function (props) {
+            _super.prototype.render.call(this, props); // for flags for updateSize
+            var splitProps = this.splitter.splitProps(props);
+            var resources = this.flattenResources(props.resourceStore, this.resourceOrderSpecs);
+            var resourceDayTable = this.buildResourceDayTable(this.props.dateProfile, this.dateProfileGenerator, resources, this.opt('datesAboveResources'));
+            if (this.header) {
+                this.header.receiveProps({
+                    resources: resources,
+                    dates: resourceDayTable.dayTable.headerDates,
+                    dateProfile: props.dateProfile,
+                    datesRepDistinctDays: true,
+                    renderIntroHtml: this.renderHeadIntroHtml
+                });
+            }
+            this.resourceTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: props.dateProfile, resourceDayTable: resourceDayTable }));
+            if (this.resourceDayGrid) {
+                this.resourceDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: props.dateProfile, resourceDayTable: resourceDayTable, isRigid: false, nextDayThreshold: this.nextDayThreshold }));
+            }
+        };
+        ResourceTimeGridView.prototype.renderNowIndicator = function (date) {
+            this.resourceTimeGrid.renderNowIndicator(date);
+        };
+        ResourceTimeGridView.needsResourceData = true; // for ResourceViewProps
+        return ResourceTimeGridView;
+    }(TimeGridPlugin.AbstractTimeGridView));
+    function buildResourceDayTable(dateProfile, dateProfileGenerator, resources, datesAboveResources) {
+        var dayTable = TimeGridPlugin.buildDayTable(dateProfile, dateProfileGenerator);
+        return datesAboveResources ?
+            new ResourceCommonPlugin.DayResourceTable(dayTable, resources) :
+            new ResourceCommonPlugin.ResourceDayTable(dayTable, resources);
+    }
+
+    var main = core.createPlugin({
+        deps: [ResourceCommonPlugin__default, TimeGridPlugin__default],
+        defaultView: 'resourceTimeGridDay',
+        views: {
+            resourceTimeGrid: {
+                class: ResourceTimeGridView,
+                // TODO: wish we didn't have to C&P from timeGrid's file
+                allDaySlot: true,
+                slotDuration: '00:30:00',
+                slotEventOverlap: true // a bad name. confused with overlap/constraint system
+            },
+            resourceTimeGridDay: {
+                type: 'resourceTimeGrid',
+                duration: { days: 1 }
+            },
+            resourceTimeGridWeek: {
+                type: 'resourceTimeGrid',
+                duration: { weeks: 1 }
+            }
+        }
+    });
+
+    exports.ResourceTimeGrid = ResourceTimeGrid;
+    exports.ResourceTimeGridView = ResourceTimeGridView;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.min.js b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..27621fea52b1899919a95c8dcd575199999f1983
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Resource Time Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@fullcalendar/core"),require("@fullcalendar/resource-common"),require("@fullcalendar/timegrid"),require("@fullcalendar/resource-daygrid")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core","@fullcalendar/resource-common","@fullcalendar/timegrid","@fullcalendar/resource-daygrid"],r):r((e=e||self).FullCalendarResourceTimeGrid={},e.FullCalendar,e.FullCalendarResourceCommon,e.FullCalendarTimeGrid,e.FullCalendarResourceDayGrid)}(this,function(e,r,t,i,o){"use strict";var s="default"in t?t.default:t,a="default"in i?i.default:i,n=function(e,r){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,r){e.__proto__=r}||function(e,r){for(var t in r)r.hasOwnProperty(t)&&(e[t]=r[t])})(e,r)};function l(e,r){function t(){this.constructor=e}n(e,r),e.prototype=null===r?Object.create(r):(t.prototype=r.prototype,new t)}var c=function(){return(c=Object.assign||function(e){for(var r,t=1,i=arguments.length;t<i;t++)for(var o in r=arguments[t])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e}).apply(this,arguments)},d=function(e){function o(o,s){var a=e.call(this,o,s.el)||this;return a.buildDayRanges=r.memoize(i.buildDayRanges),a.splitter=new t.VResourceSplitter,a.slicers={},a.joiner=new u,a.timeGrid=s,o.calendar.registerInteractiveComponent(a,{el:a.timeGrid.el}),a}return l(o,e),o.prototype.destroy=function(){this.calendar.unregisterInteractiveComponent(this)},o.prototype.render=function(e){var t=this,o=this.timeGrid,s=e.dateProfile,a=e.resourceDayTable,n=this.dayRanges=this.buildDayRanges(a.dayTable,s,this.dateEnv),l=this.splitter.splitProps(e);this.slicers=r.mapHash(l,function(e,r){return t.slicers[r]||new i.TimeGridSlicer});var d=r.mapHash(this.slicers,function(e,r){return e.sliceProps(l[r],s,null,o,n)});o.allowAcrossResources=1===n.length,o.receiveProps(c({},this.joiner.joinProps(d,a),{dateProfile:s,cells:a.cells[0]}))},o.prototype.renderNowIndicator=function(e){var r=this.timeGrid,t=this.props.resourceDayTable,i=this.slicers[""].sliceNowDate(e,r,this.dayRanges),o=this.joiner.expandSegs(t,i);r.renderNowIndicator(o,e)},o.prototype.buildPositionCaches=function(){this.timeGrid.buildPositionCaches()},o.prototype.queryHit=function(e,r){var t=this.timeGrid.positionToHit(e,r);if(t)return{component:this.timeGrid,dateSpan:{range:t.dateSpan.range,allDay:t.dateSpan.allDay,resourceId:this.props.resourceDayTable.cells[0][t.col].resource.id},dayEl:t.dayEl,rect:{left:t.relativeRect.left,right:t.relativeRect.right,top:t.relativeRect.top,bottom:t.relativeRect.bottom},layer:0}},o}(r.DateComponent),u=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return l(r,e),r.prototype.transformSeg=function(e,r,t){return[c({},e,{col:r.computeCol(e.col,t)})]},r}(t.VResourceJoiner),p=function(e){function i(i,s,a,n){var l=e.call(this,i,s,a,n)||this;return l.flattenResources=r.memoize(t.flattenResources),l.buildResourceDayTable=r.memoize(y),l.resourceOrderSpecs=r.parseFieldSpecs(l.opt("resourceOrder")),l.opt("columnHeader")&&(l.header=new t.ResourceDayHeader(l.context,l.el.querySelector(".fc-head-container"))),l.resourceTimeGrid=new d(i,l.timeGrid),l.dayGrid&&(l.resourceDayGrid=new o.ResourceDayGrid(i,l.dayGrid)),l}return l(i,e),i.prototype.destroy=function(){e.prototype.destroy.call(this),this.header&&this.header.destroy(),this.resourceTimeGrid.destroy(),this.resourceDayGrid&&this.resourceDayGrid.destroy()},i.prototype.render=function(r){e.prototype.render.call(this,r);var t=this.splitter.splitProps(r),i=this.flattenResources(r.resourceStore,this.resourceOrderSpecs),o=this.buildResourceDayTable(this.props.dateProfile,this.dateProfileGenerator,i,this.opt("datesAboveResources"));this.header&&this.header.receiveProps({resources:i,dates:o.dayTable.headerDates,dateProfile:r.dateProfile,datesRepDistinctDays:!0,renderIntroHtml:this.renderHeadIntroHtml}),this.resourceTimeGrid.receiveProps(c({},t.timed,{dateProfile:r.dateProfile,resourceDayTable:o})),this.resourceDayGrid&&this.resourceDayGrid.receiveProps(c({},t.allDay,{dateProfile:r.dateProfile,resourceDayTable:o,isRigid:!1,nextDayThreshold:this.nextDayThreshold}))},i.prototype.renderNowIndicator=function(e){this.resourceTimeGrid.renderNowIndicator(e)},i.needsResourceData=!0,i}(i.AbstractTimeGridView);function y(e,r,o,s){var a=i.buildDayTable(e,r);return s?new t.DayResourceTable(a,o):new t.ResourceDayTable(a,o)}var f=r.createPlugin({deps:[s,a],defaultView:"resourceTimeGridDay",views:{resourceTimeGrid:{class:p,allDaySlot:!0,slotDuration:"00:30:00",slotEventOverlap:!0},resourceTimeGridDay:{type:"resourceTimeGrid",duration:{days:1}},resourceTimeGridWeek:{type:"resourceTimeGrid",duration:{weeks:1}}}});e.ResourceTimeGrid=d,e.ResourceTimeGridView=p,e.default=f,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/package.json b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..d8f7b5249ddaf0fbd71cbad389d778d00c0feaaf
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timegrid/package.json
@@ -0,0 +1,38 @@
+{
+  "name": "@fullcalendar/resource-timegrid",
+  "version": "4.3.0",
+  "title": "FullCalendar Resource Time Grid Plugin",
+  "description": "Displays events on a vertical resource view with time slots",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/scheduler",
+  "docs": "https://fullcalendar.io/docs/vertical-resource-view",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar-scheduler.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar-scheduler"
+  },
+  "license": "SEE LICENSE IN LICENSE.md",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "dependencies": {
+    "@fullcalendar/resource-common": "~4.3.0",
+    "@fullcalendar/timegrid": "~4.3.0",
+    "@fullcalendar/resource-daygrid": "~4.3.0"
+  },
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/LICENSE.md b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..52296639f8117dd1cd8c5a9e629fed87e5b32cfc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/LICENSE.md
@@ -0,0 +1,18 @@
+
+For complete licensing information, visit:
+http://fullcalendar.io/scheduler/license
+
+FullCalendar Scheduler is tri-licensed, meaning you must choose
+one of three licenses to use. Here is a summary of those licenses:
+
+- Commercial License
+  (a paid license, meant for commercial use)
+  http://fullcalendar.io/scheduler/license-details
+
+- Creative Commons Non-Commercial No-Derivatives
+  (meant for trial and non-commercial use)
+  https://creativecommons.org/licenses/by-nc-nd/4.0/
+
+- GPLv3 License
+  (meant for open-source projects)
+  http://www.gnu.org/licenses/gpl-3.0.en.html
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/README.md b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4261c892dee14aa1c4867af07522142944ff38a5
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Resource Timeline Plugin
+
+Display events and resources on a horizontal time axis
+
+[View the docs &raquo;](https://fullcalendar.io/docs/timeline-view)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar-scheduler)
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.css b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..584fe3302f56b899b76231a110e4dad3b55a1d9c
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.css
@@ -0,0 +1,125 @@
+/* Divider between resources and time area
+--------------------------------------------------------------------------------------------------*/
+.fc-timeline .fc-divider {
+  width: 3px;
+  border-style: double;
+  /* overcome neighboring borders */
+}
+
+.fc-timeline .fc-head > tr > .fc-divider {
+  border-bottom: 0;
+}
+
+.fc-timeline .fc-body > tr > .fc-divider {
+  border-top: 0;
+}
+
+/* Resource Area
+--------------------------------------------------------------------------------------------------*/
+.fc-resource-area {
+  width: 30%;
+}
+
+.fc-resource-area col {
+  width: 40%;
+  min-width: 70px;
+  /* will be read by JS */
+}
+
+.fc-resource-area col.fc-main-col {
+  width: 60%;
+  /* make the first column in a nested setup bigger */
+}
+
+.fc-flat .fc-expander-space {
+  /* fc-flat is opposite of fc-nested */
+  display: none;
+}
+
+.fc-ltr .fc-resource-area tr > * {
+  text-align: left;
+}
+
+.fc-rtl .fc-resource-area tr > * {
+  text-align: right;
+}
+
+.fc-resource-area .fc-cell-content {
+  padding-left: 4px;
+  padding-right: 4px;
+}
+
+/* head */
+.fc-resource-area .fc-super th {
+  text-align: center;
+}
+
+.fc-resource-area th > div {
+  position: relative;
+}
+
+.fc-resource-area th .fc-cell-content {
+  position: relative;
+  z-index: 1;
+}
+
+.fc-resource-area th .fc-col-resizer {
+  position: absolute;
+  z-index: 2;
+  top: 0;
+  bottom: 0;
+  width: 5px;
+}
+
+.fc-timeline .fc-col-resizer {
+  cursor: col-resize;
+}
+
+.fc-ltr .fc-resource-area th .fc-col-resizer {
+  right: -3px;
+}
+
+.fc-rtl .fc-resource-area th .fc-col-resizer {
+  left: -3px;
+}
+
+/* body */
+.fc-body .fc-resource-area .fc-cell-content {
+  /* might BE the cell */
+  padding-top: 8px;
+  padding-bottom: 8px;
+}
+
+.fc-no-overlap .fc-body .fc-resource-area .fc-cell-content {
+  /* might BE the cell */
+  padding-top: 6px;
+  padding-bottom: 6px;
+}
+
+.fc-resource-area .fc-icon {
+  /* the expander and spacers before the expander */
+  display: inline-block;
+  width: 1em;
+  /* ensure constant width, esp for empty icons */
+  text-align: center;
+}
+
+.fc-resource-area .fc-expander {
+  cursor: pointer;
+  opacity: 0.65;
+}
+
+/* body resource rows */
+.fc-time-area .fc-rows {
+  position: relative;
+  z-index: 3;
+}
+
+.fc-time-area .fc-rows td > div {
+  position: relative;
+}
+
+.fc-time-area .fc-rows .fc-bgevent-container,
+.fc-time-area .fc-rows .fc-highlight-container {
+  z-index: 1;
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..289f5ece872207817f1fbdec4b8bbf71a38c83f4
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.d.ts
@@ -0,0 +1,237 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+//   ../../../../../@fullcalendar/timeline
+//   ../../../../../@fullcalendar/resource-common
+
+declare module '@fullcalendar/resource-timeline' {
+    import ResourceTimelineView from '@fullcalendar/resource-timeline/ResourceTimelineView';
+    export { ResourceTimelineView };
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/resource-timeline/ResourceTimelineView' {
+    import { ElementDragging, SplittableProps, PositionCache, Hit, View, ViewSpec, ComponentContext, DateProfileGenerator, DateProfile, Duration } from '@fullcalendar/core';
+    import { ScrollJoiner, TimelineLane, StickyScroller, TimeAxis } from '@fullcalendar/timeline';
+    import { GroupNode, ResourceNode, ResourceViewProps } from '@fullcalendar/resource-common';
+    import GroupRow from '@fullcalendar/resource-timeline/GroupRow';
+    import ResourceRow from '@fullcalendar/resource-timeline/ResourceRow';
+    import Spreadsheet from '@fullcalendar/resource-timeline/Spreadsheet';
+    export { ResourceTimelineView as default, ResourceTimelineView };
+    class ResourceTimelineView extends View {
+        static needsResourceData: boolean;
+        props: ResourceViewProps;
+        spreadsheet: Spreadsheet;
+        timeAxis: TimeAxis;
+        lane: TimelineLane;
+        bodyScrollJoiner: ScrollJoiner;
+        spreadsheetBodyStickyScroller: StickyScroller;
+        isStickyScrollDirty: boolean;
+        timeAxisTbody: HTMLElement;
+        miscHeight: number;
+        rowNodes: (GroupNode | ResourceNode)[];
+        rowComponents: (GroupRow | ResourceRow)[];
+        rowComponentsById: {
+            [id: string]: (GroupRow | ResourceRow);
+        };
+        resourceAreaHeadEl: HTMLElement;
+        resourceAreaWidth?: number;
+        resourceAreaWidthDraggings: ElementDragging[];
+        superHeaderText: any;
+        isVGrouping: any;
+        isHGrouping: any;
+        groupSpecs: any;
+        colSpecs: any;
+        orderSpecs: any;
+        rowPositions: PositionCache;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        renderSkeletonHtml(): string;
+        render(props: ResourceViewProps): void;
+        updateHasNesting(isNesting: boolean): void;
+        diffRows(newNodes: any): void;
+        addRow(index: any, rowNode: any): void;
+        removeRows(startIndex: any, len: any, oldRowNodes: any): void;
+        buildChildComponent(node: (GroupNode | ResourceNode), spreadsheetTbody: HTMLElement, spreadsheetNext: HTMLElement, timeAxisTbody: HTMLElement, timeAxisNext: HTMLElement): GroupRow | ResourceRow;
+        renderRows(dateProfile: DateProfile, fallbackBusinessHours: any, splitProps: {
+            [resourceId: string]: SplittableProps;
+        }): void;
+        updateSize(isResize: any, viewHeight: any, isAuto: any): void;
+        syncHeadHeights(): void;
+        updateRowSizes(isResize: boolean): number;
+        destroy(): void;
+        getNowIndicatorUnit(dateProfile: DateProfile): string;
+        renderNowIndicator(date: any): void;
+        unrenderNowIndicator(): void;
+        queryScroll(): any;
+        applyScroll(scroll: any, isResize: any): void;
+        computeDateScroll(duration: Duration): {
+            left: number;
+        };
+        queryDateScroll(): {
+            left: number;
+        };
+        applyDateScroll(scroll: any): void;
+        queryResourceScroll(): any;
+        applyResourceScroll(scroll: any): void;
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number): Hit;
+        setResourceAreaWidth(widthVal: any): void;
+        initResourceAreaWidthDragging(): void;
+    }
+}
+
+declare module '@fullcalendar/resource-timeline/GroupRow' {
+    import { Group } from '@fullcalendar/resource-common';
+    import Row from '@fullcalendar/resource-timeline/Row';
+    export interface GroupRowProps {
+        spreadsheetColCnt: number;
+        id: string;
+        isExpanded: boolean;
+        group: Group;
+    }
+    export { GroupRow as default, GroupRow };
+    class GroupRow extends Row<GroupRowProps> {
+        spreadsheetHeightEl: HTMLElement;
+        timeAxisHeightEl: HTMLElement;
+        expanderIconEl: HTMLElement;
+        render(props: GroupRowProps): void;
+        destroy(): void;
+        renderCells(group: Group, spreadsheetColCnt: number): void;
+        unrenderCells(): void;
+        renderSpreadsheetContent(group: Group): HTMLElement;
+        renderCellText(group: Group): any;
+        getHeightEls(): HTMLElement[];
+        updateExpanderIcon(isExpanded: boolean): void;
+        onExpanderClick: (ev: UIEvent) => void;
+    }
+}
+
+declare module '@fullcalendar/resource-timeline/ResourceRow' {
+    import { Duration, ComponentContext, EventInteractionState, DateSpan, EventUiHash, EventStore, DateProfile } from '@fullcalendar/core';
+    import { TimelineLane, TimeAxis } from '@fullcalendar/timeline';
+    import Row from '@fullcalendar/resource-timeline/Row';
+    import SpreadsheetRow from '@fullcalendar/resource-timeline/SpreadsheetRow';
+    import { Resource } from '@fullcalendar/resource-common';
+    export interface ResourceRowProps {
+        dateProfile: DateProfile;
+        nextDayThreshold: Duration;
+        businessHours: EventStore | null;
+        eventStore: EventStore | null;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+        colSpecs: any;
+        id: string;
+        rowSpans: number[];
+        depth: number;
+        isExpanded: boolean;
+        hasChildren: boolean;
+        resource: Resource;
+    }
+    export { ResourceRow as default, ResourceRow };
+    class ResourceRow extends Row<ResourceRowProps> {
+        innerContainerEl: HTMLElement;
+        spreadsheetRow: SpreadsheetRow;
+        lane: TimelineLane;
+        constructor(context: ComponentContext, a: any, b: any, c: any, d: any, timeAxis: TimeAxis);
+        destroy(): void;
+        render(props: ResourceRowProps): void;
+        updateSize(isResize: boolean): void;
+        getHeightEls(): HTMLElement[];
+    }
+}
+
+declare module '@fullcalendar/resource-timeline/Spreadsheet' {
+    import { Component, ComponentContext } from '@fullcalendar/core';
+    import { HeaderBodyLayout } from '@fullcalendar/timeline';
+    import SpreadsheetHeader from '@fullcalendar/resource-timeline/SpreadsheetHeader';
+    export interface SpreadsheetProps {
+        superHeaderText: string;
+        colSpecs: any;
+    }
+    export { Spreadsheet as default, Spreadsheet };
+    class Spreadsheet extends Component<SpreadsheetProps> {
+        header: SpreadsheetHeader;
+        layout: HeaderBodyLayout;
+        bodyContainerEl: HTMLElement;
+        bodyColGroup: HTMLElement;
+        bodyTbody: HTMLElement;
+        bodyColEls: HTMLElement[];
+        constructor(context: ComponentContext, headParentEl: HTMLElement, bodyParentEl: HTMLElement);
+        destroy(): void;
+        render(props: SpreadsheetProps): void;
+        renderCells(superHeaderText: any, colSpecs: any): void;
+        unrenderCells(): void;
+        renderColTags(colSpecs: any): string;
+        updateSize(isResize: any, totalHeight: any, isAuto: any): void;
+        applyColWidths(colWidths: (number | string)[]): void;
+    }
+}
+
+declare module '@fullcalendar/resource-timeline/Row' {
+    import { Component, ComponentContext } from '@fullcalendar/core';
+    export { Row as default, Row };
+    abstract class Row<PropsType> extends Component<PropsType> {
+        spreadsheetTr: HTMLElement;
+        timeAxisTr: HTMLElement;
+        isSizeDirty: boolean;
+        constructor(context: ComponentContext, spreadsheetParent: HTMLElement, spreadsheetNextSibling: HTMLElement, timeAxisParent: HTMLElement, timeAxisNextSibling: HTMLElement);
+        destroy(): void;
+        abstract getHeightEls(): HTMLElement[];
+        updateSize(isResize: boolean): void;
+    }
+}
+
+declare module '@fullcalendar/resource-timeline/SpreadsheetRow' {
+    import { Component, ComponentContext } from '@fullcalendar/core';
+    import { Resource } from '@fullcalendar/resource-common';
+    export interface SpreadsheetRowProps {
+        colSpecs: any;
+        id: string;
+        rowSpans: number[];
+        depth: number;
+        isExpanded: boolean;
+        hasChildren: boolean;
+        resource: Resource;
+    }
+    export { SpreadsheetRow as default, SpreadsheetRow };
+    class SpreadsheetRow extends Component<SpreadsheetRowProps> {
+        tr: HTMLElement;
+        heightEl: HTMLElement;
+        expanderIconEl: HTMLElement;
+        constructor(context: ComponentContext, tr: HTMLElement);
+        render(props: SpreadsheetRowProps): void;
+        destroy(): void;
+        renderRow(resource: Resource, rowSpans: number[], depth: number, colSpecs: any): void;
+        unrenderRow(): void;
+        updateExpanderIcon(hasChildren: boolean, isExpanded: boolean): void;
+        onExpanderClick: (ev: UIEvent) => void;
+    }
+}
+
+declare module '@fullcalendar/resource-timeline/SpreadsheetHeader' {
+    import { ElementDragging, Component, ComponentContext, EmitterMixin } from '@fullcalendar/core';
+    export interface SpreadsheetHeaderProps {
+        superHeaderText: string;
+        colSpecs: any;
+        colTags: string;
+    }
+    export { SpreadsheetHeader as default, SpreadsheetHeader };
+    class SpreadsheetHeader extends Component<SpreadsheetHeaderProps> {
+        tableEl: HTMLElement;
+        resizerEls: HTMLElement[];
+        resizables: ElementDragging[];
+        thEls: HTMLElement[];
+        colEls: HTMLElement[];
+        colWidths: number[];
+        emitter: EmitterMixin;
+        constructor(context: ComponentContext, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: SpreadsheetHeaderProps): void;
+        initColResizing(): void;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..3ca4c0deb9329997945e300bcdb87b9b68a0d568
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.esm.js
@@ -0,0 +1,1026 @@
+/*!
+FullCalendar Resource Timeline Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+import { removeElement, Component, memoizeRendering, createElement, htmlToElement, htmlEscape, isArraysEqual, EmitterMixin, memoize, parseFieldSpecs, PositionCache, applyStyleProp, View, createPlugin } from '@fullcalendar/core';
+import TimelinePlugin, { TimelineLane, HeaderBodyLayout, TimeAxis, ScrollJoiner, StickyScroller } from '@fullcalendar/timeline';
+import ResourceCommonPlugin, { isGroupsEqual, buildResourceFields, buildResourceTextFunc, ResourceApi, ResourceSplitter, buildRowNodes } from '@fullcalendar/resource-common';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+var Row = /** @class */ (function (_super) {
+    __extends(Row, _super);
+    function Row(context, spreadsheetParent, spreadsheetNextSibling, timeAxisParent, timeAxisNextSibling) {
+        var _this = _super.call(this, context) || this;
+        _this.isSizeDirty = false;
+        spreadsheetParent.insertBefore(_this.spreadsheetTr = document.createElement('tr'), spreadsheetNextSibling);
+        timeAxisParent.insertBefore(_this.timeAxisTr = document.createElement('tr'), timeAxisNextSibling);
+        return _this;
+    }
+    Row.prototype.destroy = function () {
+        removeElement(this.spreadsheetTr);
+        removeElement(this.timeAxisTr);
+        _super.prototype.destroy.call(this);
+    };
+    Row.prototype.updateSize = function (isResize) {
+        this.isSizeDirty = false;
+    };
+    return Row;
+}(Component));
+
+function updateExpanderIcon(el, isExpanded) {
+    var classList = el.classList;
+    if (isExpanded) {
+        classList.remove('fc-icon-plus-square');
+        classList.add('fc-icon-minus-square');
+    }
+    else {
+        classList.remove('fc-icon-minus-square');
+        classList.add('fc-icon-plus-square');
+    }
+}
+function clearExpanderIcon(el) {
+    var classList = el.classList;
+    classList.remove('fc-icon-minus-square');
+    classList.remove('fc-icon-plus-square');
+}
+function updateTrResourceId(tr, resourceId) {
+    tr.setAttribute('data-resource-id', resourceId);
+}
+
+var GroupRow = /** @class */ (function (_super) {
+    __extends(GroupRow, _super);
+    function GroupRow() {
+        var _this = _super !== null && _super.apply(this, arguments) || this;
+        _this._renderCells = memoizeRendering(_this.renderCells, _this.unrenderCells);
+        _this._updateExpanderIcon = memoizeRendering(_this.updateExpanderIcon, null, [_this._renderCells]);
+        _this.onExpanderClick = function (ev) {
+            var props = _this.props;
+            _this.calendar.dispatch({
+                type: 'SET_RESOURCE_ENTITY_EXPANDED',
+                id: props.id,
+                isExpanded: !props.isExpanded
+            });
+        };
+        return _this;
+    }
+    GroupRow.prototype.render = function (props) {
+        this._renderCells(props.group, props.spreadsheetColCnt);
+        this._updateExpanderIcon(props.isExpanded);
+        this.isSizeDirty = true;
+    };
+    GroupRow.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this._renderCells.unrender(); // should unrender everything else
+    };
+    GroupRow.prototype.renderCells = function (group, spreadsheetColCnt) {
+        var spreadsheetContentEl = this.renderSpreadsheetContent(group);
+        this.spreadsheetTr.appendChild(createElement('td', {
+            className: 'fc-divider',
+            colSpan: spreadsheetColCnt // span across all columns
+        }, this.spreadsheetHeightEl = createElement('div', null, spreadsheetContentEl)) // needed by setTrInnerHeight
+        );
+        this.expanderIconEl = spreadsheetContentEl.querySelector('.fc-icon');
+        this.expanderIconEl.parentElement.addEventListener('click', this.onExpanderClick);
+        // insert a single cell, with a single empty <div>.
+        // there will be no content
+        this.timeAxisTr.appendChild(createElement('td', { className: 'fc-divider' }, this.timeAxisHeightEl = document.createElement('div')));
+    };
+    GroupRow.prototype.unrenderCells = function () {
+        this.spreadsheetTr.innerHTML = '';
+        this.timeAxisTr.innerHTML = '';
+    };
+    /*
+    Renders the content wrapper element that will be inserted into this row's TD cell.
+    */
+    GroupRow.prototype.renderSpreadsheetContent = function (group) {
+        var text = this.renderCellText(group);
+        var contentEl = htmlToElement('<div class="fc-cell-content">' +
+            '<span class="fc-expander">' +
+            '<span class="fc-icon"></span>' +
+            '</span>' +
+            '<span class="fc-cell-text">' +
+            (text ? htmlEscape(text) : '&nbsp;') +
+            '</span>' +
+            '</div>');
+        var filter = group.spec.render;
+        if (typeof filter === 'function') {
+            contentEl = filter(contentEl, group.value) || contentEl;
+        }
+        return contentEl;
+    };
+    GroupRow.prototype.renderCellText = function (group) {
+        var text = group.value || ''; // might be null/undefined if an ad-hoc grouping
+        var filter = group.spec.text;
+        if (typeof filter === 'function') {
+            text = filter(text) || text;
+        }
+        return text;
+    };
+    GroupRow.prototype.getHeightEls = function () {
+        return [this.spreadsheetHeightEl, this.timeAxisHeightEl];
+    };
+    GroupRow.prototype.updateExpanderIcon = function (isExpanded) {
+        updateExpanderIcon(this.expanderIconEl, isExpanded);
+    };
+    return GroupRow;
+}(Row));
+GroupRow.addEqualityFuncs({
+    group: isGroupsEqual // HACK for ResourceTimelineView::renderRows
+});
+
+var SpreadsheetRow = /** @class */ (function (_super) {
+    __extends(SpreadsheetRow, _super);
+    function SpreadsheetRow(context, tr) {
+        var _this = _super.call(this, context) || this;
+        _this._renderRow = memoizeRendering(_this.renderRow, _this.unrenderRow);
+        _this._updateTrResourceId = memoizeRendering(updateTrResourceId, null, [_this._renderRow]);
+        _this._updateExpanderIcon = memoizeRendering(_this.updateExpanderIcon, null, [_this._renderRow]);
+        _this.onExpanderClick = function (ev) {
+            var props = _this.props;
+            _this.calendar.dispatch({
+                type: 'SET_RESOURCE_ENTITY_EXPANDED',
+                id: props.id,
+                isExpanded: !props.isExpanded
+            });
+        };
+        _this.tr = tr;
+        return _this;
+    }
+    SpreadsheetRow.prototype.render = function (props) {
+        this._renderRow(props.resource, props.rowSpans, props.depth, props.colSpecs);
+        this._updateTrResourceId(this.tr, props.resource.id); // TODO: only use public ID?
+        this._updateExpanderIcon(props.hasChildren, props.isExpanded);
+    };
+    SpreadsheetRow.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this._renderRow.unrender(); // should unrender everything else
+    };
+    SpreadsheetRow.prototype.renderRow = function (resource, rowSpans, depth, colSpecs) {
+        var _a = this, tr = _a.tr, theme = _a.theme, calendar = _a.calendar, view = _a.view;
+        var resourceFields = buildResourceFields(resource); // slightly inefficient. already done up the call stack
+        var mainTd;
+        for (var i = 0; i < colSpecs.length; i++) {
+            var colSpec = colSpecs[i];
+            var rowSpan = rowSpans[i];
+            if (rowSpan === 0) { // not responsible for group-based rows. VRowGroup is
+                continue;
+            }
+            else if (rowSpan == null) {
+                rowSpan = 1;
+            }
+            var text = void 0;
+            if (colSpec.field) {
+                text = resourceFields[colSpec.field];
+            }
+            else {
+                text = buildResourceTextFunc(colSpec.text, calendar)(resource);
+            }
+            var contentEl = htmlToElement('<div class="fc-cell-content">' +
+                (colSpec.isMain ? renderIconHtml(depth) : '') +
+                '<span class="fc-cell-text">' +
+                (text ? htmlEscape(text) : '&nbsp;') +
+                '</span>' +
+                '</div>');
+            if (typeof colSpec.render === 'function') { // a filter function for the element
+                contentEl = colSpec.render(new ResourceApi(calendar, resource), contentEl) || contentEl;
+            }
+            if (rowSpan > 1) {
+                contentEl.classList.add('fc-sticky');
+            }
+            var td = createElement('td', {
+                className: theme.getClass('widgetContent'),
+                rowspan: rowSpan
+            }, contentEl);
+            // the first cell of the row needs to have an inner div for setTrInnerHeight
+            if (colSpec.isMain) {
+                td.appendChild(this.heightEl = createElement('div', null, td.childNodes) // inner wrap
+                );
+                mainTd = td;
+            }
+            tr.appendChild(td);
+        }
+        this.expanderIconEl = tr.querySelector('.fc-expander-space .fc-icon');
+        // wait until very end
+        view.publiclyTrigger('resourceRender', [
+            {
+                resource: new ResourceApi(calendar, resource),
+                el: mainTd,
+                view: view
+            }
+        ]);
+    };
+    SpreadsheetRow.prototype.unrenderRow = function () {
+        this.tr.innerHTML = '';
+    };
+    SpreadsheetRow.prototype.updateExpanderIcon = function (hasChildren, isExpanded) {
+        var expanderIconEl = this.expanderIconEl;
+        var expanderEl = expanderIconEl.parentElement;
+        if (expanderIconEl &&
+            expanderEl // why would this be null?? was the case in IE11
+        ) {
+            if (hasChildren) {
+                expanderEl.addEventListener('click', this.onExpanderClick);
+                expanderEl.classList.add('fc-expander');
+                updateExpanderIcon(expanderIconEl, isExpanded);
+            }
+            else {
+                expanderEl.removeEventListener('click', this.onExpanderClick);
+                expanderEl.classList.remove('fc-expander');
+                clearExpanderIcon(expanderIconEl);
+            }
+        }
+    };
+    return SpreadsheetRow;
+}(Component));
+/*
+Renders the HTML responsible for the subrow expander area,
+as well as the space before it (used to align expanders of similar depths)
+*/
+function renderIconHtml(depth) {
+    var html = '';
+    for (var i = 0; i < depth; i++) {
+        html += '<span class="fc-icon"></span>';
+    }
+    html +=
+        '<span class="fc-expander-space">' +
+            '<span class="fc-icon"></span>' +
+            '</span>';
+    return html;
+}
+
+var ResourceRow = /** @class */ (function (_super) {
+    __extends(ResourceRow, _super);
+    function ResourceRow(context, a, b, c, d, timeAxis) {
+        var _this = _super.call(this, context, a, b, c, d) || this;
+        _this._updateTrResourceId = memoizeRendering(updateTrResourceId);
+        _this.spreadsheetRow = new SpreadsheetRow(context, _this.spreadsheetTr);
+        _this.timeAxisTr.appendChild(createElement('td', { className: _this.theme.getClass('widgetContent') }, _this.innerContainerEl = document.createElement('div')));
+        _this.lane = new TimelineLane(context, _this.innerContainerEl, _this.innerContainerEl, timeAxis);
+        return _this;
+    }
+    ResourceRow.prototype.destroy = function () {
+        this.spreadsheetRow.destroy();
+        this.lane.destroy();
+        _super.prototype.destroy.call(this);
+    };
+    ResourceRow.prototype.render = function (props) {
+        // spreadsheetRow handles calling updateTrResourceId for spreadsheetTr
+        this.spreadsheetRow.receiveProps({
+            colSpecs: props.colSpecs,
+            id: props.id,
+            rowSpans: props.rowSpans,
+            depth: props.depth,
+            isExpanded: props.isExpanded,
+            hasChildren: props.hasChildren,
+            resource: props.resource
+        });
+        this._updateTrResourceId(this.timeAxisTr, props.resource.id);
+        this.lane.receiveProps({
+            dateProfile: props.dateProfile,
+            nextDayThreshold: props.nextDayThreshold,
+            businessHours: props.businessHours,
+            eventStore: props.eventStore,
+            eventUiBases: props.eventUiBases,
+            dateSelection: props.dateSelection,
+            eventSelection: props.eventSelection,
+            eventDrag: props.eventDrag,
+            eventResize: props.eventResize
+        });
+        this.isSizeDirty = true;
+    };
+    ResourceRow.prototype.updateSize = function (isResize) {
+        _super.prototype.updateSize.call(this, isResize);
+        this.lane.updateSize(isResize);
+    };
+    ResourceRow.prototype.getHeightEls = function () {
+        return [this.spreadsheetRow.heightEl, this.innerContainerEl];
+    };
+    return ResourceRow;
+}(Row));
+ResourceRow.addEqualityFuncs({
+    rowSpans: isArraysEqual // HACK for isSizeDirty, ResourceTimelineView::renderRows
+});
+
+var COL_MIN_WIDTH = 30;
+var SpreadsheetHeader = /** @class */ (function (_super) {
+    __extends(SpreadsheetHeader, _super);
+    function SpreadsheetHeader(context, parentEl) {
+        var _this = _super.call(this, context) || this;
+        _this.resizables = [];
+        _this.colWidths = [];
+        _this.emitter = new EmitterMixin();
+        parentEl.appendChild(_this.tableEl = createElement('table', {
+            className: _this.theme.getClass('tableGrid')
+        }));
+        return _this;
+    }
+    SpreadsheetHeader.prototype.destroy = function () {
+        for (var _i = 0, _a = this.resizables; _i < _a.length; _i++) {
+            var resizable = _a[_i];
+            resizable.destroy();
+        }
+        removeElement(this.tableEl);
+        _super.prototype.destroy.call(this);
+    };
+    SpreadsheetHeader.prototype.render = function (props) {
+        var theme = this.theme;
+        var colSpecs = props.colSpecs;
+        var html = '<colgroup>' + props.colTags + '</colgroup>' +
+            '<tbody>';
+        if (props.superHeaderText) {
+            html +=
+                '<tr class="fc-super">' +
+                    '<th class="' + theme.getClass('widgetHeader') + '" colspan="' + colSpecs.length + '">' +
+                    '<div class="fc-cell-content">' +
+                    '<span class="fc-cell-text">' +
+                    htmlEscape(props.superHeaderText) +
+                    '</span>' +
+                    '</div>' +
+                    '</th>' +
+                    '</tr>';
+        }
+        html += '<tr>';
+        for (var i = 0; i < colSpecs.length; i++) {
+            var o = colSpecs[i];
+            var isLast = i === (colSpecs.length - 1);
+            html +=
+                "<th class=\"" + theme.getClass('widgetHeader') + "\">" +
+                    '<div>' +
+                    '<div class="fc-cell-content">' +
+                    (o.isMain ?
+                        '<span class="fc-expander-space">' +
+                            '<span class="fc-icon"></span>' +
+                            '</span>' :
+                        '') +
+                    '<span class="fc-cell-text">' +
+                    htmlEscape(o.labelText || '') + // what about normalizing this value ahead of time?
+                    '</span>' +
+                    '</div>' +
+                    (!isLast ? '<div class="fc-col-resizer"></div>' : '') +
+                    '</div>' +
+                    '</th>';
+        }
+        html += '</tr>';
+        html += '</tbody>';
+        this.tableEl.innerHTML = html;
+        this.thEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('th'));
+        this.colEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('col'));
+        this.resizerEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('.fc-col-resizer'));
+        this.initColResizing();
+    };
+    SpreadsheetHeader.prototype.initColResizing = function () {
+        var _this = this;
+        var ElementDraggingImpl = this.calendar.pluginSystem.hooks.elementDraggingImpl;
+        if (ElementDraggingImpl) {
+            this.resizables = this.resizerEls.map(function (handleEl, colIndex) {
+                var dragging = new ElementDraggingImpl(handleEl);
+                var startWidth;
+                dragging.emitter.on('dragstart', function () {
+                    startWidth = _this.colWidths[colIndex];
+                    if (typeof startWidth !== 'number') {
+                        startWidth = _this.thEls[colIndex].getBoundingClientRect().width;
+                    }
+                });
+                dragging.emitter.on('dragmove', function (pev) {
+                    _this.colWidths[colIndex] = Math.max(startWidth + pev.deltaX * (_this.isRtl ? -1 : 1), COL_MIN_WIDTH);
+                    _this.emitter.trigger('colwidthchange', _this.colWidths);
+                });
+                dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
+                return dragging;
+            });
+        }
+    };
+    return SpreadsheetHeader;
+}(Component));
+
+var Spreadsheet = /** @class */ (function (_super) {
+    __extends(Spreadsheet, _super);
+    function Spreadsheet(context, headParentEl, bodyParentEl) {
+        var _this = _super.call(this, context) || this;
+        _this._renderCells = memoizeRendering(_this.renderCells, _this.unrenderCells);
+        _this.layout = new HeaderBodyLayout(headParentEl, bodyParentEl, 'clipped-scroll');
+        var headerEnhancedScroller = _this.layout.headerScroller.enhancedScroll;
+        var bodyEnhancedScroller = _this.layout.bodyScroller.enhancedScroll;
+        _this.header = new SpreadsheetHeader(context, headerEnhancedScroller.canvas.contentEl);
+        _this.header.emitter.on('colwidthchange', function (colWidths) {
+            _this.applyColWidths(colWidths);
+        });
+        bodyEnhancedScroller.canvas.contentEl
+            .appendChild(_this.bodyContainerEl = createElement('div', { className: 'fc-rows' }, '<table>' +
+            '<colgroup />' +
+            '<tbody />' +
+            '</table>'));
+        _this.bodyColGroup = _this.bodyContainerEl.querySelector('colgroup');
+        _this.bodyTbody = _this.bodyContainerEl.querySelector('tbody');
+        return _this;
+    }
+    Spreadsheet.prototype.destroy = function () {
+        this.header.destroy();
+        this.layout.destroy();
+        this._renderCells.unrender();
+        _super.prototype.destroy.call(this);
+    };
+    Spreadsheet.prototype.render = function (props) {
+        this._renderCells(props.superHeaderText, props.colSpecs);
+    };
+    Spreadsheet.prototype.renderCells = function (superHeaderText, colSpecs) {
+        var colTags = this.renderColTags(colSpecs);
+        this.header.receiveProps({
+            superHeaderText: superHeaderText,
+            colSpecs: colSpecs,
+            colTags: colTags
+        });
+        this.bodyColGroup.innerHTML = colTags;
+        this.bodyColEls = Array.prototype.slice.call(this.bodyColGroup.querySelectorAll('col'));
+        this.applyColWidths(colSpecs.map(function (colSpec) { return colSpec.width; }));
+    };
+    Spreadsheet.prototype.unrenderCells = function () {
+        this.bodyColGroup.innerHTML = '';
+    };
+    Spreadsheet.prototype.renderColTags = function (colSpecs) {
+        var html = '';
+        for (var _i = 0, colSpecs_1 = colSpecs; _i < colSpecs_1.length; _i++) {
+            var o = colSpecs_1[_i];
+            if (o.isMain) {
+                html += '<col class="fc-main-col"/>';
+            }
+            else {
+                html += '<col/>';
+            }
+        }
+        return html;
+    };
+    Spreadsheet.prototype.updateSize = function (isResize, totalHeight, isAuto) {
+        this.layout.setHeight(totalHeight, isAuto);
+    };
+    Spreadsheet.prototype.applyColWidths = function (colWidths) {
+        var _this = this;
+        colWidths.forEach(function (colWidth, colIndex) {
+            var headEl = _this.header.colEls[colIndex]; // bad to access child
+            var bodyEl = _this.bodyColEls[colIndex];
+            var styleVal;
+            if (typeof colWidth === 'number') {
+                styleVal = colWidth + 'px';
+            }
+            else if (typeof colWidth == null) {
+                styleVal = '';
+            }
+            headEl.style.width = bodyEl.style.width = styleVal;
+        });
+    };
+    return Spreadsheet;
+}(Component));
+
+var MIN_RESOURCE_AREA_WIDTH = 30; // definitely bigger than scrollbars
+var ResourceTimelineView = /** @class */ (function (_super) {
+    __extends(ResourceTimelineView, _super);
+    function ResourceTimelineView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.isStickyScrollDirty = false;
+        _this.rowNodes = [];
+        _this.rowComponents = [];
+        _this.rowComponentsById = {};
+        _this.resourceAreaWidthDraggings = [];
+        _this.splitter = new ResourceSplitter(); // doesn't let it do businessHours tho
+        _this.hasResourceBusinessHours = memoize(hasResourceBusinessHours);
+        _this.buildRowNodes = memoize(buildRowNodes);
+        _this.hasNesting = memoize(hasNesting);
+        _this._updateHasNesting = memoizeRendering(_this.updateHasNesting);
+        var allColSpecs = _this.opt('resourceColumns') || [];
+        var labelText = _this.opt('resourceLabelText'); // TODO: view.override
+        var defaultLabelText = 'Resources'; // TODO: view.defaults
+        var superHeaderText = null;
+        if (!allColSpecs.length) {
+            allColSpecs.push({
+                labelText: labelText || defaultLabelText,
+                text: buildResourceTextFunc(_this.opt('resourceText'), _this.calendar)
+            });
+        }
+        else {
+            superHeaderText = labelText;
+        }
+        var plainColSpecs = [];
+        var groupColSpecs = [];
+        var groupSpecs = [];
+        var isVGrouping = false;
+        var isHGrouping = false;
+        for (var _i = 0, allColSpecs_1 = allColSpecs; _i < allColSpecs_1.length; _i++) {
+            var colSpec = allColSpecs_1[_i];
+            if (colSpec.group) {
+                groupColSpecs.push(colSpec);
+            }
+            else {
+                plainColSpecs.push(colSpec);
+            }
+        }
+        plainColSpecs[0].isMain = true;
+        if (groupColSpecs.length) {
+            groupSpecs = groupColSpecs;
+            isVGrouping = true;
+        }
+        else {
+            var hGroupField = _this.opt('resourceGroupField');
+            if (hGroupField) {
+                isHGrouping = true;
+                groupSpecs.push({
+                    field: hGroupField,
+                    text: _this.opt('resourceGroupText'),
+                    render: _this.opt('resourceGroupRender')
+                });
+            }
+        }
+        var allOrderSpecs = parseFieldSpecs(_this.opt('resourceOrder'));
+        var plainOrderSpecs = [];
+        for (var _a = 0, allOrderSpecs_1 = allOrderSpecs; _a < allOrderSpecs_1.length; _a++) {
+            var orderSpec = allOrderSpecs_1[_a];
+            var isGroup = false;
+            for (var _b = 0, groupSpecs_1 = groupSpecs; _b < groupSpecs_1.length; _b++) {
+                var groupSpec = groupSpecs_1[_b];
+                if (groupSpec.field === orderSpec.field) {
+                    groupSpec.order = orderSpec.order; // -1, 0, 1
+                    isGroup = true;
+                    break;
+                }
+            }
+            if (!isGroup) {
+                plainOrderSpecs.push(orderSpec);
+            }
+        }
+        _this.superHeaderText = superHeaderText;
+        _this.isVGrouping = isVGrouping;
+        _this.isHGrouping = isHGrouping;
+        _this.groupSpecs = groupSpecs;
+        _this.colSpecs = groupColSpecs.concat(plainColSpecs);
+        _this.orderSpecs = plainOrderSpecs;
+        // START RENDERING...
+        _this.el.classList.add('fc-timeline');
+        if (_this.opt('eventOverlap') === false) {
+            _this.el.classList.add('fc-no-overlap');
+        }
+        _this.el.innerHTML = _this.renderSkeletonHtml();
+        _this.resourceAreaHeadEl = _this.el.querySelector('thead .fc-resource-area');
+        _this.setResourceAreaWidth(_this.opt('resourceAreaWidth'));
+        _this.initResourceAreaWidthDragging();
+        _this.miscHeight = _this.el.getBoundingClientRect().height;
+        _this.spreadsheet = new Spreadsheet(_this.context, _this.resourceAreaHeadEl, _this.el.querySelector('tbody .fc-resource-area'));
+        _this.timeAxis = new TimeAxis(_this.context, _this.el.querySelector('thead .fc-time-area'), _this.el.querySelector('tbody .fc-time-area'));
+        var timeAxisRowContainer = createElement('div', { className: 'fc-rows' }, '<table><tbody /></table>');
+        _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl.appendChild(timeAxisRowContainer);
+        _this.timeAxisTbody = timeAxisRowContainer.querySelector('tbody');
+        _this.lane = new TimelineLane(_this.context, null, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl, _this.timeAxis);
+        _this.bodyScrollJoiner = new ScrollJoiner('vertical', [
+            _this.spreadsheet.layout.bodyScroller,
+            _this.timeAxis.layout.bodyScroller
+        ]);
+        // after scrolljoiner
+        _this.spreadsheetBodyStickyScroller = new StickyScroller(_this.spreadsheet.layout.bodyScroller.enhancedScroll, _this.isRtl, true // isVertical
+        );
+        _this.spreadsheet.receiveProps({
+            superHeaderText: _this.superHeaderText,
+            colSpecs: _this.colSpecs
+        });
+        // Component...
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.timeAxis.slats.el
+        });
+        return _this;
+    }
+    ResourceTimelineView.prototype.renderSkeletonHtml = function () {
+        var theme = this.theme;
+        return "<table class=\"" + theme.getClass('tableGrid') + "\"> <thead class=\"fc-head\"> <tr> <td class=\"fc-resource-area " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-divider fc-col-resizer " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-time-area " + theme.getClass('widgetHeader') + "\"></td> </tr> </thead> <tbody class=\"fc-body\"> <tr> <td class=\"fc-resource-area " + theme.getClass('widgetContent') + "\"></td> <td class=\"fc-divider fc-col-resizer " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-time-area " + theme.getClass('widgetContent') + "\"></td> </tr> </tbody> </table>";
+    };
+    ResourceTimelineView.prototype.render = function (props) {
+        _super.prototype.render.call(this, props);
+        var splitProps = this.splitter.splitProps(props);
+        var hasResourceBusinessHours = this.hasResourceBusinessHours(props.resourceStore);
+        this.timeAxis.receiveProps({
+            dateProfile: props.dateProfile
+        });
+        // for all-resource bg events / selections / business-hours
+        this.lane.receiveProps(__assign({}, splitProps[''], { dateProfile: props.dateProfile, nextDayThreshold: this.nextDayThreshold, businessHours: hasResourceBusinessHours ? null : props.businessHours }));
+        var newRowNodes = this.buildRowNodes(props.resourceStore, this.groupSpecs, this.orderSpecs, this.isVGrouping, props.resourceEntityExpansions, this.opt('resourcesInitiallyExpanded'));
+        this._updateHasNesting(this.hasNesting(newRowNodes));
+        this.diffRows(newRowNodes);
+        this.renderRows(props.dateProfile, hasResourceBusinessHours ? props.businessHours : null, // CONFUSING, comment
+        splitProps);
+    };
+    ResourceTimelineView.prototype.updateHasNesting = function (isNesting) {
+        var classList = this.el.classList;
+        if (isNesting) {
+            classList.remove('fc-flat');
+        }
+        else {
+            classList.add('fc-flat');
+        }
+    };
+    ResourceTimelineView.prototype.diffRows = function (newNodes) {
+        var oldNodes = this.rowNodes;
+        var oldLen = oldNodes.length;
+        var oldIndexHash = {}; // id -> index
+        var oldI = 0;
+        var newI = 0;
+        for (oldI = 0; oldI < oldLen; oldI++) {
+            oldIndexHash[oldNodes[oldI].id] = oldI;
+        }
+        // iterate new nodes
+        for (oldI = 0, newI = 0; newI < newNodes.length; newI++) {
+            var newNode = newNodes[newI];
+            var oldIFound = oldIndexHash[newNode.id];
+            if (oldIFound != null && oldIFound >= oldI) {
+                this.removeRows(newI, oldIFound - oldI, oldNodes); // won't do anything if same index
+                oldI = oldIFound + 1;
+            }
+            else {
+                this.addRow(newI, newNode);
+            }
+        }
+        // old rows that weren't found need to be removed
+        this.removeRows(newI, oldLen - oldI, oldNodes); // won't do anything if same index
+        this.rowNodes = newNodes;
+    };
+    /*
+    rowComponents is the in-progress result
+    */
+    ResourceTimelineView.prototype.addRow = function (index, rowNode) {
+        var _a = this, rowComponents = _a.rowComponents, rowComponentsById = _a.rowComponentsById;
+        var nextComponent = rowComponents[index];
+        var newComponent = this.buildChildComponent(rowNode, this.spreadsheet.bodyTbody, nextComponent ? nextComponent.spreadsheetTr : null, this.timeAxisTbody, nextComponent ? nextComponent.timeAxisTr : null);
+        rowComponents.splice(index, 0, newComponent);
+        rowComponentsById[rowNode.id] = newComponent;
+    };
+    ResourceTimelineView.prototype.removeRows = function (startIndex, len, oldRowNodes) {
+        if (len) {
+            var _a = this, rowComponents = _a.rowComponents, rowComponentsById = _a.rowComponentsById;
+            for (var i = 0; i < len; i++) {
+                var rowComponent = rowComponents[startIndex + i];
+                rowComponent.destroy();
+                delete rowComponentsById[oldRowNodes[i].id];
+            }
+            rowComponents.splice(startIndex, len);
+        }
+    };
+    ResourceTimelineView.prototype.buildChildComponent = function (node, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext) {
+        if (node.group) {
+            return new GroupRow(this.context, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext);
+        }
+        else if (node.resource) {
+            return new ResourceRow(this.context, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext, this.timeAxis);
+        }
+    };
+    ResourceTimelineView.prototype.renderRows = function (dateProfile, fallbackBusinessHours, splitProps) {
+        var _a = this, rowNodes = _a.rowNodes, rowComponents = _a.rowComponents;
+        for (var i = 0; i < rowNodes.length; i++) {
+            var rowNode = rowNodes[i];
+            var rowComponent = rowComponents[i];
+            if (rowNode.group) {
+                rowComponent.receiveProps({
+                    spreadsheetColCnt: this.colSpecs.length,
+                    id: rowNode.id,
+                    isExpanded: rowNode.isExpanded,
+                    group: rowNode.group
+                });
+            }
+            else {
+                var resource = rowNode.resource;
+                rowComponent.receiveProps(__assign({}, splitProps[resource.id], { dateProfile: dateProfile, nextDayThreshold: this.nextDayThreshold, businessHours: resource.businessHours || fallbackBusinessHours, colSpecs: this.colSpecs, id: rowNode.id, rowSpans: rowNode.rowSpans, depth: rowNode.depth, isExpanded: rowNode.isExpanded, hasChildren: rowNode.hasChildren, resource: rowNode.resource }));
+            }
+        }
+    };
+    ResourceTimelineView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+        // FYI: this ordering is really important
+        var calendar = this.calendar;
+        var isBaseSizing = isResize || calendar.isViewUpdated || calendar.isDatesUpdated || calendar.isEventsUpdated;
+        if (isBaseSizing) {
+            this.syncHeadHeights();
+            this.timeAxis.updateSize(isResize, viewHeight - this.miscHeight, isAuto);
+            this.spreadsheet.updateSize(isResize, viewHeight - this.miscHeight, isAuto);
+        }
+        var rowSizingCnt = this.updateRowSizes(isResize);
+        this.lane.updateSize(isResize); // is efficient. uses flags
+        if (isBaseSizing || rowSizingCnt) {
+            this.bodyScrollJoiner.update();
+            this.timeAxis.layout.scrollJoiner.update(); // hack
+            this.rowPositions = new PositionCache(this.timeAxis.slats.el, this.rowComponents.map(function (rowComponent) {
+                return rowComponent.timeAxisTr;
+            }), false, // isHorizontal
+            true // isVertical
+            );
+            this.rowPositions.build();
+            this.isStickyScrollDirty = true;
+        }
+    };
+    ResourceTimelineView.prototype.syncHeadHeights = function () {
+        var spreadsheetHeadEl = this.spreadsheet.header.tableEl;
+        var timeAxisHeadEl = this.timeAxis.header.tableEl;
+        spreadsheetHeadEl.style.height = '';
+        timeAxisHeadEl.style.height = '';
+        var max = Math.max(spreadsheetHeadEl.getBoundingClientRect().height, timeAxisHeadEl.getBoundingClientRect().height);
+        spreadsheetHeadEl.style.height =
+            timeAxisHeadEl.style.height = max + 'px';
+    };
+    ResourceTimelineView.prototype.updateRowSizes = function (isResize) {
+        var dirtyRowComponents = this.rowComponents;
+        if (!isResize) {
+            dirtyRowComponents = dirtyRowComponents.filter(function (rowComponent) {
+                return rowComponent.isSizeDirty;
+            });
+        }
+        var elArrays = dirtyRowComponents.map(function (rowComponent) {
+            return rowComponent.getHeightEls();
+        });
+        // reset to natural heights
+        for (var _i = 0, elArrays_1 = elArrays; _i < elArrays_1.length; _i++) {
+            var elArray = elArrays_1[_i];
+            for (var _a = 0, elArray_1 = elArray; _a < elArray_1.length; _a++) {
+                var el = elArray_1[_a];
+                el.style.height = '';
+            }
+        }
+        // let rows update their contents' heights
+        for (var _b = 0, dirtyRowComponents_1 = dirtyRowComponents; _b < dirtyRowComponents_1.length; _b++) {
+            var rowComponent = dirtyRowComponents_1[_b];
+            rowComponent.updateSize(isResize); // will reset isSizeDirty
+        }
+        var maxHeights = elArrays.map(function (elArray) {
+            var maxHeight = null;
+            for (var _i = 0, elArray_2 = elArray; _i < elArray_2.length; _i++) {
+                var el = elArray_2[_i];
+                var height = el.getBoundingClientRect().height;
+                if (maxHeight === null || height > maxHeight) {
+                    maxHeight = height;
+                }
+            }
+            return maxHeight;
+        });
+        for (var i = 0; i < elArrays.length; i++) {
+            for (var _c = 0, _d = elArrays[i]; _c < _d.length; _c++) {
+                var el = _d[_c];
+                el.style.height = maxHeights[i] + 'px';
+            }
+        }
+        return dirtyRowComponents.length;
+    };
+    ResourceTimelineView.prototype.destroy = function () {
+        for (var _i = 0, _a = this.rowComponents; _i < _a.length; _i++) {
+            var rowComponent = _a[_i];
+            rowComponent.destroy();
+        }
+        this.rowNodes = [];
+        this.rowComponents = [];
+        this.spreadsheet.destroy();
+        this.timeAxis.destroy();
+        for (var _b = 0, _c = this.resourceAreaWidthDraggings; _b < _c.length; _b++) {
+            var resourceAreaWidthDragging = _c[_b];
+            resourceAreaWidthDragging.destroy();
+        }
+        this.spreadsheetBodyStickyScroller.destroy();
+        _super.prototype.destroy.call(this);
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    // Now Indicator
+    // ------------------------------------------------------------------------------------------
+    ResourceTimelineView.prototype.getNowIndicatorUnit = function (dateProfile) {
+        return this.timeAxis.getNowIndicatorUnit(dateProfile);
+    };
+    ResourceTimelineView.prototype.renderNowIndicator = function (date) {
+        this.timeAxis.renderNowIndicator(date);
+    };
+    ResourceTimelineView.prototype.unrenderNowIndicator = function () {
+        this.timeAxis.unrenderNowIndicator();
+    };
+    // Scrolling
+    // ------------------------------------------------------------------------------------------------------------------
+    // this is useful for scrolling prev/next dates while resource is scrolled down
+    ResourceTimelineView.prototype.queryScroll = function () {
+        var scroll = _super.prototype.queryScroll.call(this);
+        if (this.props.resourceStore) {
+            __assign(scroll, this.queryResourceScroll());
+        }
+        return scroll;
+    };
+    ResourceTimelineView.prototype.applyScroll = function (scroll, isResize) {
+        _super.prototype.applyScroll.call(this, scroll, isResize);
+        if (this.props.resourceStore) {
+            this.applyResourceScroll(scroll);
+        }
+        // avoid updating stickyscroll too often
+        if (isResize || this.isStickyScrollDirty) {
+            this.isStickyScrollDirty = false;
+            this.spreadsheetBodyStickyScroller.updateSize();
+            this.timeAxis.updateStickyScrollers();
+        }
+    };
+    ResourceTimelineView.prototype.computeDateScroll = function (duration) {
+        return this.timeAxis.computeDateScroll(duration);
+    };
+    ResourceTimelineView.prototype.queryDateScroll = function () {
+        return this.timeAxis.queryDateScroll();
+    };
+    ResourceTimelineView.prototype.applyDateScroll = function (scroll) {
+        this.timeAxis.applyDateScroll(scroll);
+    };
+    ResourceTimelineView.prototype.queryResourceScroll = function () {
+        var _a = this, rowComponents = _a.rowComponents, rowNodes = _a.rowNodes;
+        var scroll = {};
+        var scrollerTop = this.timeAxis.layout.bodyScroller.el.getBoundingClientRect().top; // fixed position
+        for (var i = 0; i < rowComponents.length; i++) {
+            var rowComponent = rowComponents[i];
+            var rowNode = rowNodes[i];
+            var el = rowComponent.timeAxisTr;
+            var elBottom = el.getBoundingClientRect().bottom; // fixed position
+            if (elBottom > scrollerTop) {
+                scroll.rowId = rowNode.id;
+                scroll.bottom = elBottom - scrollerTop;
+                break;
+            }
+        }
+        // TODO: what about left scroll state for spreadsheet area?
+        return scroll;
+    };
+    ResourceTimelineView.prototype.applyResourceScroll = function (scroll) {
+        var rowId = scroll.forcedRowId || scroll.rowId;
+        if (rowId) {
+            var rowComponent = this.rowComponentsById[rowId];
+            if (rowComponent) {
+                var el = rowComponent.timeAxisTr;
+                if (el) {
+                    var innerTop = this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.el.getBoundingClientRect().top;
+                    var rowRect = el.getBoundingClientRect();
+                    var scrollTop = (scroll.forcedRowId ?
+                        rowRect.top : // just use top edge
+                        rowRect.bottom - scroll.bottom) - // pixels from bottom edge
+                        innerTop;
+                    this.timeAxis.layout.bodyScroller.enhancedScroll.setScrollTop(scrollTop);
+                    this.spreadsheet.layout.bodyScroller.enhancedScroll.setScrollTop(scrollTop);
+                }
+            }
+        }
+    };
+    // TODO: scrollToResource
+    // Hit System
+    // ------------------------------------------------------------------------------------------
+    ResourceTimelineView.prototype.buildPositionCaches = function () {
+        this.timeAxis.slats.updateSize();
+        this.rowPositions.build();
+    };
+    ResourceTimelineView.prototype.queryHit = function (positionLeft, positionTop) {
+        var rowPositions = this.rowPositions;
+        var slats = this.timeAxis.slats;
+        var rowIndex = rowPositions.topToIndex(positionTop);
+        if (rowIndex != null) {
+            var resource = this.rowNodes[rowIndex].resource;
+            if (resource) { // not a group
+                var slatHit = slats.positionToHit(positionLeft);
+                if (slatHit) {
+                    return {
+                        component: this,
+                        dateSpan: {
+                            range: slatHit.dateSpan.range,
+                            allDay: slatHit.dateSpan.allDay,
+                            resourceId: resource.id
+                        },
+                        rect: {
+                            left: slatHit.left,
+                            right: slatHit.right,
+                            top: rowPositions.tops[rowIndex],
+                            bottom: rowPositions.bottoms[rowIndex]
+                        },
+                        dayEl: slatHit.dayEl,
+                        layer: 0
+                    };
+                }
+            }
+        }
+    };
+    // Resource Area
+    // ------------------------------------------------------------------------------------------------------------------
+    ResourceTimelineView.prototype.setResourceAreaWidth = function (widthVal) {
+        this.resourceAreaWidth = widthVal;
+        applyStyleProp(this.resourceAreaHeadEl, 'width', widthVal || '');
+    };
+    ResourceTimelineView.prototype.initResourceAreaWidthDragging = function () {
+        var _this = this;
+        var resourceAreaDividerEls = Array.prototype.slice.call(this.el.querySelectorAll('.fc-col-resizer'));
+        var ElementDraggingImpl = this.calendar.pluginSystem.hooks.elementDraggingImpl;
+        if (ElementDraggingImpl) {
+            this.resourceAreaWidthDraggings = resourceAreaDividerEls.map(function (el) {
+                var dragging = new ElementDraggingImpl(el);
+                var dragStartWidth;
+                var viewWidth;
+                dragging.emitter.on('dragstart', function () {
+                    dragStartWidth = _this.resourceAreaWidth;
+                    if (typeof dragStartWidth !== 'number') {
+                        dragStartWidth = _this.resourceAreaHeadEl.getBoundingClientRect().width;
+                    }
+                    viewWidth = _this.el.getBoundingClientRect().width;
+                });
+                dragging.emitter.on('dragmove', function (pev) {
+                    var newWidth = dragStartWidth + pev.deltaX * (_this.isRtl ? -1 : 1);
+                    newWidth = Math.max(newWidth, MIN_RESOURCE_AREA_WIDTH);
+                    newWidth = Math.min(newWidth, viewWidth - MIN_RESOURCE_AREA_WIDTH);
+                    _this.setResourceAreaWidth(newWidth);
+                });
+                dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
+                return dragging;
+            });
+        }
+    };
+    ResourceTimelineView.needsResourceData = true; // for ResourceViewProps
+    return ResourceTimelineView;
+}(View));
+function hasResourceBusinessHours(resourceStore) {
+    for (var resourceId in resourceStore) {
+        var resource = resourceStore[resourceId];
+        if (resource.businessHours) {
+            return true;
+        }
+    }
+    return false;
+}
+function hasNesting(nodes) {
+    for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
+        var node = nodes_1[_i];
+        if (node.group) {
+            return true;
+        }
+        else if (node.resource) {
+            if (node.hasChildren) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+var main = createPlugin({
+    deps: [ResourceCommonPlugin, TimelinePlugin],
+    defaultView: 'resourceTimelineDay',
+    views: {
+        resourceTimeline: {
+            class: ResourceTimelineView,
+            resourceAreaWidth: '30%',
+            resourcesInitiallyExpanded: true,
+            eventResizableFromStart: true // TODO: not DRY with this same setting in the main timeline config
+        },
+        resourceTimelineDay: {
+            type: 'resourceTimeline',
+            duration: { days: 1 }
+        },
+        resourceTimelineWeek: {
+            type: 'resourceTimeline',
+            duration: { weeks: 1 }
+        },
+        resourceTimelineMonth: {
+            type: 'resourceTimeline',
+            duration: { months: 1 }
+        },
+        resourceTimelineYear: {
+            type: 'resourceTimeline',
+            duration: { years: 1 }
+        }
+    }
+});
+
+export default main;
+export { ResourceTimelineView };
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.js b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..2631955c4d91a426c9f0020540b0252c94d78bf7
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.js
@@ -0,0 +1,1035 @@
+/*!
+FullCalendar Resource Timeline Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/timeline'), require('@fullcalendar/resource-common')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/timeline', '@fullcalendar/resource-common'], factory) :
+    (global = global || self, factory(global.FullCalendarResourceTimeline = {}, global.FullCalendar, global.FullCalendarTimeline, global.FullCalendarResourceCommon));
+}(this, function (exports, core, TimelinePlugin, ResourceCommonPlugin) { 'use strict';
+
+    var TimelinePlugin__default = 'default' in TimelinePlugin ? TimelinePlugin['default'] : TimelinePlugin;
+    var ResourceCommonPlugin__default = 'default' in ResourceCommonPlugin ? ResourceCommonPlugin['default'] : ResourceCommonPlugin;
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    var Row = /** @class */ (function (_super) {
+        __extends(Row, _super);
+        function Row(context, spreadsheetParent, spreadsheetNextSibling, timeAxisParent, timeAxisNextSibling) {
+            var _this = _super.call(this, context) || this;
+            _this.isSizeDirty = false;
+            spreadsheetParent.insertBefore(_this.spreadsheetTr = document.createElement('tr'), spreadsheetNextSibling);
+            timeAxisParent.insertBefore(_this.timeAxisTr = document.createElement('tr'), timeAxisNextSibling);
+            return _this;
+        }
+        Row.prototype.destroy = function () {
+            core.removeElement(this.spreadsheetTr);
+            core.removeElement(this.timeAxisTr);
+            _super.prototype.destroy.call(this);
+        };
+        Row.prototype.updateSize = function (isResize) {
+            this.isSizeDirty = false;
+        };
+        return Row;
+    }(core.Component));
+
+    function updateExpanderIcon(el, isExpanded) {
+        var classList = el.classList;
+        if (isExpanded) {
+            classList.remove('fc-icon-plus-square');
+            classList.add('fc-icon-minus-square');
+        }
+        else {
+            classList.remove('fc-icon-minus-square');
+            classList.add('fc-icon-plus-square');
+        }
+    }
+    function clearExpanderIcon(el) {
+        var classList = el.classList;
+        classList.remove('fc-icon-minus-square');
+        classList.remove('fc-icon-plus-square');
+    }
+    function updateTrResourceId(tr, resourceId) {
+        tr.setAttribute('data-resource-id', resourceId);
+    }
+
+    var GroupRow = /** @class */ (function (_super) {
+        __extends(GroupRow, _super);
+        function GroupRow() {
+            var _this = _super !== null && _super.apply(this, arguments) || this;
+            _this._renderCells = core.memoizeRendering(_this.renderCells, _this.unrenderCells);
+            _this._updateExpanderIcon = core.memoizeRendering(_this.updateExpanderIcon, null, [_this._renderCells]);
+            _this.onExpanderClick = function (ev) {
+                var props = _this.props;
+                _this.calendar.dispatch({
+                    type: 'SET_RESOURCE_ENTITY_EXPANDED',
+                    id: props.id,
+                    isExpanded: !props.isExpanded
+                });
+            };
+            return _this;
+        }
+        GroupRow.prototype.render = function (props) {
+            this._renderCells(props.group, props.spreadsheetColCnt);
+            this._updateExpanderIcon(props.isExpanded);
+            this.isSizeDirty = true;
+        };
+        GroupRow.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this._renderCells.unrender(); // should unrender everything else
+        };
+        GroupRow.prototype.renderCells = function (group, spreadsheetColCnt) {
+            var spreadsheetContentEl = this.renderSpreadsheetContent(group);
+            this.spreadsheetTr.appendChild(core.createElement('td', {
+                className: 'fc-divider',
+                colSpan: spreadsheetColCnt // span across all columns
+            }, this.spreadsheetHeightEl = core.createElement('div', null, spreadsheetContentEl)) // needed by setTrInnerHeight
+            );
+            this.expanderIconEl = spreadsheetContentEl.querySelector('.fc-icon');
+            this.expanderIconEl.parentElement.addEventListener('click', this.onExpanderClick);
+            // insert a single cell, with a single empty <div>.
+            // there will be no content
+            this.timeAxisTr.appendChild(core.createElement('td', { className: 'fc-divider' }, this.timeAxisHeightEl = document.createElement('div')));
+        };
+        GroupRow.prototype.unrenderCells = function () {
+            this.spreadsheetTr.innerHTML = '';
+            this.timeAxisTr.innerHTML = '';
+        };
+        /*
+        Renders the content wrapper element that will be inserted into this row's TD cell.
+        */
+        GroupRow.prototype.renderSpreadsheetContent = function (group) {
+            var text = this.renderCellText(group);
+            var contentEl = core.htmlToElement('<div class="fc-cell-content">' +
+                '<span class="fc-expander">' +
+                '<span class="fc-icon"></span>' +
+                '</span>' +
+                '<span class="fc-cell-text">' +
+                (text ? core.htmlEscape(text) : '&nbsp;') +
+                '</span>' +
+                '</div>');
+            var filter = group.spec.render;
+            if (typeof filter === 'function') {
+                contentEl = filter(contentEl, group.value) || contentEl;
+            }
+            return contentEl;
+        };
+        GroupRow.prototype.renderCellText = function (group) {
+            var text = group.value || ''; // might be null/undefined if an ad-hoc grouping
+            var filter = group.spec.text;
+            if (typeof filter === 'function') {
+                text = filter(text) || text;
+            }
+            return text;
+        };
+        GroupRow.prototype.getHeightEls = function () {
+            return [this.spreadsheetHeightEl, this.timeAxisHeightEl];
+        };
+        GroupRow.prototype.updateExpanderIcon = function (isExpanded) {
+            updateExpanderIcon(this.expanderIconEl, isExpanded);
+        };
+        return GroupRow;
+    }(Row));
+    GroupRow.addEqualityFuncs({
+        group: ResourceCommonPlugin.isGroupsEqual // HACK for ResourceTimelineView::renderRows
+    });
+
+    var SpreadsheetRow = /** @class */ (function (_super) {
+        __extends(SpreadsheetRow, _super);
+        function SpreadsheetRow(context, tr) {
+            var _this = _super.call(this, context) || this;
+            _this._renderRow = core.memoizeRendering(_this.renderRow, _this.unrenderRow);
+            _this._updateTrResourceId = core.memoizeRendering(updateTrResourceId, null, [_this._renderRow]);
+            _this._updateExpanderIcon = core.memoizeRendering(_this.updateExpanderIcon, null, [_this._renderRow]);
+            _this.onExpanderClick = function (ev) {
+                var props = _this.props;
+                _this.calendar.dispatch({
+                    type: 'SET_RESOURCE_ENTITY_EXPANDED',
+                    id: props.id,
+                    isExpanded: !props.isExpanded
+                });
+            };
+            _this.tr = tr;
+            return _this;
+        }
+        SpreadsheetRow.prototype.render = function (props) {
+            this._renderRow(props.resource, props.rowSpans, props.depth, props.colSpecs);
+            this._updateTrResourceId(this.tr, props.resource.id); // TODO: only use public ID?
+            this._updateExpanderIcon(props.hasChildren, props.isExpanded);
+        };
+        SpreadsheetRow.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this._renderRow.unrender(); // should unrender everything else
+        };
+        SpreadsheetRow.prototype.renderRow = function (resource, rowSpans, depth, colSpecs) {
+            var _a = this, tr = _a.tr, theme = _a.theme, calendar = _a.calendar, view = _a.view;
+            var resourceFields = ResourceCommonPlugin.buildResourceFields(resource); // slightly inefficient. already done up the call stack
+            var mainTd;
+            for (var i = 0; i < colSpecs.length; i++) {
+                var colSpec = colSpecs[i];
+                var rowSpan = rowSpans[i];
+                if (rowSpan === 0) { // not responsible for group-based rows. VRowGroup is
+                    continue;
+                }
+                else if (rowSpan == null) {
+                    rowSpan = 1;
+                }
+                var text = void 0;
+                if (colSpec.field) {
+                    text = resourceFields[colSpec.field];
+                }
+                else {
+                    text = ResourceCommonPlugin.buildResourceTextFunc(colSpec.text, calendar)(resource);
+                }
+                var contentEl = core.htmlToElement('<div class="fc-cell-content">' +
+                    (colSpec.isMain ? renderIconHtml(depth) : '') +
+                    '<span class="fc-cell-text">' +
+                    (text ? core.htmlEscape(text) : '&nbsp;') +
+                    '</span>' +
+                    '</div>');
+                if (typeof colSpec.render === 'function') { // a filter function for the element
+                    contentEl = colSpec.render(new ResourceCommonPlugin.ResourceApi(calendar, resource), contentEl) || contentEl;
+                }
+                if (rowSpan > 1) {
+                    contentEl.classList.add('fc-sticky');
+                }
+                var td = core.createElement('td', {
+                    className: theme.getClass('widgetContent'),
+                    rowspan: rowSpan
+                }, contentEl);
+                // the first cell of the row needs to have an inner div for setTrInnerHeight
+                if (colSpec.isMain) {
+                    td.appendChild(this.heightEl = core.createElement('div', null, td.childNodes) // inner wrap
+                    );
+                    mainTd = td;
+                }
+                tr.appendChild(td);
+            }
+            this.expanderIconEl = tr.querySelector('.fc-expander-space .fc-icon');
+            // wait until very end
+            view.publiclyTrigger('resourceRender', [
+                {
+                    resource: new ResourceCommonPlugin.ResourceApi(calendar, resource),
+                    el: mainTd,
+                    view: view
+                }
+            ]);
+        };
+        SpreadsheetRow.prototype.unrenderRow = function () {
+            this.tr.innerHTML = '';
+        };
+        SpreadsheetRow.prototype.updateExpanderIcon = function (hasChildren, isExpanded) {
+            var expanderIconEl = this.expanderIconEl;
+            var expanderEl = expanderIconEl.parentElement;
+            if (expanderIconEl &&
+                expanderEl // why would this be null?? was the case in IE11
+            ) {
+                if (hasChildren) {
+                    expanderEl.addEventListener('click', this.onExpanderClick);
+                    expanderEl.classList.add('fc-expander');
+                    updateExpanderIcon(expanderIconEl, isExpanded);
+                }
+                else {
+                    expanderEl.removeEventListener('click', this.onExpanderClick);
+                    expanderEl.classList.remove('fc-expander');
+                    clearExpanderIcon(expanderIconEl);
+                }
+            }
+        };
+        return SpreadsheetRow;
+    }(core.Component));
+    /*
+    Renders the HTML responsible for the subrow expander area,
+    as well as the space before it (used to align expanders of similar depths)
+    */
+    function renderIconHtml(depth) {
+        var html = '';
+        for (var i = 0; i < depth; i++) {
+            html += '<span class="fc-icon"></span>';
+        }
+        html +=
+            '<span class="fc-expander-space">' +
+                '<span class="fc-icon"></span>' +
+                '</span>';
+        return html;
+    }
+
+    var ResourceRow = /** @class */ (function (_super) {
+        __extends(ResourceRow, _super);
+        function ResourceRow(context, a, b, c, d, timeAxis) {
+            var _this = _super.call(this, context, a, b, c, d) || this;
+            _this._updateTrResourceId = core.memoizeRendering(updateTrResourceId);
+            _this.spreadsheetRow = new SpreadsheetRow(context, _this.spreadsheetTr);
+            _this.timeAxisTr.appendChild(core.createElement('td', { className: _this.theme.getClass('widgetContent') }, _this.innerContainerEl = document.createElement('div')));
+            _this.lane = new TimelinePlugin.TimelineLane(context, _this.innerContainerEl, _this.innerContainerEl, timeAxis);
+            return _this;
+        }
+        ResourceRow.prototype.destroy = function () {
+            this.spreadsheetRow.destroy();
+            this.lane.destroy();
+            _super.prototype.destroy.call(this);
+        };
+        ResourceRow.prototype.render = function (props) {
+            // spreadsheetRow handles calling updateTrResourceId for spreadsheetTr
+            this.spreadsheetRow.receiveProps({
+                colSpecs: props.colSpecs,
+                id: props.id,
+                rowSpans: props.rowSpans,
+                depth: props.depth,
+                isExpanded: props.isExpanded,
+                hasChildren: props.hasChildren,
+                resource: props.resource
+            });
+            this._updateTrResourceId(this.timeAxisTr, props.resource.id);
+            this.lane.receiveProps({
+                dateProfile: props.dateProfile,
+                nextDayThreshold: props.nextDayThreshold,
+                businessHours: props.businessHours,
+                eventStore: props.eventStore,
+                eventUiBases: props.eventUiBases,
+                dateSelection: props.dateSelection,
+                eventSelection: props.eventSelection,
+                eventDrag: props.eventDrag,
+                eventResize: props.eventResize
+            });
+            this.isSizeDirty = true;
+        };
+        ResourceRow.prototype.updateSize = function (isResize) {
+            _super.prototype.updateSize.call(this, isResize);
+            this.lane.updateSize(isResize);
+        };
+        ResourceRow.prototype.getHeightEls = function () {
+            return [this.spreadsheetRow.heightEl, this.innerContainerEl];
+        };
+        return ResourceRow;
+    }(Row));
+    ResourceRow.addEqualityFuncs({
+        rowSpans: core.isArraysEqual // HACK for isSizeDirty, ResourceTimelineView::renderRows
+    });
+
+    var COL_MIN_WIDTH = 30;
+    var SpreadsheetHeader = /** @class */ (function (_super) {
+        __extends(SpreadsheetHeader, _super);
+        function SpreadsheetHeader(context, parentEl) {
+            var _this = _super.call(this, context) || this;
+            _this.resizables = [];
+            _this.colWidths = [];
+            _this.emitter = new core.EmitterMixin();
+            parentEl.appendChild(_this.tableEl = core.createElement('table', {
+                className: _this.theme.getClass('tableGrid')
+            }));
+            return _this;
+        }
+        SpreadsheetHeader.prototype.destroy = function () {
+            for (var _i = 0, _a = this.resizables; _i < _a.length; _i++) {
+                var resizable = _a[_i];
+                resizable.destroy();
+            }
+            core.removeElement(this.tableEl);
+            _super.prototype.destroy.call(this);
+        };
+        SpreadsheetHeader.prototype.render = function (props) {
+            var theme = this.theme;
+            var colSpecs = props.colSpecs;
+            var html = '<colgroup>' + props.colTags + '</colgroup>' +
+                '<tbody>';
+            if (props.superHeaderText) {
+                html +=
+                    '<tr class="fc-super">' +
+                        '<th class="' + theme.getClass('widgetHeader') + '" colspan="' + colSpecs.length + '">' +
+                        '<div class="fc-cell-content">' +
+                        '<span class="fc-cell-text">' +
+                        core.htmlEscape(props.superHeaderText) +
+                        '</span>' +
+                        '</div>' +
+                        '</th>' +
+                        '</tr>';
+            }
+            html += '<tr>';
+            for (var i = 0; i < colSpecs.length; i++) {
+                var o = colSpecs[i];
+                var isLast = i === (colSpecs.length - 1);
+                html +=
+                    "<th class=\"" + theme.getClass('widgetHeader') + "\">" +
+                        '<div>' +
+                        '<div class="fc-cell-content">' +
+                        (o.isMain ?
+                            '<span class="fc-expander-space">' +
+                                '<span class="fc-icon"></span>' +
+                                '</span>' :
+                            '') +
+                        '<span class="fc-cell-text">' +
+                        core.htmlEscape(o.labelText || '') + // what about normalizing this value ahead of time?
+                        '</span>' +
+                        '</div>' +
+                        (!isLast ? '<div class="fc-col-resizer"></div>' : '') +
+                        '</div>' +
+                        '</th>';
+            }
+            html += '</tr>';
+            html += '</tbody>';
+            this.tableEl.innerHTML = html;
+            this.thEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('th'));
+            this.colEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('col'));
+            this.resizerEls = Array.prototype.slice.call(this.tableEl.querySelectorAll('.fc-col-resizer'));
+            this.initColResizing();
+        };
+        SpreadsheetHeader.prototype.initColResizing = function () {
+            var _this = this;
+            var ElementDraggingImpl = this.calendar.pluginSystem.hooks.elementDraggingImpl;
+            if (ElementDraggingImpl) {
+                this.resizables = this.resizerEls.map(function (handleEl, colIndex) {
+                    var dragging = new ElementDraggingImpl(handleEl);
+                    var startWidth;
+                    dragging.emitter.on('dragstart', function () {
+                        startWidth = _this.colWidths[colIndex];
+                        if (typeof startWidth !== 'number') {
+                            startWidth = _this.thEls[colIndex].getBoundingClientRect().width;
+                        }
+                    });
+                    dragging.emitter.on('dragmove', function (pev) {
+                        _this.colWidths[colIndex] = Math.max(startWidth + pev.deltaX * (_this.isRtl ? -1 : 1), COL_MIN_WIDTH);
+                        _this.emitter.trigger('colwidthchange', _this.colWidths);
+                    });
+                    dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
+                    return dragging;
+                });
+            }
+        };
+        return SpreadsheetHeader;
+    }(core.Component));
+
+    var Spreadsheet = /** @class */ (function (_super) {
+        __extends(Spreadsheet, _super);
+        function Spreadsheet(context, headParentEl, bodyParentEl) {
+            var _this = _super.call(this, context) || this;
+            _this._renderCells = core.memoizeRendering(_this.renderCells, _this.unrenderCells);
+            _this.layout = new TimelinePlugin.HeaderBodyLayout(headParentEl, bodyParentEl, 'clipped-scroll');
+            var headerEnhancedScroller = _this.layout.headerScroller.enhancedScroll;
+            var bodyEnhancedScroller = _this.layout.bodyScroller.enhancedScroll;
+            _this.header = new SpreadsheetHeader(context, headerEnhancedScroller.canvas.contentEl);
+            _this.header.emitter.on('colwidthchange', function (colWidths) {
+                _this.applyColWidths(colWidths);
+            });
+            bodyEnhancedScroller.canvas.contentEl
+                .appendChild(_this.bodyContainerEl = core.createElement('div', { className: 'fc-rows' }, '<table>' +
+                '<colgroup />' +
+                '<tbody />' +
+                '</table>'));
+            _this.bodyColGroup = _this.bodyContainerEl.querySelector('colgroup');
+            _this.bodyTbody = _this.bodyContainerEl.querySelector('tbody');
+            return _this;
+        }
+        Spreadsheet.prototype.destroy = function () {
+            this.header.destroy();
+            this.layout.destroy();
+            this._renderCells.unrender();
+            _super.prototype.destroy.call(this);
+        };
+        Spreadsheet.prototype.render = function (props) {
+            this._renderCells(props.superHeaderText, props.colSpecs);
+        };
+        Spreadsheet.prototype.renderCells = function (superHeaderText, colSpecs) {
+            var colTags = this.renderColTags(colSpecs);
+            this.header.receiveProps({
+                superHeaderText: superHeaderText,
+                colSpecs: colSpecs,
+                colTags: colTags
+            });
+            this.bodyColGroup.innerHTML = colTags;
+            this.bodyColEls = Array.prototype.slice.call(this.bodyColGroup.querySelectorAll('col'));
+            this.applyColWidths(colSpecs.map(function (colSpec) { return colSpec.width; }));
+        };
+        Spreadsheet.prototype.unrenderCells = function () {
+            this.bodyColGroup.innerHTML = '';
+        };
+        Spreadsheet.prototype.renderColTags = function (colSpecs) {
+            var html = '';
+            for (var _i = 0, colSpecs_1 = colSpecs; _i < colSpecs_1.length; _i++) {
+                var o = colSpecs_1[_i];
+                if (o.isMain) {
+                    html += '<col class="fc-main-col"/>';
+                }
+                else {
+                    html += '<col/>';
+                }
+            }
+            return html;
+        };
+        Spreadsheet.prototype.updateSize = function (isResize, totalHeight, isAuto) {
+            this.layout.setHeight(totalHeight, isAuto);
+        };
+        Spreadsheet.prototype.applyColWidths = function (colWidths) {
+            var _this = this;
+            colWidths.forEach(function (colWidth, colIndex) {
+                var headEl = _this.header.colEls[colIndex]; // bad to access child
+                var bodyEl = _this.bodyColEls[colIndex];
+                var styleVal;
+                if (typeof colWidth === 'number') {
+                    styleVal = colWidth + 'px';
+                }
+                else if (typeof colWidth == null) {
+                    styleVal = '';
+                }
+                headEl.style.width = bodyEl.style.width = styleVal;
+            });
+        };
+        return Spreadsheet;
+    }(core.Component));
+
+    var MIN_RESOURCE_AREA_WIDTH = 30; // definitely bigger than scrollbars
+    var ResourceTimelineView = /** @class */ (function (_super) {
+        __extends(ResourceTimelineView, _super);
+        function ResourceTimelineView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.isStickyScrollDirty = false;
+            _this.rowNodes = [];
+            _this.rowComponents = [];
+            _this.rowComponentsById = {};
+            _this.resourceAreaWidthDraggings = [];
+            _this.splitter = new ResourceCommonPlugin.ResourceSplitter(); // doesn't let it do businessHours tho
+            _this.hasResourceBusinessHours = core.memoize(hasResourceBusinessHours);
+            _this.buildRowNodes = core.memoize(ResourceCommonPlugin.buildRowNodes);
+            _this.hasNesting = core.memoize(hasNesting);
+            _this._updateHasNesting = core.memoizeRendering(_this.updateHasNesting);
+            var allColSpecs = _this.opt('resourceColumns') || [];
+            var labelText = _this.opt('resourceLabelText'); // TODO: view.override
+            var defaultLabelText = 'Resources'; // TODO: view.defaults
+            var superHeaderText = null;
+            if (!allColSpecs.length) {
+                allColSpecs.push({
+                    labelText: labelText || defaultLabelText,
+                    text: ResourceCommonPlugin.buildResourceTextFunc(_this.opt('resourceText'), _this.calendar)
+                });
+            }
+            else {
+                superHeaderText = labelText;
+            }
+            var plainColSpecs = [];
+            var groupColSpecs = [];
+            var groupSpecs = [];
+            var isVGrouping = false;
+            var isHGrouping = false;
+            for (var _i = 0, allColSpecs_1 = allColSpecs; _i < allColSpecs_1.length; _i++) {
+                var colSpec = allColSpecs_1[_i];
+                if (colSpec.group) {
+                    groupColSpecs.push(colSpec);
+                }
+                else {
+                    plainColSpecs.push(colSpec);
+                }
+            }
+            plainColSpecs[0].isMain = true;
+            if (groupColSpecs.length) {
+                groupSpecs = groupColSpecs;
+                isVGrouping = true;
+            }
+            else {
+                var hGroupField = _this.opt('resourceGroupField');
+                if (hGroupField) {
+                    isHGrouping = true;
+                    groupSpecs.push({
+                        field: hGroupField,
+                        text: _this.opt('resourceGroupText'),
+                        render: _this.opt('resourceGroupRender')
+                    });
+                }
+            }
+            var allOrderSpecs = core.parseFieldSpecs(_this.opt('resourceOrder'));
+            var plainOrderSpecs = [];
+            for (var _a = 0, allOrderSpecs_1 = allOrderSpecs; _a < allOrderSpecs_1.length; _a++) {
+                var orderSpec = allOrderSpecs_1[_a];
+                var isGroup = false;
+                for (var _b = 0, groupSpecs_1 = groupSpecs; _b < groupSpecs_1.length; _b++) {
+                    var groupSpec = groupSpecs_1[_b];
+                    if (groupSpec.field === orderSpec.field) {
+                        groupSpec.order = orderSpec.order; // -1, 0, 1
+                        isGroup = true;
+                        break;
+                    }
+                }
+                if (!isGroup) {
+                    plainOrderSpecs.push(orderSpec);
+                }
+            }
+            _this.superHeaderText = superHeaderText;
+            _this.isVGrouping = isVGrouping;
+            _this.isHGrouping = isHGrouping;
+            _this.groupSpecs = groupSpecs;
+            _this.colSpecs = groupColSpecs.concat(plainColSpecs);
+            _this.orderSpecs = plainOrderSpecs;
+            // START RENDERING...
+            _this.el.classList.add('fc-timeline');
+            if (_this.opt('eventOverlap') === false) {
+                _this.el.classList.add('fc-no-overlap');
+            }
+            _this.el.innerHTML = _this.renderSkeletonHtml();
+            _this.resourceAreaHeadEl = _this.el.querySelector('thead .fc-resource-area');
+            _this.setResourceAreaWidth(_this.opt('resourceAreaWidth'));
+            _this.initResourceAreaWidthDragging();
+            _this.miscHeight = _this.el.getBoundingClientRect().height;
+            _this.spreadsheet = new Spreadsheet(_this.context, _this.resourceAreaHeadEl, _this.el.querySelector('tbody .fc-resource-area'));
+            _this.timeAxis = new TimelinePlugin.TimeAxis(_this.context, _this.el.querySelector('thead .fc-time-area'), _this.el.querySelector('tbody .fc-time-area'));
+            var timeAxisRowContainer = core.createElement('div', { className: 'fc-rows' }, '<table><tbody /></table>');
+            _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl.appendChild(timeAxisRowContainer);
+            _this.timeAxisTbody = timeAxisRowContainer.querySelector('tbody');
+            _this.lane = new TimelinePlugin.TimelineLane(_this.context, null, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl, _this.timeAxis);
+            _this.bodyScrollJoiner = new TimelinePlugin.ScrollJoiner('vertical', [
+                _this.spreadsheet.layout.bodyScroller,
+                _this.timeAxis.layout.bodyScroller
+            ]);
+            // after scrolljoiner
+            _this.spreadsheetBodyStickyScroller = new TimelinePlugin.StickyScroller(_this.spreadsheet.layout.bodyScroller.enhancedScroll, _this.isRtl, true // isVertical
+            );
+            _this.spreadsheet.receiveProps({
+                superHeaderText: _this.superHeaderText,
+                colSpecs: _this.colSpecs
+            });
+            // Component...
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.timeAxis.slats.el
+            });
+            return _this;
+        }
+        ResourceTimelineView.prototype.renderSkeletonHtml = function () {
+            var theme = this.theme;
+            return "<table class=\"" + theme.getClass('tableGrid') + "\"> <thead class=\"fc-head\"> <tr> <td class=\"fc-resource-area " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-divider fc-col-resizer " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-time-area " + theme.getClass('widgetHeader') + "\"></td> </tr> </thead> <tbody class=\"fc-body\"> <tr> <td class=\"fc-resource-area " + theme.getClass('widgetContent') + "\"></td> <td class=\"fc-divider fc-col-resizer " + theme.getClass('widgetHeader') + "\"></td> <td class=\"fc-time-area " + theme.getClass('widgetContent') + "\"></td> </tr> </tbody> </table>";
+        };
+        ResourceTimelineView.prototype.render = function (props) {
+            _super.prototype.render.call(this, props);
+            var splitProps = this.splitter.splitProps(props);
+            var hasResourceBusinessHours = this.hasResourceBusinessHours(props.resourceStore);
+            this.timeAxis.receiveProps({
+                dateProfile: props.dateProfile
+            });
+            // for all-resource bg events / selections / business-hours
+            this.lane.receiveProps(__assign({}, splitProps[''], { dateProfile: props.dateProfile, nextDayThreshold: this.nextDayThreshold, businessHours: hasResourceBusinessHours ? null : props.businessHours }));
+            var newRowNodes = this.buildRowNodes(props.resourceStore, this.groupSpecs, this.orderSpecs, this.isVGrouping, props.resourceEntityExpansions, this.opt('resourcesInitiallyExpanded'));
+            this._updateHasNesting(this.hasNesting(newRowNodes));
+            this.diffRows(newRowNodes);
+            this.renderRows(props.dateProfile, hasResourceBusinessHours ? props.businessHours : null, // CONFUSING, comment
+            splitProps);
+        };
+        ResourceTimelineView.prototype.updateHasNesting = function (isNesting) {
+            var classList = this.el.classList;
+            if (isNesting) {
+                classList.remove('fc-flat');
+            }
+            else {
+                classList.add('fc-flat');
+            }
+        };
+        ResourceTimelineView.prototype.diffRows = function (newNodes) {
+            var oldNodes = this.rowNodes;
+            var oldLen = oldNodes.length;
+            var oldIndexHash = {}; // id -> index
+            var oldI = 0;
+            var newI = 0;
+            for (oldI = 0; oldI < oldLen; oldI++) {
+                oldIndexHash[oldNodes[oldI].id] = oldI;
+            }
+            // iterate new nodes
+            for (oldI = 0, newI = 0; newI < newNodes.length; newI++) {
+                var newNode = newNodes[newI];
+                var oldIFound = oldIndexHash[newNode.id];
+                if (oldIFound != null && oldIFound >= oldI) {
+                    this.removeRows(newI, oldIFound - oldI, oldNodes); // won't do anything if same index
+                    oldI = oldIFound + 1;
+                }
+                else {
+                    this.addRow(newI, newNode);
+                }
+            }
+            // old rows that weren't found need to be removed
+            this.removeRows(newI, oldLen - oldI, oldNodes); // won't do anything if same index
+            this.rowNodes = newNodes;
+        };
+        /*
+        rowComponents is the in-progress result
+        */
+        ResourceTimelineView.prototype.addRow = function (index, rowNode) {
+            var _a = this, rowComponents = _a.rowComponents, rowComponentsById = _a.rowComponentsById;
+            var nextComponent = rowComponents[index];
+            var newComponent = this.buildChildComponent(rowNode, this.spreadsheet.bodyTbody, nextComponent ? nextComponent.spreadsheetTr : null, this.timeAxisTbody, nextComponent ? nextComponent.timeAxisTr : null);
+            rowComponents.splice(index, 0, newComponent);
+            rowComponentsById[rowNode.id] = newComponent;
+        };
+        ResourceTimelineView.prototype.removeRows = function (startIndex, len, oldRowNodes) {
+            if (len) {
+                var _a = this, rowComponents = _a.rowComponents, rowComponentsById = _a.rowComponentsById;
+                for (var i = 0; i < len; i++) {
+                    var rowComponent = rowComponents[startIndex + i];
+                    rowComponent.destroy();
+                    delete rowComponentsById[oldRowNodes[i].id];
+                }
+                rowComponents.splice(startIndex, len);
+            }
+        };
+        ResourceTimelineView.prototype.buildChildComponent = function (node, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext) {
+            if (node.group) {
+                return new GroupRow(this.context, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext);
+            }
+            else if (node.resource) {
+                return new ResourceRow(this.context, spreadsheetTbody, spreadsheetNext, timeAxisTbody, timeAxisNext, this.timeAxis);
+            }
+        };
+        ResourceTimelineView.prototype.renderRows = function (dateProfile, fallbackBusinessHours, splitProps) {
+            var _a = this, rowNodes = _a.rowNodes, rowComponents = _a.rowComponents;
+            for (var i = 0; i < rowNodes.length; i++) {
+                var rowNode = rowNodes[i];
+                var rowComponent = rowComponents[i];
+                if (rowNode.group) {
+                    rowComponent.receiveProps({
+                        spreadsheetColCnt: this.colSpecs.length,
+                        id: rowNode.id,
+                        isExpanded: rowNode.isExpanded,
+                        group: rowNode.group
+                    });
+                }
+                else {
+                    var resource = rowNode.resource;
+                    rowComponent.receiveProps(__assign({}, splitProps[resource.id], { dateProfile: dateProfile, nextDayThreshold: this.nextDayThreshold, businessHours: resource.businessHours || fallbackBusinessHours, colSpecs: this.colSpecs, id: rowNode.id, rowSpans: rowNode.rowSpans, depth: rowNode.depth, isExpanded: rowNode.isExpanded, hasChildren: rowNode.hasChildren, resource: rowNode.resource }));
+                }
+            }
+        };
+        ResourceTimelineView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+            // FYI: this ordering is really important
+            var calendar = this.calendar;
+            var isBaseSizing = isResize || calendar.isViewUpdated || calendar.isDatesUpdated || calendar.isEventsUpdated;
+            if (isBaseSizing) {
+                this.syncHeadHeights();
+                this.timeAxis.updateSize(isResize, viewHeight - this.miscHeight, isAuto);
+                this.spreadsheet.updateSize(isResize, viewHeight - this.miscHeight, isAuto);
+            }
+            var rowSizingCnt = this.updateRowSizes(isResize);
+            this.lane.updateSize(isResize); // is efficient. uses flags
+            if (isBaseSizing || rowSizingCnt) {
+                this.bodyScrollJoiner.update();
+                this.timeAxis.layout.scrollJoiner.update(); // hack
+                this.rowPositions = new core.PositionCache(this.timeAxis.slats.el, this.rowComponents.map(function (rowComponent) {
+                    return rowComponent.timeAxisTr;
+                }), false, // isHorizontal
+                true // isVertical
+                );
+                this.rowPositions.build();
+                this.isStickyScrollDirty = true;
+            }
+        };
+        ResourceTimelineView.prototype.syncHeadHeights = function () {
+            var spreadsheetHeadEl = this.spreadsheet.header.tableEl;
+            var timeAxisHeadEl = this.timeAxis.header.tableEl;
+            spreadsheetHeadEl.style.height = '';
+            timeAxisHeadEl.style.height = '';
+            var max = Math.max(spreadsheetHeadEl.getBoundingClientRect().height, timeAxisHeadEl.getBoundingClientRect().height);
+            spreadsheetHeadEl.style.height =
+                timeAxisHeadEl.style.height = max + 'px';
+        };
+        ResourceTimelineView.prototype.updateRowSizes = function (isResize) {
+            var dirtyRowComponents = this.rowComponents;
+            if (!isResize) {
+                dirtyRowComponents = dirtyRowComponents.filter(function (rowComponent) {
+                    return rowComponent.isSizeDirty;
+                });
+            }
+            var elArrays = dirtyRowComponents.map(function (rowComponent) {
+                return rowComponent.getHeightEls();
+            });
+            // reset to natural heights
+            for (var _i = 0, elArrays_1 = elArrays; _i < elArrays_1.length; _i++) {
+                var elArray = elArrays_1[_i];
+                for (var _a = 0, elArray_1 = elArray; _a < elArray_1.length; _a++) {
+                    var el = elArray_1[_a];
+                    el.style.height = '';
+                }
+            }
+            // let rows update their contents' heights
+            for (var _b = 0, dirtyRowComponents_1 = dirtyRowComponents; _b < dirtyRowComponents_1.length; _b++) {
+                var rowComponent = dirtyRowComponents_1[_b];
+                rowComponent.updateSize(isResize); // will reset isSizeDirty
+            }
+            var maxHeights = elArrays.map(function (elArray) {
+                var maxHeight = null;
+                for (var _i = 0, elArray_2 = elArray; _i < elArray_2.length; _i++) {
+                    var el = elArray_2[_i];
+                    var height = el.getBoundingClientRect().height;
+                    if (maxHeight === null || height > maxHeight) {
+                        maxHeight = height;
+                    }
+                }
+                return maxHeight;
+            });
+            for (var i = 0; i < elArrays.length; i++) {
+                for (var _c = 0, _d = elArrays[i]; _c < _d.length; _c++) {
+                    var el = _d[_c];
+                    el.style.height = maxHeights[i] + 'px';
+                }
+            }
+            return dirtyRowComponents.length;
+        };
+        ResourceTimelineView.prototype.destroy = function () {
+            for (var _i = 0, _a = this.rowComponents; _i < _a.length; _i++) {
+                var rowComponent = _a[_i];
+                rowComponent.destroy();
+            }
+            this.rowNodes = [];
+            this.rowComponents = [];
+            this.spreadsheet.destroy();
+            this.timeAxis.destroy();
+            for (var _b = 0, _c = this.resourceAreaWidthDraggings; _b < _c.length; _b++) {
+                var resourceAreaWidthDragging = _c[_b];
+                resourceAreaWidthDragging.destroy();
+            }
+            this.spreadsheetBodyStickyScroller.destroy();
+            _super.prototype.destroy.call(this);
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        // Now Indicator
+        // ------------------------------------------------------------------------------------------
+        ResourceTimelineView.prototype.getNowIndicatorUnit = function (dateProfile) {
+            return this.timeAxis.getNowIndicatorUnit(dateProfile);
+        };
+        ResourceTimelineView.prototype.renderNowIndicator = function (date) {
+            this.timeAxis.renderNowIndicator(date);
+        };
+        ResourceTimelineView.prototype.unrenderNowIndicator = function () {
+            this.timeAxis.unrenderNowIndicator();
+        };
+        // Scrolling
+        // ------------------------------------------------------------------------------------------------------------------
+        // this is useful for scrolling prev/next dates while resource is scrolled down
+        ResourceTimelineView.prototype.queryScroll = function () {
+            var scroll = _super.prototype.queryScroll.call(this);
+            if (this.props.resourceStore) {
+                __assign(scroll, this.queryResourceScroll());
+            }
+            return scroll;
+        };
+        ResourceTimelineView.prototype.applyScroll = function (scroll, isResize) {
+            _super.prototype.applyScroll.call(this, scroll, isResize);
+            if (this.props.resourceStore) {
+                this.applyResourceScroll(scroll);
+            }
+            // avoid updating stickyscroll too often
+            if (isResize || this.isStickyScrollDirty) {
+                this.isStickyScrollDirty = false;
+                this.spreadsheetBodyStickyScroller.updateSize();
+                this.timeAxis.updateStickyScrollers();
+            }
+        };
+        ResourceTimelineView.prototype.computeDateScroll = function (duration) {
+            return this.timeAxis.computeDateScroll(duration);
+        };
+        ResourceTimelineView.prototype.queryDateScroll = function () {
+            return this.timeAxis.queryDateScroll();
+        };
+        ResourceTimelineView.prototype.applyDateScroll = function (scroll) {
+            this.timeAxis.applyDateScroll(scroll);
+        };
+        ResourceTimelineView.prototype.queryResourceScroll = function () {
+            var _a = this, rowComponents = _a.rowComponents, rowNodes = _a.rowNodes;
+            var scroll = {};
+            var scrollerTop = this.timeAxis.layout.bodyScroller.el.getBoundingClientRect().top; // fixed position
+            for (var i = 0; i < rowComponents.length; i++) {
+                var rowComponent = rowComponents[i];
+                var rowNode = rowNodes[i];
+                var el = rowComponent.timeAxisTr;
+                var elBottom = el.getBoundingClientRect().bottom; // fixed position
+                if (elBottom > scrollerTop) {
+                    scroll.rowId = rowNode.id;
+                    scroll.bottom = elBottom - scrollerTop;
+                    break;
+                }
+            }
+            // TODO: what about left scroll state for spreadsheet area?
+            return scroll;
+        };
+        ResourceTimelineView.prototype.applyResourceScroll = function (scroll) {
+            var rowId = scroll.forcedRowId || scroll.rowId;
+            if (rowId) {
+                var rowComponent = this.rowComponentsById[rowId];
+                if (rowComponent) {
+                    var el = rowComponent.timeAxisTr;
+                    if (el) {
+                        var innerTop = this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.el.getBoundingClientRect().top;
+                        var rowRect = el.getBoundingClientRect();
+                        var scrollTop = (scroll.forcedRowId ?
+                            rowRect.top : // just use top edge
+                            rowRect.bottom - scroll.bottom) - // pixels from bottom edge
+                            innerTop;
+                        this.timeAxis.layout.bodyScroller.enhancedScroll.setScrollTop(scrollTop);
+                        this.spreadsheet.layout.bodyScroller.enhancedScroll.setScrollTop(scrollTop);
+                    }
+                }
+            }
+        };
+        // TODO: scrollToResource
+        // Hit System
+        // ------------------------------------------------------------------------------------------
+        ResourceTimelineView.prototype.buildPositionCaches = function () {
+            this.timeAxis.slats.updateSize();
+            this.rowPositions.build();
+        };
+        ResourceTimelineView.prototype.queryHit = function (positionLeft, positionTop) {
+            var rowPositions = this.rowPositions;
+            var slats = this.timeAxis.slats;
+            var rowIndex = rowPositions.topToIndex(positionTop);
+            if (rowIndex != null) {
+                var resource = this.rowNodes[rowIndex].resource;
+                if (resource) { // not a group
+                    var slatHit = slats.positionToHit(positionLeft);
+                    if (slatHit) {
+                        return {
+                            component: this,
+                            dateSpan: {
+                                range: slatHit.dateSpan.range,
+                                allDay: slatHit.dateSpan.allDay,
+                                resourceId: resource.id
+                            },
+                            rect: {
+                                left: slatHit.left,
+                                right: slatHit.right,
+                                top: rowPositions.tops[rowIndex],
+                                bottom: rowPositions.bottoms[rowIndex]
+                            },
+                            dayEl: slatHit.dayEl,
+                            layer: 0
+                        };
+                    }
+                }
+            }
+        };
+        // Resource Area
+        // ------------------------------------------------------------------------------------------------------------------
+        ResourceTimelineView.prototype.setResourceAreaWidth = function (widthVal) {
+            this.resourceAreaWidth = widthVal;
+            core.applyStyleProp(this.resourceAreaHeadEl, 'width', widthVal || '');
+        };
+        ResourceTimelineView.prototype.initResourceAreaWidthDragging = function () {
+            var _this = this;
+            var resourceAreaDividerEls = Array.prototype.slice.call(this.el.querySelectorAll('.fc-col-resizer'));
+            var ElementDraggingImpl = this.calendar.pluginSystem.hooks.elementDraggingImpl;
+            if (ElementDraggingImpl) {
+                this.resourceAreaWidthDraggings = resourceAreaDividerEls.map(function (el) {
+                    var dragging = new ElementDraggingImpl(el);
+                    var dragStartWidth;
+                    var viewWidth;
+                    dragging.emitter.on('dragstart', function () {
+                        dragStartWidth = _this.resourceAreaWidth;
+                        if (typeof dragStartWidth !== 'number') {
+                            dragStartWidth = _this.resourceAreaHeadEl.getBoundingClientRect().width;
+                        }
+                        viewWidth = _this.el.getBoundingClientRect().width;
+                    });
+                    dragging.emitter.on('dragmove', function (pev) {
+                        var newWidth = dragStartWidth + pev.deltaX * (_this.isRtl ? -1 : 1);
+                        newWidth = Math.max(newWidth, MIN_RESOURCE_AREA_WIDTH);
+                        newWidth = Math.min(newWidth, viewWidth - MIN_RESOURCE_AREA_WIDTH);
+                        _this.setResourceAreaWidth(newWidth);
+                    });
+                    dragging.setAutoScrollEnabled(false); // because gets weird with auto-scrolling time area
+                    return dragging;
+                });
+            }
+        };
+        ResourceTimelineView.needsResourceData = true; // for ResourceViewProps
+        return ResourceTimelineView;
+    }(core.View));
+    function hasResourceBusinessHours(resourceStore) {
+        for (var resourceId in resourceStore) {
+            var resource = resourceStore[resourceId];
+            if (resource.businessHours) {
+                return true;
+            }
+        }
+        return false;
+    }
+    function hasNesting(nodes) {
+        for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
+            var node = nodes_1[_i];
+            if (node.group) {
+                return true;
+            }
+            else if (node.resource) {
+                if (node.hasChildren) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    var main = core.createPlugin({
+        deps: [ResourceCommonPlugin__default, TimelinePlugin__default],
+        defaultView: 'resourceTimelineDay',
+        views: {
+            resourceTimeline: {
+                class: ResourceTimelineView,
+                resourceAreaWidth: '30%',
+                resourcesInitiallyExpanded: true,
+                eventResizableFromStart: true // TODO: not DRY with this same setting in the main timeline config
+            },
+            resourceTimelineDay: {
+                type: 'resourceTimeline',
+                duration: { days: 1 }
+            },
+            resourceTimelineWeek: {
+                type: 'resourceTimeline',
+                duration: { weeks: 1 }
+            },
+            resourceTimelineMonth: {
+                type: 'resourceTimeline',
+                duration: { months: 1 }
+            },
+            resourceTimelineYear: {
+                type: 'resourceTimeline',
+                duration: { years: 1 }
+            }
+        }
+    });
+
+    exports.ResourceTimelineView = ResourceTimelineView;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.min.css b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..a365c608d3c4b455135320750f27d180e14ec113
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.min.css
@@ -0,0 +1 @@
+.fc-timeline .fc-divider{width:3px;border-style:double}.fc-timeline .fc-head>tr>.fc-divider{border-bottom:0}.fc-timeline .fc-body>tr>.fc-divider{border-top:0}.fc-resource-area{width:30%}.fc-resource-area col{width:40%;min-width:70px}.fc-resource-area col.fc-main-col{width:60%}.fc-flat .fc-expander-space{display:none}.fc-ltr .fc-resource-area tr>*{text-align:left}.fc-rtl .fc-resource-area tr>*{text-align:right}.fc-resource-area .fc-cell-content{padding-left:4px;padding-right:4px}.fc-resource-area .fc-super th{text-align:center}.fc-resource-area th>div{position:relative}.fc-resource-area th .fc-cell-content{position:relative;z-index:1}.fc-resource-area th .fc-col-resizer{position:absolute;z-index:2;top:0;bottom:0;width:5px}.fc-timeline .fc-col-resizer{cursor:col-resize}.fc-ltr .fc-resource-area th .fc-col-resizer{right:-3px}.fc-rtl .fc-resource-area th .fc-col-resizer{left:-3px}.fc-body .fc-resource-area .fc-cell-content{padding-top:8px;padding-bottom:8px}.fc-no-overlap .fc-body .fc-resource-area .fc-cell-content{padding-top:6px;padding-bottom:6px}.fc-resource-area .fc-icon{display:inline-block;width:1em;text-align:center}.fc-resource-area .fc-expander{cursor:pointer;opacity:.65}.fc-time-area .fc-rows{position:relative;z-index:3}.fc-time-area .fc-rows td>div{position:relative}.fc-time-area .fc-rows .fc-bgevent-container,.fc-time-area .fc-rows .fc-highlight-container{z-index:1}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.min.js b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..c3467bffe4238ae4cd8089d9c05060ca064fcaef
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Resource Timeline Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core"),require("@fullcalendar/timeline"),require("@fullcalendar/resource-common")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core","@fullcalendar/timeline","@fullcalendar/resource-common"],t):t((e=e||self).FullCalendarResourceTimeline={},e.FullCalendar,e.FullCalendarTimeline,e.FullCalendarResourceCommon)}(this,function(e,t,r,o){"use strict";var i="default"in r?r.default:r,s="default"in o?o.default:o,n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function l(e,t){function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var a=function(){return(a=Object.assign||function(e){for(var t,r=1,o=arguments.length;r<o;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},c=function(e){function r(t,r,o,i,s){var n=e.call(this,t)||this;return n.isSizeDirty=!1,r.insertBefore(n.spreadsheetTr=document.createElement("tr"),o),i.insertBefore(n.timeAxisTr=document.createElement("tr"),s),n}return l(r,e),r.prototype.destroy=function(){t.removeElement(this.spreadsheetTr),t.removeElement(this.timeAxisTr),e.prototype.destroy.call(this)},r.prototype.updateSize=function(e){this.isSizeDirty=!1},r}(t.Component);function d(e,t){var r=e.classList;t?(r.remove("fc-icon-plus-square"),r.add("fc-icon-minus-square")):(r.remove("fc-icon-minus-square"),r.add("fc-icon-plus-square"))}function p(e,t){e.setAttribute("data-resource-id",t)}var u=function(e){function r(){var r=null!==e&&e.apply(this,arguments)||this;return r._renderCells=t.memoizeRendering(r.renderCells,r.unrenderCells),r._updateExpanderIcon=t.memoizeRendering(r.updateExpanderIcon,null,[r._renderCells]),r.onExpanderClick=function(e){var t=r.props;r.calendar.dispatch({type:"SET_RESOURCE_ENTITY_EXPANDED",id:t.id,isExpanded:!t.isExpanded})},r}return l(r,e),r.prototype.render=function(e){this._renderCells(e.group,e.spreadsheetColCnt),this._updateExpanderIcon(e.isExpanded),this.isSizeDirty=!0},r.prototype.destroy=function(){e.prototype.destroy.call(this),this._renderCells.unrender()},r.prototype.renderCells=function(e,r){var o=this.renderSpreadsheetContent(e);this.spreadsheetTr.appendChild(t.createElement("td",{className:"fc-divider",colSpan:r},this.spreadsheetHeightEl=t.createElement("div",null,o))),this.expanderIconEl=o.querySelector(".fc-icon"),this.expanderIconEl.parentElement.addEventListener("click",this.onExpanderClick),this.timeAxisTr.appendChild(t.createElement("td",{className:"fc-divider"},this.timeAxisHeightEl=document.createElement("div")))},r.prototype.unrenderCells=function(){this.spreadsheetTr.innerHTML="",this.timeAxisTr.innerHTML=""},r.prototype.renderSpreadsheetContent=function(e){var r=this.renderCellText(e),o=t.htmlToElement('<div class="fc-cell-content"><span class="fc-expander"><span class="fc-icon"></span></span><span class="fc-cell-text">'+(r?t.htmlEscape(r):"&nbsp;")+"</span></div>"),i=e.spec.render;return"function"==typeof i&&(o=i(o,e.value)||o),o},r.prototype.renderCellText=function(e){var t=e.value||"",r=e.spec.text;return"function"==typeof r&&(t=r(t)||t),t},r.prototype.getHeightEls=function(){return[this.spreadsheetHeightEl,this.timeAxisHeightEl]},r.prototype.updateExpanderIcon=function(e){d(this.expanderIconEl,e)},r}(c);u.addEqualityFuncs({group:o.isGroupsEqual});var h=function(e){function r(r,o){var i=e.call(this,r)||this;return i._renderRow=t.memoizeRendering(i.renderRow,i.unrenderRow),i._updateTrResourceId=t.memoizeRendering(p,null,[i._renderRow]),i._updateExpanderIcon=t.memoizeRendering(i.updateExpanderIcon,null,[i._renderRow]),i.onExpanderClick=function(e){var t=i.props;i.calendar.dispatch({type:"SET_RESOURCE_ENTITY_EXPANDED",id:t.id,isExpanded:!t.isExpanded})},i.tr=o,i}return l(r,e),r.prototype.render=function(e){this._renderRow(e.resource,e.rowSpans,e.depth,e.colSpecs),this._updateTrResourceId(this.tr,e.resource.id),this._updateExpanderIcon(e.hasChildren,e.isExpanded)},r.prototype.destroy=function(){e.prototype.destroy.call(this),this._renderRow.unrender()},r.prototype.renderRow=function(e,r,i,s){for(var n,l=this.tr,a=this.theme,c=this.calendar,d=this.view,p=o.buildResourceFields(e),u=0;u<s.length;u++){var h=s[u],f=r[u];if(0!==f){null==f&&(f=1);var m=void 0;m=h.field?p[h.field]:o.buildResourceTextFunc(h.text,c)(e);var g=t.htmlToElement('<div class="fc-cell-content">'+(h.isMain?y(i):"")+'<span class="fc-cell-text">'+(m?t.htmlEscape(m):"&nbsp;")+"</span></div>");"function"==typeof h.render&&(g=h.render(new o.ResourceApi(c,e),g)||g),f>1&&g.classList.add("fc-sticky");var v=t.createElement("td",{className:a.getClass("widgetContent"),rowspan:f},g);h.isMain&&(v.appendChild(this.heightEl=t.createElement("div",null,v.childNodes)),n=v),l.appendChild(v)}}this.expanderIconEl=l.querySelector(".fc-expander-space .fc-icon"),d.publiclyTrigger("resourceRender",[{resource:new o.ResourceApi(c,e),el:n,view:d}])},r.prototype.unrenderRow=function(){this.tr.innerHTML=""},r.prototype.updateExpanderIcon=function(e,t){var r,o=this.expanderIconEl,i=o.parentElement;o&&i&&(e?(i.addEventListener("click",this.onExpanderClick),i.classList.add("fc-expander"),d(o,t)):(i.removeEventListener("click",this.onExpanderClick),i.classList.remove("fc-expander"),(r=o.classList).remove("fc-icon-minus-square"),r.remove("fc-icon-plus-square")))},r}(t.Component);function y(e){for(var t="",r=0;r<e;r++)t+='<span class="fc-icon"></span>';return t+='<span class="fc-expander-space"><span class="fc-icon"></span></span>'}var f=function(e){function o(o,i,s,n,l,a){var c=e.call(this,o,i,s,n,l)||this;return c._updateTrResourceId=t.memoizeRendering(p),c.spreadsheetRow=new h(o,c.spreadsheetTr),c.timeAxisTr.appendChild(t.createElement("td",{className:c.theme.getClass("widgetContent")},c.innerContainerEl=document.createElement("div"))),c.lane=new r.TimelineLane(o,c.innerContainerEl,c.innerContainerEl,a),c}return l(o,e),o.prototype.destroy=function(){this.spreadsheetRow.destroy(),this.lane.destroy(),e.prototype.destroy.call(this)},o.prototype.render=function(e){this.spreadsheetRow.receiveProps({colSpecs:e.colSpecs,id:e.id,rowSpans:e.rowSpans,depth:e.depth,isExpanded:e.isExpanded,hasChildren:e.hasChildren,resource:e.resource}),this._updateTrResourceId(this.timeAxisTr,e.resource.id),this.lane.receiveProps({dateProfile:e.dateProfile,nextDayThreshold:e.nextDayThreshold,businessHours:e.businessHours,eventStore:e.eventStore,eventUiBases:e.eventUiBases,dateSelection:e.dateSelection,eventSelection:e.eventSelection,eventDrag:e.eventDrag,eventResize:e.eventResize}),this.isSizeDirty=!0},o.prototype.updateSize=function(t){e.prototype.updateSize.call(this,t),this.lane.updateSize(t)},o.prototype.getHeightEls=function(){return[this.spreadsheetRow.heightEl,this.innerContainerEl]},o}(c);f.addEqualityFuncs({rowSpans:t.isArraysEqual});var m=function(e){function r(r,o){var i=e.call(this,r)||this;return i.resizables=[],i.colWidths=[],i.emitter=new t.EmitterMixin,o.appendChild(i.tableEl=t.createElement("table",{className:i.theme.getClass("tableGrid")})),i}return l(r,e),r.prototype.destroy=function(){for(var r=0,o=this.resizables;r<o.length;r++){o[r].destroy()}t.removeElement(this.tableEl),e.prototype.destroy.call(this)},r.prototype.render=function(e){var r=this.theme,o=e.colSpecs,i="<colgroup>"+e.colTags+"</colgroup><tbody>";e.superHeaderText&&(i+='<tr class="fc-super"><th class="'+r.getClass("widgetHeader")+'" colspan="'+o.length+'"><div class="fc-cell-content"><span class="fc-cell-text">'+t.htmlEscape(e.superHeaderText)+"</span></div></th></tr>"),i+="<tr>";for(var s=0;s<o.length;s++){var n=o[s],l=s===o.length-1;i+='<th class="'+r.getClass("widgetHeader")+'"><div><div class="fc-cell-content">'+(n.isMain?'<span class="fc-expander-space"><span class="fc-icon"></span></span>':"")+'<span class="fc-cell-text">'+t.htmlEscape(n.labelText||"")+"</span></div>"+(l?"":'<div class="fc-col-resizer"></div>')+"</div></th>"}i+="</tr>",i+="</tbody>",this.tableEl.innerHTML=i,this.thEls=Array.prototype.slice.call(this.tableEl.querySelectorAll("th")),this.colEls=Array.prototype.slice.call(this.tableEl.querySelectorAll("col")),this.resizerEls=Array.prototype.slice.call(this.tableEl.querySelectorAll(".fc-col-resizer")),this.initColResizing()},r.prototype.initColResizing=function(){var e=this,t=this.calendar.pluginSystem.hooks.elementDraggingImpl;t&&(this.resizables=this.resizerEls.map(function(r,o){var i,s=new t(r);return s.emitter.on("dragstart",function(){"number"!=typeof(i=e.colWidths[o])&&(i=e.thEls[o].getBoundingClientRect().width)}),s.emitter.on("dragmove",function(t){e.colWidths[o]=Math.max(i+t.deltaX*(e.isRtl?-1:1),30),e.emitter.trigger("colwidthchange",e.colWidths)}),s.setAutoScrollEnabled(!1),s}))},r}(t.Component),g=function(e){function o(o,i,s){var n=e.call(this,o)||this;n._renderCells=t.memoizeRendering(n.renderCells,n.unrenderCells),n.layout=new r.HeaderBodyLayout(i,s,"clipped-scroll");var l=n.layout.headerScroller.enhancedScroll,a=n.layout.bodyScroller.enhancedScroll;return n.header=new m(o,l.canvas.contentEl),n.header.emitter.on("colwidthchange",function(e){n.applyColWidths(e)}),a.canvas.contentEl.appendChild(n.bodyContainerEl=t.createElement("div",{className:"fc-rows"},"<table><colgroup /><tbody /></table>")),n.bodyColGroup=n.bodyContainerEl.querySelector("colgroup"),n.bodyTbody=n.bodyContainerEl.querySelector("tbody"),n}return l(o,e),o.prototype.destroy=function(){this.header.destroy(),this.layout.destroy(),this._renderCells.unrender(),e.prototype.destroy.call(this)},o.prototype.render=function(e){this._renderCells(e.superHeaderText,e.colSpecs)},o.prototype.renderCells=function(e,t){var r=this.renderColTags(t);this.header.receiveProps({superHeaderText:e,colSpecs:t,colTags:r}),this.bodyColGroup.innerHTML=r,this.bodyColEls=Array.prototype.slice.call(this.bodyColGroup.querySelectorAll("col")),this.applyColWidths(t.map(function(e){return e.width}))},o.prototype.unrenderCells=function(){this.bodyColGroup.innerHTML=""},o.prototype.renderColTags=function(e){for(var t="",r=0,o=e;r<o.length;r++){o[r].isMain?t+='<col class="fc-main-col"/>':t+="<col/>"}return t},o.prototype.updateSize=function(e,t,r){this.layout.setHeight(t,r)},o.prototype.applyColWidths=function(e){var t=this;e.forEach(function(e,r){var o,i=t.header.colEls[r],s=t.bodyColEls[r];"number"==typeof e?o=e+"px":null==typeof e&&(o=""),i.style.width=s.style.width=o})},o}(t.Component),v=function(e){function i(i,s,n,l){var a=e.call(this,i,s,n,l)||this;a.isStickyScrollDirty=!1,a.rowNodes=[],a.rowComponents=[],a.rowComponentsById={},a.resourceAreaWidthDraggings=[],a.splitter=new o.ResourceSplitter,a.hasResourceBusinessHours=t.memoize(S),a.buildRowNodes=t.memoize(o.buildRowNodes),a.hasNesting=t.memoize(x),a._updateHasNesting=t.memoizeRendering(a.updateHasNesting);var c=a.opt("resourceColumns")||[],d=a.opt("resourceLabelText"),p=null;c.length?p=d:c.push({labelText:d||"Resources",text:o.buildResourceTextFunc(a.opt("resourceText"),a.calendar)});for(var u=[],h=[],y=[],f=!1,m=!1,v=0,E=c;v<E.length;v++){var C=E[v];C.group?h.push(C):u.push(C)}if(u[0].isMain=!0,h.length)y=h,f=!0;else{var w=a.opt("resourceGroupField");w&&(m=!0,y.push({field:w,text:a.opt("resourceGroupText"),render:a.opt("resourceGroupRender")}))}for(var b=[],R=0,T=t.parseFieldSpecs(a.opt("resourceOrder"));R<T.length;R++){for(var A=T[R],H=!1,z=0,I=y;z<I.length;z++){var D=I[z];if(D.field===A.field){D.order=A.order,H=!0;break}}H||b.push(A)}a.superHeaderText=p,a.isVGrouping=f,a.isHGrouping=m,a.groupSpecs=y,a.colSpecs=h.concat(u),a.orderSpecs=b,a.el.classList.add("fc-timeline"),!1===a.opt("eventOverlap")&&a.el.classList.add("fc-no-overlap"),a.el.innerHTML=a.renderSkeletonHtml(),a.resourceAreaHeadEl=a.el.querySelector("thead .fc-resource-area"),a.setResourceAreaWidth(a.opt("resourceAreaWidth")),a.initResourceAreaWidthDragging(),a.miscHeight=a.el.getBoundingClientRect().height,a.spreadsheet=new g(a.context,a.resourceAreaHeadEl,a.el.querySelector("tbody .fc-resource-area")),a.timeAxis=new r.TimeAxis(a.context,a.el.querySelector("thead .fc-time-area"),a.el.querySelector("tbody .fc-time-area"));var _=t.createElement("div",{className:"fc-rows"},"<table><tbody /></table>");return a.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl.appendChild(_),a.timeAxisTbody=_.querySelector("tbody"),a.lane=new r.TimelineLane(a.context,null,a.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl,a.timeAxis),a.bodyScrollJoiner=new r.ScrollJoiner("vertical",[a.spreadsheet.layout.bodyScroller,a.timeAxis.layout.bodyScroller]),a.spreadsheetBodyStickyScroller=new r.StickyScroller(a.spreadsheet.layout.bodyScroller.enhancedScroll,a.isRtl,!0),a.spreadsheet.receiveProps({superHeaderText:a.superHeaderText,colSpecs:a.colSpecs}),i.calendar.registerInteractiveComponent(a,{el:a.timeAxis.slats.el}),a}return l(i,e),i.prototype.renderSkeletonHtml=function(){var e=this.theme;return'<table class="'+e.getClass("tableGrid")+'"> <thead class="fc-head"> <tr> <td class="fc-resource-area '+e.getClass("widgetHeader")+'"></td> <td class="fc-divider fc-col-resizer '+e.getClass("widgetHeader")+'"></td> <td class="fc-time-area '+e.getClass("widgetHeader")+'"></td> </tr> </thead> <tbody class="fc-body"> <tr> <td class="fc-resource-area '+e.getClass("widgetContent")+'"></td> <td class="fc-divider fc-col-resizer '+e.getClass("widgetHeader")+'"></td> <td class="fc-time-area '+e.getClass("widgetContent")+'"></td> </tr> </tbody> </table>'},i.prototype.render=function(t){e.prototype.render.call(this,t);var r=this.splitter.splitProps(t),o=this.hasResourceBusinessHours(t.resourceStore);this.timeAxis.receiveProps({dateProfile:t.dateProfile}),this.lane.receiveProps(a({},r[""],{dateProfile:t.dateProfile,nextDayThreshold:this.nextDayThreshold,businessHours:o?null:t.businessHours}));var i=this.buildRowNodes(t.resourceStore,this.groupSpecs,this.orderSpecs,this.isVGrouping,t.resourceEntityExpansions,this.opt("resourcesInitiallyExpanded"));this._updateHasNesting(this.hasNesting(i)),this.diffRows(i),this.renderRows(t.dateProfile,o?t.businessHours:null,r)},i.prototype.updateHasNesting=function(e){var t=this.el.classList;e?t.remove("fc-flat"):t.add("fc-flat")},i.prototype.diffRows=function(e){var t=this.rowNodes,r=t.length,o={},i=0,s=0;for(i=0;i<r;i++)o[t[i].id]=i;for(i=0,s=0;s<e.length;s++){var n=e[s],l=o[n.id];null!=l&&l>=i?(this.removeRows(s,l-i,t),i=l+1):this.addRow(s,n)}this.removeRows(s,r-i,t),this.rowNodes=e},i.prototype.addRow=function(e,t){var r=this.rowComponents,o=this.rowComponentsById,i=r[e],s=this.buildChildComponent(t,this.spreadsheet.bodyTbody,i?i.spreadsheetTr:null,this.timeAxisTbody,i?i.timeAxisTr:null);r.splice(e,0,s),o[t.id]=s},i.prototype.removeRows=function(e,t,r){if(t){for(var o=this.rowComponents,i=this.rowComponentsById,s=0;s<t;s++){o[e+s].destroy(),delete i[r[s].id]}o.splice(e,t)}},i.prototype.buildChildComponent=function(e,t,r,o,i){return e.group?new u(this.context,t,r,o,i):e.resource?new f(this.context,t,r,o,i,this.timeAxis):void 0},i.prototype.renderRows=function(e,t,r){for(var o=this.rowNodes,i=this.rowComponents,s=0;s<o.length;s++){var n=o[s],l=i[s];if(n.group)l.receiveProps({spreadsheetColCnt:this.colSpecs.length,id:n.id,isExpanded:n.isExpanded,group:n.group});else{var c=n.resource;l.receiveProps(a({},r[c.id],{dateProfile:e,nextDayThreshold:this.nextDayThreshold,businessHours:c.businessHours||t,colSpecs:this.colSpecs,id:n.id,rowSpans:n.rowSpans,depth:n.depth,isExpanded:n.isExpanded,hasChildren:n.hasChildren,resource:n.resource}))}}},i.prototype.updateSize=function(e,r,o){var i=this.calendar,s=e||i.isViewUpdated||i.isDatesUpdated||i.isEventsUpdated;s&&(this.syncHeadHeights(),this.timeAxis.updateSize(e,r-this.miscHeight,o),this.spreadsheet.updateSize(e,r-this.miscHeight,o));var n=this.updateRowSizes(e);this.lane.updateSize(e),(s||n)&&(this.bodyScrollJoiner.update(),this.timeAxis.layout.scrollJoiner.update(),this.rowPositions=new t.PositionCache(this.timeAxis.slats.el,this.rowComponents.map(function(e){return e.timeAxisTr}),!1,!0),this.rowPositions.build(),this.isStickyScrollDirty=!0)},i.prototype.syncHeadHeights=function(){var e=this.spreadsheet.header.tableEl,t=this.timeAxis.header.tableEl;e.style.height="",t.style.height="";var r=Math.max(e.getBoundingClientRect().height,t.getBoundingClientRect().height);e.style.height=t.style.height=r+"px"},i.prototype.updateRowSizes=function(e){var t=this.rowComponents;e||(t=t.filter(function(e){return e.isSizeDirty}));for(var r=t.map(function(e){return e.getHeightEls()}),o=0,i=r;o<i.length;o++)for(var s=0,n=i[o];s<n.length;s++){n[s].style.height=""}for(var l=0,a=t;l<a.length;l++){a[l].updateSize(e)}for(var c=r.map(function(e){for(var t=null,r=0,o=e;r<o.length;r++){var i=o[r].getBoundingClientRect().height;(null===t||i>t)&&(t=i)}return t}),d=0;d<r.length;d++)for(var p=0,u=r[d];p<u.length;p++){u[p].style.height=c[d]+"px"}return t.length},i.prototype.destroy=function(){for(var t=0,r=this.rowComponents;t<r.length;t++){r[t].destroy()}this.rowNodes=[],this.rowComponents=[],this.spreadsheet.destroy(),this.timeAxis.destroy();for(var o=0,i=this.resourceAreaWidthDraggings;o<i.length;o++){i[o].destroy()}this.spreadsheetBodyStickyScroller.destroy(),e.prototype.destroy.call(this),this.calendar.unregisterInteractiveComponent(this)},i.prototype.getNowIndicatorUnit=function(e){return this.timeAxis.getNowIndicatorUnit(e)},i.prototype.renderNowIndicator=function(e){this.timeAxis.renderNowIndicator(e)},i.prototype.unrenderNowIndicator=function(){this.timeAxis.unrenderNowIndicator()},i.prototype.queryScroll=function(){var t=e.prototype.queryScroll.call(this);return this.props.resourceStore&&a(t,this.queryResourceScroll()),t},i.prototype.applyScroll=function(t,r){e.prototype.applyScroll.call(this,t,r),this.props.resourceStore&&this.applyResourceScroll(t),(r||this.isStickyScrollDirty)&&(this.isStickyScrollDirty=!1,this.spreadsheetBodyStickyScroller.updateSize(),this.timeAxis.updateStickyScrollers())},i.prototype.computeDateScroll=function(e){return this.timeAxis.computeDateScroll(e)},i.prototype.queryDateScroll=function(){return this.timeAxis.queryDateScroll()},i.prototype.applyDateScroll=function(e){this.timeAxis.applyDateScroll(e)},i.prototype.queryResourceScroll=function(){for(var e=this.rowComponents,t=this.rowNodes,r={},o=this.timeAxis.layout.bodyScroller.el.getBoundingClientRect().top,i=0;i<e.length;i++){var s=e[i],n=t[i],l=s.timeAxisTr.getBoundingClientRect().bottom;if(l>o){r.rowId=n.id,r.bottom=l-o;break}}return r},i.prototype.applyResourceScroll=function(e){var t=e.forcedRowId||e.rowId;if(t){var r=this.rowComponentsById[t];if(r){var o=r.timeAxisTr;if(o){var i=this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.el.getBoundingClientRect().top,s=o.getBoundingClientRect(),n=(e.forcedRowId?s.top:s.bottom-e.bottom)-i;this.timeAxis.layout.bodyScroller.enhancedScroll.setScrollTop(n),this.spreadsheet.layout.bodyScroller.enhancedScroll.setScrollTop(n)}}}},i.prototype.buildPositionCaches=function(){this.timeAxis.slats.updateSize(),this.rowPositions.build()},i.prototype.queryHit=function(e,t){var r=this.rowPositions,o=this.timeAxis.slats,i=r.topToIndex(t);if(null!=i){var s=this.rowNodes[i].resource;if(s){var n=o.positionToHit(e);if(n)return{component:this,dateSpan:{range:n.dateSpan.range,allDay:n.dateSpan.allDay,resourceId:s.id},rect:{left:n.left,right:n.right,top:r.tops[i],bottom:r.bottoms[i]},dayEl:n.dayEl,layer:0}}}},i.prototype.setResourceAreaWidth=function(e){this.resourceAreaWidth=e,t.applyStyleProp(this.resourceAreaHeadEl,"width",e||"")},i.prototype.initResourceAreaWidthDragging=function(){var e=this,t=Array.prototype.slice.call(this.el.querySelectorAll(".fc-col-resizer")),r=this.calendar.pluginSystem.hooks.elementDraggingImpl;r&&(this.resourceAreaWidthDraggings=t.map(function(t){var o,i,s=new r(t);return s.emitter.on("dragstart",function(){"number"!=typeof(o=e.resourceAreaWidth)&&(o=e.resourceAreaHeadEl.getBoundingClientRect().width),i=e.el.getBoundingClientRect().width}),s.emitter.on("dragmove",function(t){var r=o+t.deltaX*(e.isRtl?-1:1);r=Math.max(r,30),r=Math.min(r,i-30),e.setResourceAreaWidth(r)}),s.setAutoScrollEnabled(!1),s}))},i.needsResourceData=!0,i}(t.View);function S(e){for(var t in e){if(e[t].businessHours)return!0}return!1}function x(e){for(var t=0,r=e;t<r.length;t++){var o=r[t];if(o.group)return!0;if(o.resource&&o.hasChildren)return!0}return!1}var E=t.createPlugin({deps:[s,i],defaultView:"resourceTimelineDay",views:{resourceTimeline:{class:v,resourceAreaWidth:"30%",resourcesInitiallyExpanded:!0,eventResizableFromStart:!0},resourceTimelineDay:{type:"resourceTimeline",duration:{days:1}},resourceTimelineWeek:{type:"resourceTimeline",duration:{weeks:1}},resourceTimelineMonth:{type:"resourceTimeline",duration:{months:1}},resourceTimelineYear:{type:"resourceTimeline",duration:{years:1}}}});e.ResourceTimelineView=v,e.default=E,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/resource-timeline/package.json b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..75c1775fb8b3a62225d727aa508b49bfc27c9def
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/resource-timeline/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "@fullcalendar/resource-timeline",
+  "version": "4.3.0",
+  "title": "FullCalendar Resource Timeline Plugin",
+  "description": "Display events and resources on a horizontal time axis",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/scheduler",
+  "docs": "https://fullcalendar.io/docs/timeline-view",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar-scheduler.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar-scheduler"
+  },
+  "license": "SEE LICENSE IN LICENSE.md",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "dependencies": {
+    "@fullcalendar/resource-common": "~4.3.0",
+    "@fullcalendar/timeline": "~4.3.0"
+  },
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/rrule/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/README.md b/AKPlan/static/AKPlan/fullcalendar/rrule/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7b56bed789e521bbc60d5963867b603435977341
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar RRule Plugin
+
+A connector to the RRule library, for recurring events
+
+[View the docs &raquo;](https://fullcalendar.io/docs/rrule-plugin)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/rrule/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8ce74e975573a1a7c8409ab3340bd389ba70e000
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/main.d.ts
@@ -0,0 +1,9 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/rrule' {
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/rrule/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..b201264ee51038b34cb8d6ce27b0dec7637d6bcf
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/main.esm.js
@@ -0,0 +1,121 @@
+/*!
+FullCalendar RRule Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { rrulestr, RRule } from 'rrule';
+import { createPlugin, refineProps, createDuration } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+var EVENT_DEF_PROPS = {
+    rrule: null,
+    duration: createDuration
+};
+var recurring = {
+    parse: function (rawEvent, leftoverProps, dateEnv) {
+        if (rawEvent.rrule != null) {
+            var props = refineProps(rawEvent, EVENT_DEF_PROPS, {}, leftoverProps);
+            var parsed = parseRRule(props.rrule, dateEnv);
+            if (parsed) {
+                return {
+                    typeData: parsed.rrule,
+                    allDayGuess: parsed.allDayGuess,
+                    duration: props.duration
+                };
+            }
+        }
+        return null;
+    },
+    expand: function (rrule, framingRange) {
+        // we WANT an inclusive start and in exclusive end, but the js rrule lib will only do either BOTH
+        // inclusive or BOTH exclusive, which is stupid: https://github.com/jakubroztocil/rrule/issues/84
+        // Workaround: make inclusive, which will generate extra occurences, and then trim.
+        return rrule.between(framingRange.start, framingRange.end, true)
+            .filter(function (date) {
+            return date.valueOf() < framingRange.end.valueOf();
+        });
+    }
+};
+var main = createPlugin({
+    recurringTypes: [recurring]
+});
+function parseRRule(input, dateEnv) {
+    var allDayGuess = null;
+    var rrule;
+    if (typeof input === 'string') {
+        rrule = rrulestr(input);
+    }
+    else if (typeof input === 'object' && input) { // non-null object
+        var refined = __assign({}, input); // copy
+        if (typeof refined.dtstart === 'string') {
+            var dtstartMeta = dateEnv.createMarkerMeta(refined.dtstart);
+            if (dtstartMeta) {
+                refined.dtstart = dtstartMeta.marker;
+                allDayGuess = dtstartMeta.isTimeUnspecified;
+            }
+            else {
+                delete refined.dtstart;
+            }
+        }
+        if (typeof refined.until === 'string') {
+            refined.until = dateEnv.createMarker(refined.until);
+        }
+        if (refined.freq != null) {
+            refined.freq = convertConstant(refined.freq);
+        }
+        if (refined.wkst != null) {
+            refined.wkst = convertConstant(refined.wkst);
+        }
+        else {
+            refined.wkst = (dateEnv.weekDow - 1 + 7) % 7; // convert Sunday-first to Monday-first
+        }
+        if (refined.byweekday != null) {
+            refined.byweekday = convertConstants(refined.byweekday); // the plural version
+        }
+        rrule = new RRule(refined);
+    }
+    if (rrule) {
+        return { rrule: rrule, allDayGuess: allDayGuess };
+    }
+    return null;
+}
+function convertConstants(input) {
+    if (Array.isArray(input)) {
+        return input.map(convertConstant);
+    }
+    return convertConstant(input);
+}
+function convertConstant(input) {
+    if (typeof input === 'string') {
+        return RRule[input.toUpperCase()];
+    }
+    return input;
+}
+
+export default main;
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/main.js b/AKPlan/static/AKPlan/fullcalendar/rrule/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..1ed7bd76b275add2a0397f9323d9d8f3305fefdd
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/main.js
@@ -0,0 +1,128 @@
+/*!
+FullCalendar RRule Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('rrule'), require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', 'rrule', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarRrule = {}, global.rrule, global.FullCalendar));
+}(this, function (exports, rrule, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    var EVENT_DEF_PROPS = {
+        rrule: null,
+        duration: core.createDuration
+    };
+    var recurring = {
+        parse: function (rawEvent, leftoverProps, dateEnv) {
+            if (rawEvent.rrule != null) {
+                var props = core.refineProps(rawEvent, EVENT_DEF_PROPS, {}, leftoverProps);
+                var parsed = parseRRule(props.rrule, dateEnv);
+                if (parsed) {
+                    return {
+                        typeData: parsed.rrule,
+                        allDayGuess: parsed.allDayGuess,
+                        duration: props.duration
+                    };
+                }
+            }
+            return null;
+        },
+        expand: function (rrule, framingRange) {
+            // we WANT an inclusive start and in exclusive end, but the js rrule lib will only do either BOTH
+            // inclusive or BOTH exclusive, which is stupid: https://github.com/jakubroztocil/rrule/issues/84
+            // Workaround: make inclusive, which will generate extra occurences, and then trim.
+            return rrule.between(framingRange.start, framingRange.end, true)
+                .filter(function (date) {
+                return date.valueOf() < framingRange.end.valueOf();
+            });
+        }
+    };
+    var main = core.createPlugin({
+        recurringTypes: [recurring]
+    });
+    function parseRRule(input, dateEnv) {
+        var allDayGuess = null;
+        var rrule$1;
+        if (typeof input === 'string') {
+            rrule$1 = rrule.rrulestr(input);
+        }
+        else if (typeof input === 'object' && input) { // non-null object
+            var refined = __assign({}, input); // copy
+            if (typeof refined.dtstart === 'string') {
+                var dtstartMeta = dateEnv.createMarkerMeta(refined.dtstart);
+                if (dtstartMeta) {
+                    refined.dtstart = dtstartMeta.marker;
+                    allDayGuess = dtstartMeta.isTimeUnspecified;
+                }
+                else {
+                    delete refined.dtstart;
+                }
+            }
+            if (typeof refined.until === 'string') {
+                refined.until = dateEnv.createMarker(refined.until);
+            }
+            if (refined.freq != null) {
+                refined.freq = convertConstant(refined.freq);
+            }
+            if (refined.wkst != null) {
+                refined.wkst = convertConstant(refined.wkst);
+            }
+            else {
+                refined.wkst = (dateEnv.weekDow - 1 + 7) % 7; // convert Sunday-first to Monday-first
+            }
+            if (refined.byweekday != null) {
+                refined.byweekday = convertConstants(refined.byweekday); // the plural version
+            }
+            rrule$1 = new rrule.RRule(refined);
+        }
+        if (rrule$1) {
+            return { rrule: rrule$1, allDayGuess: allDayGuess };
+        }
+        return null;
+    }
+    function convertConstants(input) {
+        if (Array.isArray(input)) {
+            return input.map(convertConstant);
+        }
+        return convertConstant(input);
+    }
+    function convertConstant(input) {
+        if (typeof input === 'string') {
+            return rrule.RRule[input.toUpperCase()];
+        }
+        return input;
+    }
+
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/main.min.js b/AKPlan/static/AKPlan/fullcalendar/rrule/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..44fff76b93b023a6a74c1ca9e3daa9ca58a555b8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar RRule Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("rrule"),require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","rrule","@fullcalendar/core"],r):r((e=e||self).FullCalendarRrule={},e.rrule,e.FullCalendar)}(this,function(e,r,t){"use strict";var n=function(){return(n=Object.assign||function(e){for(var r,t=1,n=arguments.length;t<n;t++)for(var u in r=arguments[t])Object.prototype.hasOwnProperty.call(r,u)&&(e[u]=r[u]);return e}).apply(this,arguments)},u={rrule:null,duration:t.createDuration},l={parse:function(e,l,a){if(null!=e.rrule){var f=t.refineProps(e,u,{},l),o=function(e,t){var u,l=null;if("string"==typeof e)u=r.rrulestr(e);else if("object"==typeof e&&e){var a=n({},e);if("string"==typeof a.dtstart){var f=t.createMarkerMeta(a.dtstart);f?(a.dtstart=f.marker,l=f.isTimeUnspecified):delete a.dtstart}"string"==typeof a.until&&(a.until=t.createMarker(a.until)),null!=a.freq&&(a.freq=i(a.freq)),null!=a.wkst?a.wkst=i(a.wkst):a.wkst=(t.weekDow-1+7)%7,null!=a.byweekday&&(a.byweekday=function(e){if(Array.isArray(e))return e.map(i);return i(e)}(a.byweekday)),u=new r.RRule(a)}if(u)return{rrule:u,allDayGuess:l};return null}(f.rrule,a);if(o)return{typeData:o.rrule,allDayGuess:o.allDayGuess,duration:f.duration}}return null},expand:function(e,r){return e.between(r.start,r.end,!0).filter(function(e){return e.valueOf()<r.end.valueOf()})}},a=t.createPlugin({recurringTypes:[l]});function i(e){return"string"==typeof e?r.RRule[e.toUpperCase()]:e}e.default=a,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/rrule/package.json b/AKPlan/static/AKPlan/fullcalendar/rrule/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..cf9706bc617c8ceb2fc3a1b57d5c0dd759742452
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/rrule/package.json
@@ -0,0 +1,34 @@
+{
+  "name": "@fullcalendar/rrule",
+  "version": "4.3.0",
+  "title": "FullCalendar RRule Plugin",
+  "description": "A connector to the RRule library, for recurring events",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/rrule-plugin",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0",
+    "rrule": "^2.6.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/LICENSE.txt b/AKPlan/static/AKPlan/fullcalendar/timegrid/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2149cfbeff4f2e3649bc950982aa72b894f7e521
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2019 Adam Shaw
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/README.md b/AKPlan/static/AKPlan/fullcalendar/timegrid/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ac13676c847d14e5f73afe063781fc85756d842d
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Time Grid Plugin
+
+Display your events on a grid of time slots
+
+[View the docs &raquo;](https://fullcalendar.io/docs/timegrid-view)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar)
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/main.css b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..b8ee6448a2f7f06a2892d0a2bb64991b73dd95cd
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.css
@@ -0,0 +1,309 @@
+@charset "UTF-8";
+/* TimeGridView all-day area
+--------------------------------------------------------------------------------------------------*/
+.fc-timeGrid-view .fc-day-grid {
+  position: relative;
+  z-index: 2;
+  /* so the "more.." popover will be over the time grid */
+}
+
+.fc-timeGrid-view .fc-day-grid .fc-row {
+  min-height: 3em;
+  /* all-day section will never get shorter than this */
+}
+
+.fc-timeGrid-view .fc-day-grid .fc-row .fc-content-skeleton {
+  padding-bottom: 1em;
+  /* give space underneath events for clicking/selecting days */
+}
+
+/* TimeGrid axis running down the side (for both the all-day area and the slot area)
+--------------------------------------------------------------------------------------------------*/
+.fc .fc-axis {
+  /* .fc to overcome default cell styles */
+  vertical-align: middle;
+  padding: 0 4px;
+  white-space: nowrap;
+}
+
+.fc-ltr .fc-axis {
+  text-align: right;
+}
+
+.fc-rtl .fc-axis {
+  text-align: left;
+}
+
+/* TimeGrid Structure
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid-container,
+.fc-time-grid {
+  /* so slats/bg/content/etc positions get scoped within here */
+  position: relative;
+  z-index: 1;
+}
+
+.fc-time-grid {
+  min-height: 100%;
+  /* so if height setting is 'auto', .fc-bg stretches to fill height */
+}
+
+.fc-time-grid table {
+  /* don't put outer borders on slats/bg/content/etc */
+  border: 0 hidden transparent;
+}
+
+.fc-time-grid > .fc-bg {
+  z-index: 1;
+}
+
+.fc-time-grid .fc-slats,
+.fc-time-grid > hr {
+  /* the <hr> TimeGridView injects when grid is shorter than scroller */
+  position: relative;
+  z-index: 2;
+}
+
+.fc-time-grid .fc-content-col {
+  position: relative;
+  /* because now-indicator lives directly inside */
+}
+
+.fc-time-grid .fc-content-skeleton {
+  position: absolute;
+  z-index: 3;
+  top: 0;
+  left: 0;
+  right: 0;
+}
+
+/* divs within a cell within the fc-content-skeleton */
+.fc-time-grid .fc-business-container {
+  position: relative;
+  z-index: 1;
+}
+
+.fc-time-grid .fc-bgevent-container {
+  position: relative;
+  z-index: 2;
+}
+
+.fc-time-grid .fc-highlight-container {
+  position: relative;
+  z-index: 3;
+}
+
+.fc-time-grid .fc-event-container {
+  position: relative;
+  z-index: 4;
+}
+
+.fc-time-grid .fc-now-indicator-line {
+  z-index: 5;
+}
+
+.fc-time-grid .fc-mirror-container {
+  /* also is fc-event-container */
+  position: relative;
+  z-index: 6;
+}
+
+/* TimeGrid Slats (lines that run horizontally)
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-slats td {
+  height: 1.5em;
+  border-bottom: 0;
+  /* each cell is responsible for its top border */
+}
+
+.fc-time-grid .fc-slats .fc-minor td {
+  border-top-style: dotted;
+}
+
+/* TimeGrid Highlighting Slots
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-highlight-container {
+  /* a div within a cell within the fc-highlight-skeleton */
+  position: relative;
+  /* scopes the left/right of the fc-highlight to be in the column */
+}
+
+.fc-time-grid .fc-highlight {
+  position: absolute;
+  left: 0;
+  right: 0;
+  /* top and bottom will be in by JS */
+}
+
+/* TimeGrid Event Containment
+--------------------------------------------------------------------------------------------------*/
+.fc-ltr .fc-time-grid .fc-event-container {
+  /* space on the sides of events for LTR (default) */
+  margin: 0 2.5% 0 2px;
+}
+
+.fc-rtl .fc-time-grid .fc-event-container {
+  /* space on the sides of events for RTL */
+  margin: 0 2px 0 2.5%;
+}
+
+.fc-time-grid .fc-event,
+.fc-time-grid .fc-bgevent {
+  position: absolute;
+  z-index: 1;
+  /* scope inner z-index's */
+}
+
+.fc-time-grid .fc-bgevent {
+  /* background events always span full width */
+  left: 0;
+  right: 0;
+}
+
+/* TimeGrid Event Styling
+----------------------------------------------------------------------------------------------------
+We use the full "fc-time-grid-event" class instead of using descendants because the event won't
+be a descendant of the grid when it is being dragged.
+*/
+.fc-time-grid-event {
+  margin-bottom: 1px;
+}
+
+.fc-time-grid-event-inset {
+  -webkit-box-shadow: 0px 0px 0px 1px #fff;
+  box-shadow: 0px 0px 0px 1px #fff;
+}
+
+.fc-time-grid-event.fc-not-start {
+  /* events that are continuing from another day */
+  /* replace space made by the top border with padding */
+  border-top-width: 0;
+  padding-top: 1px;
+  /* remove top rounded corners */
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+
+.fc-time-grid-event.fc-not-end {
+  /* replace space made by the top border with padding */
+  border-bottom-width: 0;
+  padding-bottom: 1px;
+  /* remove bottom rounded corners */
+  border-bottom-left-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+.fc-time-grid-event .fc-content {
+  overflow: hidden;
+  max-height: 100%;
+}
+
+.fc-time-grid-event .fc-time,
+.fc-time-grid-event .fc-title {
+  padding: 0 1px;
+}
+
+.fc-time-grid-event .fc-time {
+  font-size: 0.85em;
+  white-space: nowrap;
+}
+
+/* short mode, where time and title are on the same line */
+.fc-time-grid-event.fc-short .fc-content {
+  /* don't wrap to second line (now that contents will be inline) */
+  white-space: nowrap;
+}
+
+.fc-time-grid-event.fc-short .fc-time,
+.fc-time-grid-event.fc-short .fc-title {
+  /* put the time and title on the same line */
+  display: inline-block;
+  vertical-align: top;
+}
+
+.fc-time-grid-event.fc-short .fc-time span {
+  display: none;
+  /* don't display the full time text... */
+}
+
+.fc-time-grid-event.fc-short .fc-time:before {
+  content: attr(data-start);
+  /* ...instead, display only the start time */
+}
+
+.fc-time-grid-event.fc-short .fc-time:after {
+  content: " - ";
+  /* seperate with a dash, wrapped in nbsp's */
+}
+
+.fc-time-grid-event.fc-short .fc-title {
+  font-size: 0.85em;
+  /* make the title text the same size as the time */
+  padding: 0;
+  /* undo padding from above */
+}
+
+/* resizer (cursor device) */
+.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer {
+  left: 0;
+  right: 0;
+  bottom: 0;
+  height: 8px;
+  overflow: hidden;
+  line-height: 8px;
+  font-size: 11px;
+  font-family: monospace;
+  text-align: center;
+  cursor: s-resize;
+}
+
+.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after {
+  content: "=";
+}
+
+/* resizer (touch device) */
+.fc-time-grid-event.fc-selected .fc-resizer {
+  /* 10x10 dot */
+  border-radius: 5px;
+  border-width: 1px;
+  width: 8px;
+  height: 8px;
+  border-style: solid;
+  border-color: inherit;
+  background: #fff;
+  /* horizontally center */
+  left: 50%;
+  margin-left: -5px;
+  /* center on the bottom edge */
+  bottom: -5px;
+}
+
+/* Now Indicator
+--------------------------------------------------------------------------------------------------*/
+.fc-time-grid .fc-now-indicator-line {
+  border-top-width: 1px;
+  left: 0;
+  right: 0;
+}
+
+/* arrow on axis */
+.fc-time-grid .fc-now-indicator-arrow {
+  margin-top: -5px;
+  /* vertically center on top coordinate */
+}
+
+.fc-ltr .fc-time-grid .fc-now-indicator-arrow {
+  left: 0;
+  /* triangle pointing right... */
+  border-width: 5px 0 5px 6px;
+  border-top-color: transparent;
+  border-bottom-color: transparent;
+}
+
+.fc-rtl .fc-time-grid .fc-now-indicator-arrow {
+  right: 0;
+  /* triangle pointing left... */
+  border-width: 5px 6px 5px 0;
+  border-top-color: transparent;
+  border-bottom-color: transparent;
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2ec73d9e897a3d9b947b5b4344313d7a707fbc9b
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.d.ts
@@ -0,0 +1,220 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+//   ../../../../../@fullcalendar/daygrid
+
+declare module '@fullcalendar/timegrid' {
+    import AbstractTimeGridView from '@fullcalendar/timegrid/AbstractTimeGridView';
+    import TimeGridView, { buildDayTable } from '@fullcalendar/timegrid/TimeGridView';
+    import { TimeGridSeg } from '@fullcalendar/timegrid/TimeGrid';
+    import { TimeGridSlicer, buildDayRanges } from '@fullcalendar/timegrid/SimpleTimeGrid';
+    export { TimeGridView, AbstractTimeGridView, buildDayTable, buildDayRanges, TimeGridSlicer, TimeGridSeg };
+    export { default as TimeGrid } from '@fullcalendar/timegrid/TimeGrid';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/timegrid/AbstractTimeGridView' {
+    import { ScrollComponent, View, ViewSpec, DateProfileGenerator, ComponentContext, Duration } from '@fullcalendar/core';
+    import { DayGrid } from '@fullcalendar/daygrid';
+    import TimeGrid from '@fullcalendar/timegrid/TimeGrid';
+    import AllDaySplitter from '@fullcalendar/timegrid/AllDaySplitter';
+    export { TimeGridView as default, TimeGridView };
+    abstract class TimeGridView extends View {
+        timeGrid: TimeGrid;
+        dayGrid: DayGrid;
+        scroller: ScrollComponent;
+        axisWidth: any;
+        protected splitter: AllDaySplitter;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        renderSkeletonHtml(): string;
+        getNowIndicatorUnit(): string;
+        unrenderNowIndicator(): void;
+        updateSize(isResize: boolean, viewHeight: number, isAuto: boolean): void;
+        updateBaseSize(isResize: any, viewHeight: any, isAuto: any): void;
+        computeScrollerHeight(viewHeight: any): number;
+        computeDateScroll(duration: Duration): {
+            top: any;
+        };
+        queryDateScroll(): {
+            top: number;
+        };
+        applyDateScroll(scroll: any): void;
+        renderHeadIntroHtml: () => string;
+        axisStyleAttr(): string;
+        renderTimeGridBgIntroHtml: () => string;
+        renderTimeGridIntroHtml: () => string;
+        renderDayGridBgIntroHtml: () => string;
+        renderDayGridIntroHtml: () => string;
+    }
+}
+
+declare module '@fullcalendar/timegrid/TimeGridView' {
+    import { DateProfileGenerator, DateProfile, ComponentContext, ViewSpec, DayHeader, DayTable, ViewProps } from '@fullcalendar/core';
+    import { SimpleDayGrid } from '@fullcalendar/daygrid';
+    import SimpleTimeGrid from '@fullcalendar/timegrid/SimpleTimeGrid';
+    import AbstractTimeGridView from '@fullcalendar/timegrid/AbstractTimeGridView';
+    export { TimeGridView as default, TimeGridView };
+    class TimeGridView extends AbstractTimeGridView {
+        header: DayHeader;
+        simpleDayGrid: SimpleDayGrid;
+        simpleTimeGrid: SimpleTimeGrid;
+        constructor(_context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: ViewProps): void;
+        renderNowIndicator(date: any): void;
+    }
+    export function buildDayTable(dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator): DayTable;
+}
+
+declare module '@fullcalendar/timegrid/TimeGrid' {
+    import { PositionCache, Duration, DateMarker, DateFormatter, ComponentContext, DateComponent, Seg, EventSegUiInteractionState, DateProfile } from '@fullcalendar/core';
+    export interface RenderProps {
+        renderBgIntroHtml: () => string;
+        renderIntroHtml: () => string;
+    }
+    export interface TimeGridSeg extends Seg {
+        col: number;
+        start: DateMarker;
+        end: DateMarker;
+    }
+    export interface TimeGridCell {
+        date: DateMarker;
+        htmlAttrs?: string;
+    }
+    export interface TimeGridProps {
+        dateProfile: DateProfile;
+        cells: TimeGridCell[];
+        businessHourSegs: TimeGridSeg[];
+        bgEventSegs: TimeGridSeg[];
+        fgEventSegs: TimeGridSeg[];
+        dateSelectionSegs: TimeGridSeg[];
+        eventSelection: string;
+        eventDrag: EventSegUiInteractionState | null;
+        eventResize: EventSegUiInteractionState | null;
+    }
+    export { TimeGrid as default, TimeGrid };
+    class TimeGrid extends DateComponent<TimeGridProps> {
+        renderProps: RenderProps;
+        slotDuration: Duration;
+        snapDuration: Duration;
+        snapsPerSlot: any;
+        labelFormat: DateFormatter;
+        labelInterval: Duration;
+        colCnt: number;
+        colEls: HTMLElement[];
+        slatContainerEl: HTMLElement;
+        slatEls: HTMLElement[];
+        nowIndicatorEls: HTMLElement[];
+        colPositions: PositionCache;
+        slatPositions: PositionCache;
+        isSlatSizesDirty: boolean;
+        isColSizesDirty: boolean;
+        rootBgContainerEl: HTMLElement;
+        bottomRuleEl: HTMLElement;
+        contentSkeletonEl: HTMLElement;
+        colContainerEls: HTMLElement[];
+        fgContainerEls: HTMLElement[];
+        bgContainerEls: HTMLElement[];
+        mirrorContainerEls: HTMLElement[];
+        highlightContainerEls: HTMLElement[];
+        businessContainerEls: HTMLElement[];
+        constructor(context: ComponentContext, el: HTMLElement, renderProps: RenderProps);
+        processOptions(): void;
+        computeLabelInterval(slotDuration: any): any;
+        render(props: TimeGridProps): void;
+        destroy(): void;
+        updateSize(isResize: boolean): void;
+        _renderSlats(dateProfile: DateProfile): void;
+        renderSlatRowHtml(dateProfile: DateProfile): string;
+        _renderColumns(cells: TimeGridCell[], dateProfile: DateProfile): void;
+        _unrenderColumns(): void;
+        renderContentSkeleton(): void;
+        unrenderContentSkeleton(): void;
+        groupSegsByCol(segs: any): any[];
+        attachSegsByCol(segsByCol: any, containerEls: HTMLElement[]): void;
+        getNowIndicatorUnit(): string;
+        renderNowIndicator(segs: TimeGridSeg[], date: any): void;
+        unrenderNowIndicator(): void;
+        getTotalSlatHeight(): number;
+        computeDateTop(when: DateMarker, startOfDayDate?: DateMarker): any;
+        computeTimeTop(duration: Duration): any;
+        computeSegVerticals(segs: any): void;
+        assignSegVerticals(segs: any): void;
+        generateSegVerticalCss(seg: any): {
+            top: any;
+            bottom: number;
+        };
+        buildPositionCaches(): void;
+        buildColPositions(): void;
+        buildSlatPositions(): void;
+        positionToHit(positionLeft: any, positionTop: any): {
+            col: any;
+            dateSpan: {
+                range: {
+                    start: Date;
+                    end: Date;
+                };
+                allDay: boolean;
+            };
+            dayEl: HTMLElement;
+            relativeRect: {
+                left: any;
+                right: any;
+                top: any;
+                bottom: any;
+            };
+        };
+        _renderEventDrag(state: EventSegUiInteractionState): void;
+        _unrenderEventDrag(state: EventSegUiInteractionState): void;
+        _renderEventResize(state: EventSegUiInteractionState): void;
+        _unrenderEventResize(state: EventSegUiInteractionState): void;
+        _renderDateSelection(segs: Seg[]): void;
+        _unrenderDateSelection(segs: Seg[]): void;
+    }
+}
+
+declare module '@fullcalendar/timegrid/SimpleTimeGrid' {
+    import { DateComponent, DateProfile, EventStore, EventUiHash, EventInteractionState, DateSpan, DateRange, DayTable, DateEnv, DateMarker, Slicer, Hit, ComponentContext } from '@fullcalendar/core';
+    import TimeGrid, { TimeGridSeg } from '@fullcalendar/timegrid/TimeGrid';
+    export interface SimpleTimeGridProps {
+        dateProfile: DateProfile | null;
+        dayTable: DayTable;
+        businessHours: EventStore;
+        eventStore: EventStore;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+    }
+    export { SimpleTimeGrid as default, SimpleTimeGrid };
+    class SimpleTimeGrid extends DateComponent<SimpleTimeGridProps> {
+        timeGrid: TimeGrid;
+        constructor(context: ComponentContext, timeGrid: TimeGrid);
+        destroy(): void;
+        render(props: SimpleTimeGridProps): void;
+        renderNowIndicator(date: DateMarker): void;
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number): Hit;
+    }
+    export function buildDayRanges(dayTable: DayTable, dateProfile: DateProfile, dateEnv: DateEnv): DateRange[];
+    export class TimeGridSlicer extends Slicer<TimeGridSeg, [DateRange[]]> {
+        sliceRange(range: DateRange, dayRanges: DateRange[]): TimeGridSeg[];
+    }
+}
+
+declare module '@fullcalendar/timegrid/AllDaySplitter' {
+    import { Splitter, EventDef, DateSpan } from '@fullcalendar/core';
+    export { AllDaySplitter as default, AllDaySplitter };
+    class AllDaySplitter extends Splitter {
+        getKeyInfo(): {
+            allDay: {};
+            timed: {};
+        };
+        getKeysForDateSpan(dateSpan: DateSpan): string[];
+        getKeysForEventDef(eventDef: EventDef): string[];
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..0c5178c17ea3c17ac0f0462b097084635e2fe133
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.esm.js
@@ -0,0 +1,1345 @@
+/*!
+FullCalendar Time Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+import { createFormatter, removeElement, cssToStr, isMultiDayRange, htmlEscape, compareByFieldSpecs, applyStyle, FgEventRenderer, buildSegCompareObj, FillRenderer, memoizeRendering, createDuration, wholeDivideDurations, findElements, PositionCache, startOfDay, asRoughMs, formatIsoTimeString, addDurations, htmlToElement, createElement, multiplyDuration, DateComponent, hasBgRendering, Splitter, diffDays, buildGotoAnchorHtml, getAllDayHtml, ScrollComponent, matchCellWidths, uncompensateScroll, compensateScroll, subtractInnerElHeight, View, memoize, intersectRanges, Slicer, DayHeader, DaySeries, DayTable, createPlugin } from '@fullcalendar/core';
+import { DayBgRow, DayGrid, SimpleDayGrid } from '@fullcalendar/daygrid';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+/*
+Only handles foreground segs.
+Does not own rendering. Use for low-level util methods by TimeGrid.
+*/
+var TimeGridEventRenderer = /** @class */ (function (_super) {
+    __extends(TimeGridEventRenderer, _super);
+    function TimeGridEventRenderer(timeGrid) {
+        var _this = _super.call(this, timeGrid.context) || this;
+        _this.timeGrid = timeGrid;
+        _this.fullTimeFormat = createFormatter({
+            hour: 'numeric',
+            minute: '2-digit',
+            separator: _this.context.options.defaultRangeSeparator
+        });
+        return _this;
+    }
+    // Given an array of foreground segments, render a DOM element for each, computes position,
+    // and attaches to the column inner-container elements.
+    TimeGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+        var segsByCol = this.timeGrid.groupSegsByCol(segs);
+        // order the segs within each column
+        // TODO: have groupSegsByCol do this?
+        for (var col = 0; col < segsByCol.length; col++) {
+            segsByCol[col] = this.sortEventSegs(segsByCol[col]);
+        }
+        this.segsByCol = segsByCol;
+        this.timeGrid.attachSegsByCol(segsByCol, this.timeGrid.fgContainerEls);
+    };
+    TimeGridEventRenderer.prototype.detachSegs = function (segs) {
+        segs.forEach(function (seg) {
+            removeElement(seg.el);
+        });
+        this.segsByCol = null;
+    };
+    TimeGridEventRenderer.prototype.computeSegSizes = function (allSegs) {
+        var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
+        var colCnt = timeGrid.colCnt;
+        timeGrid.computeSegVerticals(allSegs); // horizontals relies on this
+        if (segsByCol) {
+            for (var col = 0; col < colCnt; col++) {
+                this.computeSegHorizontals(segsByCol[col]); // compute horizontal coordinates, z-index's, and reorder the array
+            }
+        }
+    };
+    TimeGridEventRenderer.prototype.assignSegSizes = function (allSegs) {
+        var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
+        var colCnt = timeGrid.colCnt;
+        timeGrid.assignSegVerticals(allSegs); // horizontals relies on this
+        if (segsByCol) {
+            for (var col = 0; col < colCnt; col++) {
+                this.assignSegCss(segsByCol[col]);
+            }
+        }
+    };
+    // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
+    TimeGridEventRenderer.prototype.computeEventTimeFormat = function () {
+        return {
+            hour: 'numeric',
+            minute: '2-digit',
+            meridiem: false
+        };
+    };
+    // Computes a default `displayEventEnd` value if one is not expliclty defined
+    TimeGridEventRenderer.prototype.computeDisplayEventEnd = function () {
+        return true;
+    };
+    // Renders the HTML for a single event segment's default rendering
+    TimeGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
+        var view = this.context.view;
+        var eventRange = seg.eventRange;
+        var eventDef = eventRange.def;
+        var eventUi = eventRange.ui;
+        var allDay = eventDef.allDay;
+        var isDraggable = view.computeEventDraggable(eventDef, eventUi);
+        var isResizableFromStart = seg.isStart && view.computeEventStartResizable(eventDef, eventUi);
+        var isResizableFromEnd = seg.isEnd && view.computeEventEndResizable(eventDef, eventUi);
+        var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
+        var skinCss = cssToStr(this.getSkinCss(eventUi));
+        var timeText;
+        var fullTimeText; // more verbose time text. for the print stylesheet
+        var startTimeText; // just the start time text
+        classes.unshift('fc-time-grid-event');
+        // if the event appears to span more than one day...
+        if (isMultiDayRange(eventRange.range)) {
+            // Don't display time text on segments that run entirely through a day.
+            // That would appear as midnight-midnight and would look dumb.
+            // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
+            if (seg.isStart || seg.isEnd) {
+                var unzonedStart = seg.start;
+                var unzonedEnd = seg.end;
+                timeText = this._getTimeText(unzonedStart, unzonedEnd, allDay); // TODO: give the timezones
+                fullTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, this.fullTimeFormat);
+                startTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, null, false); // displayEnd=false
+            }
+        }
+        else {
+            // Display the normal time text for the *event's* times
+            timeText = this.getTimeText(eventRange);
+            fullTimeText = this.getTimeText(eventRange, this.fullTimeFormat);
+            startTimeText = this.getTimeText(eventRange, null, false); // displayEnd=false
+        }
+        return '<a class="' + classes.join(' ') + '"' +
+            (eventDef.url ?
+                ' href="' + htmlEscape(eventDef.url) + '"' :
+                '') +
+            (skinCss ?
+                ' style="' + skinCss + '"' :
+                '') +
+            '>' +
+            '<div class="fc-content">' +
+            (timeText ?
+                '<div class="fc-time"' +
+                    ' data-start="' + htmlEscape(startTimeText) + '"' +
+                    ' data-full="' + htmlEscape(fullTimeText) + '"' +
+                    '>' +
+                    '<span>' + htmlEscape(timeText) + '</span>' +
+                    '</div>' :
+                '') +
+            (eventDef.title ?
+                '<div class="fc-title">' +
+                    htmlEscape(eventDef.title) +
+                    '</div>' :
+                '') +
+            '</div>' +
+            /* TODO: write CSS for this
+            (isResizableFromStart ?
+              '<div class="fc-resizer fc-start-resizer"></div>' :
+              ''
+              ) +
+            */
+            (isResizableFromEnd ?
+                '<div class="fc-resizer fc-end-resizer"></div>' :
+                '') +
+            '</a>';
+    };
+    // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
+    // Assumed the segs are already ordered.
+    // NOTE: Also reorders the given array by date!
+    TimeGridEventRenderer.prototype.computeSegHorizontals = function (segs) {
+        var levels;
+        var level0;
+        var i;
+        levels = buildSlotSegLevels(segs);
+        computeForwardSlotSegs(levels);
+        if ((level0 = levels[0])) {
+            for (i = 0; i < level0.length; i++) {
+                computeSlotSegPressures(level0[i]);
+            }
+            for (i = 0; i < level0.length; i++) {
+                this.computeSegForwardBack(level0[i], 0, 0);
+            }
+        }
+    };
+    // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
+    // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
+    // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
+    //
+    // The segment might be part of a "series", which means consecutive segments with the same pressure
+    // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
+    // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
+    // coordinate of the first segment in the series.
+    TimeGridEventRenderer.prototype.computeSegForwardBack = function (seg, seriesBackwardPressure, seriesBackwardCoord) {
+        var forwardSegs = seg.forwardSegs;
+        var i;
+        if (seg.forwardCoord === undefined) { // not already computed
+            if (!forwardSegs.length) {
+                // if there are no forward segments, this segment should butt up against the edge
+                seg.forwardCoord = 1;
+            }
+            else {
+                // sort highest pressure first
+                this.sortForwardSegs(forwardSegs);
+                // this segment's forwardCoord will be calculated from the backwardCoord of the
+                // highest-pressure forward segment.
+                this.computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
+                seg.forwardCoord = forwardSegs[0].backwardCoord;
+            }
+            // calculate the backwardCoord from the forwardCoord. consider the series
+            seg.backwardCoord = seg.forwardCoord -
+                (seg.forwardCoord - seriesBackwardCoord) / // available width for series
+                    (seriesBackwardPressure + 1); // # of segments in the series
+            // use this segment's coordinates to computed the coordinates of the less-pressurized
+            // forward segments
+            for (i = 0; i < forwardSegs.length; i++) {
+                this.computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
+            }
+        }
+    };
+    TimeGridEventRenderer.prototype.sortForwardSegs = function (forwardSegs) {
+        var objs = forwardSegs.map(buildTimeGridSegCompareObj);
+        var specs = [
+            // put higher-pressure first
+            { field: 'forwardPressure', order: -1 },
+            // put segments that are closer to initial edge first (and favor ones with no coords yet)
+            { field: 'backwardCoord', order: 1 }
+        ].concat(this.context.view.eventOrderSpecs);
+        objs.sort(function (obj0, obj1) {
+            return compareByFieldSpecs(obj0, obj1, specs);
+        });
+        return objs.map(function (c) {
+            return c._seg;
+        });
+    };
+    // Given foreground event segments that have already had their position coordinates computed,
+    // assigns position-related CSS values to their elements.
+    TimeGridEventRenderer.prototype.assignSegCss = function (segs) {
+        for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+            var seg = segs_1[_i];
+            applyStyle(seg.el, this.generateSegCss(seg));
+            if (seg.level > 0) {
+                seg.el.classList.add('fc-time-grid-event-inset');
+            }
+            // if the event is short that the title will be cut off,
+            // attach a className that condenses the title into the time area.
+            if (seg.eventRange.def.title && seg.bottom - seg.top < 30) {
+                seg.el.classList.add('fc-short'); // TODO: "condensed" is a better name
+            }
+        }
+    };
+    // Generates an object with CSS properties/values that should be applied to an event segment element.
+    // Contains important positioning-related properties that should be applied to any event element, customized or not.
+    TimeGridEventRenderer.prototype.generateSegCss = function (seg) {
+        var shouldOverlap = this.context.options.slotEventOverlap;
+        var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
+        var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
+        var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
+        var isRtl = this.timeGrid.isRtl;
+        var left; // amount of space from left edge, a fraction of the total width
+        var right; // amount of space from right edge, a fraction of the total width
+        if (shouldOverlap) {
+            // double the width, but don't go beyond the maximum forward coordinate (1.0)
+            forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
+        }
+        if (isRtl) {
+            left = 1 - forwardCoord;
+            right = backwardCoord;
+        }
+        else {
+            left = backwardCoord;
+            right = 1 - forwardCoord;
+        }
+        props.zIndex = seg.level + 1; // convert from 0-base to 1-based
+        props.left = left * 100 + '%';
+        props.right = right * 100 + '%';
+        if (shouldOverlap && seg.forwardPressure) {
+            // add padding to the edge so that forward stacked events don't cover the resizer's icon
+            props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
+        }
+        return props;
+    };
+    return TimeGridEventRenderer;
+}(FgEventRenderer));
+// Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is
+// left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date.
+function buildSlotSegLevels(segs) {
+    var levels = [];
+    var i;
+    var seg;
+    var j;
+    for (i = 0; i < segs.length; i++) {
+        seg = segs[i];
+        // go through all the levels and stop on the first level where there are no collisions
+        for (j = 0; j < levels.length; j++) {
+            if (!computeSlotSegCollisions(seg, levels[j]).length) {
+                break;
+            }
+        }
+        seg.level = j;
+        (levels[j] || (levels[j] = [])).push(seg);
+    }
+    return levels;
+}
+// For every segment, figure out the other segments that are in subsequent
+// levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
+function computeForwardSlotSegs(levels) {
+    var i;
+    var level;
+    var j;
+    var seg;
+    var k;
+    for (i = 0; i < levels.length; i++) {
+        level = levels[i];
+        for (j = 0; j < level.length; j++) {
+            seg = level[j];
+            seg.forwardSegs = [];
+            for (k = i + 1; k < levels.length; k++) {
+                computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
+            }
+        }
+    }
+}
+// Figure out which path forward (via seg.forwardSegs) results in the longest path until
+// the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
+function computeSlotSegPressures(seg) {
+    var forwardSegs = seg.forwardSegs;
+    var forwardPressure = 0;
+    var i;
+    var forwardSeg;
+    if (seg.forwardPressure === undefined) { // not already computed
+        for (i = 0; i < forwardSegs.length; i++) {
+            forwardSeg = forwardSegs[i];
+            // figure out the child's maximum forward path
+            computeSlotSegPressures(forwardSeg);
+            // either use the existing maximum, or use the child's forward pressure
+            // plus one (for the forwardSeg itself)
+            forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure);
+        }
+        seg.forwardPressure = forwardPressure;
+    }
+}
+// Find all the segments in `otherSegs` that vertically collide with `seg`.
+// Append into an optionally-supplied `results` array and return.
+function computeSlotSegCollisions(seg, otherSegs, results) {
+    if (results === void 0) { results = []; }
+    for (var i = 0; i < otherSegs.length; i++) {
+        if (isSlotSegCollision(seg, otherSegs[i])) {
+            results.push(otherSegs[i]);
+        }
+    }
+    return results;
+}
+// Do these segments occupy the same vertical space?
+function isSlotSegCollision(seg1, seg2) {
+    return seg1.bottom > seg2.top && seg1.top < seg2.bottom;
+}
+function buildTimeGridSegCompareObj(seg) {
+    var obj = buildSegCompareObj(seg);
+    obj.forwardPressure = seg.forwardPressure;
+    obj.backwardCoord = seg.backwardCoord;
+    return obj;
+}
+
+var TimeGridMirrorRenderer = /** @class */ (function (_super) {
+    __extends(TimeGridMirrorRenderer, _super);
+    function TimeGridMirrorRenderer() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    TimeGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+        this.segsByCol = this.timeGrid.groupSegsByCol(segs);
+        this.timeGrid.attachSegsByCol(this.segsByCol, this.timeGrid.mirrorContainerEls);
+        this.sourceSeg = mirrorInfo.sourceSeg;
+    };
+    TimeGridMirrorRenderer.prototype.generateSegCss = function (seg) {
+        var props = _super.prototype.generateSegCss.call(this, seg);
+        var sourceSeg = this.sourceSeg;
+        if (sourceSeg && sourceSeg.col === seg.col) {
+            var sourceSegProps = _super.prototype.generateSegCss.call(this, sourceSeg);
+            props.left = sourceSegProps.left;
+            props.right = sourceSegProps.right;
+            props.marginLeft = sourceSegProps.marginLeft;
+            props.marginRight = sourceSegProps.marginRight;
+        }
+        return props;
+    };
+    return TimeGridMirrorRenderer;
+}(TimeGridEventRenderer));
+
+var TimeGridFillRenderer = /** @class */ (function (_super) {
+    __extends(TimeGridFillRenderer, _super);
+    function TimeGridFillRenderer(timeGrid) {
+        var _this = _super.call(this, timeGrid.context) || this;
+        _this.timeGrid = timeGrid;
+        return _this;
+    }
+    TimeGridFillRenderer.prototype.attachSegs = function (type, segs) {
+        var timeGrid = this.timeGrid;
+        var containerEls;
+        // TODO: more efficient lookup
+        if (type === 'bgEvent') {
+            containerEls = timeGrid.bgContainerEls;
+        }
+        else if (type === 'businessHours') {
+            containerEls = timeGrid.businessContainerEls;
+        }
+        else if (type === 'highlight') {
+            containerEls = timeGrid.highlightContainerEls;
+        }
+        timeGrid.attachSegsByCol(timeGrid.groupSegsByCol(segs), containerEls);
+        return segs.map(function (seg) {
+            return seg.el;
+        });
+    };
+    TimeGridFillRenderer.prototype.computeSegSizes = function (segs) {
+        this.timeGrid.computeSegVerticals(segs);
+    };
+    TimeGridFillRenderer.prototype.assignSegSizes = function (segs) {
+        this.timeGrid.assignSegVerticals(segs);
+    };
+    return TimeGridFillRenderer;
+}(FillRenderer));
+
+/* A component that renders one or more columns of vertical time slots
+----------------------------------------------------------------------------------------------------------------------*/
+// potential nice values for the slot-duration and interval-duration
+// from largest to smallest
+var AGENDA_STOCK_SUB_DURATIONS = [
+    { hours: 1 },
+    { minutes: 30 },
+    { minutes: 15 },
+    { seconds: 30 },
+    { seconds: 15 }
+];
+var TimeGrid = /** @class */ (function (_super) {
+    __extends(TimeGrid, _super);
+    function TimeGrid(context, el, renderProps) {
+        var _this = _super.call(this, context, el) || this;
+        _this.isSlatSizesDirty = false;
+        _this.isColSizesDirty = false;
+        _this.renderSlats = memoizeRendering(_this._renderSlats);
+        var eventRenderer = _this.eventRenderer = new TimeGridEventRenderer(_this);
+        var fillRenderer = _this.fillRenderer = new TimeGridFillRenderer(_this);
+        _this.mirrorRenderer = new TimeGridMirrorRenderer(_this);
+        var renderColumns = _this.renderColumns = memoizeRendering(_this._renderColumns, _this._unrenderColumns);
+        _this.renderBusinessHours = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderColumns]);
+        _this.renderDateSelection = memoizeRendering(_this._renderDateSelection, _this._unrenderDateSelection, [renderColumns]);
+        _this.renderFgEvents = memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderColumns]);
+        _this.renderBgEvents = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderColumns]);
+        _this.renderEventSelection = memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+        _this.renderEventDrag = memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderColumns]);
+        _this.renderEventResize = memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderColumns]);
+        _this.processOptions();
+        el.innerHTML =
+            '<div class="fc-bg"></div>' +
+                '<div class="fc-slats"></div>' +
+                '<hr class="fc-divider ' + _this.theme.getClass('widgetHeader') + '" style="display:none" />';
+        _this.rootBgContainerEl = el.querySelector('.fc-bg');
+        _this.slatContainerEl = el.querySelector('.fc-slats');
+        _this.bottomRuleEl = el.querySelector('.fc-divider');
+        _this.renderProps = renderProps;
+        return _this;
+    }
+    /* Options
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Parses various options into properties of this object
+    TimeGrid.prototype.processOptions = function () {
+        var slotDuration = this.opt('slotDuration');
+        var snapDuration = this.opt('snapDuration');
+        var snapsPerSlot;
+        var input;
+        slotDuration = createDuration(slotDuration);
+        snapDuration = snapDuration ? createDuration(snapDuration) : slotDuration;
+        snapsPerSlot = wholeDivideDurations(slotDuration, snapDuration);
+        if (snapsPerSlot === null) {
+            snapDuration = slotDuration;
+            snapsPerSlot = 1;
+            // TODO: say warning?
+        }
+        this.slotDuration = slotDuration;
+        this.snapDuration = snapDuration;
+        this.snapsPerSlot = snapsPerSlot;
+        // might be an array value (for TimelineView).
+        // if so, getting the most granular entry (the last one probably).
+        input = this.opt('slotLabelFormat');
+        if (Array.isArray(input)) {
+            input = input[input.length - 1];
+        }
+        this.labelFormat = createFormatter(input || {
+            hour: 'numeric',
+            minute: '2-digit',
+            omitZeroMinute: true,
+            meridiem: 'short'
+        });
+        input = this.opt('slotLabelInterval');
+        this.labelInterval = input ?
+            createDuration(input) :
+            this.computeLabelInterval(slotDuration);
+    };
+    // Computes an automatic value for slotLabelInterval
+    TimeGrid.prototype.computeLabelInterval = function (slotDuration) {
+        var i;
+        var labelInterval;
+        var slotsPerLabel;
+        // find the smallest stock label interval that results in more than one slots-per-label
+        for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
+            labelInterval = createDuration(AGENDA_STOCK_SUB_DURATIONS[i]);
+            slotsPerLabel = wholeDivideDurations(labelInterval, slotDuration);
+            if (slotsPerLabel !== null && slotsPerLabel > 1) {
+                return labelInterval;
+            }
+        }
+        return slotDuration; // fall back
+    };
+    /* Rendering
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype.render = function (props) {
+        var cells = props.cells;
+        this.colCnt = cells.length;
+        this.renderSlats(props.dateProfile);
+        this.renderColumns(props.cells, props.dateProfile);
+        this.renderBusinessHours(props.businessHourSegs);
+        this.renderDateSelection(props.dateSelectionSegs);
+        this.renderFgEvents(props.fgEventSegs);
+        this.renderBgEvents(props.bgEventSegs);
+        this.renderEventSelection(props.eventSelection);
+        this.renderEventDrag(props.eventDrag);
+        this.renderEventResize(props.eventResize);
+    };
+    TimeGrid.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        // should unrender everything else too
+        this.renderSlats.unrender();
+        this.renderColumns.unrender();
+    };
+    TimeGrid.prototype.updateSize = function (isResize) {
+        var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
+        if (isResize || this.isSlatSizesDirty) {
+            this.buildSlatPositions();
+            this.isSlatSizesDirty = false;
+        }
+        if (isResize || this.isColSizesDirty) {
+            this.buildColPositions();
+            this.isColSizesDirty = false;
+        }
+        fillRenderer.computeSizes(isResize);
+        eventRenderer.computeSizes(isResize);
+        mirrorRenderer.computeSizes(isResize);
+        fillRenderer.assignSizes(isResize);
+        eventRenderer.assignSizes(isResize);
+        mirrorRenderer.assignSizes(isResize);
+    };
+    TimeGrid.prototype._renderSlats = function (dateProfile) {
+        var theme = this.theme;
+        this.slatContainerEl.innerHTML =
+            '<table class="' + theme.getClass('tableGrid') + '">' +
+                this.renderSlatRowHtml(dateProfile) +
+                '</table>';
+        this.slatEls = findElements(this.slatContainerEl, 'tr');
+        this.slatPositions = new PositionCache(this.el, this.slatEls, false, true // vertical
+        );
+        this.isSlatSizesDirty = true;
+    };
+    // Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
+    TimeGrid.prototype.renderSlatRowHtml = function (dateProfile) {
+        var _a = this, dateEnv = _a.dateEnv, theme = _a.theme, isRtl = _a.isRtl;
+        var html = '';
+        var dayStart = startOfDay(dateProfile.renderRange.start);
+        var slotTime = dateProfile.minTime;
+        var slotIterator = createDuration(0);
+        var slotDate; // will be on the view's first day, but we only care about its time
+        var isLabeled;
+        var axisHtml;
+        // Calculate the time for each slot
+        while (asRoughMs(slotTime) < asRoughMs(dateProfile.maxTime)) {
+            slotDate = dateEnv.add(dayStart, slotTime);
+            isLabeled = wholeDivideDurations(slotIterator, this.labelInterval) !== null;
+            axisHtml =
+                '<td class="fc-axis fc-time ' + theme.getClass('widgetContent') + '">' +
+                    (isLabeled ?
+                        '<span>' + // for matchCellWidths
+                            htmlEscape(dateEnv.format(slotDate, this.labelFormat)) +
+                            '</span>' :
+                        '') +
+                    '</td>';
+            html +=
+                '<tr data-time="' + formatIsoTimeString(slotDate) + '"' +
+                    (isLabeled ? '' : ' class="fc-minor"') +
+                    '>' +
+                    (!isRtl ? axisHtml : '') +
+                    '<td class="' + theme.getClass('widgetContent') + '"></td>' +
+                    (isRtl ? axisHtml : '') +
+                    '</tr>';
+            slotTime = addDurations(slotTime, this.slotDuration);
+            slotIterator = addDurations(slotIterator, this.slotDuration);
+        }
+        return html;
+    };
+    TimeGrid.prototype._renderColumns = function (cells, dateProfile) {
+        var _a = this, theme = _a.theme, dateEnv = _a.dateEnv, view = _a.view;
+        var bgRow = new DayBgRow(this.context);
+        this.rootBgContainerEl.innerHTML =
+            '<table class="' + theme.getClass('tableGrid') + '">' +
+                bgRow.renderHtml({
+                    cells: cells,
+                    dateProfile: dateProfile,
+                    renderIntroHtml: this.renderProps.renderBgIntroHtml
+                }) +
+                '</table>';
+        this.colEls = findElements(this.el, '.fc-day, .fc-disabled-day');
+        for (var col = 0; col < this.colCnt; col++) {
+            this.publiclyTrigger('dayRender', [
+                {
+                    date: dateEnv.toDate(cells[col].date),
+                    el: this.colEls[col],
+                    view: view
+                }
+            ]);
+        }
+        if (this.isRtl) {
+            this.colEls.reverse();
+        }
+        this.colPositions = new PositionCache(this.el, this.colEls, true, // horizontal
+        false);
+        this.renderContentSkeleton();
+        this.isColSizesDirty = true;
+    };
+    TimeGrid.prototype._unrenderColumns = function () {
+        this.unrenderContentSkeleton();
+    };
+    /* Content Skeleton
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Renders the DOM that the view's content will live in
+    TimeGrid.prototype.renderContentSkeleton = function () {
+        var parts = [];
+        var skeletonEl;
+        parts.push(this.renderProps.renderIntroHtml());
+        for (var i = 0; i < this.colCnt; i++) {
+            parts.push('<td>' +
+                '<div class="fc-content-col">' +
+                '<div class="fc-event-container fc-mirror-container"></div>' +
+                '<div class="fc-event-container"></div>' +
+                '<div class="fc-highlight-container"></div>' +
+                '<div class="fc-bgevent-container"></div>' +
+                '<div class="fc-business-container"></div>' +
+                '</div>' +
+                '</td>');
+        }
+        if (this.isRtl) {
+            parts.reverse();
+        }
+        skeletonEl = this.contentSkeletonEl = htmlToElement('<div class="fc-content-skeleton">' +
+            '<table>' +
+            '<tr>' + parts.join('') + '</tr>' +
+            '</table>' +
+            '</div>');
+        this.colContainerEls = findElements(skeletonEl, '.fc-content-col');
+        this.mirrorContainerEls = findElements(skeletonEl, '.fc-mirror-container');
+        this.fgContainerEls = findElements(skeletonEl, '.fc-event-container:not(.fc-mirror-container)');
+        this.bgContainerEls = findElements(skeletonEl, '.fc-bgevent-container');
+        this.highlightContainerEls = findElements(skeletonEl, '.fc-highlight-container');
+        this.businessContainerEls = findElements(skeletonEl, '.fc-business-container');
+        if (this.isRtl) {
+            this.colContainerEls.reverse();
+            this.mirrorContainerEls.reverse();
+            this.fgContainerEls.reverse();
+            this.bgContainerEls.reverse();
+            this.highlightContainerEls.reverse();
+            this.businessContainerEls.reverse();
+        }
+        this.el.appendChild(skeletonEl);
+    };
+    TimeGrid.prototype.unrenderContentSkeleton = function () {
+        removeElement(this.contentSkeletonEl);
+    };
+    // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
+    TimeGrid.prototype.groupSegsByCol = function (segs) {
+        var segsByCol = [];
+        var i;
+        for (i = 0; i < this.colCnt; i++) {
+            segsByCol.push([]);
+        }
+        for (i = 0; i < segs.length; i++) {
+            segsByCol[segs[i].col].push(segs[i]);
+        }
+        return segsByCol;
+    };
+    // Given segments grouped by column, insert the segments' elements into a parallel array of container
+    // elements, each living within a column.
+    TimeGrid.prototype.attachSegsByCol = function (segsByCol, containerEls) {
+        var col;
+        var segs;
+        var i;
+        for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
+            segs = segsByCol[col];
+            for (i = 0; i < segs.length; i++) {
+                containerEls[col].appendChild(segs[i].el);
+            }
+        }
+    };
+    /* Now Indicator
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype.getNowIndicatorUnit = function () {
+        return 'minute'; // will refresh on the minute
+    };
+    TimeGrid.prototype.renderNowIndicator = function (segs, date) {
+        // HACK: if date columns not ready for some reason (scheduler)
+        if (!this.colContainerEls) {
+            return;
+        }
+        var top = this.computeDateTop(date);
+        var nodes = [];
+        var i;
+        // render lines within the columns
+        for (i = 0; i < segs.length; i++) {
+            var lineEl = createElement('div', { className: 'fc-now-indicator fc-now-indicator-line' });
+            lineEl.style.top = top + 'px';
+            this.colContainerEls[segs[i].col].appendChild(lineEl);
+            nodes.push(lineEl);
+        }
+        // render an arrow over the axis
+        if (segs.length > 0) { // is the current time in view?
+            var arrowEl = createElement('div', { className: 'fc-now-indicator fc-now-indicator-arrow' });
+            arrowEl.style.top = top + 'px';
+            this.contentSkeletonEl.appendChild(arrowEl);
+            nodes.push(arrowEl);
+        }
+        this.nowIndicatorEls = nodes;
+    };
+    TimeGrid.prototype.unrenderNowIndicator = function () {
+        if (this.nowIndicatorEls) {
+            this.nowIndicatorEls.forEach(removeElement);
+            this.nowIndicatorEls = null;
+        }
+    };
+    /* Coordinates
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype.getTotalSlatHeight = function () {
+        return this.slatContainerEl.getBoundingClientRect().height;
+    };
+    // Computes the top coordinate, relative to the bounds of the grid, of the given date.
+    // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
+    TimeGrid.prototype.computeDateTop = function (when, startOfDayDate) {
+        if (!startOfDayDate) {
+            startOfDayDate = startOfDay(when);
+        }
+        return this.computeTimeTop(createDuration(when.valueOf() - startOfDayDate.valueOf()));
+    };
+    // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
+    TimeGrid.prototype.computeTimeTop = function (duration) {
+        var len = this.slatEls.length;
+        var dateProfile = this.props.dateProfile;
+        var slatCoverage = (duration.milliseconds - asRoughMs(dateProfile.minTime)) / asRoughMs(this.slotDuration); // floating-point value of # of slots covered
+        var slatIndex;
+        var slatRemainder;
+        // compute a floating-point number for how many slats should be progressed through.
+        // from 0 to number of slats (inclusive)
+        // constrained because minTime/maxTime might be customized.
+        slatCoverage = Math.max(0, slatCoverage);
+        slatCoverage = Math.min(len, slatCoverage);
+        // an integer index of the furthest whole slat
+        // from 0 to number slats (*exclusive*, so len-1)
+        slatIndex = Math.floor(slatCoverage);
+        slatIndex = Math.min(slatIndex, len - 1);
+        // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
+        // could be 1.0 if slatCoverage is covering *all* the slots
+        slatRemainder = slatCoverage - slatIndex;
+        return this.slatPositions.tops[slatIndex] +
+            this.slatPositions.getHeight(slatIndex) * slatRemainder;
+    };
+    // For each segment in an array, computes and assigns its top and bottom properties
+    TimeGrid.prototype.computeSegVerticals = function (segs) {
+        var eventMinHeight = this.opt('timeGridEventMinHeight');
+        var i;
+        var seg;
+        var dayDate;
+        for (i = 0; i < segs.length; i++) {
+            seg = segs[i];
+            dayDate = this.props.cells[seg.col].date;
+            seg.top = this.computeDateTop(seg.start, dayDate);
+            seg.bottom = Math.max(seg.top + eventMinHeight, this.computeDateTop(seg.end, dayDate));
+        }
+    };
+    // Given segments that already have their top/bottom properties computed, applies those values to
+    // the segments' elements.
+    TimeGrid.prototype.assignSegVerticals = function (segs) {
+        var i;
+        var seg;
+        for (i = 0; i < segs.length; i++) {
+            seg = segs[i];
+            applyStyle(seg.el, this.generateSegVerticalCss(seg));
+        }
+    };
+    // Generates an object with CSS properties for the top/bottom coordinates of a segment element
+    TimeGrid.prototype.generateSegVerticalCss = function (seg) {
+        return {
+            top: seg.top,
+            bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
+        };
+    };
+    /* Sizing
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype.buildPositionCaches = function () {
+        this.buildColPositions();
+        this.buildSlatPositions();
+    };
+    TimeGrid.prototype.buildColPositions = function () {
+        this.colPositions.build();
+    };
+    TimeGrid.prototype.buildSlatPositions = function () {
+        this.slatPositions.build();
+    };
+    /* Hit System
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype.positionToHit = function (positionLeft, positionTop) {
+        var _a = this, dateEnv = _a.dateEnv, snapsPerSlot = _a.snapsPerSlot, slatPositions = _a.slatPositions, colPositions = _a.colPositions;
+        var colIndex = colPositions.leftToIndex(positionLeft);
+        var slatIndex = slatPositions.topToIndex(positionTop);
+        if (colIndex != null && slatIndex != null) {
+            var slatTop = slatPositions.tops[slatIndex];
+            var slatHeight = slatPositions.getHeight(slatIndex);
+            var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
+            var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
+            var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
+            var dayDate = this.props.cells[colIndex].date;
+            var time = addDurations(this.props.dateProfile.minTime, multiplyDuration(this.snapDuration, snapIndex));
+            var start = dateEnv.add(dayDate, time);
+            var end = dateEnv.add(start, this.snapDuration);
+            return {
+                col: colIndex,
+                dateSpan: {
+                    range: { start: start, end: end },
+                    allDay: false
+                },
+                dayEl: this.colEls[colIndex],
+                relativeRect: {
+                    left: colPositions.lefts[colIndex],
+                    right: colPositions.rights[colIndex],
+                    top: slatTop,
+                    bottom: slatTop + slatHeight
+                }
+            };
+        }
+    };
+    /* Event Drag Visualization
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype._renderEventDrag = function (state) {
+        if (state) {
+            this.eventRenderer.hideByHash(state.affectedInstances);
+            if (state.isEvent) {
+                this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+            }
+            else {
+                this.fillRenderer.renderSegs('highlight', state.segs);
+            }
+        }
+    };
+    TimeGrid.prototype._unrenderEventDrag = function (state) {
+        if (state) {
+            this.eventRenderer.showByHash(state.affectedInstances);
+            this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+            this.fillRenderer.unrender('highlight');
+        }
+    };
+    /* Event Resize Visualization
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGrid.prototype._renderEventResize = function (state) {
+        if (state) {
+            this.eventRenderer.hideByHash(state.affectedInstances);
+            this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    TimeGrid.prototype._unrenderEventResize = function (state) {
+        if (state) {
+            this.eventRenderer.showByHash(state.affectedInstances);
+            this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    /* Selection
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight.
+    TimeGrid.prototype._renderDateSelection = function (segs) {
+        if (segs) {
+            if (this.opt('selectMirror')) {
+                this.mirrorRenderer.renderSegs(segs, { isSelecting: true });
+            }
+            else {
+                this.fillRenderer.renderSegs('highlight', segs);
+            }
+        }
+    };
+    TimeGrid.prototype._unrenderDateSelection = function (segs) {
+        this.mirrorRenderer.unrender(segs, { isSelecting: true });
+        this.fillRenderer.unrender('highlight');
+    };
+    return TimeGrid;
+}(DateComponent));
+
+var AllDaySplitter = /** @class */ (function (_super) {
+    __extends(AllDaySplitter, _super);
+    function AllDaySplitter() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    AllDaySplitter.prototype.getKeyInfo = function () {
+        return {
+            allDay: {},
+            timed: {}
+        };
+    };
+    AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+        if (dateSpan.allDay) {
+            return ['allDay'];
+        }
+        else {
+            return ['timed'];
+        }
+    };
+    AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) {
+        if (!eventDef.allDay) {
+            return ['timed'];
+        }
+        else if (hasBgRendering(eventDef)) {
+            return ['timed', 'allDay'];
+        }
+        else {
+            return ['allDay'];
+        }
+    };
+    return AllDaySplitter;
+}(Splitter));
+
+var TIMEGRID_ALL_DAY_EVENT_LIMIT = 5;
+var WEEK_HEADER_FORMAT = createFormatter({ week: 'short' });
+/* An abstract class for all timegrid-related views. Displays one more columns with time slots running vertically.
+----------------------------------------------------------------------------------------------------------------------*/
+// Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on).
+// Responsible for managing width/height.
+var TimeGridView = /** @class */ (function (_super) {
+    __extends(TimeGridView, _super);
+    function TimeGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.splitter = new AllDaySplitter();
+        /* Header Render Methods
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Generates the HTML that will go before the day-of week header cells
+        _this.renderHeadIntroHtml = function () {
+            var _a = _this, theme = _a.theme, dateEnv = _a.dateEnv;
+            var range = _this.props.dateProfile.renderRange;
+            var dayCnt = diffDays(range.start, range.end);
+            var weekText;
+            if (_this.opt('weekNumbers')) {
+                weekText = dateEnv.format(range.start, WEEK_HEADER_FORMAT);
+                return '' +
+                    '<th class="fc-axis fc-week-number ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '>' +
+                    buildGotoAnchorHtml(// aside from link, important for matchCellWidths
+                    _this, { date: range.start, type: 'week', forceOff: dayCnt > 1 }, htmlEscape(weekText) // inner HTML
+                    ) +
+                    '</th>';
+            }
+            else {
+                return '<th class="fc-axis ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '></th>';
+            }
+        };
+        /* Time Grid Render Methods
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column.
+        _this.renderTimeGridBgIntroHtml = function () {
+            var theme = _this.theme;
+            return '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '></td>';
+        };
+        // Generates the HTML that goes before all other types of cells.
+        // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
+        _this.renderTimeGridIntroHtml = function () {
+            return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
+        };
+        /* Day Grid Render Methods
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Generates the HTML that goes before the all-day cells
+        _this.renderDayGridBgIntroHtml = function () {
+            var theme = _this.theme;
+            return '' +
+                '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '>' +
+                '<span>' + // needed for matchCellWidths
+                getAllDayHtml(_this) +
+                '</span>' +
+                '</td>';
+        };
+        // Generates the HTML that goes before all other types of cells.
+        // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
+        _this.renderDayGridIntroHtml = function () {
+            return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
+        };
+        _this.el.classList.add('fc-timeGrid-view');
+        _this.el.innerHTML = _this.renderSkeletonHtml();
+        _this.scroller = new ScrollComponent('hidden', // overflow x
+        'auto' // overflow y
+        );
+        var timeGridWrapEl = _this.scroller.el;
+        _this.el.querySelector('.fc-body > tr > td').appendChild(timeGridWrapEl);
+        timeGridWrapEl.classList.add('fc-time-grid-container');
+        var timeGridEl = createElement('div', { className: 'fc-time-grid' });
+        timeGridWrapEl.appendChild(timeGridEl);
+        _this.timeGrid = new TimeGrid(_this.context, timeGridEl, {
+            renderBgIntroHtml: _this.renderTimeGridBgIntroHtml,
+            renderIntroHtml: _this.renderTimeGridIntroHtml
+        });
+        if (_this.opt('allDaySlot')) { // should we display the "all-day" area?
+            _this.dayGrid = new DayGrid(// the all-day subcomponent of this view
+            _this.context, _this.el.querySelector('.fc-day-grid'), {
+                renderNumberIntroHtml: _this.renderDayGridIntroHtml,
+                renderBgIntroHtml: _this.renderDayGridBgIntroHtml,
+                renderIntroHtml: _this.renderDayGridIntroHtml,
+                colWeekNumbersVisible: false,
+                cellWeekNumbersVisible: false
+            });
+            // have the day-grid extend it's coordinate area over the <hr> dividing the two grids
+            var dividerEl = _this.el.querySelector('.fc-divider');
+            _this.dayGrid.bottomCoordPadding = dividerEl.getBoundingClientRect().height;
+        }
+        return _this;
+    }
+    TimeGridView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.timeGrid.destroy();
+        if (this.dayGrid) {
+            this.dayGrid.destroy();
+        }
+        this.scroller.destroy();
+    };
+    /* Rendering
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Builds the HTML skeleton for the view.
+    // The day-grid and time-grid components will render inside containers defined by this HTML.
+    TimeGridView.prototype.renderSkeletonHtml = function () {
+        var theme = this.theme;
+        return '' +
+            '<table class="' + theme.getClass('tableGrid') + '">' +
+            (this.opt('columnHeader') ?
+                '<thead class="fc-head">' +
+                    '<tr>' +
+                    '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
+                    '</tr>' +
+                    '</thead>' :
+                '') +
+            '<tbody class="fc-body">' +
+            '<tr>' +
+            '<td class="' + theme.getClass('widgetContent') + '">' +
+            (this.opt('allDaySlot') ?
+                '<div class="fc-day-grid"></div>' +
+                    '<hr class="fc-divider ' + theme.getClass('widgetHeader') + '" />' :
+                '') +
+            '</td>' +
+            '</tr>' +
+            '</tbody>' +
+            '</table>';
+    };
+    /* Now Indicator
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGridView.prototype.getNowIndicatorUnit = function () {
+        return this.timeGrid.getNowIndicatorUnit();
+    };
+    // subclasses should implement
+    // renderNowIndicator(date: DateMarker) {
+    // }
+    TimeGridView.prototype.unrenderNowIndicator = function () {
+        this.timeGrid.unrenderNowIndicator();
+    };
+    /* Dimensions
+    ------------------------------------------------------------------------------------------------------------------*/
+    TimeGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+        _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
+        this.timeGrid.updateSize(isResize);
+        if (this.dayGrid) {
+            this.dayGrid.updateSize(isResize);
+        }
+    };
+    // Adjusts the vertical dimensions of the view to the specified values
+    TimeGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
+        var _this = this;
+        var eventLimit;
+        var scrollerHeight;
+        var scrollbarWidths;
+        // make all axis cells line up
+        this.axisWidth = matchCellWidths(findElements(this.el, '.fc-axis'));
+        // hack to give the view some height prior to timeGrid's columns being rendered
+        // TODO: separate setting height from scroller VS timeGrid.
+        if (!this.timeGrid.colEls) {
+            if (!isAuto) {
+                scrollerHeight = this.computeScrollerHeight(viewHeight);
+                this.scroller.setHeight(scrollerHeight);
+            }
+            return;
+        }
+        // set of fake row elements that must compensate when scroller has scrollbars
+        var noScrollRowEls = findElements(this.el, '.fc-row').filter(function (node) {
+            return !_this.scroller.el.contains(node);
+        });
+        // reset all dimensions back to the original state
+        this.timeGrid.bottomRuleEl.style.display = 'none'; // will be shown later if this <hr> is necessary
+        this.scroller.clear(); // sets height to 'auto' and clears overflow
+        noScrollRowEls.forEach(uncompensateScroll);
+        // limit number of events in the all-day area
+        if (this.dayGrid) {
+            this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
+            eventLimit = this.opt('eventLimit');
+            if (eventLimit && typeof eventLimit !== 'number') {
+                eventLimit = TIMEGRID_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number
+            }
+            if (eventLimit) {
+                this.dayGrid.limitRows(eventLimit);
+            }
+        }
+        if (!isAuto) { // should we force dimensions of the scroll container?
+            scrollerHeight = this.computeScrollerHeight(viewHeight);
+            this.scroller.setHeight(scrollerHeight);
+            scrollbarWidths = this.scroller.getScrollbarWidths();
+            if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
+                // make the all-day and header rows lines up
+                noScrollRowEls.forEach(function (rowEl) {
+                    compensateScroll(rowEl, scrollbarWidths);
+                });
+                // the scrollbar compensation might have changed text flow, which might affect height, so recalculate
+                // and reapply the desired height to the scroller.
+                scrollerHeight = this.computeScrollerHeight(viewHeight);
+                this.scroller.setHeight(scrollerHeight);
+            }
+            // guarantees the same scrollbar widths
+            this.scroller.lockOverflow(scrollbarWidths);
+            // if there's any space below the slats, show the horizontal rule.
+            // this won't cause any new overflow, because lockOverflow already called.
+            if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
+                this.timeGrid.bottomRuleEl.style.display = '';
+            }
+        }
+    };
+    // given a desired total height of the view, returns what the height of the scroller should be
+    TimeGridView.prototype.computeScrollerHeight = function (viewHeight) {
+        return viewHeight -
+            subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
+    };
+    /* Scroll
+    ------------------------------------------------------------------------------------------------------------------*/
+    // Computes the initial pre-configured scroll state prior to allowing the user to change it
+    TimeGridView.prototype.computeDateScroll = function (duration) {
+        var top = this.timeGrid.computeTimeTop(duration);
+        // zoom can give weird floating-point values. rather scroll a little bit further
+        top = Math.ceil(top);
+        if (top) {
+            top++; // to overcome top border that slots beyond the first have. looks better
+        }
+        return { top: top };
+    };
+    TimeGridView.prototype.queryDateScroll = function () {
+        return { top: this.scroller.getScrollTop() };
+    };
+    TimeGridView.prototype.applyDateScroll = function (scroll) {
+        if (scroll.top !== undefined) {
+            this.scroller.setScrollTop(scroll.top);
+        }
+    };
+    // Generates an HTML attribute string for setting the width of the axis, if it is known
+    TimeGridView.prototype.axisStyleAttr = function () {
+        if (this.axisWidth != null) {
+            return 'style="width:' + this.axisWidth + 'px"';
+        }
+        return '';
+    };
+    return TimeGridView;
+}(View));
+TimeGridView.prototype.usesMinMaxTime = true; // indicates that minTime/maxTime affects rendering
+
+var SimpleTimeGrid = /** @class */ (function (_super) {
+    __extends(SimpleTimeGrid, _super);
+    function SimpleTimeGrid(context, timeGrid) {
+        var _this = _super.call(this, context, timeGrid.el) || this;
+        _this.buildDayRanges = memoize(buildDayRanges);
+        _this.slicer = new TimeGridSlicer();
+        _this.timeGrid = timeGrid;
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.timeGrid.el
+        });
+        return _this;
+    }
+    SimpleTimeGrid.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    SimpleTimeGrid.prototype.render = function (props) {
+        var dateProfile = props.dateProfile, dayTable = props.dayTable;
+        var dayRanges = this.dayRanges = this.buildDayRanges(dayTable, dateProfile, this.dateEnv);
+        this.timeGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, null, this.timeGrid, dayRanges), { dateProfile: dateProfile, cells: dayTable.cells[0] }));
+    };
+    SimpleTimeGrid.prototype.renderNowIndicator = function (date) {
+        this.timeGrid.renderNowIndicator(this.slicer.sliceNowDate(date, this.timeGrid, this.dayRanges), date);
+    };
+    SimpleTimeGrid.prototype.buildPositionCaches = function () {
+        this.timeGrid.buildPositionCaches();
+    };
+    SimpleTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
+        var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
+        if (rawHit) {
+            return {
+                component: this.timeGrid,
+                dateSpan: rawHit.dateSpan,
+                dayEl: rawHit.dayEl,
+                rect: {
+                    left: rawHit.relativeRect.left,
+                    right: rawHit.relativeRect.right,
+                    top: rawHit.relativeRect.top,
+                    bottom: rawHit.relativeRect.bottom
+                },
+                layer: 0
+            };
+        }
+    };
+    return SimpleTimeGrid;
+}(DateComponent));
+function buildDayRanges(dayTable, dateProfile, dateEnv) {
+    var ranges = [];
+    for (var _i = 0, _a = dayTable.headerDates; _i < _a.length; _i++) {
+        var date = _a[_i];
+        ranges.push({
+            start: dateEnv.add(date, dateProfile.minTime),
+            end: dateEnv.add(date, dateProfile.maxTime)
+        });
+    }
+    return ranges;
+}
+var TimeGridSlicer = /** @class */ (function (_super) {
+    __extends(TimeGridSlicer, _super);
+    function TimeGridSlicer() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    TimeGridSlicer.prototype.sliceRange = function (range, dayRanges) {
+        var segs = [];
+        for (var col = 0; col < dayRanges.length; col++) {
+            var segRange = intersectRanges(range, dayRanges[col]);
+            if (segRange) {
+                segs.push({
+                    start: segRange.start,
+                    end: segRange.end,
+                    isStart: segRange.start.valueOf() === range.start.valueOf(),
+                    isEnd: segRange.end.valueOf() === range.end.valueOf(),
+                    col: col
+                });
+            }
+        }
+        return segs;
+    };
+    return TimeGridSlicer;
+}(Slicer));
+
+var TimeGridView$1 = /** @class */ (function (_super) {
+    __extends(TimeGridView, _super);
+    function TimeGridView(_context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, _context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.buildDayTable = memoize(buildDayTable);
+        if (_this.opt('columnHeader')) {
+            _this.header = new DayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+        }
+        _this.simpleTimeGrid = new SimpleTimeGrid(_this.context, _this.timeGrid);
+        if (_this.dayGrid) {
+            _this.simpleDayGrid = new SimpleDayGrid(_this.context, _this.dayGrid);
+        }
+        return _this;
+    }
+    TimeGridView.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        if (this.header) {
+            this.header.destroy();
+        }
+        this.simpleTimeGrid.destroy();
+        if (this.simpleDayGrid) {
+            this.simpleDayGrid.destroy();
+        }
+    };
+    TimeGridView.prototype.render = function (props) {
+        _super.prototype.render.call(this, props); // for flags for updateSize
+        var dateProfile = this.props.dateProfile;
+        var dayTable = this.buildDayTable(dateProfile, this.dateProfileGenerator);
+        var splitProps = this.splitter.splitProps(props);
+        if (this.header) {
+            this.header.receiveProps({
+                dateProfile: dateProfile,
+                dates: dayTable.headerDates,
+                datesRepDistinctDays: true,
+                renderIntroHtml: this.renderHeadIntroHtml
+            });
+        }
+        this.simpleTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: dateProfile,
+            dayTable: dayTable }));
+        if (this.simpleDayGrid) {
+            this.simpleDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: dateProfile,
+                dayTable: dayTable, nextDayThreshold: this.nextDayThreshold, isRigid: false }));
+        }
+    };
+    TimeGridView.prototype.renderNowIndicator = function (date) {
+        this.simpleTimeGrid.renderNowIndicator(date);
+    };
+    return TimeGridView;
+}(TimeGridView));
+function buildDayTable(dateProfile, dateProfileGenerator) {
+    var daySeries = new DaySeries(dateProfile.renderRange, dateProfileGenerator);
+    return new DayTable(daySeries, false);
+}
+
+var main = createPlugin({
+    defaultView: 'timeGridWeek',
+    views: {
+        timeGrid: {
+            class: TimeGridView$1,
+            allDaySlot: true,
+            slotDuration: '00:30:00',
+            slotEventOverlap: true // a bad name. confused with overlap/constraint system
+        },
+        timeGridDay: {
+            type: 'timeGrid',
+            duration: { days: 1 }
+        },
+        timeGridWeek: {
+            type: 'timeGrid',
+            duration: { weeks: 1 }
+        }
+    }
+});
+
+export default main;
+export { TimeGridView as AbstractTimeGridView, TimeGrid, TimeGridSlicer, TimeGridView$1 as TimeGridView, buildDayRanges, buildDayTable };
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/main.js b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..470925e8b839b4800fa3f605055dcc40442e47f8
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.js
@@ -0,0 +1,1357 @@
+/*!
+FullCalendar Time Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core'), require('@fullcalendar/daygrid')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core', '@fullcalendar/daygrid'], factory) :
+    (global = global || self, factory(global.FullCalendarTimeGrid = {}, global.FullCalendar, global.FullCalendarDayGrid));
+}(this, function (exports, core, daygrid) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    /*
+    Only handles foreground segs.
+    Does not own rendering. Use for low-level util methods by TimeGrid.
+    */
+    var TimeGridEventRenderer = /** @class */ (function (_super) {
+        __extends(TimeGridEventRenderer, _super);
+        function TimeGridEventRenderer(timeGrid) {
+            var _this = _super.call(this, timeGrid.context) || this;
+            _this.timeGrid = timeGrid;
+            _this.fullTimeFormat = core.createFormatter({
+                hour: 'numeric',
+                minute: '2-digit',
+                separator: _this.context.options.defaultRangeSeparator
+            });
+            return _this;
+        }
+        // Given an array of foreground segments, render a DOM element for each, computes position,
+        // and attaches to the column inner-container elements.
+        TimeGridEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+            var segsByCol = this.timeGrid.groupSegsByCol(segs);
+            // order the segs within each column
+            // TODO: have groupSegsByCol do this?
+            for (var col = 0; col < segsByCol.length; col++) {
+                segsByCol[col] = this.sortEventSegs(segsByCol[col]);
+            }
+            this.segsByCol = segsByCol;
+            this.timeGrid.attachSegsByCol(segsByCol, this.timeGrid.fgContainerEls);
+        };
+        TimeGridEventRenderer.prototype.detachSegs = function (segs) {
+            segs.forEach(function (seg) {
+                core.removeElement(seg.el);
+            });
+            this.segsByCol = null;
+        };
+        TimeGridEventRenderer.prototype.computeSegSizes = function (allSegs) {
+            var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
+            var colCnt = timeGrid.colCnt;
+            timeGrid.computeSegVerticals(allSegs); // horizontals relies on this
+            if (segsByCol) {
+                for (var col = 0; col < colCnt; col++) {
+                    this.computeSegHorizontals(segsByCol[col]); // compute horizontal coordinates, z-index's, and reorder the array
+                }
+            }
+        };
+        TimeGridEventRenderer.prototype.assignSegSizes = function (allSegs) {
+            var _a = this, timeGrid = _a.timeGrid, segsByCol = _a.segsByCol;
+            var colCnt = timeGrid.colCnt;
+            timeGrid.assignSegVerticals(allSegs); // horizontals relies on this
+            if (segsByCol) {
+                for (var col = 0; col < colCnt; col++) {
+                    this.assignSegCss(segsByCol[col]);
+                }
+            }
+        };
+        // Computes a default event time formatting string if `eventTimeFormat` is not explicitly defined
+        TimeGridEventRenderer.prototype.computeEventTimeFormat = function () {
+            return {
+                hour: 'numeric',
+                minute: '2-digit',
+                meridiem: false
+            };
+        };
+        // Computes a default `displayEventEnd` value if one is not expliclty defined
+        TimeGridEventRenderer.prototype.computeDisplayEventEnd = function () {
+            return true;
+        };
+        // Renders the HTML for a single event segment's default rendering
+        TimeGridEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
+            var view = this.context.view;
+            var eventRange = seg.eventRange;
+            var eventDef = eventRange.def;
+            var eventUi = eventRange.ui;
+            var allDay = eventDef.allDay;
+            var isDraggable = view.computeEventDraggable(eventDef, eventUi);
+            var isResizableFromStart = seg.isStart && view.computeEventStartResizable(eventDef, eventUi);
+            var isResizableFromEnd = seg.isEnd && view.computeEventEndResizable(eventDef, eventUi);
+            var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
+            var skinCss = core.cssToStr(this.getSkinCss(eventUi));
+            var timeText;
+            var fullTimeText; // more verbose time text. for the print stylesheet
+            var startTimeText; // just the start time text
+            classes.unshift('fc-time-grid-event');
+            // if the event appears to span more than one day...
+            if (core.isMultiDayRange(eventRange.range)) {
+                // Don't display time text on segments that run entirely through a day.
+                // That would appear as midnight-midnight and would look dumb.
+                // Otherwise, display the time text for the *segment's* times (like 6pm-midnight or midnight-10am)
+                if (seg.isStart || seg.isEnd) {
+                    var unzonedStart = seg.start;
+                    var unzonedEnd = seg.end;
+                    timeText = this._getTimeText(unzonedStart, unzonedEnd, allDay); // TODO: give the timezones
+                    fullTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, this.fullTimeFormat);
+                    startTimeText = this._getTimeText(unzonedStart, unzonedEnd, allDay, null, false); // displayEnd=false
+                }
+            }
+            else {
+                // Display the normal time text for the *event's* times
+                timeText = this.getTimeText(eventRange);
+                fullTimeText = this.getTimeText(eventRange, this.fullTimeFormat);
+                startTimeText = this.getTimeText(eventRange, null, false); // displayEnd=false
+            }
+            return '<a class="' + classes.join(' ') + '"' +
+                (eventDef.url ?
+                    ' href="' + core.htmlEscape(eventDef.url) + '"' :
+                    '') +
+                (skinCss ?
+                    ' style="' + skinCss + '"' :
+                    '') +
+                '>' +
+                '<div class="fc-content">' +
+                (timeText ?
+                    '<div class="fc-time"' +
+                        ' data-start="' + core.htmlEscape(startTimeText) + '"' +
+                        ' data-full="' + core.htmlEscape(fullTimeText) + '"' +
+                        '>' +
+                        '<span>' + core.htmlEscape(timeText) + '</span>' +
+                        '</div>' :
+                    '') +
+                (eventDef.title ?
+                    '<div class="fc-title">' +
+                        core.htmlEscape(eventDef.title) +
+                        '</div>' :
+                    '') +
+                '</div>' +
+                /* TODO: write CSS for this
+                (isResizableFromStart ?
+                  '<div class="fc-resizer fc-start-resizer"></div>' :
+                  ''
+                  ) +
+                */
+                (isResizableFromEnd ?
+                    '<div class="fc-resizer fc-end-resizer"></div>' :
+                    '') +
+                '</a>';
+        };
+        // Given an array of segments that are all in the same column, sets the backwardCoord and forwardCoord on each.
+        // Assumed the segs are already ordered.
+        // NOTE: Also reorders the given array by date!
+        TimeGridEventRenderer.prototype.computeSegHorizontals = function (segs) {
+            var levels;
+            var level0;
+            var i;
+            levels = buildSlotSegLevels(segs);
+            computeForwardSlotSegs(levels);
+            if ((level0 = levels[0])) {
+                for (i = 0; i < level0.length; i++) {
+                    computeSlotSegPressures(level0[i]);
+                }
+                for (i = 0; i < level0.length; i++) {
+                    this.computeSegForwardBack(level0[i], 0, 0);
+                }
+            }
+        };
+        // Calculate seg.forwardCoord and seg.backwardCoord for the segment, where both values range
+        // from 0 to 1. If the calendar is left-to-right, the seg.backwardCoord maps to "left" and
+        // seg.forwardCoord maps to "right" (via percentage). Vice-versa if the calendar is right-to-left.
+        //
+        // The segment might be part of a "series", which means consecutive segments with the same pressure
+        // who's width is unknown until an edge has been hit. `seriesBackwardPressure` is the number of
+        // segments behind this one in the current series, and `seriesBackwardCoord` is the starting
+        // coordinate of the first segment in the series.
+        TimeGridEventRenderer.prototype.computeSegForwardBack = function (seg, seriesBackwardPressure, seriesBackwardCoord) {
+            var forwardSegs = seg.forwardSegs;
+            var i;
+            if (seg.forwardCoord === undefined) { // not already computed
+                if (!forwardSegs.length) {
+                    // if there are no forward segments, this segment should butt up against the edge
+                    seg.forwardCoord = 1;
+                }
+                else {
+                    // sort highest pressure first
+                    this.sortForwardSegs(forwardSegs);
+                    // this segment's forwardCoord will be calculated from the backwardCoord of the
+                    // highest-pressure forward segment.
+                    this.computeSegForwardBack(forwardSegs[0], seriesBackwardPressure + 1, seriesBackwardCoord);
+                    seg.forwardCoord = forwardSegs[0].backwardCoord;
+                }
+                // calculate the backwardCoord from the forwardCoord. consider the series
+                seg.backwardCoord = seg.forwardCoord -
+                    (seg.forwardCoord - seriesBackwardCoord) / // available width for series
+                        (seriesBackwardPressure + 1); // # of segments in the series
+                // use this segment's coordinates to computed the coordinates of the less-pressurized
+                // forward segments
+                for (i = 0; i < forwardSegs.length; i++) {
+                    this.computeSegForwardBack(forwardSegs[i], 0, seg.forwardCoord);
+                }
+            }
+        };
+        TimeGridEventRenderer.prototype.sortForwardSegs = function (forwardSegs) {
+            var objs = forwardSegs.map(buildTimeGridSegCompareObj);
+            var specs = [
+                // put higher-pressure first
+                { field: 'forwardPressure', order: -1 },
+                // put segments that are closer to initial edge first (and favor ones with no coords yet)
+                { field: 'backwardCoord', order: 1 }
+            ].concat(this.context.view.eventOrderSpecs);
+            objs.sort(function (obj0, obj1) {
+                return core.compareByFieldSpecs(obj0, obj1, specs);
+            });
+            return objs.map(function (c) {
+                return c._seg;
+            });
+        };
+        // Given foreground event segments that have already had their position coordinates computed,
+        // assigns position-related CSS values to their elements.
+        TimeGridEventRenderer.prototype.assignSegCss = function (segs) {
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                core.applyStyle(seg.el, this.generateSegCss(seg));
+                if (seg.level > 0) {
+                    seg.el.classList.add('fc-time-grid-event-inset');
+                }
+                // if the event is short that the title will be cut off,
+                // attach a className that condenses the title into the time area.
+                if (seg.eventRange.def.title && seg.bottom - seg.top < 30) {
+                    seg.el.classList.add('fc-short'); // TODO: "condensed" is a better name
+                }
+            }
+        };
+        // Generates an object with CSS properties/values that should be applied to an event segment element.
+        // Contains important positioning-related properties that should be applied to any event element, customized or not.
+        TimeGridEventRenderer.prototype.generateSegCss = function (seg) {
+            var shouldOverlap = this.context.options.slotEventOverlap;
+            var backwardCoord = seg.backwardCoord; // the left side if LTR. the right side if RTL. floating-point
+            var forwardCoord = seg.forwardCoord; // the right side if LTR. the left side if RTL. floating-point
+            var props = this.timeGrid.generateSegVerticalCss(seg); // get top/bottom first
+            var isRtl = this.timeGrid.isRtl;
+            var left; // amount of space from left edge, a fraction of the total width
+            var right; // amount of space from right edge, a fraction of the total width
+            if (shouldOverlap) {
+                // double the width, but don't go beyond the maximum forward coordinate (1.0)
+                forwardCoord = Math.min(1, backwardCoord + (forwardCoord - backwardCoord) * 2);
+            }
+            if (isRtl) {
+                left = 1 - forwardCoord;
+                right = backwardCoord;
+            }
+            else {
+                left = backwardCoord;
+                right = 1 - forwardCoord;
+            }
+            props.zIndex = seg.level + 1; // convert from 0-base to 1-based
+            props.left = left * 100 + '%';
+            props.right = right * 100 + '%';
+            if (shouldOverlap && seg.forwardPressure) {
+                // add padding to the edge so that forward stacked events don't cover the resizer's icon
+                props[isRtl ? 'marginLeft' : 'marginRight'] = 10 * 2; // 10 is a guesstimate of the icon's width
+            }
+            return props;
+        };
+        return TimeGridEventRenderer;
+    }(core.FgEventRenderer));
+    // Builds an array of segments "levels". The first level will be the leftmost tier of segments if the calendar is
+    // left-to-right, or the rightmost if the calendar is right-to-left. Assumes the segments are already ordered by date.
+    function buildSlotSegLevels(segs) {
+        var levels = [];
+        var i;
+        var seg;
+        var j;
+        for (i = 0; i < segs.length; i++) {
+            seg = segs[i];
+            // go through all the levels and stop on the first level where there are no collisions
+            for (j = 0; j < levels.length; j++) {
+                if (!computeSlotSegCollisions(seg, levels[j]).length) {
+                    break;
+                }
+            }
+            seg.level = j;
+            (levels[j] || (levels[j] = [])).push(seg);
+        }
+        return levels;
+    }
+    // For every segment, figure out the other segments that are in subsequent
+    // levels that also occupy the same vertical space. Accumulate in seg.forwardSegs
+    function computeForwardSlotSegs(levels) {
+        var i;
+        var level;
+        var j;
+        var seg;
+        var k;
+        for (i = 0; i < levels.length; i++) {
+            level = levels[i];
+            for (j = 0; j < level.length; j++) {
+                seg = level[j];
+                seg.forwardSegs = [];
+                for (k = i + 1; k < levels.length; k++) {
+                    computeSlotSegCollisions(seg, levels[k], seg.forwardSegs);
+                }
+            }
+        }
+    }
+    // Figure out which path forward (via seg.forwardSegs) results in the longest path until
+    // the furthest edge is reached. The number of segments in this path will be seg.forwardPressure
+    function computeSlotSegPressures(seg) {
+        var forwardSegs = seg.forwardSegs;
+        var forwardPressure = 0;
+        var i;
+        var forwardSeg;
+        if (seg.forwardPressure === undefined) { // not already computed
+            for (i = 0; i < forwardSegs.length; i++) {
+                forwardSeg = forwardSegs[i];
+                // figure out the child's maximum forward path
+                computeSlotSegPressures(forwardSeg);
+                // either use the existing maximum, or use the child's forward pressure
+                // plus one (for the forwardSeg itself)
+                forwardPressure = Math.max(forwardPressure, 1 + forwardSeg.forwardPressure);
+            }
+            seg.forwardPressure = forwardPressure;
+        }
+    }
+    // Find all the segments in `otherSegs` that vertically collide with `seg`.
+    // Append into an optionally-supplied `results` array and return.
+    function computeSlotSegCollisions(seg, otherSegs, results) {
+        if (results === void 0) { results = []; }
+        for (var i = 0; i < otherSegs.length; i++) {
+            if (isSlotSegCollision(seg, otherSegs[i])) {
+                results.push(otherSegs[i]);
+            }
+        }
+        return results;
+    }
+    // Do these segments occupy the same vertical space?
+    function isSlotSegCollision(seg1, seg2) {
+        return seg1.bottom > seg2.top && seg1.top < seg2.bottom;
+    }
+    function buildTimeGridSegCompareObj(seg) {
+        var obj = core.buildSegCompareObj(seg);
+        obj.forwardPressure = seg.forwardPressure;
+        obj.backwardCoord = seg.backwardCoord;
+        return obj;
+    }
+
+    var TimeGridMirrorRenderer = /** @class */ (function (_super) {
+        __extends(TimeGridMirrorRenderer, _super);
+        function TimeGridMirrorRenderer() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        TimeGridMirrorRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+            this.segsByCol = this.timeGrid.groupSegsByCol(segs);
+            this.timeGrid.attachSegsByCol(this.segsByCol, this.timeGrid.mirrorContainerEls);
+            this.sourceSeg = mirrorInfo.sourceSeg;
+        };
+        TimeGridMirrorRenderer.prototype.generateSegCss = function (seg) {
+            var props = _super.prototype.generateSegCss.call(this, seg);
+            var sourceSeg = this.sourceSeg;
+            if (sourceSeg && sourceSeg.col === seg.col) {
+                var sourceSegProps = _super.prototype.generateSegCss.call(this, sourceSeg);
+                props.left = sourceSegProps.left;
+                props.right = sourceSegProps.right;
+                props.marginLeft = sourceSegProps.marginLeft;
+                props.marginRight = sourceSegProps.marginRight;
+            }
+            return props;
+        };
+        return TimeGridMirrorRenderer;
+    }(TimeGridEventRenderer));
+
+    var TimeGridFillRenderer = /** @class */ (function (_super) {
+        __extends(TimeGridFillRenderer, _super);
+        function TimeGridFillRenderer(timeGrid) {
+            var _this = _super.call(this, timeGrid.context) || this;
+            _this.timeGrid = timeGrid;
+            return _this;
+        }
+        TimeGridFillRenderer.prototype.attachSegs = function (type, segs) {
+            var timeGrid = this.timeGrid;
+            var containerEls;
+            // TODO: more efficient lookup
+            if (type === 'bgEvent') {
+                containerEls = timeGrid.bgContainerEls;
+            }
+            else if (type === 'businessHours') {
+                containerEls = timeGrid.businessContainerEls;
+            }
+            else if (type === 'highlight') {
+                containerEls = timeGrid.highlightContainerEls;
+            }
+            timeGrid.attachSegsByCol(timeGrid.groupSegsByCol(segs), containerEls);
+            return segs.map(function (seg) {
+                return seg.el;
+            });
+        };
+        TimeGridFillRenderer.prototype.computeSegSizes = function (segs) {
+            this.timeGrid.computeSegVerticals(segs);
+        };
+        TimeGridFillRenderer.prototype.assignSegSizes = function (segs) {
+            this.timeGrid.assignSegVerticals(segs);
+        };
+        return TimeGridFillRenderer;
+    }(core.FillRenderer));
+
+    /* A component that renders one or more columns of vertical time slots
+    ----------------------------------------------------------------------------------------------------------------------*/
+    // potential nice values for the slot-duration and interval-duration
+    // from largest to smallest
+    var AGENDA_STOCK_SUB_DURATIONS = [
+        { hours: 1 },
+        { minutes: 30 },
+        { minutes: 15 },
+        { seconds: 30 },
+        { seconds: 15 }
+    ];
+    var TimeGrid = /** @class */ (function (_super) {
+        __extends(TimeGrid, _super);
+        function TimeGrid(context, el, renderProps) {
+            var _this = _super.call(this, context, el) || this;
+            _this.isSlatSizesDirty = false;
+            _this.isColSizesDirty = false;
+            _this.renderSlats = core.memoizeRendering(_this._renderSlats);
+            var eventRenderer = _this.eventRenderer = new TimeGridEventRenderer(_this);
+            var fillRenderer = _this.fillRenderer = new TimeGridFillRenderer(_this);
+            _this.mirrorRenderer = new TimeGridMirrorRenderer(_this);
+            var renderColumns = _this.renderColumns = core.memoizeRendering(_this._renderColumns, _this._unrenderColumns);
+            _this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'), [renderColumns]);
+            _this.renderDateSelection = core.memoizeRendering(_this._renderDateSelection, _this._unrenderDateSelection, [renderColumns]);
+            _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [renderColumns]);
+            _this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'), [renderColumns]);
+            _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+            _this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag, [renderColumns]);
+            _this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize, [renderColumns]);
+            _this.processOptions();
+            el.innerHTML =
+                '<div class="fc-bg"></div>' +
+                    '<div class="fc-slats"></div>' +
+                    '<hr class="fc-divider ' + _this.theme.getClass('widgetHeader') + '" style="display:none" />';
+            _this.rootBgContainerEl = el.querySelector('.fc-bg');
+            _this.slatContainerEl = el.querySelector('.fc-slats');
+            _this.bottomRuleEl = el.querySelector('.fc-divider');
+            _this.renderProps = renderProps;
+            return _this;
+        }
+        /* Options
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Parses various options into properties of this object
+        TimeGrid.prototype.processOptions = function () {
+            var slotDuration = this.opt('slotDuration');
+            var snapDuration = this.opt('snapDuration');
+            var snapsPerSlot;
+            var input;
+            slotDuration = core.createDuration(slotDuration);
+            snapDuration = snapDuration ? core.createDuration(snapDuration) : slotDuration;
+            snapsPerSlot = core.wholeDivideDurations(slotDuration, snapDuration);
+            if (snapsPerSlot === null) {
+                snapDuration = slotDuration;
+                snapsPerSlot = 1;
+                // TODO: say warning?
+            }
+            this.slotDuration = slotDuration;
+            this.snapDuration = snapDuration;
+            this.snapsPerSlot = snapsPerSlot;
+            // might be an array value (for TimelineView).
+            // if so, getting the most granular entry (the last one probably).
+            input = this.opt('slotLabelFormat');
+            if (Array.isArray(input)) {
+                input = input[input.length - 1];
+            }
+            this.labelFormat = core.createFormatter(input || {
+                hour: 'numeric',
+                minute: '2-digit',
+                omitZeroMinute: true,
+                meridiem: 'short'
+            });
+            input = this.opt('slotLabelInterval');
+            this.labelInterval = input ?
+                core.createDuration(input) :
+                this.computeLabelInterval(slotDuration);
+        };
+        // Computes an automatic value for slotLabelInterval
+        TimeGrid.prototype.computeLabelInterval = function (slotDuration) {
+            var i;
+            var labelInterval;
+            var slotsPerLabel;
+            // find the smallest stock label interval that results in more than one slots-per-label
+            for (i = AGENDA_STOCK_SUB_DURATIONS.length - 1; i >= 0; i--) {
+                labelInterval = core.createDuration(AGENDA_STOCK_SUB_DURATIONS[i]);
+                slotsPerLabel = core.wholeDivideDurations(labelInterval, slotDuration);
+                if (slotsPerLabel !== null && slotsPerLabel > 1) {
+                    return labelInterval;
+                }
+            }
+            return slotDuration; // fall back
+        };
+        /* Rendering
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype.render = function (props) {
+            var cells = props.cells;
+            this.colCnt = cells.length;
+            this.renderSlats(props.dateProfile);
+            this.renderColumns(props.cells, props.dateProfile);
+            this.renderBusinessHours(props.businessHourSegs);
+            this.renderDateSelection(props.dateSelectionSegs);
+            this.renderFgEvents(props.fgEventSegs);
+            this.renderBgEvents(props.bgEventSegs);
+            this.renderEventSelection(props.eventSelection);
+            this.renderEventDrag(props.eventDrag);
+            this.renderEventResize(props.eventResize);
+        };
+        TimeGrid.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            // should unrender everything else too
+            this.renderSlats.unrender();
+            this.renderColumns.unrender();
+        };
+        TimeGrid.prototype.updateSize = function (isResize) {
+            var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
+            if (isResize || this.isSlatSizesDirty) {
+                this.buildSlatPositions();
+                this.isSlatSizesDirty = false;
+            }
+            if (isResize || this.isColSizesDirty) {
+                this.buildColPositions();
+                this.isColSizesDirty = false;
+            }
+            fillRenderer.computeSizes(isResize);
+            eventRenderer.computeSizes(isResize);
+            mirrorRenderer.computeSizes(isResize);
+            fillRenderer.assignSizes(isResize);
+            eventRenderer.assignSizes(isResize);
+            mirrorRenderer.assignSizes(isResize);
+        };
+        TimeGrid.prototype._renderSlats = function (dateProfile) {
+            var theme = this.theme;
+            this.slatContainerEl.innerHTML =
+                '<table class="' + theme.getClass('tableGrid') + '">' +
+                    this.renderSlatRowHtml(dateProfile) +
+                    '</table>';
+            this.slatEls = core.findElements(this.slatContainerEl, 'tr');
+            this.slatPositions = new core.PositionCache(this.el, this.slatEls, false, true // vertical
+            );
+            this.isSlatSizesDirty = true;
+        };
+        // Generates the HTML for the horizontal "slats" that run width-wise. Has a time axis on a side. Depends on RTL.
+        TimeGrid.prototype.renderSlatRowHtml = function (dateProfile) {
+            var _a = this, dateEnv = _a.dateEnv, theme = _a.theme, isRtl = _a.isRtl;
+            var html = '';
+            var dayStart = core.startOfDay(dateProfile.renderRange.start);
+            var slotTime = dateProfile.minTime;
+            var slotIterator = core.createDuration(0);
+            var slotDate; // will be on the view's first day, but we only care about its time
+            var isLabeled;
+            var axisHtml;
+            // Calculate the time for each slot
+            while (core.asRoughMs(slotTime) < core.asRoughMs(dateProfile.maxTime)) {
+                slotDate = dateEnv.add(dayStart, slotTime);
+                isLabeled = core.wholeDivideDurations(slotIterator, this.labelInterval) !== null;
+                axisHtml =
+                    '<td class="fc-axis fc-time ' + theme.getClass('widgetContent') + '">' +
+                        (isLabeled ?
+                            '<span>' + // for matchCellWidths
+                                core.htmlEscape(dateEnv.format(slotDate, this.labelFormat)) +
+                                '</span>' :
+                            '') +
+                        '</td>';
+                html +=
+                    '<tr data-time="' + core.formatIsoTimeString(slotDate) + '"' +
+                        (isLabeled ? '' : ' class="fc-minor"') +
+                        '>' +
+                        (!isRtl ? axisHtml : '') +
+                        '<td class="' + theme.getClass('widgetContent') + '"></td>' +
+                        (isRtl ? axisHtml : '') +
+                        '</tr>';
+                slotTime = core.addDurations(slotTime, this.slotDuration);
+                slotIterator = core.addDurations(slotIterator, this.slotDuration);
+            }
+            return html;
+        };
+        TimeGrid.prototype._renderColumns = function (cells, dateProfile) {
+            var _a = this, theme = _a.theme, dateEnv = _a.dateEnv, view = _a.view;
+            var bgRow = new daygrid.DayBgRow(this.context);
+            this.rootBgContainerEl.innerHTML =
+                '<table class="' + theme.getClass('tableGrid') + '">' +
+                    bgRow.renderHtml({
+                        cells: cells,
+                        dateProfile: dateProfile,
+                        renderIntroHtml: this.renderProps.renderBgIntroHtml
+                    }) +
+                    '</table>';
+            this.colEls = core.findElements(this.el, '.fc-day, .fc-disabled-day');
+            for (var col = 0; col < this.colCnt; col++) {
+                this.publiclyTrigger('dayRender', [
+                    {
+                        date: dateEnv.toDate(cells[col].date),
+                        el: this.colEls[col],
+                        view: view
+                    }
+                ]);
+            }
+            if (this.isRtl) {
+                this.colEls.reverse();
+            }
+            this.colPositions = new core.PositionCache(this.el, this.colEls, true, // horizontal
+            false);
+            this.renderContentSkeleton();
+            this.isColSizesDirty = true;
+        };
+        TimeGrid.prototype._unrenderColumns = function () {
+            this.unrenderContentSkeleton();
+        };
+        /* Content Skeleton
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Renders the DOM that the view's content will live in
+        TimeGrid.prototype.renderContentSkeleton = function () {
+            var parts = [];
+            var skeletonEl;
+            parts.push(this.renderProps.renderIntroHtml());
+            for (var i = 0; i < this.colCnt; i++) {
+                parts.push('<td>' +
+                    '<div class="fc-content-col">' +
+                    '<div class="fc-event-container fc-mirror-container"></div>' +
+                    '<div class="fc-event-container"></div>' +
+                    '<div class="fc-highlight-container"></div>' +
+                    '<div class="fc-bgevent-container"></div>' +
+                    '<div class="fc-business-container"></div>' +
+                    '</div>' +
+                    '</td>');
+            }
+            if (this.isRtl) {
+                parts.reverse();
+            }
+            skeletonEl = this.contentSkeletonEl = core.htmlToElement('<div class="fc-content-skeleton">' +
+                '<table>' +
+                '<tr>' + parts.join('') + '</tr>' +
+                '</table>' +
+                '</div>');
+            this.colContainerEls = core.findElements(skeletonEl, '.fc-content-col');
+            this.mirrorContainerEls = core.findElements(skeletonEl, '.fc-mirror-container');
+            this.fgContainerEls = core.findElements(skeletonEl, '.fc-event-container:not(.fc-mirror-container)');
+            this.bgContainerEls = core.findElements(skeletonEl, '.fc-bgevent-container');
+            this.highlightContainerEls = core.findElements(skeletonEl, '.fc-highlight-container');
+            this.businessContainerEls = core.findElements(skeletonEl, '.fc-business-container');
+            if (this.isRtl) {
+                this.colContainerEls.reverse();
+                this.mirrorContainerEls.reverse();
+                this.fgContainerEls.reverse();
+                this.bgContainerEls.reverse();
+                this.highlightContainerEls.reverse();
+                this.businessContainerEls.reverse();
+            }
+            this.el.appendChild(skeletonEl);
+        };
+        TimeGrid.prototype.unrenderContentSkeleton = function () {
+            core.removeElement(this.contentSkeletonEl);
+        };
+        // Given a flat array of segments, return an array of sub-arrays, grouped by each segment's col
+        TimeGrid.prototype.groupSegsByCol = function (segs) {
+            var segsByCol = [];
+            var i;
+            for (i = 0; i < this.colCnt; i++) {
+                segsByCol.push([]);
+            }
+            for (i = 0; i < segs.length; i++) {
+                segsByCol[segs[i].col].push(segs[i]);
+            }
+            return segsByCol;
+        };
+        // Given segments grouped by column, insert the segments' elements into a parallel array of container
+        // elements, each living within a column.
+        TimeGrid.prototype.attachSegsByCol = function (segsByCol, containerEls) {
+            var col;
+            var segs;
+            var i;
+            for (col = 0; col < this.colCnt; col++) { // iterate each column grouping
+                segs = segsByCol[col];
+                for (i = 0; i < segs.length; i++) {
+                    containerEls[col].appendChild(segs[i].el);
+                }
+            }
+        };
+        /* Now Indicator
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype.getNowIndicatorUnit = function () {
+            return 'minute'; // will refresh on the minute
+        };
+        TimeGrid.prototype.renderNowIndicator = function (segs, date) {
+            // HACK: if date columns not ready for some reason (scheduler)
+            if (!this.colContainerEls) {
+                return;
+            }
+            var top = this.computeDateTop(date);
+            var nodes = [];
+            var i;
+            // render lines within the columns
+            for (i = 0; i < segs.length; i++) {
+                var lineEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-line' });
+                lineEl.style.top = top + 'px';
+                this.colContainerEls[segs[i].col].appendChild(lineEl);
+                nodes.push(lineEl);
+            }
+            // render an arrow over the axis
+            if (segs.length > 0) { // is the current time in view?
+                var arrowEl = core.createElement('div', { className: 'fc-now-indicator fc-now-indicator-arrow' });
+                arrowEl.style.top = top + 'px';
+                this.contentSkeletonEl.appendChild(arrowEl);
+                nodes.push(arrowEl);
+            }
+            this.nowIndicatorEls = nodes;
+        };
+        TimeGrid.prototype.unrenderNowIndicator = function () {
+            if (this.nowIndicatorEls) {
+                this.nowIndicatorEls.forEach(core.removeElement);
+                this.nowIndicatorEls = null;
+            }
+        };
+        /* Coordinates
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype.getTotalSlatHeight = function () {
+            return this.slatContainerEl.getBoundingClientRect().height;
+        };
+        // Computes the top coordinate, relative to the bounds of the grid, of the given date.
+        // A `startOfDayDate` must be given for avoiding ambiguity over how to treat midnight.
+        TimeGrid.prototype.computeDateTop = function (when, startOfDayDate) {
+            if (!startOfDayDate) {
+                startOfDayDate = core.startOfDay(when);
+            }
+            return this.computeTimeTop(core.createDuration(when.valueOf() - startOfDayDate.valueOf()));
+        };
+        // Computes the top coordinate, relative to the bounds of the grid, of the given time (a Duration).
+        TimeGrid.prototype.computeTimeTop = function (duration) {
+            var len = this.slatEls.length;
+            var dateProfile = this.props.dateProfile;
+            var slatCoverage = (duration.milliseconds - core.asRoughMs(dateProfile.minTime)) / core.asRoughMs(this.slotDuration); // floating-point value of # of slots covered
+            var slatIndex;
+            var slatRemainder;
+            // compute a floating-point number for how many slats should be progressed through.
+            // from 0 to number of slats (inclusive)
+            // constrained because minTime/maxTime might be customized.
+            slatCoverage = Math.max(0, slatCoverage);
+            slatCoverage = Math.min(len, slatCoverage);
+            // an integer index of the furthest whole slat
+            // from 0 to number slats (*exclusive*, so len-1)
+            slatIndex = Math.floor(slatCoverage);
+            slatIndex = Math.min(slatIndex, len - 1);
+            // how much further through the slatIndex slat (from 0.0-1.0) must be covered in addition.
+            // could be 1.0 if slatCoverage is covering *all* the slots
+            slatRemainder = slatCoverage - slatIndex;
+            return this.slatPositions.tops[slatIndex] +
+                this.slatPositions.getHeight(slatIndex) * slatRemainder;
+        };
+        // For each segment in an array, computes and assigns its top and bottom properties
+        TimeGrid.prototype.computeSegVerticals = function (segs) {
+            var eventMinHeight = this.opt('timeGridEventMinHeight');
+            var i;
+            var seg;
+            var dayDate;
+            for (i = 0; i < segs.length; i++) {
+                seg = segs[i];
+                dayDate = this.props.cells[seg.col].date;
+                seg.top = this.computeDateTop(seg.start, dayDate);
+                seg.bottom = Math.max(seg.top + eventMinHeight, this.computeDateTop(seg.end, dayDate));
+            }
+        };
+        // Given segments that already have their top/bottom properties computed, applies those values to
+        // the segments' elements.
+        TimeGrid.prototype.assignSegVerticals = function (segs) {
+            var i;
+            var seg;
+            for (i = 0; i < segs.length; i++) {
+                seg = segs[i];
+                core.applyStyle(seg.el, this.generateSegVerticalCss(seg));
+            }
+        };
+        // Generates an object with CSS properties for the top/bottom coordinates of a segment element
+        TimeGrid.prototype.generateSegVerticalCss = function (seg) {
+            return {
+                top: seg.top,
+                bottom: -seg.bottom // flipped because needs to be space beyond bottom edge of event container
+            };
+        };
+        /* Sizing
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype.buildPositionCaches = function () {
+            this.buildColPositions();
+            this.buildSlatPositions();
+        };
+        TimeGrid.prototype.buildColPositions = function () {
+            this.colPositions.build();
+        };
+        TimeGrid.prototype.buildSlatPositions = function () {
+            this.slatPositions.build();
+        };
+        /* Hit System
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype.positionToHit = function (positionLeft, positionTop) {
+            var _a = this, dateEnv = _a.dateEnv, snapsPerSlot = _a.snapsPerSlot, slatPositions = _a.slatPositions, colPositions = _a.colPositions;
+            var colIndex = colPositions.leftToIndex(positionLeft);
+            var slatIndex = slatPositions.topToIndex(positionTop);
+            if (colIndex != null && slatIndex != null) {
+                var slatTop = slatPositions.tops[slatIndex];
+                var slatHeight = slatPositions.getHeight(slatIndex);
+                var partial = (positionTop - slatTop) / slatHeight; // floating point number between 0 and 1
+                var localSnapIndex = Math.floor(partial * snapsPerSlot); // the snap # relative to start of slat
+                var snapIndex = slatIndex * snapsPerSlot + localSnapIndex;
+                var dayDate = this.props.cells[colIndex].date;
+                var time = core.addDurations(this.props.dateProfile.minTime, core.multiplyDuration(this.snapDuration, snapIndex));
+                var start = dateEnv.add(dayDate, time);
+                var end = dateEnv.add(start, this.snapDuration);
+                return {
+                    col: colIndex,
+                    dateSpan: {
+                        range: { start: start, end: end },
+                        allDay: false
+                    },
+                    dayEl: this.colEls[colIndex],
+                    relativeRect: {
+                        left: colPositions.lefts[colIndex],
+                        right: colPositions.rights[colIndex],
+                        top: slatTop,
+                        bottom: slatTop + slatHeight
+                    }
+                };
+            }
+        };
+        /* Event Drag Visualization
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype._renderEventDrag = function (state) {
+            if (state) {
+                this.eventRenderer.hideByHash(state.affectedInstances);
+                if (state.isEvent) {
+                    this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+                }
+                else {
+                    this.fillRenderer.renderSegs('highlight', state.segs);
+                }
+            }
+        };
+        TimeGrid.prototype._unrenderEventDrag = function (state) {
+            if (state) {
+                this.eventRenderer.showByHash(state.affectedInstances);
+                this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+                this.fillRenderer.unrender('highlight');
+            }
+        };
+        /* Event Resize Visualization
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGrid.prototype._renderEventResize = function (state) {
+            if (state) {
+                this.eventRenderer.hideByHash(state.affectedInstances);
+                this.mirrorRenderer.renderSegs(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        TimeGrid.prototype._unrenderEventResize = function (state) {
+            if (state) {
+                this.eventRenderer.showByHash(state.affectedInstances);
+                this.mirrorRenderer.unrender(state.segs, { isResizing: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        /* Selection
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Renders a visual indication of a selection. Overrides the default, which was to simply render a highlight.
+        TimeGrid.prototype._renderDateSelection = function (segs) {
+            if (segs) {
+                if (this.opt('selectMirror')) {
+                    this.mirrorRenderer.renderSegs(segs, { isSelecting: true });
+                }
+                else {
+                    this.fillRenderer.renderSegs('highlight', segs);
+                }
+            }
+        };
+        TimeGrid.prototype._unrenderDateSelection = function (segs) {
+            this.mirrorRenderer.unrender(segs, { isSelecting: true });
+            this.fillRenderer.unrender('highlight');
+        };
+        return TimeGrid;
+    }(core.DateComponent));
+
+    var AllDaySplitter = /** @class */ (function (_super) {
+        __extends(AllDaySplitter, _super);
+        function AllDaySplitter() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        AllDaySplitter.prototype.getKeyInfo = function () {
+            return {
+                allDay: {},
+                timed: {}
+            };
+        };
+        AllDaySplitter.prototype.getKeysForDateSpan = function (dateSpan) {
+            if (dateSpan.allDay) {
+                return ['allDay'];
+            }
+            else {
+                return ['timed'];
+            }
+        };
+        AllDaySplitter.prototype.getKeysForEventDef = function (eventDef) {
+            if (!eventDef.allDay) {
+                return ['timed'];
+            }
+            else if (core.hasBgRendering(eventDef)) {
+                return ['timed', 'allDay'];
+            }
+            else {
+                return ['allDay'];
+            }
+        };
+        return AllDaySplitter;
+    }(core.Splitter));
+
+    var TIMEGRID_ALL_DAY_EVENT_LIMIT = 5;
+    var WEEK_HEADER_FORMAT = core.createFormatter({ week: 'short' });
+    /* An abstract class for all timegrid-related views. Displays one more columns with time slots running vertically.
+    ----------------------------------------------------------------------------------------------------------------------*/
+    // Is a manager for the TimeGrid subcomponent and possibly the DayGrid subcomponent (if allDaySlot is on).
+    // Responsible for managing width/height.
+    var TimeGridView = /** @class */ (function (_super) {
+        __extends(TimeGridView, _super);
+        function TimeGridView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.splitter = new AllDaySplitter();
+            /* Header Render Methods
+            ------------------------------------------------------------------------------------------------------------------*/
+            // Generates the HTML that will go before the day-of week header cells
+            _this.renderHeadIntroHtml = function () {
+                var _a = _this, theme = _a.theme, dateEnv = _a.dateEnv;
+                var range = _this.props.dateProfile.renderRange;
+                var dayCnt = core.diffDays(range.start, range.end);
+                var weekText;
+                if (_this.opt('weekNumbers')) {
+                    weekText = dateEnv.format(range.start, WEEK_HEADER_FORMAT);
+                    return '' +
+                        '<th class="fc-axis fc-week-number ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '>' +
+                        core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths
+                        _this, { date: range.start, type: 'week', forceOff: dayCnt > 1 }, core.htmlEscape(weekText) // inner HTML
+                        ) +
+                        '</th>';
+                }
+                else {
+                    return '<th class="fc-axis ' + theme.getClass('widgetHeader') + '" ' + _this.axisStyleAttr() + '></th>';
+                }
+            };
+            /* Time Grid Render Methods
+            ------------------------------------------------------------------------------------------------------------------*/
+            // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column.
+            _this.renderTimeGridBgIntroHtml = function () {
+                var theme = _this.theme;
+                return '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '></td>';
+            };
+            // Generates the HTML that goes before all other types of cells.
+            // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
+            _this.renderTimeGridIntroHtml = function () {
+                return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
+            };
+            /* Day Grid Render Methods
+            ------------------------------------------------------------------------------------------------------------------*/
+            // Generates the HTML that goes before the all-day cells
+            _this.renderDayGridBgIntroHtml = function () {
+                var theme = _this.theme;
+                return '' +
+                    '<td class="fc-axis ' + theme.getClass('widgetContent') + '" ' + _this.axisStyleAttr() + '>' +
+                    '<span>' + // needed for matchCellWidths
+                    core.getAllDayHtml(_this) +
+                    '</span>' +
+                    '</td>';
+            };
+            // Generates the HTML that goes before all other types of cells.
+            // Affects content-skeleton, mirror-skeleton, highlight-skeleton for both the time-grid and day-grid.
+            _this.renderDayGridIntroHtml = function () {
+                return '<td class="fc-axis" ' + _this.axisStyleAttr() + '></td>';
+            };
+            _this.el.classList.add('fc-timeGrid-view');
+            _this.el.innerHTML = _this.renderSkeletonHtml();
+            _this.scroller = new core.ScrollComponent('hidden', // overflow x
+            'auto' // overflow y
+            );
+            var timeGridWrapEl = _this.scroller.el;
+            _this.el.querySelector('.fc-body > tr > td').appendChild(timeGridWrapEl);
+            timeGridWrapEl.classList.add('fc-time-grid-container');
+            var timeGridEl = core.createElement('div', { className: 'fc-time-grid' });
+            timeGridWrapEl.appendChild(timeGridEl);
+            _this.timeGrid = new TimeGrid(_this.context, timeGridEl, {
+                renderBgIntroHtml: _this.renderTimeGridBgIntroHtml,
+                renderIntroHtml: _this.renderTimeGridIntroHtml
+            });
+            if (_this.opt('allDaySlot')) { // should we display the "all-day" area?
+                _this.dayGrid = new daygrid.DayGrid(// the all-day subcomponent of this view
+                _this.context, _this.el.querySelector('.fc-day-grid'), {
+                    renderNumberIntroHtml: _this.renderDayGridIntroHtml,
+                    renderBgIntroHtml: _this.renderDayGridBgIntroHtml,
+                    renderIntroHtml: _this.renderDayGridIntroHtml,
+                    colWeekNumbersVisible: false,
+                    cellWeekNumbersVisible: false
+                });
+                // have the day-grid extend it's coordinate area over the <hr> dividing the two grids
+                var dividerEl = _this.el.querySelector('.fc-divider');
+                _this.dayGrid.bottomCoordPadding = dividerEl.getBoundingClientRect().height;
+            }
+            return _this;
+        }
+        TimeGridView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.timeGrid.destroy();
+            if (this.dayGrid) {
+                this.dayGrid.destroy();
+            }
+            this.scroller.destroy();
+        };
+        /* Rendering
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Builds the HTML skeleton for the view.
+        // The day-grid and time-grid components will render inside containers defined by this HTML.
+        TimeGridView.prototype.renderSkeletonHtml = function () {
+            var theme = this.theme;
+            return '' +
+                '<table class="' + theme.getClass('tableGrid') + '">' +
+                (this.opt('columnHeader') ?
+                    '<thead class="fc-head">' +
+                        '<tr>' +
+                        '<td class="fc-head-container ' + theme.getClass('widgetHeader') + '">&nbsp;</td>' +
+                        '</tr>' +
+                        '</thead>' :
+                    '') +
+                '<tbody class="fc-body">' +
+                '<tr>' +
+                '<td class="' + theme.getClass('widgetContent') + '">' +
+                (this.opt('allDaySlot') ?
+                    '<div class="fc-day-grid"></div>' +
+                        '<hr class="fc-divider ' + theme.getClass('widgetHeader') + '" />' :
+                    '') +
+                '</td>' +
+                '</tr>' +
+                '</tbody>' +
+                '</table>';
+        };
+        /* Now Indicator
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGridView.prototype.getNowIndicatorUnit = function () {
+            return this.timeGrid.getNowIndicatorUnit();
+        };
+        // subclasses should implement
+        // renderNowIndicator(date: DateMarker) {
+        // }
+        TimeGridView.prototype.unrenderNowIndicator = function () {
+            this.timeGrid.unrenderNowIndicator();
+        };
+        /* Dimensions
+        ------------------------------------------------------------------------------------------------------------------*/
+        TimeGridView.prototype.updateSize = function (isResize, viewHeight, isAuto) {
+            _super.prototype.updateSize.call(this, isResize, viewHeight, isAuto); // will call updateBaseSize. important that executes first
+            this.timeGrid.updateSize(isResize);
+            if (this.dayGrid) {
+                this.dayGrid.updateSize(isResize);
+            }
+        };
+        // Adjusts the vertical dimensions of the view to the specified values
+        TimeGridView.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) {
+            var _this = this;
+            var eventLimit;
+            var scrollerHeight;
+            var scrollbarWidths;
+            // make all axis cells line up
+            this.axisWidth = core.matchCellWidths(core.findElements(this.el, '.fc-axis'));
+            // hack to give the view some height prior to timeGrid's columns being rendered
+            // TODO: separate setting height from scroller VS timeGrid.
+            if (!this.timeGrid.colEls) {
+                if (!isAuto) {
+                    scrollerHeight = this.computeScrollerHeight(viewHeight);
+                    this.scroller.setHeight(scrollerHeight);
+                }
+                return;
+            }
+            // set of fake row elements that must compensate when scroller has scrollbars
+            var noScrollRowEls = core.findElements(this.el, '.fc-row').filter(function (node) {
+                return !_this.scroller.el.contains(node);
+            });
+            // reset all dimensions back to the original state
+            this.timeGrid.bottomRuleEl.style.display = 'none'; // will be shown later if this <hr> is necessary
+            this.scroller.clear(); // sets height to 'auto' and clears overflow
+            noScrollRowEls.forEach(core.uncompensateScroll);
+            // limit number of events in the all-day area
+            if (this.dayGrid) {
+                this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
+                eventLimit = this.opt('eventLimit');
+                if (eventLimit && typeof eventLimit !== 'number') {
+                    eventLimit = TIMEGRID_ALL_DAY_EVENT_LIMIT; // make sure "auto" goes to a real number
+                }
+                if (eventLimit) {
+                    this.dayGrid.limitRows(eventLimit);
+                }
+            }
+            if (!isAuto) { // should we force dimensions of the scroll container?
+                scrollerHeight = this.computeScrollerHeight(viewHeight);
+                this.scroller.setHeight(scrollerHeight);
+                scrollbarWidths = this.scroller.getScrollbarWidths();
+                if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars?
+                    // make the all-day and header rows lines up
+                    noScrollRowEls.forEach(function (rowEl) {
+                        core.compensateScroll(rowEl, scrollbarWidths);
+                    });
+                    // the scrollbar compensation might have changed text flow, which might affect height, so recalculate
+                    // and reapply the desired height to the scroller.
+                    scrollerHeight = this.computeScrollerHeight(viewHeight);
+                    this.scroller.setHeight(scrollerHeight);
+                }
+                // guarantees the same scrollbar widths
+                this.scroller.lockOverflow(scrollbarWidths);
+                // if there's any space below the slats, show the horizontal rule.
+                // this won't cause any new overflow, because lockOverflow already called.
+                if (this.timeGrid.getTotalSlatHeight() < scrollerHeight) {
+                    this.timeGrid.bottomRuleEl.style.display = '';
+                }
+            }
+        };
+        // given a desired total height of the view, returns what the height of the scroller should be
+        TimeGridView.prototype.computeScrollerHeight = function (viewHeight) {
+            return viewHeight -
+                core.subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller
+        };
+        /* Scroll
+        ------------------------------------------------------------------------------------------------------------------*/
+        // Computes the initial pre-configured scroll state prior to allowing the user to change it
+        TimeGridView.prototype.computeDateScroll = function (duration) {
+            var top = this.timeGrid.computeTimeTop(duration);
+            // zoom can give weird floating-point values. rather scroll a little bit further
+            top = Math.ceil(top);
+            if (top) {
+                top++; // to overcome top border that slots beyond the first have. looks better
+            }
+            return { top: top };
+        };
+        TimeGridView.prototype.queryDateScroll = function () {
+            return { top: this.scroller.getScrollTop() };
+        };
+        TimeGridView.prototype.applyDateScroll = function (scroll) {
+            if (scroll.top !== undefined) {
+                this.scroller.setScrollTop(scroll.top);
+            }
+        };
+        // Generates an HTML attribute string for setting the width of the axis, if it is known
+        TimeGridView.prototype.axisStyleAttr = function () {
+            if (this.axisWidth != null) {
+                return 'style="width:' + this.axisWidth + 'px"';
+            }
+            return '';
+        };
+        return TimeGridView;
+    }(core.View));
+    TimeGridView.prototype.usesMinMaxTime = true; // indicates that minTime/maxTime affects rendering
+
+    var SimpleTimeGrid = /** @class */ (function (_super) {
+        __extends(SimpleTimeGrid, _super);
+        function SimpleTimeGrid(context, timeGrid) {
+            var _this = _super.call(this, context, timeGrid.el) || this;
+            _this.buildDayRanges = core.memoize(buildDayRanges);
+            _this.slicer = new TimeGridSlicer();
+            _this.timeGrid = timeGrid;
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.timeGrid.el
+            });
+            return _this;
+        }
+        SimpleTimeGrid.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        SimpleTimeGrid.prototype.render = function (props) {
+            var dateProfile = props.dateProfile, dayTable = props.dayTable;
+            var dayRanges = this.dayRanges = this.buildDayRanges(dayTable, dateProfile, this.dateEnv);
+            this.timeGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, null, this.timeGrid, dayRanges), { dateProfile: dateProfile, cells: dayTable.cells[0] }));
+        };
+        SimpleTimeGrid.prototype.renderNowIndicator = function (date) {
+            this.timeGrid.renderNowIndicator(this.slicer.sliceNowDate(date, this.timeGrid, this.dayRanges), date);
+        };
+        SimpleTimeGrid.prototype.buildPositionCaches = function () {
+            this.timeGrid.buildPositionCaches();
+        };
+        SimpleTimeGrid.prototype.queryHit = function (positionLeft, positionTop) {
+            var rawHit = this.timeGrid.positionToHit(positionLeft, positionTop);
+            if (rawHit) {
+                return {
+                    component: this.timeGrid,
+                    dateSpan: rawHit.dateSpan,
+                    dayEl: rawHit.dayEl,
+                    rect: {
+                        left: rawHit.relativeRect.left,
+                        right: rawHit.relativeRect.right,
+                        top: rawHit.relativeRect.top,
+                        bottom: rawHit.relativeRect.bottom
+                    },
+                    layer: 0
+                };
+            }
+        };
+        return SimpleTimeGrid;
+    }(core.DateComponent));
+    function buildDayRanges(dayTable, dateProfile, dateEnv) {
+        var ranges = [];
+        for (var _i = 0, _a = dayTable.headerDates; _i < _a.length; _i++) {
+            var date = _a[_i];
+            ranges.push({
+                start: dateEnv.add(date, dateProfile.minTime),
+                end: dateEnv.add(date, dateProfile.maxTime)
+            });
+        }
+        return ranges;
+    }
+    var TimeGridSlicer = /** @class */ (function (_super) {
+        __extends(TimeGridSlicer, _super);
+        function TimeGridSlicer() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        TimeGridSlicer.prototype.sliceRange = function (range, dayRanges) {
+            var segs = [];
+            for (var col = 0; col < dayRanges.length; col++) {
+                var segRange = core.intersectRanges(range, dayRanges[col]);
+                if (segRange) {
+                    segs.push({
+                        start: segRange.start,
+                        end: segRange.end,
+                        isStart: segRange.start.valueOf() === range.start.valueOf(),
+                        isEnd: segRange.end.valueOf() === range.end.valueOf(),
+                        col: col
+                    });
+                }
+            }
+            return segs;
+        };
+        return TimeGridSlicer;
+    }(core.Slicer));
+
+    var TimeGridView$1 = /** @class */ (function (_super) {
+        __extends(TimeGridView, _super);
+        function TimeGridView(_context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, _context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.buildDayTable = core.memoize(buildDayTable);
+            if (_this.opt('columnHeader')) {
+                _this.header = new core.DayHeader(_this.context, _this.el.querySelector('.fc-head-container'));
+            }
+            _this.simpleTimeGrid = new SimpleTimeGrid(_this.context, _this.timeGrid);
+            if (_this.dayGrid) {
+                _this.simpleDayGrid = new daygrid.SimpleDayGrid(_this.context, _this.dayGrid);
+            }
+            return _this;
+        }
+        TimeGridView.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            if (this.header) {
+                this.header.destroy();
+            }
+            this.simpleTimeGrid.destroy();
+            if (this.simpleDayGrid) {
+                this.simpleDayGrid.destroy();
+            }
+        };
+        TimeGridView.prototype.render = function (props) {
+            _super.prototype.render.call(this, props); // for flags for updateSize
+            var dateProfile = this.props.dateProfile;
+            var dayTable = this.buildDayTable(dateProfile, this.dateProfileGenerator);
+            var splitProps = this.splitter.splitProps(props);
+            if (this.header) {
+                this.header.receiveProps({
+                    dateProfile: dateProfile,
+                    dates: dayTable.headerDates,
+                    datesRepDistinctDays: true,
+                    renderIntroHtml: this.renderHeadIntroHtml
+                });
+            }
+            this.simpleTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: dateProfile,
+                dayTable: dayTable }));
+            if (this.simpleDayGrid) {
+                this.simpleDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: dateProfile,
+                    dayTable: dayTable, nextDayThreshold: this.nextDayThreshold, isRigid: false }));
+            }
+        };
+        TimeGridView.prototype.renderNowIndicator = function (date) {
+            this.simpleTimeGrid.renderNowIndicator(date);
+        };
+        return TimeGridView;
+    }(TimeGridView));
+    function buildDayTable(dateProfile, dateProfileGenerator) {
+        var daySeries = new core.DaySeries(dateProfile.renderRange, dateProfileGenerator);
+        return new core.DayTable(daySeries, false);
+    }
+
+    var main = core.createPlugin({
+        defaultView: 'timeGridWeek',
+        views: {
+            timeGrid: {
+                class: TimeGridView$1,
+                allDaySlot: true,
+                slotDuration: '00:30:00',
+                slotEventOverlap: true // a bad name. confused with overlap/constraint system
+            },
+            timeGridDay: {
+                type: 'timeGrid',
+                duration: { days: 1 }
+            },
+            timeGridWeek: {
+                type: 'timeGrid',
+                duration: { weeks: 1 }
+            }
+        }
+    });
+
+    exports.AbstractTimeGridView = TimeGridView;
+    exports.TimeGrid = TimeGrid;
+    exports.TimeGridSlicer = TimeGridSlicer;
+    exports.TimeGridView = TimeGridView$1;
+    exports.buildDayRanges = buildDayRanges;
+    exports.buildDayTable = buildDayTable;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/main.min.css b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..a1abf91f558e3cd6e6c64b27dd4ee1531dd15410
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.min.css
@@ -0,0 +1 @@
+@charset "UTF-8";.fc-timeGrid-view .fc-day-grid{position:relative;z-index:2}.fc-timeGrid-view .fc-day-grid .fc-row{min-height:3em}.fc-timeGrid-view .fc-day-grid .fc-row .fc-content-skeleton{padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-content-col{position:relative}.fc-time-grid .fc-content-skeleton{position:absolute;z-index:3;top:0;left:0;right:0}.fc-time-grid .fc-business-container{position:relative;z-index:1}.fc-time-grid .fc-bgevent-container{position:relative;z-index:2}.fc-time-grid .fc-highlight-container{z-index:3;position:relative}.fc-time-grid .fc-event-container{position:relative;z-index:4}.fc-time-grid .fc-now-indicator-line{z-index:5}.fc-time-grid .fc-mirror-container{position:relative;z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-time-grid-event{margin-bottom:1px}.fc-time-grid-event-inset{-webkit-box-shadow:0 0 0 1px #fff;box-shadow:0 0 0 1px #fff}.fc-time-grid-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-time-grid-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event .fc-content{overflow:hidden;max-height:100%}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:" - "}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event.fc-allow-mouse-resize .fc-resizer:after{content:"="}.fc-time-grid-event.fc-selected .fc-resizer{border-radius:5px;border-width:1px;width:8px;height:8px;border-style:solid;border-color:inherit;background:#fff;left:50%;margin-left:-5px;bottom:-5px}.fc-time-grid .fc-now-indicator-line{border-top-width:1px;left:0;right:0}.fc-time-grid .fc-now-indicator-arrow{margin-top:-5px}.fc-ltr .fc-time-grid .fc-now-indicator-arrow{left:0;border-width:5px 0 5px 6px;border-top-color:transparent;border-bottom-color:transparent}.fc-rtl .fc-time-grid .fc-now-indicator-arrow{right:0;border-width:5px 6px 5px 0;border-top-color:transparent;border-bottom-color:transparent}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/main.min.js b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..13c3433e9f0380fa64c972b20a2bb647df4299bc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Time Grid Plugin v4.3.0
+Docs & License: https://fullcalendar.io/
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core"),require("@fullcalendar/daygrid")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core","@fullcalendar/daygrid"],t):t((e=e||self).FullCalendarTimeGrid={},e.FullCalendar,e.FullCalendarDayGrid)}(this,function(e,t,r){"use strict";var i=function(e,t){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function n(e,t){function r(){this.constructor=e}i(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}var o=function(){return(o=Object.assign||function(e){for(var t,r=1,i=arguments.length;r<i;r++)for(var n in t=arguments[r])Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e}).apply(this,arguments)},s=function(e){function r(r){var i=e.call(this,r.context)||this;return i.timeGrid=r,i.fullTimeFormat=t.createFormatter({hour:"numeric",minute:"2-digit",separator:i.context.options.defaultRangeSeparator}),i}return n(r,e),r.prototype.attachSegs=function(e,t){for(var r=this.timeGrid.groupSegsByCol(e),i=0;i<r.length;i++)r[i]=this.sortEventSegs(r[i]);this.segsByCol=r,this.timeGrid.attachSegsByCol(r,this.timeGrid.fgContainerEls)},r.prototype.detachSegs=function(e){e.forEach(function(e){t.removeElement(e.el)}),this.segsByCol=null},r.prototype.computeSegSizes=function(e){var t=this.timeGrid,r=this.segsByCol,i=t.colCnt;if(t.computeSegVerticals(e),r)for(var n=0;n<i;n++)this.computeSegHorizontals(r[n])},r.prototype.assignSegSizes=function(e){var t=this.timeGrid,r=this.segsByCol,i=t.colCnt;if(t.assignSegVerticals(e),r)for(var n=0;n<i;n++)this.assignSegCss(r[n])},r.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",meridiem:!1}},r.prototype.computeDisplayEventEnd=function(){return!0},r.prototype.renderSegHtml=function(e,r){var i,n,o,s=this.context.view,a=e.eventRange,l=a.def,d=a.ui,c=l.allDay,h=s.computeEventDraggable(l,d),u=e.isStart&&s.computeEventStartResizable(l,d),p=e.isEnd&&s.computeEventEndResizable(l,d),f=this.getSegClasses(e,h,u||p,r),g=t.cssToStr(this.getSkinCss(d));if(f.unshift("fc-time-grid-event"),t.isMultiDayRange(a.range)){if(e.isStart||e.isEnd){var m=e.start,y=e.end;i=this._getTimeText(m,y,c),n=this._getTimeText(m,y,c,this.fullTimeFormat),o=this._getTimeText(m,y,c,null,!1)}}else i=this.getTimeText(a),n=this.getTimeText(a,this.fullTimeFormat),o=this.getTimeText(a,null,!1);return'<a class="'+f.join(" ")+'"'+(l.url?' href="'+t.htmlEscape(l.url)+'"':"")+(g?' style="'+g+'"':"")+'><div class="fc-content">'+(i?'<div class="fc-time" data-start="'+t.htmlEscape(o)+'" data-full="'+t.htmlEscape(n)+'"><span>'+t.htmlEscape(i)+"</span></div>":"")+(l.title?'<div class="fc-title">'+t.htmlEscape(l.title)+"</div>":"")+"</div>"+(p?'<div class="fc-resizer fc-end-resizer"></div>':"")+"</a>"},r.prototype.computeSegHorizontals=function(e){var t,r,i;if(function(e){var t,r,i,n,o;for(t=0;t<e.length;t++)for(r=e[t],i=0;i<r.length;i++)for((n=r[i]).forwardSegs=[],o=t+1;o<e.length;o++)l(n,e[o],n.forwardSegs)}(t=function(e){var t,r,i,n=[];for(t=0;t<e.length;t++){for(r=e[t],i=0;i<n.length&&l(r,n[i]).length;i++);r.level=i,(n[i]||(n[i]=[])).push(r)}return n}(e)),r=t[0]){for(i=0;i<r.length;i++)a(r[i]);for(i=0;i<r.length;i++)this.computeSegForwardBack(r[i],0,0)}},r.prototype.computeSegForwardBack=function(e,t,r){var i,n=e.forwardSegs;if(void 0===e.forwardCoord)for(n.length?(this.sortForwardSegs(n),this.computeSegForwardBack(n[0],t+1,r),e.forwardCoord=n[0].backwardCoord):e.forwardCoord=1,e.backwardCoord=e.forwardCoord-(e.forwardCoord-r)/(t+1),i=0;i<n.length;i++)this.computeSegForwardBack(n[i],0,e.forwardCoord)},r.prototype.sortForwardSegs=function(e){var r=e.map(d),i=[{field:"forwardPressure",order:-1},{field:"backwardCoord",order:1}].concat(this.context.view.eventOrderSpecs);return r.sort(function(e,r){return t.compareByFieldSpecs(e,r,i)}),r.map(function(e){return e._seg})},r.prototype.assignSegCss=function(e){for(var r=0,i=e;r<i.length;r++){var n=i[r];t.applyStyle(n.el,this.generateSegCss(n)),n.level>0&&n.el.classList.add("fc-time-grid-event-inset"),n.eventRange.def.title&&n.bottom-n.top<30&&n.el.classList.add("fc-short")}},r.prototype.generateSegCss=function(e){var t,r,i=this.context.options.slotEventOverlap,n=e.backwardCoord,o=e.forwardCoord,s=this.timeGrid.generateSegVerticalCss(e),a=this.timeGrid.isRtl;return i&&(o=Math.min(1,n+2*(o-n))),a?(t=1-o,r=n):(t=n,r=1-o),s.zIndex=e.level+1,s.left=100*t+"%",s.right=100*r+"%",i&&e.forwardPressure&&(s[a?"marginLeft":"marginRight"]=20),s},r}(t.FgEventRenderer);function a(e){var t,r,i=e.forwardSegs,n=0;if(void 0===e.forwardPressure){for(t=0;t<i.length;t++)a(r=i[t]),n=Math.max(n,1+r.forwardPressure);e.forwardPressure=n}}function l(e,t,r){void 0===r&&(r=[]);for(var i=0;i<t.length;i++)n=e,o=t[i],n.bottom>o.top&&n.top<o.bottom&&r.push(t[i]);var n,o;return r}function d(e){var r=t.buildSegCompareObj(e);return r.forwardPressure=e.forwardPressure,r.backwardCoord=e.backwardCoord,r}var c=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return n(t,e),t.prototype.attachSegs=function(e,t){this.segsByCol=this.timeGrid.groupSegsByCol(e),this.timeGrid.attachSegsByCol(this.segsByCol,this.timeGrid.mirrorContainerEls),this.sourceSeg=t.sourceSeg},t.prototype.generateSegCss=function(t){var r=e.prototype.generateSegCss.call(this,t),i=this.sourceSeg;if(i&&i.col===t.col){var n=e.prototype.generateSegCss.call(this,i);r.left=n.left,r.right=n.right,r.marginLeft=n.marginLeft,r.marginRight=n.marginRight}return r},t}(s),h=function(e){function t(t){var r=e.call(this,t.context)||this;return r.timeGrid=t,r}return n(t,e),t.prototype.attachSegs=function(e,t){var r,i=this.timeGrid;return"bgEvent"===e?r=i.bgContainerEls:"businessHours"===e?r=i.businessContainerEls:"highlight"===e&&(r=i.highlightContainerEls),i.attachSegsByCol(i.groupSegsByCol(t),r),t.map(function(e){return e.el})},t.prototype.computeSegSizes=function(e){this.timeGrid.computeSegVerticals(e)},t.prototype.assignSegSizes=function(e){this.timeGrid.assignSegVerticals(e)},t}(t.FillRenderer),u=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}],p=function(e){function i(r,i,n){var o=e.call(this,r,i)||this;o.isSlatSizesDirty=!1,o.isColSizesDirty=!1,o.renderSlats=t.memoizeRendering(o._renderSlats);var a=o.eventRenderer=new s(o),l=o.fillRenderer=new h(o);o.mirrorRenderer=new c(o);var d=o.renderColumns=t.memoizeRendering(o._renderColumns,o._unrenderColumns);return o.renderBusinessHours=t.memoizeRendering(l.renderSegs.bind(l,"businessHours"),l.unrender.bind(l,"businessHours"),[d]),o.renderDateSelection=t.memoizeRendering(o._renderDateSelection,o._unrenderDateSelection,[d]),o.renderFgEvents=t.memoizeRendering(a.renderSegs.bind(a),a.unrender.bind(a),[d]),o.renderBgEvents=t.memoizeRendering(l.renderSegs.bind(l,"bgEvent"),l.unrender.bind(l,"bgEvent"),[d]),o.renderEventSelection=t.memoizeRendering(a.selectByInstanceId.bind(a),a.unselectByInstanceId.bind(a),[o.renderFgEvents]),o.renderEventDrag=t.memoizeRendering(o._renderEventDrag,o._unrenderEventDrag,[d]),o.renderEventResize=t.memoizeRendering(o._renderEventResize,o._unrenderEventResize,[d]),o.processOptions(),i.innerHTML='<div class="fc-bg"></div><div class="fc-slats"></div><hr class="fc-divider '+o.theme.getClass("widgetHeader")+'" style="display:none" />',o.rootBgContainerEl=i.querySelector(".fc-bg"),o.slatContainerEl=i.querySelector(".fc-slats"),o.bottomRuleEl=i.querySelector(".fc-divider"),o.renderProps=n,o}return n(i,e),i.prototype.processOptions=function(){var e,r,i=this.opt("slotDuration"),n=this.opt("snapDuration");i=t.createDuration(i),n=n?t.createDuration(n):i,null===(e=t.wholeDivideDurations(i,n))&&(n=i,e=1),this.slotDuration=i,this.snapDuration=n,this.snapsPerSlot=e,r=this.opt("slotLabelFormat"),Array.isArray(r)&&(r=r[r.length-1]),this.labelFormat=t.createFormatter(r||{hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"short"}),r=this.opt("slotLabelInterval"),this.labelInterval=r?t.createDuration(r):this.computeLabelInterval(i)},i.prototype.computeLabelInterval=function(e){var r,i,n;for(r=u.length-1;r>=0;r--)if(i=t.createDuration(u[r]),null!==(n=t.wholeDivideDurations(i,e))&&n>1)return i;return e},i.prototype.render=function(e){var t=e.cells;this.colCnt=t.length,this.renderSlats(e.dateProfile),this.renderColumns(e.cells,e.dateProfile),this.renderBusinessHours(e.businessHourSegs),this.renderDateSelection(e.dateSelectionSegs),this.renderFgEvents(e.fgEventSegs),this.renderBgEvents(e.bgEventSegs),this.renderEventSelection(e.eventSelection),this.renderEventDrag(e.eventDrag),this.renderEventResize(e.eventResize)},i.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderSlats.unrender(),this.renderColumns.unrender()},i.prototype.updateSize=function(e){var t=this.fillRenderer,r=this.eventRenderer,i=this.mirrorRenderer;(e||this.isSlatSizesDirty)&&(this.buildSlatPositions(),this.isSlatSizesDirty=!1),(e||this.isColSizesDirty)&&(this.buildColPositions(),this.isColSizesDirty=!1),t.computeSizes(e),r.computeSizes(e),i.computeSizes(e),t.assignSizes(e),r.assignSizes(e),i.assignSizes(e)},i.prototype._renderSlats=function(e){var r=this.theme;this.slatContainerEl.innerHTML='<table class="'+r.getClass("tableGrid")+'">'+this.renderSlatRowHtml(e)+"</table>",this.slatEls=t.findElements(this.slatContainerEl,"tr"),this.slatPositions=new t.PositionCache(this.el,this.slatEls,!1,!0),this.isSlatSizesDirty=!0},i.prototype.renderSlatRowHtml=function(e){for(var r,i,n,o=this.dateEnv,s=this.theme,a=this.isRtl,l="",d=t.startOfDay(e.renderRange.start),c=e.minTime,h=t.createDuration(0);t.asRoughMs(c)<t.asRoughMs(e.maxTime);)r=o.add(d,c),i=null!==t.wholeDivideDurations(h,this.labelInterval),n='<td class="fc-axis fc-time '+s.getClass("widgetContent")+'">'+(i?"<span>"+t.htmlEscape(o.format(r,this.labelFormat))+"</span>":"")+"</td>",l+='<tr data-time="'+t.formatIsoTimeString(r)+'"'+(i?"":' class="fc-minor"')+">"+(a?"":n)+'<td class="'+s.getClass("widgetContent")+'"></td>'+(a?n:"")+"</tr>",c=t.addDurations(c,this.slotDuration),h=t.addDurations(h,this.slotDuration);return l},i.prototype._renderColumns=function(e,i){var n=this.theme,o=this.dateEnv,s=this.view,a=new r.DayBgRow(this.context);this.rootBgContainerEl.innerHTML='<table class="'+n.getClass("tableGrid")+'">'+a.renderHtml({cells:e,dateProfile:i,renderIntroHtml:this.renderProps.renderBgIntroHtml})+"</table>",this.colEls=t.findElements(this.el,".fc-day, .fc-disabled-day");for(var l=0;l<this.colCnt;l++)this.publiclyTrigger("dayRender",[{date:o.toDate(e[l].date),el:this.colEls[l],view:s}]);this.isRtl&&this.colEls.reverse(),this.colPositions=new t.PositionCache(this.el,this.colEls,!0,!1),this.renderContentSkeleton(),this.isColSizesDirty=!0},i.prototype._unrenderColumns=function(){this.unrenderContentSkeleton()},i.prototype.renderContentSkeleton=function(){var e,r=[];r.push(this.renderProps.renderIntroHtml());for(var i=0;i<this.colCnt;i++)r.push('<td><div class="fc-content-col"><div class="fc-event-container fc-mirror-container"></div><div class="fc-event-container"></div><div class="fc-highlight-container"></div><div class="fc-bgevent-container"></div><div class="fc-business-container"></div></div></td>');this.isRtl&&r.reverse(),e=this.contentSkeletonEl=t.htmlToElement('<div class="fc-content-skeleton"><table><tr>'+r.join("")+"</tr></table></div>"),this.colContainerEls=t.findElements(e,".fc-content-col"),this.mirrorContainerEls=t.findElements(e,".fc-mirror-container"),this.fgContainerEls=t.findElements(e,".fc-event-container:not(.fc-mirror-container)"),this.bgContainerEls=t.findElements(e,".fc-bgevent-container"),this.highlightContainerEls=t.findElements(e,".fc-highlight-container"),this.businessContainerEls=t.findElements(e,".fc-business-container"),this.isRtl&&(this.colContainerEls.reverse(),this.mirrorContainerEls.reverse(),this.fgContainerEls.reverse(),this.bgContainerEls.reverse(),this.highlightContainerEls.reverse(),this.businessContainerEls.reverse()),this.el.appendChild(e)},i.prototype.unrenderContentSkeleton=function(){t.removeElement(this.contentSkeletonEl)},i.prototype.groupSegsByCol=function(e){var t,r=[];for(t=0;t<this.colCnt;t++)r.push([]);for(t=0;t<e.length;t++)r[e[t].col].push(e[t]);return r},i.prototype.attachSegsByCol=function(e,t){var r,i,n;for(r=0;r<this.colCnt;r++)for(i=e[r],n=0;n<i.length;n++)t[r].appendChild(i[n].el)},i.prototype.getNowIndicatorUnit=function(){return"minute"},i.prototype.renderNowIndicator=function(e,r){if(this.colContainerEls){var i,n=this.computeDateTop(r),o=[];for(i=0;i<e.length;i++){var s=t.createElement("div",{className:"fc-now-indicator fc-now-indicator-line"});s.style.top=n+"px",this.colContainerEls[e[i].col].appendChild(s),o.push(s)}if(e.length>0){var a=t.createElement("div",{className:"fc-now-indicator fc-now-indicator-arrow"});a.style.top=n+"px",this.contentSkeletonEl.appendChild(a),o.push(a)}this.nowIndicatorEls=o}},i.prototype.unrenderNowIndicator=function(){this.nowIndicatorEls&&(this.nowIndicatorEls.forEach(t.removeElement),this.nowIndicatorEls=null)},i.prototype.getTotalSlatHeight=function(){return this.slatContainerEl.getBoundingClientRect().height},i.prototype.computeDateTop=function(e,r){return r||(r=t.startOfDay(e)),this.computeTimeTop(t.createDuration(e.valueOf()-r.valueOf()))},i.prototype.computeTimeTop=function(e){var r,i,n=this.slatEls.length,o=this.props.dateProfile,s=(e.milliseconds-t.asRoughMs(o.minTime))/t.asRoughMs(this.slotDuration);return s=Math.max(0,s),s=Math.min(n,s),r=Math.floor(s),i=s-(r=Math.min(r,n-1)),this.slatPositions.tops[r]+this.slatPositions.getHeight(r)*i},i.prototype.computeSegVerticals=function(e){var t,r,i,n=this.opt("timeGridEventMinHeight");for(t=0;t<e.length;t++)r=e[t],i=this.props.cells[r.col].date,r.top=this.computeDateTop(r.start,i),r.bottom=Math.max(r.top+n,this.computeDateTop(r.end,i))},i.prototype.assignSegVerticals=function(e){var r,i;for(r=0;r<e.length;r++)i=e[r],t.applyStyle(i.el,this.generateSegVerticalCss(i))},i.prototype.generateSegVerticalCss=function(e){return{top:e.top,bottom:-e.bottom}},i.prototype.buildPositionCaches=function(){this.buildColPositions(),this.buildSlatPositions()},i.prototype.buildColPositions=function(){this.colPositions.build()},i.prototype.buildSlatPositions=function(){this.slatPositions.build()},i.prototype.positionToHit=function(e,r){var i=this.dateEnv,n=this.snapsPerSlot,o=this.slatPositions,s=this.colPositions,a=s.leftToIndex(e),l=o.topToIndex(r);if(null!=a&&null!=l){var d=o.tops[l],c=o.getHeight(l),h=(r-d)/c,u=l*n+Math.floor(h*n),p=this.props.cells[a].date,f=t.addDurations(this.props.dateProfile.minTime,t.multiplyDuration(this.snapDuration,u)),g=i.add(p,f);return{col:a,dateSpan:{range:{start:g,end:i.add(g,this.snapDuration)},allDay:!1},dayEl:this.colEls[a],relativeRect:{left:s.lefts[a],right:s.rights[a],top:d,bottom:d+c}}}},i.prototype._renderEventDrag=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),e.isEvent?this.mirrorRenderer.renderSegs(e.segs,{isDragging:!0,sourceSeg:e.sourceSeg}):this.fillRenderer.renderSegs("highlight",e.segs))},i.prototype._unrenderEventDrag=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.mirrorRenderer.unrender(e.segs,{isDragging:!0,sourceSeg:e.sourceSeg}),this.fillRenderer.unrender("highlight"))},i.prototype._renderEventResize=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),this.mirrorRenderer.renderSegs(e.segs,{isResizing:!0,sourceSeg:e.sourceSeg}))},i.prototype._unrenderEventResize=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.mirrorRenderer.unrender(e.segs,{isResizing:!0,sourceSeg:e.sourceSeg}))},i.prototype._renderDateSelection=function(e){e&&(this.opt("selectMirror")?this.mirrorRenderer.renderSegs(e,{isSelecting:!0}):this.fillRenderer.renderSegs("highlight",e))},i.prototype._unrenderDateSelection=function(e){this.mirrorRenderer.unrender(e,{isSelecting:!0}),this.fillRenderer.unrender("highlight")},i}(t.DateComponent),f=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.getKeyInfo=function(){return{allDay:{},timed:{}}},r.prototype.getKeysForDateSpan=function(e){return e.allDay?["allDay"]:["timed"]},r.prototype.getKeysForEventDef=function(e){return e.allDay?t.hasBgRendering(e)?["timed","allDay"]:["allDay"]:["timed"]},r}(t.Splitter),g=t.createFormatter({week:"short"}),m=function(e){function i(i,n,o,s){var a=e.call(this,i,n,o,s)||this;a.splitter=new f,a.renderHeadIntroHtml=function(){var e,r=a,i=r.theme,n=r.dateEnv,o=a.props.dateProfile.renderRange,s=t.diffDays(o.start,o.end);return a.opt("weekNumbers")?(e=n.format(o.start,g),'<th class="fc-axis fc-week-number '+i.getClass("widgetHeader")+'" '+a.axisStyleAttr()+">"+t.buildGotoAnchorHtml(a,{date:o.start,type:"week",forceOff:s>1},t.htmlEscape(e))+"</th>"):'<th class="fc-axis '+i.getClass("widgetHeader")+'" '+a.axisStyleAttr()+"></th>"},a.renderTimeGridBgIntroHtml=function(){return'<td class="fc-axis '+a.theme.getClass("widgetContent")+'" '+a.axisStyleAttr()+"></td>"},a.renderTimeGridIntroHtml=function(){return'<td class="fc-axis" '+a.axisStyleAttr()+"></td>"},a.renderDayGridBgIntroHtml=function(){return'<td class="fc-axis '+a.theme.getClass("widgetContent")+'" '+a.axisStyleAttr()+"><span>"+t.getAllDayHtml(a)+"</span></td>"},a.renderDayGridIntroHtml=function(){return'<td class="fc-axis" '+a.axisStyleAttr()+"></td>"},a.el.classList.add("fc-timeGrid-view"),a.el.innerHTML=a.renderSkeletonHtml(),a.scroller=new t.ScrollComponent("hidden","auto");var l=a.scroller.el;a.el.querySelector(".fc-body > tr > td").appendChild(l),l.classList.add("fc-time-grid-container");var d=t.createElement("div",{className:"fc-time-grid"});if(l.appendChild(d),a.timeGrid=new p(a.context,d,{renderBgIntroHtml:a.renderTimeGridBgIntroHtml,renderIntroHtml:a.renderTimeGridIntroHtml}),a.opt("allDaySlot")){a.dayGrid=new r.DayGrid(a.context,a.el.querySelector(".fc-day-grid"),{renderNumberIntroHtml:a.renderDayGridIntroHtml,renderBgIntroHtml:a.renderDayGridBgIntroHtml,renderIntroHtml:a.renderDayGridIntroHtml,colWeekNumbersVisible:!1,cellWeekNumbersVisible:!1});var c=a.el.querySelector(".fc-divider");a.dayGrid.bottomCoordPadding=c.getBoundingClientRect().height}return a}return n(i,e),i.prototype.destroy=function(){e.prototype.destroy.call(this),this.timeGrid.destroy(),this.dayGrid&&this.dayGrid.destroy(),this.scroller.destroy()},i.prototype.renderSkeletonHtml=function(){var e=this.theme;return'<table class="'+e.getClass("tableGrid")+'">'+(this.opt("columnHeader")?'<thead class="fc-head"><tr><td class="fc-head-container '+e.getClass("widgetHeader")+'">&nbsp;</td></tr></thead>':"")+'<tbody class="fc-body"><tr><td class="'+e.getClass("widgetContent")+'">'+(this.opt("allDaySlot")?'<div class="fc-day-grid"></div><hr class="fc-divider '+e.getClass("widgetHeader")+'" />':"")+"</td></tr></tbody></table>"},i.prototype.getNowIndicatorUnit=function(){return this.timeGrid.getNowIndicatorUnit()},i.prototype.unrenderNowIndicator=function(){this.timeGrid.unrenderNowIndicator()},i.prototype.updateSize=function(t,r,i){e.prototype.updateSize.call(this,t,r,i),this.timeGrid.updateSize(t),this.dayGrid&&this.dayGrid.updateSize(t)},i.prototype.updateBaseSize=function(e,r,i){var n,o,s,a=this;if(this.axisWidth=t.matchCellWidths(t.findElements(this.el,".fc-axis")),this.timeGrid.colEls){var l=t.findElements(this.el,".fc-row").filter(function(e){return!a.scroller.el.contains(e)});this.timeGrid.bottomRuleEl.style.display="none",this.scroller.clear(),l.forEach(t.uncompensateScroll),this.dayGrid&&(this.dayGrid.removeSegPopover(),(n=this.opt("eventLimit"))&&"number"!=typeof n&&(n=5),n&&this.dayGrid.limitRows(n)),i||(o=this.computeScrollerHeight(r),this.scroller.setHeight(o),((s=this.scroller.getScrollbarWidths()).left||s.right)&&(l.forEach(function(e){t.compensateScroll(e,s)}),o=this.computeScrollerHeight(r),this.scroller.setHeight(o)),this.scroller.lockOverflow(s),this.timeGrid.getTotalSlatHeight()<o&&(this.timeGrid.bottomRuleEl.style.display=""))}else i||(o=this.computeScrollerHeight(r),this.scroller.setHeight(o))},i.prototype.computeScrollerHeight=function(e){return e-t.subtractInnerElHeight(this.el,this.scroller.el)},i.prototype.computeDateScroll=function(e){var t=this.timeGrid.computeTimeTop(e);return(t=Math.ceil(t))&&t++,{top:t}},i.prototype.queryDateScroll=function(){return{top:this.scroller.getScrollTop()}},i.prototype.applyDateScroll=function(e){void 0!==e.top&&this.scroller.setScrollTop(e.top)},i.prototype.axisStyleAttr=function(){return null!=this.axisWidth?'style="width:'+this.axisWidth+'px"':""},i}(t.View);m.prototype.usesMinMaxTime=!0;var y=function(e){function r(r,i){var n=e.call(this,r,i.el)||this;return n.buildDayRanges=t.memoize(v),n.slicer=new S,n.timeGrid=i,r.calendar.registerInteractiveComponent(n,{el:n.timeGrid.el}),n}return n(r,e),r.prototype.destroy=function(){e.prototype.destroy.call(this),this.calendar.unregisterInteractiveComponent(this)},r.prototype.render=function(e){var t=e.dateProfile,r=e.dayTable,i=this.dayRanges=this.buildDayRanges(r,t,this.dateEnv);this.timeGrid.receiveProps(o({},this.slicer.sliceProps(e,t,null,this.timeGrid,i),{dateProfile:t,cells:r.cells[0]}))},r.prototype.renderNowIndicator=function(e){this.timeGrid.renderNowIndicator(this.slicer.sliceNowDate(e,this.timeGrid,this.dayRanges),e)},r.prototype.buildPositionCaches=function(){this.timeGrid.buildPositionCaches()},r.prototype.queryHit=function(e,t){var r=this.timeGrid.positionToHit(e,t);if(r)return{component:this.timeGrid,dateSpan:r.dateSpan,dayEl:r.dayEl,rect:{left:r.relativeRect.left,right:r.relativeRect.right,top:r.relativeRect.top,bottom:r.relativeRect.bottom},layer:0}},r}(t.DateComponent);function v(e,t,r){for(var i=[],n=0,o=e.headerDates;n<o.length;n++){var s=o[n];i.push({start:r.add(s,t.minTime),end:r.add(s,t.maxTime)})}return i}var S=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.sliceRange=function(e,r){for(var i=[],n=0;n<r.length;n++){var o=t.intersectRanges(e,r[n]);o&&i.push({start:o.start,end:o.end,isStart:o.start.valueOf()===e.start.valueOf(),isEnd:o.end.valueOf()===e.end.valueOf(),col:n})}return i},r}(t.Slicer),C=function(e){function i(i,n,o,s){var a=e.call(this,i,n,o,s)||this;return a.buildDayTable=t.memoize(E),a.opt("columnHeader")&&(a.header=new t.DayHeader(a.context,a.el.querySelector(".fc-head-container"))),a.simpleTimeGrid=new y(a.context,a.timeGrid),a.dayGrid&&(a.simpleDayGrid=new r.SimpleDayGrid(a.context,a.dayGrid)),a}return n(i,e),i.prototype.destroy=function(){e.prototype.destroy.call(this),this.header&&this.header.destroy(),this.simpleTimeGrid.destroy(),this.simpleDayGrid&&this.simpleDayGrid.destroy()},i.prototype.render=function(t){e.prototype.render.call(this,t);var r=this.props.dateProfile,i=this.buildDayTable(r,this.dateProfileGenerator),n=this.splitter.splitProps(t);this.header&&this.header.receiveProps({dateProfile:r,dates:i.headerDates,datesRepDistinctDays:!0,renderIntroHtml:this.renderHeadIntroHtml}),this.simpleTimeGrid.receiveProps(o({},n.timed,{dateProfile:r,dayTable:i})),this.simpleDayGrid&&this.simpleDayGrid.receiveProps(o({},n.allDay,{dateProfile:r,dayTable:i,nextDayThreshold:this.nextDayThreshold,isRigid:!1}))},i.prototype.renderNowIndicator=function(e){this.simpleTimeGrid.renderNowIndicator(e)},i}(m);function E(e,r){var i=new t.DaySeries(e.renderRange,r);return new t.DayTable(i,!1)}var b=t.createPlugin({defaultView:"timeGridWeek",views:{timeGrid:{class:C,allDaySlot:!0,slotDuration:"00:30:00",slotEventOverlap:!0},timeGridDay:{type:"timeGrid",duration:{days:1}},timeGridWeek:{type:"timeGrid",duration:{weeks:1}}}});e.AbstractTimeGridView=m,e.TimeGrid=p,e.TimeGridSlicer=S,e.TimeGridView=C,e.buildDayRanges=v,e.buildDayTable=E,e.default=b,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/timegrid/package.json b/AKPlan/static/AKPlan/fullcalendar/timegrid/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..9226f35056822412bfffc445bb05596e26de2fb6
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timegrid/package.json
@@ -0,0 +1,36 @@
+{
+  "name": "@fullcalendar/timegrid",
+  "version": "4.3.0",
+  "title": "FullCalendar Time Grid Plugin",
+  "description": "Display your events on a grid of time slots",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/",
+  "docs": "https://fullcalendar.io/docs/timegrid-view",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar"
+  },
+  "license": "MIT",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "dependencies": {
+    "@fullcalendar/daygrid": "~4.3.0"
+  },
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/LICENSE.md b/AKPlan/static/AKPlan/fullcalendar/timeline/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..52296639f8117dd1cd8c5a9e629fed87e5b32cfc
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/LICENSE.md
@@ -0,0 +1,18 @@
+
+For complete licensing information, visit:
+http://fullcalendar.io/scheduler/license
+
+FullCalendar Scheduler is tri-licensed, meaning you must choose
+one of three licenses to use. Here is a summary of those licenses:
+
+- Commercial License
+  (a paid license, meant for commercial use)
+  http://fullcalendar.io/scheduler/license-details
+
+- Creative Commons Non-Commercial No-Derivatives
+  (meant for trial and non-commercial use)
+  https://creativecommons.org/licenses/by-nc-nd/4.0/
+
+- GPLv3 License
+  (meant for open-source projects)
+  http://www.gnu.org/licenses/gpl-3.0.en.html
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/README.md b/AKPlan/static/AKPlan/fullcalendar/timeline/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..ade1cab521515d4f22d7d5c2b05c38685af893c5
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/README.md
@@ -0,0 +1,8 @@
+
+# FullCalendar Timeline Plugin
+
+Display events on a horizontal time axis (without resources)
+
+[View the docs &raquo;](https://fullcalendar.io/docs/timeline-view-no-resources)
+
+This package was created from the [FullCalendar monorepo &raquo;](https://github.com/fullcalendar/fullcalendar-scheduler)
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/main.css b/AKPlan/static/AKPlan/fullcalendar/timeline/main.css
new file mode 100644
index 0000000000000000000000000000000000000000..69f5999c6d35f9a078fde0eb4a499c42bb5bf598
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/main.css
@@ -0,0 +1,351 @@
+/* Scroller
+--------------------------------------------------------------------------------------------------*/
+.fc-scroller-clip {
+  overflow: hidden;
+  /* for clipping scrollbars */
+  position: relative;
+  /* so things like scrollfollowers can attach to this */
+}
+
+/* supresses rendering of native scrollbars */
+/* on .fc-scroller */
+.fc-no-scrollbars {
+  background: rgba(255, 255, 255, 0);
+  /* hack for dynamic DOM nodes (in Chrome OSX at least) */
+}
+
+.fc-no-scrollbars::-webkit-scrollbar {
+  width: 0;
+  height: 0;
+}
+
+.fc-scroller-canvas {
+  position: relative;
+  /* origin for bg */
+  box-sizing: border-box;
+  /* so that padding (for gutter) will be part of height */
+  min-height: 100%;
+}
+
+.fc-scroller-canvas > .fc-bg {
+  z-index: 1;
+  /* make default? */
+}
+
+.fc-scroller-canvas > .fc-content {
+  z-index: 2;
+  /* make default? */
+  position: relative;
+  /* origin for inner content */
+  border-style: solid;
+  border-width: 0;
+}
+
+.fc-scroller-canvas.fc-gutter-left > .fc-content {
+  border-left-width: 1px;
+  margin-left: -1px;
+}
+
+.fc-scroller-canvas.fc-gutter-right > .fc-content {
+  border-right-width: 1px;
+  margin-right: -1px;
+}
+
+.fc-scroller-canvas.fc-gutter-top > .fc-content {
+  border-top-width: 1px;
+  margin-top: -1px;
+}
+
+/* content is responsible for bottom border */
+/* View Structure
+--------------------------------------------------------------------------------------------------*/
+.fc-rtl .fc-timeline {
+  direction: rtl;
+}
+
+.fc-scrolled .fc-head .fc-scroller {
+  z-index: 2;
+  /* so drop shadow will go above body panes */
+}
+
+.fc-timeline.fc-scrolled .fc-head .fc-scroller {
+  box-shadow: 0 3px 4px rgba(0, 0, 0, 0.075);
+}
+
+.fc-timeline .fc-body .fc-scroller {
+  z-index: 1;
+}
+
+/*
+on most tables that expand to the edges, kill the outer border,
+because the container elements take care of it.
+example tables:
+.fc-scroller-canvas .fc-content table
+.fc-scroller-canvas .fc-bg .fc-slats table
+*/
+.fc-timeline .fc-scroller-canvas > div > table,
+.fc-timeline .fc-scroller-canvas > div > div > table {
+  border-style: hidden;
+}
+
+/*
+for resource rows (in both the spreadsheet and timeline areas),
+undo previous rule in order to always show last border.
+*/
+.fc-timeline .fc-scroller-canvas > .fc-content > .fc-rows > table {
+  border-bottom-style: none;
+}
+
+/* Table Cell Common
+--------------------------------------------------------------------------------------------------*/
+.fc-timeline th,
+.fc-timeline td {
+  white-space: nowrap;
+}
+
+.fc-timeline .fc-cell-content {
+  overflow: hidden;
+}
+
+.fc-timeline .fc-cell-text {
+  display: inline-block;
+  padding-left: 4px;
+  padding-right: 4px;
+}
+
+/*
+Cells at the start of a week
+TODO: figure out better styling
+
+.fc-ltr .fc-timeline .fc-em-cell div {
+  border-left: 3px solid #eee;
+  height: 100%;
+}
+.fc-rtl .fc-timeline .fc-em-cell {
+  border-right-width: 3px;
+}
+*/
+/* head */
+.fc-timeline th {
+  vertical-align: middle;
+}
+
+.fc-timeline .fc-head .fc-cell-content {
+  padding-top: 3px;
+  padding-bottom: 3px;
+}
+
+.fc-timeline .fc-head .fc-time-area .fc-cell-content {
+  overflow: visible;
+}
+
+/* Time Area
+--------------------------------------------------------------------------------------------------*/
+.fc-time-area col {
+  min-width: 2.2em;
+  /* detected by JS */
+}
+
+/* head */
+.fc-ltr .fc-time-area .fc-chrono th {
+  text-align: left;
+}
+
+.fc-rtl .fc-time-area .fc-chrono th {
+  text-align: right;
+}
+
+/* body slats (vertical lines) */
+.fc-time-area .fc-slats {
+  /* fc-bg is responsible for a lot of this now! */
+  position: absolute;
+  z-index: 1;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.fc-time-area .fc-slats table {
+  height: 100%;
+}
+
+.fc-time-area .fc-slats .fc-minor {
+  border-style: dotted;
+}
+
+.fc-time-area .fc-slats td {
+  border-width: 0 1px;
+  /* need to do this. sometimes -1 margin wouldn't hide the dotted */
+}
+
+.fc-ltr .fc-time-area .fc-slats td {
+  border-right-width: 0;
+}
+
+.fc-rtl .fc-time-area .fc-slats td {
+  border-left-width: 0;
+}
+
+/* body content containers
+   can be within rows or directly within the pane's content
+*/
+.fc-time-area .fc-bgevent-container,
+.fc-time-area .fc-highlight-container {
+  position: absolute;
+  z-index: 2;
+  /* only for directly within pane. not for row. overridden later */
+  top: 0;
+  bottom: 0;
+  width: 0;
+}
+
+.fc-ltr .fc-time-area .fc-mirror-container,
+.fc-ltr .fc-time-area .fc-bgevent-container,
+.fc-ltr .fc-time-area .fc-highlight-container {
+  left: 0;
+}
+
+.fc-rtl .fc-time-area .fc-mirror-container,
+.fc-rtl .fc-time-area .fc-bgevent-container,
+.fc-rtl .fc-time-area .fc-highlight-container {
+  right: 0;
+}
+
+.fc-time-area .fc-bgevent,
+.fc-time-area .fc-highlight {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+}
+
+/* Now Indicator
+--------------------------------------------------------------------------------------------------*/
+.fc-timeline .fc-now-indicator {
+  /* both the arrow and the line */
+  z-index: 3;
+  /* one above scroller's fc-content */
+  top: 0;
+}
+
+.fc-time-area .fc-now-indicator-arrow {
+  margin: 0 -6px;
+  /* 5, then one more to counteract scroller's negative margins */
+  /* triangle pointing down... */
+  border-width: 6px 5px 0 5px;
+  border-left-color: transparent;
+  border-right-color: transparent;
+}
+
+.fc-time-area .fc-now-indicator-line {
+  margin: 0 -1px;
+  /* counteract scroller's negative margins */
+  bottom: 0;
+  border-left-width: 1px;
+}
+
+/* Event Container
+--------------------------------------------------------------------------------------------------*/
+.fc-time-area .fc-event-container {
+  position: relative;
+  z-index: 2;
+  /* above bgevent and highlight */
+  width: 0;
+  /* for event positioning. will end up on correct side based on dir */
+}
+
+.fc-time-area .fc-mirror-container {
+  /* also an fc-event-container */
+  position: absolute;
+  z-index: 3;
+  top: 0;
+}
+
+.fc-time-area .fc-event-container {
+  padding-bottom: 8px;
+  top: -1px;
+}
+
+.fc-time-area tr:first-child .fc-event-container {
+  top: 0;
+}
+
+.fc-no-overlap .fc-time-area .fc-event-container {
+  padding-bottom: 0;
+  top: 0;
+}
+
+/* Time Grid Events
+--------------------------------------------------------------------------------------------------*/
+.fc-timeline-event {
+  position: absolute;
+  display: flex;
+  border-radius: 0;
+  padding: 2px 1px;
+  margin-bottom: 1px;
+}
+
+.fc-no-overlap .fc-timeline-event {
+  padding-top: 5px;
+  padding-bottom: 5px;
+  margin-bottom: 0;
+}
+
+.fc-ltr .fc-timeline-event {
+  flex-direction: row;
+  margin-right: 1px;
+}
+
+.fc-rtl .fc-timeline-event {
+  margin-left: 1px;
+}
+
+.fc-timeline-event .fc-time-wrap {
+  flex-shrink: 0;
+  min-width: 0;
+}
+
+.fc-timeline-event .fc-title-wrap {
+  flex-grow: 1;
+  min-width: 0;
+}
+
+.fc-timeline-event .fc-time,
+.fc-timeline-event .fc-title {
+  display: inline-block;
+  vertical-align: top;
+  max-width: 100%;
+  padding: 0 2px;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  white-space: nowrap;
+  overflow: hidden;
+}
+
+.fc-timeline-event .fc-time {
+  font-weight: bold;
+}
+
+.fc-timeline-event.fc-not-start:before,
+.fc-timeline-event.fc-not-end:after {
+  content: "";
+  align-self: center;
+  width: 0;
+  height: 0;
+  margin: 0 1px;
+  border: 5px solid #000;
+  border-top-color: transparent;
+  border-bottom-color: transparent;
+  opacity: 0.5;
+}
+
+.fc-ltr .fc-timeline-event.fc-not-start:before,
+.fc-rtl .fc-timeline-event.fc-not-end:after {
+  border-left: 0;
+}
+
+.fc-ltr .fc-timeline-event.fc-not-end:after,
+.fc-rtl .fc-timeline-event.fc-not-start:before {
+  border-right: 0;
+}
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/main.d.ts b/AKPlan/static/AKPlan/fullcalendar/timeline/main.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dd4e64efc37a2120c166b819ccb1ca8eedb1f81a
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/main.d.ts
@@ -0,0 +1,353 @@
+// Generated by dts-bundle v0.7.3-fork.1
+// Dependencies for this module:
+//   ../../../../../@fullcalendar/core
+
+declare module '@fullcalendar/timeline' {
+    import TimelineView from '@fullcalendar/timeline/TimelineView';
+    export { TimelineView };
+    export { default as TimelineLane } from '@fullcalendar/timeline/TimelineLane';
+    export { default as ScrollJoiner } from '@fullcalendar/timeline/util/ScrollJoiner';
+    export { default as StickyScroller } from '@fullcalendar/timeline/util/StickyScroller';
+    export { default as TimeAxis } from '@fullcalendar/timeline/TimeAxis';
+    export { default as HeaderBodyLayout } from '@fullcalendar/timeline/HeaderBodyLayout';
+    const _default: import("@fullcalendar/core").PluginDef;
+    export default _default;
+}
+
+declare module '@fullcalendar/timeline/TimelineView' {
+    import { Hit, View, ViewProps, ComponentContext, ViewSpec, DateProfileGenerator, DateProfile, Duration } from '@fullcalendar/core';
+    import TimeAxis from '@fullcalendar/timeline/TimeAxis';
+    import TimelineLane from '@fullcalendar/timeline/TimelineLane';
+    export { TimelineView as default, TimelineView };
+    class TimelineView extends View {
+        timeAxis: TimeAxis;
+        lane: TimelineLane;
+        constructor(context: ComponentContext, viewSpec: ViewSpec, dateProfileGenerator: DateProfileGenerator, parentEl: HTMLElement);
+        destroy(): void;
+        renderSkeletonHtml(): string;
+        render(props: ViewProps): void;
+        updateSize(isResize: any, totalHeight: any, isAuto: any): void;
+        getNowIndicatorUnit(dateProfile: DateProfile): string;
+        renderNowIndicator(date: any): void;
+        unrenderNowIndicator(): void;
+        computeDateScroll(duration: Duration): {
+            left: number;
+        };
+        applyScroll(scroll: any, isResize: any): void;
+        applyDateScroll(scroll: any): void;
+        queryScroll(): {
+            top: number;
+            left: number;
+        };
+        buildPositionCaches(): void;
+        queryHit(positionLeft: number, positionTop: number, elWidth: number, elHeight: number): Hit;
+    }
+}
+
+declare module '@fullcalendar/timeline/TimelineLane' {
+    import { Duration, EventStore, EventUiHash, DateMarker, DateSpan, EventInteractionState, EventSegUiInteractionState, DateComponent, ComponentContext, Seg, DateProfile } from '@fullcalendar/core';
+    import TimeAxis from '@fullcalendar/timeline/TimeAxis';
+    export interface TimelineLaneSeg extends Seg {
+        start: DateMarker;
+        end: DateMarker;
+    }
+    export interface TimelineLaneProps {
+        dateProfile: DateProfile;
+        nextDayThreshold: Duration;
+        businessHours: EventStore | null;
+        eventStore: EventStore | null;
+        eventUiBases: EventUiHash;
+        dateSelection: DateSpan | null;
+        eventSelection: string;
+        eventDrag: EventInteractionState | null;
+        eventResize: EventInteractionState | null;
+    }
+    export { TimelineLane as default, TimelineLane };
+    class TimelineLane extends DateComponent<TimelineLaneProps> {
+        timeAxis: TimeAxis;
+        constructor(context: ComponentContext, fgContainerEl: HTMLElement, bgContainerEl: HTMLElement, timeAxis: TimeAxis);
+        render(props: TimelineLaneProps): void;
+        destroy(): void;
+        _renderEventDrag(state: EventSegUiInteractionState): void;
+        _unrenderEventDrag(state: EventSegUiInteractionState): void;
+        _renderEventResize(state: EventSegUiInteractionState): void;
+        _unrenderEventResize(state: EventSegUiInteractionState): void;
+        updateSize(isResize: boolean): void;
+    }
+}
+
+declare module '@fullcalendar/timeline/util/ScrollJoiner' {
+    import ClippedScroller from '@fullcalendar/timeline/util/ClippedScroller';
+    export { ScrollJoiner as default, ScrollJoiner };
+    class ScrollJoiner {
+        axis: any;
+        scrollers: ClippedScroller[];
+        masterScroller: ClippedScroller;
+        constructor(axis: any, scrollers: ClippedScroller[]);
+        initScroller(scroller: ClippedScroller): void;
+        assignMasterScroller(scroller: any): void;
+        unassignMasterScroller(): void;
+        update(): void;
+    }
+}
+
+declare module '@fullcalendar/timeline/util/StickyScroller' {
+    import { Rect, Point } from '@fullcalendar/core';
+    import EnhancedScroller from '@fullcalendar/timeline/util/EnhancedScroller';
+    interface ElementGeom {
+        parentBound: Rect;
+        naturalBound: Rect | null;
+        elWidth: number;
+        elHeight: number;
+        computedTextAlign: string;
+        intendedTextAlign: string;
+    }
+    export { StickyScroller as default, StickyScroller };
+    class StickyScroller {
+        scroller: EnhancedScroller;
+        usingRelative: boolean | null;
+        constructor(scroller: EnhancedScroller, isRtl: boolean, isVertical: boolean);
+        destroy(): void;
+        updateSize: () => void;
+        queryElGeoms(els: HTMLElement[]): ElementGeom[];
+        computeElDestinations(elGeoms: ElementGeom[], viewportWidth: number): Point[];
+    }
+    export {};
+}
+
+declare module '@fullcalendar/timeline/TimeAxis' {
+    import { DateProfile, DateMarker, Component, ComponentContext, Duration } from '@fullcalendar/core';
+    import HeaderBodyLayout from '@fullcalendar/timeline/HeaderBodyLayout';
+    import TimelineHeader from '@fullcalendar/timeline/TimelineHeader';
+    import TimelineSlats from '@fullcalendar/timeline/TimelineSlats';
+    import { TimelineDateProfile } from '@fullcalendar/timeline/timeline-date-profile';
+    import TimelineNowIndicator from '@fullcalendar/timeline/TimelineNowIndicator';
+    import StickyScroller from '@fullcalendar/timeline/util/StickyScroller';
+    export interface TimeAxisProps {
+        dateProfile: DateProfile;
+    }
+    export { TimeAxis as default, TimeAxis };
+    class TimeAxis extends Component<TimeAxisProps> {
+        layout: HeaderBodyLayout;
+        header: TimelineHeader;
+        slats: TimelineSlats;
+        nowIndicator: TimelineNowIndicator;
+        headStickyScroller: StickyScroller;
+        bodyStickyScroller: StickyScroller;
+        tDateProfile: TimelineDateProfile;
+        constructor(context: ComponentContext, headerContainerEl: any, bodyContainerEl: any);
+        destroy(): void;
+        render(props: TimeAxisProps): void;
+        getNowIndicatorUnit(dateProfile: DateProfile): string;
+        renderNowIndicator(date: any): void;
+        unrenderNowIndicator(): void;
+        updateSize(isResize: any, totalHeight: any, isAuto: any): void;
+        updateStickyScrollers(): void;
+        computeSlotWidth(): any;
+        computeDefaultSlotWidth(tDateProfile: any): number;
+        applySlotWidth(slotWidth: number | string): void;
+        computeDateSnapCoverage(date: DateMarker): number;
+        dateToCoord(date: any): any;
+        rangeToCoords(range: any): {
+            right: any;
+            left: any;
+        };
+        computeDateScroll(duration: Duration): {
+            left: number;
+        };
+        queryDateScroll(): {
+            left: number;
+        };
+        applyDateScroll(scroll: any): void;
+    }
+}
+
+declare module '@fullcalendar/timeline/HeaderBodyLayout' {
+    import ClippedScroller from '@fullcalendar/timeline/util/ClippedScroller';
+    import ScrollJoiner from '@fullcalendar/timeline/util/ScrollJoiner';
+    export { HeaderBodyLayout as default, HeaderBodyLayout };
+    class HeaderBodyLayout {
+        headerScroller: ClippedScroller;
+        bodyScroller: ClippedScroller;
+        scrollJoiner: ScrollJoiner;
+        constructor(headerContainerEl: any, bodyContainerEl: any, verticalScroll: any);
+        destroy(): void;
+        setHeight(totalHeight: any, isAuto: any): void;
+        queryHeadHeight(): number;
+    }
+}
+
+declare module '@fullcalendar/timeline/util/ClippedScroller' {
+    import { ScrollbarWidths } from '@fullcalendar/core';
+    import EnhancedScroller from '@fullcalendar/timeline/util/EnhancedScroller';
+    export { ClippedScroller as default, ClippedScroller };
+    class ClippedScroller {
+        el: HTMLElement;
+        enhancedScroll: EnhancedScroller;
+        isHScrollbarsClipped: boolean;
+        isVScrollbarsClipped: boolean;
+        constructor(overflowX: string, overflowY: string, parentEl: HTMLElement);
+        destroy(): void;
+        updateSize(): void;
+        setHeight(height: number | string): void;
+        getScrollbarWidths(): ScrollbarWidths;
+    }
+}
+
+declare module '@fullcalendar/timeline/util/EnhancedScroller' {
+    import { ScrollComponent, EmitterInterface } from '@fullcalendar/core';
+    import ScrollerCanvas from '@fullcalendar/timeline/util/ScrollerCanvas';
+    export { EnhancedScroller as default, EnhancedScroller };
+    class EnhancedScroller extends ScrollComponent {
+        on: EmitterInterface['on'];
+        one: EmitterInterface['one'];
+        off: EmitterInterface['off'];
+        trigger: EmitterInterface['trigger'];
+        triggerWith: EmitterInterface['triggerWith'];
+        hasHandlers: EmitterInterface['hasHandlers'];
+        canvas: ScrollerCanvas;
+        isScrolling: boolean;
+        isTouching: boolean;
+        isMoving: boolean;
+        isTouchScrollEnabled: boolean;
+        preventTouchScrollHandler: any;
+        requestMovingEnd: any;
+        constructor(overflowX: string, overflowY: string);
+        destroy(): void;
+        disableTouchScroll(): void;
+        enableTouchScroll(): void;
+        bindPreventTouchScroll(): void;
+        unbindPreventTouchScroll(): void;
+        bindHandlers(): void;
+        unbindHandlers(): void;
+        reportScroll: () => void;
+        reportScrollStart: () => void;
+        reportMovingEnd(): void;
+        reportScrollEnd(): void;
+        reportTouchStart: () => void;
+        reportTouchEnd: () => void;
+        getScrollLeft(): number;
+        setScrollLeft(val: any): void;
+        getScrollFromLeft(): number;
+    }
+}
+
+declare module '@fullcalendar/timeline/TimelineHeader' {
+    import { Component, ComponentContext, DateProfile } from '@fullcalendar/core';
+    import { TimelineDateProfile } from '@fullcalendar/timeline/timeline-date-profile';
+    export interface TimelineHeaderProps {
+        dateProfile: DateProfile;
+        tDateProfile: TimelineDateProfile;
+    }
+    export { TimelineHeader as default, TimelineHeader };
+    class TimelineHeader extends Component<TimelineHeaderProps> {
+        tableEl: HTMLElement;
+        slatColEls: HTMLElement[];
+        innerEls: HTMLElement[];
+        constructor(context: ComponentContext, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: TimelineHeaderProps): void;
+        renderDates(tDateProfile: TimelineDateProfile): void;
+    }
+}
+
+declare module '@fullcalendar/timeline/TimelineSlats' {
+    import { PositionCache, Component, ComponentContext, DateProfile } from '@fullcalendar/core';
+    import { TimelineDateProfile } from '@fullcalendar/timeline/timeline-date-profile';
+    export interface TimelineSlatsProps {
+        dateProfile: DateProfile;
+        tDateProfile: TimelineDateProfile;
+    }
+    export { TimelineSlats as default, TimelineSlats };
+    class TimelineSlats extends Component<TimelineSlatsProps> {
+        el: HTMLElement;
+        slatColEls: HTMLElement[];
+        slatEls: HTMLElement[];
+        outerCoordCache: PositionCache;
+        innerCoordCache: PositionCache;
+        constructor(context: ComponentContext, parentEl: HTMLElement);
+        destroy(): void;
+        render(props: TimelineSlatsProps): void;
+        renderDates(tDateProfile: TimelineDateProfile): void;
+        slatCellHtml(date: any, isEm: any, tDateProfile: TimelineDateProfile): string;
+        updateSize(): void;
+        positionToHit(leftPosition: any): {
+            dateSpan: {
+                range: {
+                    start: Date;
+                    end: Date;
+                };
+                allDay: boolean;
+            };
+            dayEl: HTMLElement;
+            left: any;
+            right: any;
+        };
+    }
+}
+
+declare module '@fullcalendar/timeline/timeline-date-profile' {
+    import { Duration, View, DateProfile, DateMarker, DateEnv, DateRange } from '@fullcalendar/core';
+    export interface TimelineDateProfile {
+        labelInterval: Duration;
+        slotDuration: Duration;
+        headerFormats: any;
+        isTimeScale: boolean;
+        largeUnit: string;
+        emphasizeWeeks: boolean;
+        snapDuration: Duration;
+        snapsPerSlot: number;
+        normalizedRange: DateRange;
+        timeWindowMs: number;
+        slotDates: DateMarker[];
+        isWeekStarts: boolean[];
+        snapDiffToIndex: number[];
+        snapIndexToDiff: number[];
+        snapCnt: number;
+        slotCnt: number;
+        cellRows: TimelineHeaderCell[][];
+    }
+    export interface TimelineHeaderCell {
+        text: string;
+        spanHtml: string;
+        date: DateMarker;
+        colspan: number;
+        isWeekStart: boolean;
+    }
+    export function buildTimelineDateProfile(dateProfile: DateProfile, view: View): TimelineDateProfile;
+    export function normalizeDate(date: DateMarker, tDateProfile: TimelineDateProfile, dateEnv: DateEnv): DateMarker;
+    export function normalizeRange(range: DateRange, tDateProfile: TimelineDateProfile, dateEnv: DateEnv): DateRange;
+    export function isValidDate(date: DateMarker, tDateProfile: TimelineDateProfile, dateProfile: DateProfile, view: View): boolean;
+}
+
+declare module '@fullcalendar/timeline/TimelineNowIndicator' {
+    export { TimelineNowIndicator as default, TimelineNowIndicator };
+    class TimelineNowIndicator {
+        headParent: HTMLElement;
+        bodyParent: HTMLElement;
+        arrowEl: HTMLElement;
+        lineEl: HTMLElement;
+        constructor(headParent: HTMLElement, bodyParent: HTMLElement);
+        render(coord: number, isRtl: boolean): void;
+        unrender(): void;
+    }
+}
+
+declare module '@fullcalendar/timeline/util/ScrollerCanvas' {
+    export { ScrollerCanvas as default, ScrollerCanvas };
+    class ScrollerCanvas {
+        el: HTMLElement;
+        contentEl: HTMLElement;
+        bgEl: HTMLElement;
+        gutters: any;
+        width: any;
+        minWidth: any;
+        constructor();
+        setGutters(gutters: any): void;
+        setWidth(width: any): void;
+        setMinWidth(minWidth: any): void;
+        clearWidth(): void;
+        updateSize(): void;
+    }
+}
+
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/main.esm.js b/AKPlan/static/AKPlan/fullcalendar/timeline/main.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..2cbb80b87d736bcfc82e5bbcaf2bfee99f762689
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/main.esm.js
@@ -0,0 +1,1983 @@
+/*!
+FullCalendar Timeline Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+import { htmlToElement, forceClassName, applyStyle, debounce, preventDefault, ScrollComponent, EmitterMixin, removeElement, createElement, computeEdges, asRoughMs, isSingleDay, getDayClasses, findElements, Component, PositionCache, findChildren, isInt, multiplyDuration, config, createFormatter, greatestDurationDenominator, createDuration, wholeDivideDurations, addDays, startOfDay, computeVisibleDayRange, asRoughMinutes, padStart, asRoughSeconds, diffWholeDays, buildGotoAnchorHtml, htmlEscape, translateRect, rangeContainsMarker, cssToStr, computeHeightAndMargins, applyStyleProp, FgEventRenderer, FillRenderer, memoizeRendering, DateComponent, intersectRanges, addMs, Slicer, View, createPlugin } from '@fullcalendar/core';
+
+/*! *****************************************************************************
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at http://www.apache.org/licenses/LICENSE-2.0
+
+THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+MERCHANTABLITY OR NON-INFRINGEMENT.
+
+See the Apache Version 2.0 License for specific language governing permissions
+and limitations under the License.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}
+
+var __assign = function() {
+    __assign = Object.assign || function __assign(t) {
+        for (var s, i = 1, n = arguments.length; i < n; i++) {
+            s = arguments[i];
+            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+        }
+        return t;
+    };
+    return __assign.apply(this, arguments);
+};
+
+/*
+A rectangular area of content that lives within a Scroller.
+Can have "gutters", areas of dead spacing around the perimeter.
+Also very useful for forcing a width, which a Scroller cannot do alone.
+Has a content area that lives above a background area.
+*/
+var ScrollerCanvas = /** @class */ (function () {
+    function ScrollerCanvas() {
+        this.gutters = {};
+        this.el = htmlToElement("<div class=\"fc-scroller-canvas\"> <div class=\"fc-content\"></div> <div class=\"fc-bg\"></div> </div>");
+        this.contentEl = this.el.querySelector('.fc-content');
+        this.bgEl = this.el.querySelector('.fc-bg');
+    }
+    /*
+    If falsy, resets all the gutters to 0
+    */
+    ScrollerCanvas.prototype.setGutters = function (gutters) {
+        if (!gutters) {
+            this.gutters = {};
+        }
+        else {
+            __assign(this.gutters, gutters);
+        }
+        this.updateSize();
+    };
+    ScrollerCanvas.prototype.setWidth = function (width) {
+        this.width = width;
+        this.updateSize();
+    };
+    ScrollerCanvas.prototype.setMinWidth = function (minWidth) {
+        this.minWidth = minWidth;
+        this.updateSize();
+    };
+    ScrollerCanvas.prototype.clearWidth = function () {
+        this.width = null;
+        this.minWidth = null;
+        this.updateSize();
+    };
+    ScrollerCanvas.prototype.updateSize = function () {
+        var _a = this, gutters = _a.gutters, el = _a.el;
+        // is border-box (width includes padding)
+        forceClassName(el, 'fc-gutter-left', gutters.left);
+        forceClassName(el, 'fc-gutter-right', gutters.right);
+        forceClassName(el, 'fc-gutter-top', gutters.top);
+        forceClassName(el, 'fc-gutter-bottom', gutters.bottom);
+        applyStyle(el, {
+            paddingLeft: gutters.left || '',
+            paddingRight: gutters.right || '',
+            paddingTop: gutters.top || '',
+            paddingBottom: gutters.bottom || '',
+            width: (this.width != null) ?
+                this.width + (gutters.left || 0) + (gutters.right || 0) :
+                '',
+            minWidth: (this.minWidth != null) ?
+                this.minWidth + (gutters.left || 0) + (gutters.right || 0) :
+                ''
+        });
+        applyStyle(this.bgEl, {
+            left: gutters.left || '',
+            right: gutters.right || '',
+            top: gutters.top || '',
+            bottom: gutters.bottom || ''
+        });
+    };
+    return ScrollerCanvas;
+}());
+
+var EnhancedScroller = /** @class */ (function (_super) {
+    __extends(EnhancedScroller, _super);
+    function EnhancedScroller(overflowX, overflowY) {
+        var _this = _super.call(this, overflowX, overflowY) || this;
+        // Scroll Events
+        // ----------------------------------------------------------------------------------------------
+        _this.reportScroll = function () {
+            if (!_this.isScrolling) {
+                _this.reportScrollStart();
+            }
+            _this.trigger('scroll');
+            _this.isMoving = true;
+            _this.requestMovingEnd();
+        };
+        _this.reportScrollStart = function () {
+            if (!_this.isScrolling) {
+                _this.isScrolling = true;
+                _this.trigger('scrollStart', _this.isTouching); // created in constructor
+            }
+        };
+        // Touch Events
+        // ----------------------------------------------------------------------------------------------
+        // will fire *before* the scroll event is fired
+        _this.reportTouchStart = function () {
+            _this.isTouching = true;
+        };
+        _this.reportTouchEnd = function () {
+            if (_this.isTouching) {
+                _this.isTouching = false;
+                // if touch scrolling was re-enabled during a recent touch scroll
+                // then unbind the handlers that are preventing it from happening.
+                if (_this.isTouchScrollEnabled) {
+                    _this.unbindPreventTouchScroll(); // won't do anything if not bound
+                }
+                // if the user ended their touch, and the scroll area wasn't moving,
+                // we consider this to be the end of the scroll.
+                if (!_this.isMoving) {
+                    _this.reportScrollEnd(); // won't fire if already ended
+                }
+            }
+        };
+        _this.isScrolling = false;
+        _this.isTouching = false;
+        _this.isMoving = false;
+        _this.isTouchScrollEnabled = true;
+        _this.requestMovingEnd = debounce(_this.reportMovingEnd, 500);
+        _this.canvas = new ScrollerCanvas();
+        _this.el.appendChild(_this.canvas.el);
+        _this.applyOverflow();
+        _this.bindHandlers();
+        return _this;
+    }
+    EnhancedScroller.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.unbindHandlers();
+    };
+    // Touch scroll prevention
+    // ----------------------------------------------------------------------------------------------
+    EnhancedScroller.prototype.disableTouchScroll = function () {
+        this.isTouchScrollEnabled = false;
+        this.bindPreventTouchScroll(); // will be unbound in enableTouchScroll or reportTouchEnd
+    };
+    EnhancedScroller.prototype.enableTouchScroll = function () {
+        this.isTouchScrollEnabled = true;
+        // only immediately unbind if a touch event is NOT in progress.
+        // otherwise, it will be handled by reportTouchEnd.
+        if (!this.isTouching) {
+            this.unbindPreventTouchScroll();
+        }
+    };
+    EnhancedScroller.prototype.bindPreventTouchScroll = function () {
+        if (!this.preventTouchScrollHandler) {
+            this.el.addEventListener('touchmove', (this.preventTouchScrollHandler = preventDefault));
+        }
+    };
+    EnhancedScroller.prototype.unbindPreventTouchScroll = function () {
+        if (this.preventTouchScrollHandler) {
+            this.el.removeEventListener('touchmove', this.preventTouchScrollHandler);
+            this.preventTouchScrollHandler = null;
+        }
+    };
+    // Handlers
+    // ----------------------------------------------------------------------------------------------
+    EnhancedScroller.prototype.bindHandlers = function () {
+        this.el.addEventListener('scroll', this.reportScroll);
+        this.el.addEventListener('touchstart', this.reportTouchStart, { passive: true });
+        this.el.addEventListener('touchend', this.reportTouchEnd);
+    };
+    EnhancedScroller.prototype.unbindHandlers = function () {
+        this.el.removeEventListener('scroll', this.reportScroll);
+        this.el.removeEventListener('touchstart', this.reportTouchStart, { passive: true });
+        this.el.removeEventListener('touchend', this.reportTouchEnd);
+    };
+    EnhancedScroller.prototype.reportMovingEnd = function () {
+        this.isMoving = false;
+        // only end the scroll if not currently touching.
+        // if touching, the scrolling will end later, on touchend.
+        if (!this.isTouching) {
+            this.reportScrollEnd();
+        }
+    };
+    EnhancedScroller.prototype.reportScrollEnd = function () {
+        if (this.isScrolling) {
+            this.trigger('scrollEnd');
+            this.isScrolling = false;
+        }
+    };
+    // Horizontal Scroll Normalization
+    // ----------------------------------------------------------------------------------------------
+    // http://stackoverflow.com/questions/24276619/better-way-to-get-the-viewport-of-a-scrollable-div-in-rtl-mode/24394376#24394376
+    // TODO: move all this to util functions
+    /*
+    If RTL, and scrolled to the left, returns NEGATIVE value (like Firefox)
+    */
+    EnhancedScroller.prototype.getScrollLeft = function () {
+        var el = this.el;
+        var direction = window.getComputedStyle(el).direction;
+        var val = el.scrollLeft;
+        if (direction === 'rtl') {
+            switch (getRtlScrollSystem()) {
+                case 'positive':
+                    val = (val + el.clientWidth) - el.scrollWidth;
+                    break;
+                case 'reverse':
+                    val = -val;
+                    break;
+            }
+        }
+        return val;
+    };
+    /*
+    Accepts a NEGATIVE value for when scrolled in RTL
+    */
+    EnhancedScroller.prototype.setScrollLeft = function (val) {
+        var el = this.el;
+        var direction = window.getComputedStyle(el).direction;
+        if (direction === 'rtl') {
+            switch (getRtlScrollSystem()) {
+                case 'positive':
+                    val = (val - el.clientWidth) + el.scrollWidth;
+                    break;
+                case 'reverse':
+                    val = -val;
+                    break;
+            }
+        }
+        el.scrollLeft = val;
+    };
+    /*
+    Always returns the number of pixels scrolled from the leftmost position (even if RTL).
+    Always positive.
+    */
+    EnhancedScroller.prototype.getScrollFromLeft = function () {
+        var el = this.el;
+        var direction = window.getComputedStyle(el).direction;
+        var val = el.scrollLeft;
+        if (direction === 'rtl') {
+            switch (getRtlScrollSystem()) {
+                case 'negative':
+                    val = (val - el.clientWidth) + el.scrollWidth;
+                    break;
+                case 'reverse':
+                    val = (-val - el.clientWidth) + el.scrollWidth;
+                    break;
+            }
+        }
+        return val;
+    };
+    return EnhancedScroller;
+}(ScrollComponent));
+EmitterMixin.mixInto(EnhancedScroller);
+// Horizontal Scroll System Detection
+// ----------------------------------------------------------------------------------------------
+var _rtlScrollSystem;
+function getRtlScrollSystem() {
+    return _rtlScrollSystem || (_rtlScrollSystem = detectRtlScrollSystem());
+}
+function detectRtlScrollSystem() {
+    var el = htmlToElement("<div style=\" position: absolute; top: -1000px; width: 1px; height: 1px; overflow: scroll; direction: rtl; font-size: 100px; \">A</div>");
+    document.body.appendChild(el);
+    var system;
+    if (el.scrollLeft > 0) {
+        system = 'positive';
+    }
+    else {
+        el.scrollLeft = 1;
+        if (el.scrollLeft > 0) {
+            system = 'reverse';
+        }
+        else {
+            system = 'negative';
+        }
+    }
+    removeElement(el);
+    return system;
+}
+
+/*
+A Scroller, but with a wrapping div that allows "clipping" away of native scrollbars,
+giving the appearance that there are no scrollbars.
+*/
+var ClippedScroller = /** @class */ (function () {
+    /*
+    Received overflows can be set to 'clipped', meaning scrollbars shouldn't be visible
+    to the user, but the area should still scroll.
+    */
+    function ClippedScroller(overflowX, overflowY, parentEl) {
+        this.isHScrollbarsClipped = false;
+        this.isVScrollbarsClipped = false;
+        if (overflowX === 'clipped-scroll') {
+            overflowX = 'scroll';
+            this.isHScrollbarsClipped = true;
+        }
+        if (overflowY === 'clipped-scroll') {
+            overflowY = 'scroll';
+            this.isVScrollbarsClipped = true;
+        }
+        this.enhancedScroll = new EnhancedScroller(overflowX, overflowY);
+        parentEl.appendChild(this.el = createElement('div', {
+            className: 'fc-scroller-clip'
+        }));
+        this.el.appendChild(this.enhancedScroll.el);
+    }
+    ClippedScroller.prototype.destroy = function () {
+        removeElement(this.el);
+    };
+    ClippedScroller.prototype.updateSize = function () {
+        var enhancedScroll = this.enhancedScroll;
+        var scrollEl = enhancedScroll.el;
+        var edges = computeEdges(scrollEl);
+        var cssProps = { marginLeft: 0, marginRight: 0, marginTop: 0, marginBottom: 0 };
+        // give the inner scrolling div negative margins so that its scrollbars
+        // are nudged outside of the bounding box of the wrapper, which is overflow:hidden
+        if (this.isVScrollbarsClipped) {
+            cssProps.marginLeft = -edges.scrollbarLeft;
+            cssProps.marginRight = -edges.scrollbarRight;
+        }
+        if (this.isHScrollbarsClipped) {
+            cssProps.marginBottom = -edges.scrollbarBottom;
+        }
+        applyStyle(scrollEl, cssProps);
+        // if we are attempting to hide the scrollbars offscreen, OSX/iOS will still
+        // display the floating scrollbars. attach a className to force-hide them.
+        if ((this.isHScrollbarsClipped || (enhancedScroll.overflowX === 'hidden')) && // should never show?
+            (this.isVScrollbarsClipped || (enhancedScroll.overflowY === 'hidden')) && // should never show?
+            !( // doesn't have any scrollbar mass
+            edges.scrollbarLeft ||
+                edges.scrollbarRight ||
+                edges.scrollbarBottom)) {
+            scrollEl.classList.add('fc-no-scrollbars');
+        }
+        else {
+            scrollEl.classList.remove('fc-no-scrollbars');
+        }
+    };
+    ClippedScroller.prototype.setHeight = function (height) {
+        this.enhancedScroll.setHeight(height);
+    };
+    /*
+    Accounts for 'clipped' scrollbars
+    */
+    ClippedScroller.prototype.getScrollbarWidths = function () {
+        var widths = this.enhancedScroll.getScrollbarWidths();
+        if (this.isVScrollbarsClipped) {
+            widths.left = 0;
+            widths.right = 0;
+        }
+        if (this.isHScrollbarsClipped) {
+            widths.bottom = 0;
+        }
+        return widths;
+    };
+    return ClippedScroller;
+}());
+
+var ScrollJoiner = /** @class */ (function () {
+    function ScrollJoiner(axis, scrollers) {
+        this.axis = axis;
+        this.scrollers = scrollers;
+        for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
+            var scroller = _a[_i];
+            this.initScroller(scroller);
+        }
+    }
+    ScrollJoiner.prototype.initScroller = function (scroller) {
+        var _this = this;
+        var enhancedScroll = scroller.enhancedScroll;
+        // when the user scrolls via mousewheel, we know for sure the target
+        // scroller should be the master. capture the various x-browser events that fire.
+        var onScroll = function () {
+            _this.assignMasterScroller(scroller);
+        };
+        'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ').forEach(function (evName) {
+            enhancedScroll.el.addEventListener(evName, onScroll);
+        });
+        enhancedScroll
+            .on('scrollStart', function () {
+            if (!_this.masterScroller) {
+                _this.assignMasterScroller(scroller);
+            }
+        })
+            .on('scroll', function () {
+            if (scroller === _this.masterScroller) {
+                for (var _i = 0, _a = _this.scrollers; _i < _a.length; _i++) {
+                    var otherScroller = _a[_i];
+                    if (otherScroller !== scroller) {
+                        switch (_this.axis) {
+                            case 'horizontal':
+                                otherScroller.enhancedScroll.el.scrollLeft = enhancedScroll.el.scrollLeft;
+                                break;
+                            case 'vertical':
+                                otherScroller.enhancedScroll.setScrollTop(enhancedScroll.getScrollTop());
+                                break;
+                        }
+                    }
+                }
+            }
+        })
+            .on('scrollEnd', function () {
+            if (scroller === _this.masterScroller) {
+                _this.unassignMasterScroller();
+            }
+        });
+    };
+    ScrollJoiner.prototype.assignMasterScroller = function (scroller) {
+        this.unassignMasterScroller();
+        this.masterScroller = scroller;
+        for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
+            var otherScroller = _a[_i];
+            if (otherScroller !== scroller) {
+                otherScroller.enhancedScroll.disableTouchScroll();
+            }
+        }
+    };
+    ScrollJoiner.prototype.unassignMasterScroller = function () {
+        if (this.masterScroller) {
+            for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
+                var otherScroller = _a[_i];
+                otherScroller.enhancedScroll.enableTouchScroll();
+            }
+            this.masterScroller = null;
+        }
+    };
+    ScrollJoiner.prototype.update = function () {
+        var allWidths = this.scrollers.map(function (scroller) { return scroller.getScrollbarWidths(); });
+        var maxLeft = 0;
+        var maxRight = 0;
+        var maxTop = 0;
+        var maxBottom = 0;
+        var widths;
+        var i;
+        for (var _i = 0, allWidths_1 = allWidths; _i < allWidths_1.length; _i++) {
+            widths = allWidths_1[_i];
+            maxLeft = Math.max(maxLeft, widths.left);
+            maxRight = Math.max(maxRight, widths.right);
+            maxTop = Math.max(maxTop, widths.top);
+            maxBottom = Math.max(maxBottom, widths.bottom);
+        }
+        for (i = 0; i < this.scrollers.length; i++) {
+            var scroller = this.scrollers[i];
+            widths = allWidths[i];
+            scroller.enhancedScroll.canvas.setGutters(this.axis === 'horizontal' ?
+                {
+                    left: maxLeft - widths.left,
+                    right: maxRight - widths.right
+                } :
+                {
+                    top: maxTop - widths.top,
+                    bottom: maxBottom - widths.bottom
+                });
+        }
+    };
+    return ScrollJoiner;
+}());
+
+var HeaderBodyLayout = /** @class */ (function () {
+    /*
+    verticalScroll = 'auto' | 'clipped-scroll'
+    */
+    function HeaderBodyLayout(headerContainerEl, bodyContainerEl, verticalScroll) {
+        this.headerScroller = new ClippedScroller('clipped-scroll', 'hidden', headerContainerEl);
+        this.bodyScroller = new ClippedScroller('auto', verticalScroll, bodyContainerEl);
+        this.scrollJoiner = new ScrollJoiner('horizontal', [
+            this.headerScroller,
+            this.bodyScroller
+        ]);
+    }
+    HeaderBodyLayout.prototype.destroy = function () {
+        this.headerScroller.destroy();
+        this.bodyScroller.destroy();
+    };
+    HeaderBodyLayout.prototype.setHeight = function (totalHeight, isAuto) {
+        var bodyHeight;
+        if (isAuto) {
+            bodyHeight = 'auto';
+        }
+        else {
+            bodyHeight = totalHeight - this.queryHeadHeight();
+        }
+        this.bodyScroller.setHeight(bodyHeight);
+        this.headerScroller.updateSize(); // adjusts gutters and classNames
+        this.bodyScroller.updateSize(); // adjusts gutters and classNames
+        this.scrollJoiner.update();
+    };
+    HeaderBodyLayout.prototype.queryHeadHeight = function () {
+        return this.headerScroller.enhancedScroll.canvas.contentEl.getBoundingClientRect().height;
+    };
+    return HeaderBodyLayout;
+}());
+
+var TimelineHeader = /** @class */ (function (_super) {
+    __extends(TimelineHeader, _super);
+    function TimelineHeader(context, parentEl) {
+        var _this = _super.call(this, context) || this;
+        parentEl.appendChild(_this.tableEl = createElement('table', {
+            className: _this.theme.getClass('tableGrid')
+        }));
+        return _this;
+    }
+    TimelineHeader.prototype.destroy = function () {
+        removeElement(this.tableEl);
+        _super.prototype.destroy.call(this);
+    };
+    TimelineHeader.prototype.render = function (props) {
+        this.renderDates(props.tDateProfile);
+    };
+    TimelineHeader.prototype.renderDates = function (tDateProfile) {
+        var _a = this, dateEnv = _a.dateEnv, theme = _a.theme;
+        var cellRows = tDateProfile.cellRows;
+        var lastRow = cellRows[cellRows.length - 1];
+        var isChrono = asRoughMs(tDateProfile.labelInterval) > asRoughMs(tDateProfile.slotDuration);
+        var oneDay = isSingleDay(tDateProfile.slotDuration);
+        var html = '<colgroup>';
+        // needs to be a col for each body slat. header cells will have colspans
+        for (var i = tDateProfile.slotCnt - 1; i >= 0; i--) {
+            html += '<col/>';
+        }
+        html += '</colgroup>';
+        html += '<tbody>';
+        for (var _i = 0, cellRows_1 = cellRows; _i < cellRows_1.length; _i++) {
+            var rowCells = cellRows_1[_i];
+            var isLast = rowCells === lastRow;
+            html += '<tr' + (isChrono && isLast ? ' class="fc-chrono"' : '') + '>';
+            for (var _b = 0, rowCells_1 = rowCells; _b < rowCells_1.length; _b++) {
+                var cell = rowCells_1[_b];
+                var headerCellClassNames = [theme.getClass('widgetHeader')];
+                if (cell.isWeekStart) {
+                    headerCellClassNames.push('fc-em-cell');
+                }
+                if (oneDay) {
+                    headerCellClassNames = headerCellClassNames.concat(getDayClasses(cell.date, this.props.dateProfile, this.context, true) // adds "today" class and other day-based classes
+                    );
+                }
+                html +=
+                    '<th class="' + headerCellClassNames.join(' ') + '"' +
+                        ' data-date="' + dateEnv.formatIso(cell.date, { omitTime: !tDateProfile.isTimeScale, omitTimeZoneOffset: true }) + '"' +
+                        (cell.colspan > 1 ? ' colspan="' + cell.colspan + '"' : '') +
+                        '>' +
+                        '<div class="fc-cell-content">' +
+                        cell.spanHtml +
+                        '</div>' +
+                        '</th>';
+            }
+            html += '</tr>';
+        }
+        html += '</tbody>';
+        this.tableEl.innerHTML = html; // TODO: does this work cross-browser?
+        this.slatColEls = findElements(this.tableEl, 'col');
+        this.innerEls = findElements(this.tableEl.querySelector('tr:last-child'), // compound selector won't work because of query-root problem
+        'th .fc-cell-text');
+        findElements(this.tableEl.querySelectorAll('tr:not(:last-child)'), // compound selector won't work because of query-root problem
+        'th .fc-cell-text').forEach(function (innerEl) {
+            innerEl.classList.add('fc-sticky');
+        });
+    };
+    return TimelineHeader;
+}(Component));
+
+var TimelineSlats = /** @class */ (function (_super) {
+    __extends(TimelineSlats, _super);
+    function TimelineSlats(context, parentEl) {
+        var _this = _super.call(this, context) || this;
+        parentEl.appendChild(_this.el = createElement('div', { className: 'fc-slats' }));
+        return _this;
+    }
+    TimelineSlats.prototype.destroy = function () {
+        removeElement(this.el);
+        _super.prototype.destroy.call(this);
+    };
+    TimelineSlats.prototype.render = function (props) {
+        this.renderDates(props.tDateProfile);
+    };
+    TimelineSlats.prototype.renderDates = function (tDateProfile) {
+        var _a = this, theme = _a.theme, view = _a.view, dateEnv = _a.dateEnv;
+        var slotDates = tDateProfile.slotDates, isWeekStarts = tDateProfile.isWeekStarts;
+        var html = '<table class="' + theme.getClass('tableGrid') + '">' +
+            '<colgroup>';
+        for (var i = 0; i < slotDates.length; i++) {
+            html += '<col/>';
+        }
+        html += '</colgroup>';
+        html += '<tbody><tr>';
+        for (var i = 0; i < slotDates.length; i++) {
+            html += this.slatCellHtml(slotDates[i], isWeekStarts[i], tDateProfile);
+        }
+        html += '</tr></tbody></table>';
+        this.el.innerHTML = html;
+        this.slatColEls = findElements(this.el, 'col');
+        this.slatEls = findElements(this.el, 'td');
+        for (var i = 0; i < slotDates.length; i++) {
+            view.publiclyTrigger('dayRender', [
+                {
+                    date: dateEnv.toDate(slotDates[i]),
+                    el: this.slatEls[i],
+                    view: view
+                }
+            ]);
+        }
+        this.outerCoordCache = new PositionCache(this.el, this.slatEls, true, // isHorizontal
+        false // isVertical
+        );
+        // for the inner divs within the slats
+        // used for event rendering and scrollTime, to disregard slat border
+        this.innerCoordCache = new PositionCache(this.el, findChildren(this.slatEls, 'div'), true, // isHorizontal
+        false // isVertical
+        );
+    };
+    TimelineSlats.prototype.slatCellHtml = function (date, isEm, tDateProfile) {
+        var _a = this, theme = _a.theme, dateEnv = _a.dateEnv;
+        var classes;
+        if (tDateProfile.isTimeScale) {
+            classes = [];
+            classes.push(isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval)) ?
+                'fc-major' :
+                'fc-minor');
+        }
+        else {
+            classes = getDayClasses(date, this.props.dateProfile, this.context);
+            classes.push('fc-day');
+        }
+        classes.unshift(theme.getClass('widgetContent'));
+        if (isEm) {
+            classes.push('fc-em-cell');
+        }
+        return '<td class="' + classes.join(' ') + '"' +
+            ' data-date="' + dateEnv.formatIso(date, { omitTime: !tDateProfile.isTimeScale, omitTimeZoneOffset: true }) + '"' +
+            '><div></div></td>';
+    };
+    TimelineSlats.prototype.updateSize = function () {
+        this.outerCoordCache.build();
+        this.innerCoordCache.build();
+    };
+    TimelineSlats.prototype.positionToHit = function (leftPosition) {
+        var outerCoordCache = this.outerCoordCache;
+        var tDateProfile = this.props.tDateProfile;
+        var slatIndex = outerCoordCache.leftToIndex(leftPosition);
+        if (slatIndex != null) {
+            // somewhat similar to what TimeGrid does. consolidate?
+            var slatWidth = outerCoordCache.getWidth(slatIndex);
+            var partial = this.isRtl ?
+                (outerCoordCache.rights[slatIndex] - leftPosition) / slatWidth :
+                (leftPosition - outerCoordCache.lefts[slatIndex]) / slatWidth;
+            var localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot);
+            var start = this.dateEnv.add(tDateProfile.slotDates[slatIndex], multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
+            var end = this.dateEnv.add(start, tDateProfile.snapDuration);
+            return {
+                dateSpan: {
+                    range: { start: start, end: end },
+                    allDay: !this.props.tDateProfile.isTimeScale
+                },
+                dayEl: this.slatColEls[slatIndex],
+                left: outerCoordCache.lefts[slatIndex],
+                right: outerCoordCache.rights[slatIndex]
+            };
+        }
+        return null;
+    };
+    return TimelineSlats;
+}(Component));
+
+var MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
+var MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
+var MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
+config.MAX_TIMELINE_SLOTS = 1000;
+// potential nice values for slot-duration and interval-duration
+var STOCK_SUB_DURATIONS = [
+    { years: 1 },
+    { months: 1 },
+    { days: 1 },
+    { hours: 1 },
+    { minutes: 30 },
+    { minutes: 15 },
+    { minutes: 10 },
+    { minutes: 5 },
+    { minutes: 1 },
+    { seconds: 30 },
+    { seconds: 15 },
+    { seconds: 10 },
+    { seconds: 5 },
+    { seconds: 1 },
+    { milliseconds: 500 },
+    { milliseconds: 100 },
+    { milliseconds: 10 },
+    { milliseconds: 1 }
+];
+function buildTimelineDateProfile(dateProfile, view) {
+    var dateEnv = view.dateEnv;
+    var tDateProfile = {
+        labelInterval: queryDurationOption(view, 'slotLabelInterval'),
+        slotDuration: queryDurationOption(view, 'slotDuration')
+    };
+    validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
+    ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
+    ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
+    var input = view.opt('slotLabelFormat');
+    var rawFormats = Array.isArray(input) ?
+        input
+        : (input != null) ?
+            [input]
+            :
+                computeHeaderFormats(tDateProfile, dateProfile, dateEnv, view);
+    tDateProfile.headerFormats = rawFormats.map(function (rawFormat) {
+        return createFormatter(rawFormat);
+    });
+    tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
+    var largeUnit = null;
+    if (!tDateProfile.isTimeScale) {
+        var slotUnit = greatestDurationDenominator(tDateProfile.slotDuration).unit;
+        if (/year|month|week/.test(slotUnit)) {
+            largeUnit = slotUnit;
+        }
+    }
+    tDateProfile.largeUnit = largeUnit;
+    tDateProfile.emphasizeWeeks =
+        isSingleDay(tDateProfile.slotDuration) &&
+            currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
+            !view.opt('businessHours');
+    /*
+    console.log('label interval =', timelineView.labelInterval.humanize())
+    console.log('slot duration =', timelineView.slotDuration.humanize())
+    console.log('header formats =', timelineView.headerFormats)
+    console.log('isTimeScale', timelineView.isTimeScale)
+    console.log('largeUnit', timelineView.largeUnit)
+    */
+    var rawSnapDuration = view.opt('snapDuration');
+    var snapDuration;
+    var snapsPerSlot;
+    if (rawSnapDuration) {
+        snapDuration = createDuration(rawSnapDuration);
+        snapsPerSlot = wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
+        // ^ TODO: warning if not whole?
+    }
+    if (snapsPerSlot == null) {
+        snapDuration = tDateProfile.slotDuration;
+        snapsPerSlot = 1;
+    }
+    tDateProfile.snapDuration = snapDuration;
+    tDateProfile.snapsPerSlot = snapsPerSlot;
+    // more...
+    var timeWindowMs = asRoughMs(dateProfile.maxTime) - asRoughMs(dateProfile.minTime);
+    // TODO: why not use normalizeRange!?
+    var normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
+    var normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
+    // apply minTime/maxTime
+    // TODO: View should be responsible.
+    if (tDateProfile.isTimeScale) {
+        normalizedStart = dateEnv.add(normalizedStart, dateProfile.minTime);
+        normalizedEnd = dateEnv.add(addDays(normalizedEnd, -1), dateProfile.maxTime);
+    }
+    tDateProfile.timeWindowMs = timeWindowMs;
+    tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
+    var slotDates = [];
+    var date = normalizedStart;
+    while (date < normalizedEnd) {
+        if (isValidDate(date, tDateProfile, dateProfile, view)) {
+            slotDates.push(date);
+        }
+        date = dateEnv.add(date, tDateProfile.slotDuration);
+    }
+    tDateProfile.slotDates = slotDates;
+    // more...
+    var snapIndex = -1;
+    var snapDiff = 0; // index of the diff :(
+    var snapDiffToIndex = [];
+    var snapIndexToDiff = [];
+    date = normalizedStart;
+    while (date < normalizedEnd) {
+        if (isValidDate(date, tDateProfile, dateProfile, view)) {
+            snapIndex++;
+            snapDiffToIndex.push(snapIndex);
+            snapIndexToDiff.push(snapDiff);
+        }
+        else {
+            snapDiffToIndex.push(snapIndex + 0.5);
+        }
+        date = dateEnv.add(date, tDateProfile.snapDuration);
+        snapDiff++;
+    }
+    tDateProfile.snapDiffToIndex = snapDiffToIndex;
+    tDateProfile.snapIndexToDiff = snapIndexToDiff;
+    tDateProfile.snapCnt = snapIndex + 1; // is always one behind
+    tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
+    // more...
+    tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
+    tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, view);
+    return tDateProfile;
+}
+/*
+snaps to appropriate unit
+*/
+function normalizeDate(date, tDateProfile, dateEnv) {
+    var normalDate = date;
+    if (!tDateProfile.isTimeScale) {
+        normalDate = startOfDay(normalDate);
+        if (tDateProfile.largeUnit) {
+            normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
+        }
+    }
+    return normalDate;
+}
+/*
+snaps to appropriate unit
+*/
+function normalizeRange(range, tDateProfile, dateEnv) {
+    if (!tDateProfile.isTimeScale) {
+        range = computeVisibleDayRange(range);
+        if (tDateProfile.largeUnit) {
+            var dayRange = range; // preserve original result
+            range = {
+                start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
+                end: dateEnv.startOf(range.end, tDateProfile.largeUnit)
+            };
+            // if date is partially through the interval, or is in the same interval as the start,
+            // make the exclusive end be the *next* interval
+            if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
+                range = {
+                    start: range.start,
+                    end: dateEnv.add(range.end, tDateProfile.slotDuration)
+                };
+            }
+        }
+    }
+    return range;
+}
+function isValidDate(date, tDateProfile, dateProfile, view) {
+    if (view.dateProfileGenerator.isHiddenDay(date)) {
+        return false;
+    }
+    else if (tDateProfile.isTimeScale) {
+        // determine if the time is within minTime/maxTime, which may have wacky values
+        var day = startOfDay(date);
+        var timeMs = date.valueOf() - day.valueOf();
+        var ms = timeMs - asRoughMs(dateProfile.minTime); // milliseconds since minTime
+        ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
+        return ms < tDateProfile.timeWindowMs; // before the maxTime?
+    }
+    else {
+        return true;
+    }
+}
+function queryDurationOption(view, name) {
+    var input = view.opt(name);
+    if (input != null) {
+        return createDuration(input);
+    }
+}
+function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
+    var currentRange = dateProfile.currentRange;
+    // make sure labelInterval doesn't exceed the max number of cells
+    if (tDateProfile.labelInterval) {
+        var labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
+        if (labelCnt > config.MAX_TIMELINE_SLOTS) {
+            console.warn('slotLabelInterval results in too many cells');
+            tDateProfile.labelInterval = null;
+        }
+    }
+    // make sure slotDuration doesn't exceed the maximum number of cells
+    if (tDateProfile.slotDuration) {
+        var slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
+        if (slotCnt > config.MAX_TIMELINE_SLOTS) {
+            console.warn('slotDuration results in too many cells');
+            tDateProfile.slotDuration = null;
+        }
+    }
+    // make sure labelInterval is a multiple of slotDuration
+    if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
+        var slotsPerLabel = wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
+        if (slotsPerLabel === null || slotsPerLabel < 1) {
+            console.warn('slotLabelInterval must be a multiple of slotDuration');
+            tDateProfile.slotDuration = null;
+        }
+    }
+}
+function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
+    var currentRange = dateProfile.currentRange;
+    var labelInterval = tDateProfile.labelInterval;
+    if (!labelInterval) {
+        // compute based off the slot duration
+        // find the largest label interval with an acceptable slots-per-label
+        var input = void 0;
+        if (tDateProfile.slotDuration) {
+            for (var _i = 0, STOCK_SUB_DURATIONS_1 = STOCK_SUB_DURATIONS; _i < STOCK_SUB_DURATIONS_1.length; _i++) {
+                input = STOCK_SUB_DURATIONS_1[_i];
+                var tryLabelInterval = createDuration(input);
+                var slotsPerLabel = wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
+                if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
+                    labelInterval = tryLabelInterval;
+                    break;
+                }
+            }
+            // use the slot duration as a last resort
+            if (!labelInterval) {
+                labelInterval = tDateProfile.slotDuration;
+            }
+            // compute based off the view's duration
+            // find the largest label interval that yields the minimum number of labels
+        }
+        else {
+            for (var _a = 0, STOCK_SUB_DURATIONS_2 = STOCK_SUB_DURATIONS; _a < STOCK_SUB_DURATIONS_2.length; _a++) {
+                input = STOCK_SUB_DURATIONS_2[_a];
+                labelInterval = createDuration(input);
+                var labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
+                if (labelCnt >= MIN_AUTO_LABELS) {
+                    break;
+                }
+            }
+        }
+        tDateProfile.labelInterval = labelInterval;
+    }
+    return labelInterval;
+}
+function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
+    var currentRange = dateProfile.currentRange;
+    var slotDuration = tDateProfile.slotDuration;
+    if (!slotDuration) {
+        var labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
+        // compute based off the label interval
+        // find the largest slot duration that is different from labelInterval, but still acceptable
+        for (var _i = 0, STOCK_SUB_DURATIONS_3 = STOCK_SUB_DURATIONS; _i < STOCK_SUB_DURATIONS_3.length; _i++) {
+            var input = STOCK_SUB_DURATIONS_3[_i];
+            var trySlotDuration = createDuration(input);
+            var slotsPerLabel = wholeDivideDurations(labelInterval, trySlotDuration);
+            if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
+                slotDuration = trySlotDuration;
+                break;
+            }
+        }
+        // only allow the value if it won't exceed the view's # of slots limit
+        if (slotDuration) {
+            var slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
+            if (slotCnt > MAX_AUTO_CELLS) {
+                slotDuration = null;
+            }
+        }
+        // use the label interval as a last resort
+        if (!slotDuration) {
+            slotDuration = labelInterval;
+        }
+        tDateProfile.slotDuration = slotDuration;
+    }
+    return slotDuration;
+}
+function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, view) {
+    var format1;
+    var format2;
+    var labelInterval = tDateProfile.labelInterval;
+    var unit = greatestDurationDenominator(labelInterval).unit;
+    var weekNumbersVisible = view.opt('weekNumbers');
+    var format0 = (format1 = (format2 = null));
+    // NOTE: weekNumber computation function wont work
+    if ((unit === 'week') && !weekNumbersVisible) {
+        unit = 'day';
+    }
+    switch (unit) {
+        case 'year':
+            format0 = { year: 'numeric' }; // '2015'
+            break;
+        case 'month':
+            if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
+                format0 = { year: 'numeric' }; // '2015'
+            }
+            format1 = { month: 'short' }; // 'Jan'
+            break;
+        case 'week':
+            if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
+                format0 = { year: 'numeric' }; // '2015'
+            }
+            format1 = { week: 'narrow' }; // 'Wk4'
+            break;
+        case 'day':
+            if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
+                format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
+            }
+            else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
+                format0 = { month: 'long' }; // 'January'
+            }
+            if (weekNumbersVisible) {
+                format1 = { week: 'short' }; // 'Wk 4'
+            }
+            format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
+            break;
+        case 'hour':
+            if (weekNumbersVisible) {
+                format0 = { week: 'short' }; // 'Wk 4'
+            }
+            if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
+                format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
+            }
+            format2 = {
+                hour: 'numeric',
+                minute: '2-digit',
+                omitZeroMinute: true,
+                meridiem: 'short'
+            };
+            break;
+        case 'minute':
+            // sufficiently large number of different minute cells?
+            if ((asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
+                format0 = {
+                    hour: 'numeric',
+                    meridiem: 'short'
+                };
+                format1 = function (params) {
+                    return ':' + padStart(params.date.minute, 2); // ':30'
+                };
+            }
+            else {
+                format0 = {
+                    hour: 'numeric',
+                    minute: 'numeric',
+                    meridiem: 'short'
+                };
+            }
+            break;
+        case 'second':
+            // sufficiently large number of different second cells?
+            if ((asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
+                format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
+                format1 = function (params) {
+                    return ':' + padStart(params.date.second, 2); // ':30'
+                };
+            }
+            else {
+                format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
+            }
+            break;
+        case 'millisecond':
+            format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
+            format1 = function (params) {
+                return '.' + padStart(params.millisecond, 3);
+            };
+            break;
+    }
+    return [].concat(format0 || [], format1 || [], format2 || []);
+}
+// Compute the number of the give units in the "current" range.
+// Won't go more precise than days.
+// Will return `0` if there's not a clean whole interval.
+function currentRangeAs(unit, dateProfile, dateEnv) {
+    var range = dateProfile.currentRange;
+    var res = null;
+    if (unit === 'years') {
+        res = dateEnv.diffWholeYears(range.start, range.end);
+    }
+    else if (unit === 'months') {
+        res = dateEnv.diffWholeMonths(range.start, range.end);
+    }
+    else if (unit === 'weeks') {
+        res = dateEnv.diffWholeMonths(range.start, range.end);
+    }
+    else if (unit === 'days') {
+        res = diffWholeDays(range.start, range.end);
+    }
+    return res || 0;
+}
+function buildIsWeekStarts(tDateProfile, dateEnv) {
+    var slotDates = tDateProfile.slotDates, emphasizeWeeks = tDateProfile.emphasizeWeeks;
+    var prevWeekNumber = null;
+    var isWeekStarts = [];
+    for (var _i = 0, slotDates_1 = slotDates; _i < slotDates_1.length; _i++) {
+        var slotDate = slotDates_1[_i];
+        var weekNumber = dateEnv.computeWeekNumber(slotDate);
+        var isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
+        prevWeekNumber = weekNumber;
+        isWeekStarts.push(isWeekStart);
+    }
+    return isWeekStarts;
+}
+function buildCellRows(tDateProfile, dateEnv, view) {
+    var slotDates = tDateProfile.slotDates;
+    var formats = tDateProfile.headerFormats;
+    var cellRows = formats.map(function (format) { return []; }); // indexed by row,col
+    // specifically for navclicks
+    var rowUnits = formats.map(function (format) {
+        return format.getLargestUnit ? format.getLargestUnit() : null;
+    });
+    // builds cellRows and slotCells
+    for (var i = 0; i < slotDates.length; i++) {
+        var date = slotDates[i];
+        var isWeekStart = tDateProfile.isWeekStarts[i];
+        for (var row = 0; row < formats.length; row++) {
+            var format = formats[row];
+            var rowCells = cellRows[row];
+            var leadingCell = rowCells[rowCells.length - 1];
+            var isSuperRow = (formats.length > 1) && (row < (formats.length - 1)); // more than one row and not the last
+            var newCell = null;
+            if (isSuperRow) {
+                var text = dateEnv.format(date, format);
+                if (!leadingCell || (leadingCell.text !== text)) {
+                    newCell = buildCellObject(date, text, rowUnits[row], view);
+                }
+                else {
+                    leadingCell.colspan += 1;
+                }
+            }
+            else {
+                if (!leadingCell ||
+                    isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
+                    var text = dateEnv.format(date, format);
+                    newCell = buildCellObject(date, text, rowUnits[row], view);
+                }
+                else {
+                    leadingCell.colspan += 1;
+                }
+            }
+            if (newCell) {
+                newCell.weekStart = isWeekStart;
+                rowCells.push(newCell);
+            }
+        }
+    }
+    return cellRows;
+}
+function buildCellObject(date, text, rowUnit, view) {
+    var spanHtml = buildGotoAnchorHtml(view, {
+        date: date,
+        type: rowUnit,
+        forceOff: !rowUnit
+    }, {
+        'class': 'fc-cell-text'
+    }, htmlEscape(text));
+    return { text: text, spanHtml: spanHtml, date: date, colspan: 1, isWeekStart: false };
+}
+
+var TimelineNowIndicator = /** @class */ (function () {
+    function TimelineNowIndicator(headParent, bodyParent) {
+        this.headParent = headParent;
+        this.bodyParent = bodyParent;
+    }
+    TimelineNowIndicator.prototype.render = function (coord, isRtl) {
+        var styleProps = isRtl ? { right: -coord } : { left: coord };
+        this.headParent.appendChild(this.arrowEl = createElement('div', {
+            className: 'fc-now-indicator fc-now-indicator-arrow',
+            style: styleProps
+        }));
+        this.bodyParent.appendChild(this.lineEl = createElement('div', {
+            className: 'fc-now-indicator fc-now-indicator-line',
+            style: styleProps
+        }));
+    };
+    TimelineNowIndicator.prototype.unrender = function () {
+        if (this.arrowEl) {
+            removeElement(this.arrowEl);
+        }
+        if (this.lineEl) {
+            removeElement(this.lineEl);
+        }
+    };
+    return TimelineNowIndicator;
+}());
+
+var STICKY_PROP_VAL = computeStickyPropVal(); // if null, means not supported at all
+var IS_MS_EDGE = /Edge/.test(navigator.userAgent);
+var IS_SAFARI = STICKY_PROP_VAL === '-webkit-sticky'; // good b/c doesn't confuse chrome
+var STICKY_CLASSNAME = 'fc-sticky';
+/*
+useful beyond the native position:sticky for these reasons:
+- support in IE11
+- nice centering support
+*/
+var StickyScroller = /** @class */ (function () {
+    function StickyScroller(scroller, isRtl, isVertical) {
+        var _this = this;
+        this.usingRelative = null;
+        /*
+        known bug: called twice on init. problem when mixing with ScrollJoiner
+        */
+        this.updateSize = function () {
+            var els = Array.prototype.slice.call(_this.scroller.canvas.el.querySelectorAll('.' + STICKY_CLASSNAME));
+            var elGeoms = _this.queryElGeoms(els);
+            var viewportWidth = _this.scroller.el.clientWidth;
+            if (_this.usingRelative) {
+                var elDestinations = _this.computeElDestinations(elGeoms, viewportWidth); // read before prepPositioning
+                assignRelativePositions(els, elGeoms, elDestinations);
+            }
+            else {
+                assignStickyPositions(els, elGeoms, viewportWidth);
+            }
+        };
+        this.scroller = scroller;
+        this.usingRelative =
+            !STICKY_PROP_VAL || // IE11
+                (IS_MS_EDGE && isRtl) || // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/18883305/
+                ((IS_MS_EDGE || IS_SAFARI) && isVertical); // because doesn't work with rowspan in tables, our only vertial use
+        if (this.usingRelative) {
+            scroller.on('scrollEnd', this.updateSize);
+        }
+    }
+    StickyScroller.prototype.destroy = function () {
+        this.scroller.off('scrollEnd', this.updateSize);
+    };
+    StickyScroller.prototype.queryElGeoms = function (els) {
+        var canvasOrigin = this.scroller.canvas.el.getBoundingClientRect();
+        var elGeoms = [];
+        for (var _i = 0, els_1 = els; _i < els_1.length; _i++) {
+            var el = els_1[_i];
+            var parentBound = translateRect(el.parentNode.getBoundingClientRect(), -canvasOrigin.left, -canvasOrigin.top);
+            var elRect = el.getBoundingClientRect();
+            var computedStyles = window.getComputedStyle(el);
+            var computedTextAlign = window.getComputedStyle(el.parentNode).textAlign; // ask the parent
+            var intendedTextAlign = computedTextAlign;
+            var naturalBound = null;
+            if (computedStyles.position !== 'sticky') {
+                naturalBound = translateRect(elRect, -canvasOrigin.left - (parseFloat(computedStyles.left) || 0), // could be 'auto'
+                -canvasOrigin.top - (parseFloat(computedStyles.top) || 0));
+            }
+            if (el.hasAttribute('data-sticky-center')) {
+                intendedTextAlign = 'center';
+            }
+            elGeoms.push({
+                parentBound: parentBound,
+                naturalBound: naturalBound,
+                elWidth: elRect.width,
+                elHeight: elRect.height,
+                computedTextAlign: computedTextAlign,
+                intendedTextAlign: intendedTextAlign
+            });
+        }
+        return elGeoms;
+    };
+    StickyScroller.prototype.computeElDestinations = function (elGeoms, viewportWidth) {
+        var viewportLeft = this.scroller.getScrollFromLeft();
+        var viewportTop = this.scroller.getScrollTop();
+        var viewportRight = viewportLeft + viewportWidth;
+        return elGeoms.map(function (elGeom) {
+            var elWidth = elGeom.elWidth, elHeight = elGeom.elHeight, parentBound = elGeom.parentBound, naturalBound = elGeom.naturalBound;
+            var destLeft; // relative to canvas topleft
+            var destTop; // "
+            switch (elGeom.intendedTextAlign) {
+                case 'left':
+                    destLeft = viewportLeft;
+                    break;
+                case 'right':
+                    destLeft = viewportRight - elWidth;
+                    break;
+                case 'center':
+                    destLeft = (viewportLeft + viewportRight) / 2 - elWidth / 2;
+                    break;
+            }
+            destLeft = Math.min(destLeft, parentBound.right - elWidth);
+            destLeft = Math.max(destLeft, parentBound.left);
+            destTop = viewportTop;
+            destTop = Math.min(destTop, parentBound.bottom - elHeight);
+            destTop = Math.max(destTop, naturalBound.top); // better to use natural top for upper bound
+            return { left: destLeft, top: destTop };
+        });
+    };
+    return StickyScroller;
+}());
+function assignRelativePositions(els, elGeoms, elDestinations) {
+    els.forEach(function (el, i) {
+        var naturalBound = elGeoms[i].naturalBound;
+        applyStyle(el, {
+            position: 'relative',
+            left: elDestinations[i].left - naturalBound.left,
+            top: elDestinations[i].top - naturalBound.top
+        });
+    });
+}
+function assignStickyPositions(els, elGeoms, viewportWidth) {
+    els.forEach(function (el, i) {
+        var stickyLeft = 0;
+        if (elGeoms[i].intendedTextAlign === 'center') {
+            stickyLeft = (viewportWidth - elGeoms[i].elWidth) / 2;
+            // needs to be forced to left?
+            if (elGeoms[i].computedTextAlign === 'center') {
+                el.setAttribute('data-sticky-center', '') // remember for next queryElGeoms
+                ;
+                el.parentNode.style.textAlign = 'left';
+            }
+        }
+        applyStyle(el, {
+            position: STICKY_PROP_VAL,
+            left: stickyLeft,
+            right: 0,
+            top: 0
+        });
+    });
+}
+function computeStickyPropVal() {
+    var el = htmlToElement('<div style="position:-webkit-sticky;position:sticky"></div>');
+    var val = el.style.position;
+    if (val.indexOf('sticky') !== -1) {
+        return val;
+    }
+    else {
+        return null;
+    }
+}
+
+var TimeAxis = /** @class */ (function (_super) {
+    __extends(TimeAxis, _super);
+    function TimeAxis(context, headerContainerEl, bodyContainerEl) {
+        var _this = _super.call(this, context) || this;
+        var layout = _this.layout = new HeaderBodyLayout(headerContainerEl, bodyContainerEl, 'auto');
+        var headerEnhancedScroller = layout.headerScroller.enhancedScroll;
+        var bodyEnhancedScroller = layout.bodyScroller.enhancedScroll;
+        // needs to go after layout, which has ScrollJoiner
+        _this.headStickyScroller = new StickyScroller(headerEnhancedScroller, _this.isRtl, false); // isVertical=false
+        _this.bodyStickyScroller = new StickyScroller(bodyEnhancedScroller, _this.isRtl, false); // isVertical=false
+        _this.header = new TimelineHeader(context, headerEnhancedScroller.canvas.contentEl);
+        _this.slats = new TimelineSlats(context, bodyEnhancedScroller.canvas.bgEl);
+        _this.nowIndicator = new TimelineNowIndicator(headerEnhancedScroller.canvas.el, bodyEnhancedScroller.canvas.el);
+        return _this;
+    }
+    TimeAxis.prototype.destroy = function () {
+        this.layout.destroy();
+        this.header.destroy();
+        this.slats.destroy();
+        this.nowIndicator.unrender();
+        this.headStickyScroller.destroy();
+        this.bodyStickyScroller.destroy();
+        _super.prototype.destroy.call(this);
+    };
+    TimeAxis.prototype.render = function (props) {
+        var tDateProfile = this.tDateProfile =
+            buildTimelineDateProfile(props.dateProfile, this.view); // TODO: cache
+        this.header.receiveProps({
+            dateProfile: props.dateProfile,
+            tDateProfile: tDateProfile
+        });
+        this.slats.receiveProps({
+            dateProfile: props.dateProfile,
+            tDateProfile: tDateProfile
+        });
+    };
+    // Now Indicator
+    // ------------------------------------------------------------------------------------------
+    TimeAxis.prototype.getNowIndicatorUnit = function (dateProfile) {
+        // yuck
+        var tDateProfile = this.tDateProfile =
+            buildTimelineDateProfile(dateProfile, this.view); // TODO: cache
+        if (tDateProfile.isTimeScale) {
+            return greatestDurationDenominator(tDateProfile.slotDuration).unit;
+        }
+    };
+    // will only execute if isTimeScale
+    TimeAxis.prototype.renderNowIndicator = function (date) {
+        if (rangeContainsMarker(this.tDateProfile.normalizedRange, date)) {
+            this.nowIndicator.render(this.dateToCoord(date), this.isRtl);
+        }
+    };
+    // will only execute if isTimeScale
+    TimeAxis.prototype.unrenderNowIndicator = function () {
+        this.nowIndicator.unrender();
+    };
+    // Sizing
+    // ------------------------------------------------------------------------------------------
+    TimeAxis.prototype.updateSize = function (isResize, totalHeight, isAuto) {
+        this.applySlotWidth(this.computeSlotWidth());
+        // adjusts gutters. do after slot widths set
+        this.layout.setHeight(totalHeight, isAuto);
+        // pretty much just queries coords. do last
+        this.slats.updateSize();
+    };
+    TimeAxis.prototype.updateStickyScrollers = function () {
+        this.headStickyScroller.updateSize();
+        this.bodyStickyScroller.updateSize();
+    };
+    TimeAxis.prototype.computeSlotWidth = function () {
+        var slotWidth = this.opt('slotWidth') || '';
+        if (slotWidth === '') {
+            slotWidth = this.computeDefaultSlotWidth(this.tDateProfile);
+        }
+        return slotWidth;
+    };
+    TimeAxis.prototype.computeDefaultSlotWidth = function (tDateProfile) {
+        var maxInnerWidth = 0; // TODO: harness core's `matchCellWidths` for this
+        this.header.innerEls.forEach(function (innerEl, i) {
+            maxInnerWidth = Math.max(maxInnerWidth, innerEl.getBoundingClientRect().width);
+        });
+        var headingCellWidth = Math.ceil(maxInnerWidth) + 1; // assume no padding, and one pixel border
+        // in TimelineView.defaults we ensured that labelInterval is an interval of slotDuration
+        // TODO: rename labelDuration?
+        var slotsPerLabel = wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
+        var slotWidth = Math.ceil(headingCellWidth / slotsPerLabel);
+        var minWidth = window.getComputedStyle(this.header.slatColEls[0]).minWidth;
+        if (minWidth) {
+            minWidth = parseInt(minWidth, 10);
+            if (minWidth) {
+                slotWidth = Math.max(slotWidth, minWidth);
+            }
+        }
+        return slotWidth;
+    };
+    TimeAxis.prototype.applySlotWidth = function (slotWidth) {
+        var _a = this, layout = _a.layout, tDateProfile = _a.tDateProfile;
+        var containerWidth = '';
+        var containerMinWidth = '';
+        var nonLastSlotWidth = '';
+        if (slotWidth !== '') {
+            slotWidth = Math.round(slotWidth);
+            containerWidth = slotWidth * tDateProfile.slotDates.length;
+            containerMinWidth = '';
+            nonLastSlotWidth = slotWidth;
+            var availableWidth = layout.bodyScroller.enhancedScroll.getClientWidth();
+            if (availableWidth > containerWidth) {
+                containerMinWidth = availableWidth;
+                containerWidth = '';
+                nonLastSlotWidth = Math.floor(availableWidth / tDateProfile.slotDates.length);
+            }
+        }
+        layout.headerScroller.enhancedScroll.canvas.setWidth(containerWidth);
+        layout.headerScroller.enhancedScroll.canvas.setMinWidth(containerMinWidth);
+        layout.bodyScroller.enhancedScroll.canvas.setWidth(containerWidth);
+        layout.bodyScroller.enhancedScroll.canvas.setMinWidth(containerMinWidth);
+        if (nonLastSlotWidth !== '') {
+            this.header.slatColEls.slice(0, -1).concat(this.slats.slatColEls.slice(0, -1)).forEach(function (el) {
+                el.style.width = nonLastSlotWidth + 'px';
+            });
+        }
+    };
+    // returned value is between 0 and the number of snaps
+    TimeAxis.prototype.computeDateSnapCoverage = function (date) {
+        var _a = this, dateEnv = _a.dateEnv, tDateProfile = _a.tDateProfile;
+        var snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
+        if (snapDiff < 0) {
+            return 0;
+        }
+        else if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
+            return tDateProfile.snapCnt;
+        }
+        else {
+            var snapDiffInt = Math.floor(snapDiff);
+            var snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
+            if (isInt(snapCoverage)) { // not an in-between value
+                snapCoverage += snapDiff - snapDiffInt; // add the remainder
+            }
+            else {
+                // a fractional value, meaning the date is not visible
+                // always round up in this case. works for start AND end dates in a range.
+                snapCoverage = Math.ceil(snapCoverage);
+            }
+            return snapCoverage;
+        }
+    };
+    // for LTR, results range from 0 to width of area
+    // for RTL, results range from negative width of area to 0
+    TimeAxis.prototype.dateToCoord = function (date) {
+        var tDateProfile = this.tDateProfile;
+        var snapCoverage = this.computeDateSnapCoverage(date);
+        var slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
+        var slotIndex = Math.floor(slotCoverage);
+        slotIndex = Math.min(slotIndex, tDateProfile.slotCnt - 1);
+        var partial = slotCoverage - slotIndex;
+        var _a = this.slats, innerCoordCache = _a.innerCoordCache, outerCoordCache = _a.outerCoordCache;
+        if (this.isRtl) {
+            return (outerCoordCache.rights[slotIndex] -
+                (innerCoordCache.getWidth(slotIndex) * partial)) - outerCoordCache.originClientRect.width;
+        }
+        else {
+            return (outerCoordCache.lefts[slotIndex] +
+                (innerCoordCache.getWidth(slotIndex) * partial));
+        }
+    };
+    TimeAxis.prototype.rangeToCoords = function (range) {
+        if (this.isRtl) {
+            return { right: this.dateToCoord(range.start), left: this.dateToCoord(range.end) };
+        }
+        else {
+            return { left: this.dateToCoord(range.start), right: this.dateToCoord(range.end) };
+        }
+    };
+    // Scrolling
+    // ------------------------------------------------------------------------------------------
+    TimeAxis.prototype.computeDateScroll = function (duration) {
+        var dateEnv = this.dateEnv;
+        var dateProfile = this.props.dateProfile;
+        var left = 0;
+        if (dateProfile) {
+            left = this.dateToCoord(dateEnv.add(startOfDay(dateProfile.activeRange.start), // startOfDay needed?
+            duration));
+            // hack to overcome the left borders of non-first slat
+            if (!this.isRtl && left) {
+                left += 1;
+            }
+        }
+        return { left: left };
+    };
+    TimeAxis.prototype.queryDateScroll = function () {
+        var enhancedScroll = this.layout.bodyScroller.enhancedScroll;
+        return {
+            left: enhancedScroll.getScrollLeft()
+        };
+    };
+    TimeAxis.prototype.applyDateScroll = function (scroll) {
+        // TODO: lame we have to update both. use the scrolljoiner instead maybe
+        this.layout.bodyScroller.enhancedScroll.setScrollLeft(scroll.left || 0);
+        this.layout.headerScroller.enhancedScroll.setScrollLeft(scroll.left || 0);
+    };
+    return TimeAxis;
+}(Component));
+
+var TimelineLaneEventRenderer = /** @class */ (function (_super) {
+    __extends(TimelineLaneEventRenderer, _super);
+    function TimelineLaneEventRenderer(context, masterContainerEl, timeAxis) {
+        var _this = _super.call(this, context) || this;
+        _this.masterContainerEl = masterContainerEl;
+        _this.timeAxis = timeAxis;
+        return _this;
+    }
+    TimelineLaneEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
+        var view = this.context.view;
+        var eventRange = seg.eventRange;
+        var eventDef = eventRange.def;
+        var eventUi = eventRange.ui;
+        var isDraggable = view.computeEventDraggable(eventDef, eventUi);
+        var isResizableFromStart = seg.isStart && view.computeEventStartResizable(eventDef, eventUi);
+        var isResizableFromEnd = seg.isEnd && view.computeEventEndResizable(eventDef, eventUi);
+        var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
+        classes.unshift('fc-timeline-event', 'fc-h-event');
+        var timeText = this.getTimeText(eventRange);
+        return '<a class="' + classes.join(' ') + '" style="' + cssToStr(this.getSkinCss(eventUi)) + '"' +
+            (eventDef.url ?
+                ' href="' + htmlEscape(eventDef.url) + '"' :
+                '') +
+            '>' +
+            (timeText ?
+                '<span class="fc-time-wrap">' +
+                    '<span class="fc-time">' +
+                    htmlEscape(timeText) +
+                    '</span>' +
+                    '</span>'
+                :
+                    '') +
+            '<span class="fc-title-wrap">' +
+            '<span class="fc-title fc-sticky">' +
+            (eventDef.title ? htmlEscape(eventDef.title) : '&nbsp;') +
+            '</span>' +
+            '</span>' +
+            (isResizableFromStart ?
+                '<div class="fc-resizer fc-start-resizer"></div>' :
+                '') +
+            (isResizableFromEnd ?
+                '<div class="fc-resizer fc-end-resizer"></div>' :
+                '') +
+            '</a>';
+    };
+    TimelineLaneEventRenderer.prototype.computeDisplayEventTime = function () {
+        return !this.timeAxis.tDateProfile.isTimeScale; // because times should be obvious via axis
+    };
+    TimelineLaneEventRenderer.prototype.computeDisplayEventEnd = function () {
+        return false;
+    };
+    // Computes a default event time formatting string if `timeFormat` is not explicitly defined
+    TimelineLaneEventRenderer.prototype.computeEventTimeFormat = function () {
+        return {
+            hour: 'numeric',
+            minute: '2-digit',
+            omitZeroMinute: true,
+            meridiem: 'narrow'
+        };
+    };
+    TimelineLaneEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+        if (!this.el && this.masterContainerEl) {
+            this.el = createElement('div', { className: 'fc-event-container' });
+            if (mirrorInfo) {
+                this.el.classList.add('fc-mirror-container');
+            }
+            this.masterContainerEl.appendChild(this.el);
+        }
+        if (this.el) {
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                this.el.appendChild(seg.el);
+            }
+        }
+    };
+    TimelineLaneEventRenderer.prototype.detachSegs = function (segs) {
+        for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+            var seg = segs_2[_i];
+            removeElement(seg.el);
+        }
+    };
+    // computes AND assigns (assigns the left/right at least). bad
+    TimelineLaneEventRenderer.prototype.computeSegSizes = function (segs) {
+        var timeAxis = this.timeAxis;
+        for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) {
+            var seg = segs_3[_i];
+            var coords = timeAxis.rangeToCoords(seg); // works because Seg has start/end
+            applyStyle(seg.el, {
+                left: (seg.left = coords.left),
+                right: -(seg.right = coords.right)
+            });
+        }
+    };
+    TimelineLaneEventRenderer.prototype.assignSegSizes = function (segs) {
+        if (!this.el) {
+            return;
+        }
+        // compute seg verticals
+        for (var _i = 0, segs_4 = segs; _i < segs_4.length; _i++) {
+            var seg = segs_4[_i];
+            seg.height = computeHeightAndMargins(seg.el);
+        }
+        this.buildSegLevels(segs); // populates above/below props for computeOffsetForSegs
+        var totalHeight = computeOffsetForSegs(segs); // also assigns seg.top
+        applyStyleProp(this.el, 'height', totalHeight);
+        // assign seg verticals
+        for (var _a = 0, segs_5 = segs; _a < segs_5.length; _a++) {
+            var seg = segs_5[_a];
+            applyStyleProp(seg.el, 'top', seg.top);
+        }
+    };
+    TimelineLaneEventRenderer.prototype.buildSegLevels = function (segs) {
+        var segLevels = [];
+        segs = this.sortEventSegs(segs);
+        for (var _i = 0, segs_6 = segs; _i < segs_6.length; _i++) {
+            var unplacedSeg = segs_6[_i];
+            unplacedSeg.above = [];
+            // determine the first level with no collisions
+            var level = 0; // level index
+            while (level < segLevels.length) {
+                var isLevelCollision = false;
+                // determine collisions
+                for (var _a = 0, _b = segLevels[level]; _a < _b.length; _a++) {
+                    var placedSeg = _b[_a];
+                    if (timeRowSegsCollide(unplacedSeg, placedSeg)) {
+                        unplacedSeg.above.push(placedSeg);
+                        isLevelCollision = true;
+                    }
+                }
+                if (isLevelCollision) {
+                    level += 1;
+                }
+                else {
+                    break;
+                }
+            }
+            // insert into the first non-colliding level. create if necessary
+            (segLevels[level] || (segLevels[level] = []))
+                .push(unplacedSeg);
+            // record possible colliding segments below (TODO: automated test for this)
+            level += 1;
+            while (level < segLevels.length) {
+                for (var _c = 0, _d = segLevels[level]; _c < _d.length; _c++) {
+                    var belowSeg = _d[_c];
+                    if (timeRowSegsCollide(unplacedSeg, belowSeg)) {
+                        belowSeg.above.push(unplacedSeg);
+                    }
+                }
+                level += 1;
+            }
+        }
+        return segLevels;
+    };
+    return TimelineLaneEventRenderer;
+}(FgEventRenderer));
+function computeOffsetForSegs(segs) {
+    var max = 0;
+    for (var _i = 0, segs_7 = segs; _i < segs_7.length; _i++) {
+        var seg = segs_7[_i];
+        max = Math.max(max, computeOffsetForSeg(seg));
+    }
+    return max;
+}
+function computeOffsetForSeg(seg) {
+    if ((seg.top == null)) {
+        seg.top = computeOffsetForSegs(seg.above);
+    }
+    return seg.top + seg.height;
+}
+function timeRowSegsCollide(seg0, seg1) {
+    return (seg0.left < seg1.right) && (seg0.right > seg1.left);
+}
+
+var TimelineLaneFillRenderer = /** @class */ (function (_super) {
+    __extends(TimelineLaneFillRenderer, _super);
+    function TimelineLaneFillRenderer(context, masterContainerEl, timeAxis) {
+        var _this = _super.call(this, context) || this;
+        _this.masterContainerEl = masterContainerEl;
+        _this.timeAxis = timeAxis;
+        return _this;
+    }
+    TimelineLaneFillRenderer.prototype.attachSegs = function (type, segs) {
+        if (segs.length) {
+            var className = void 0;
+            if (type === 'businessHours') {
+                className = 'bgevent';
+            }
+            else {
+                className = type.toLowerCase();
+            }
+            // making a new container each time is OKAY
+            // all types of segs (background or business hours or whatever) are rendered in one pass
+            var containerEl = createElement('div', { className: 'fc-' + className + '-container' });
+            this.masterContainerEl.appendChild(containerEl);
+            for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                var seg = segs_1[_i];
+                containerEl.appendChild(seg.el);
+            }
+            return [containerEl]; // return value
+        }
+    };
+    TimelineLaneFillRenderer.prototype.computeSegSizes = function (segs) {
+        var timeAxis = this.timeAxis;
+        for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+            var seg = segs_2[_i];
+            var coords = timeAxis.rangeToCoords(seg);
+            seg.left = coords.left;
+            seg.right = coords.right;
+        }
+    };
+    TimelineLaneFillRenderer.prototype.assignSegSizes = function (segs) {
+        for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) {
+            var seg = segs_3[_i];
+            applyStyle(seg.el, {
+                left: seg.left,
+                right: -seg.right
+            });
+        }
+    };
+    return TimelineLaneFillRenderer;
+}(FillRenderer));
+
+var TimelineLane = /** @class */ (function (_super) {
+    __extends(TimelineLane, _super);
+    function TimelineLane(context, fgContainerEl, bgContainerEl, timeAxis) {
+        var _this = _super.call(this, context, bgContainerEl) // should el be bgContainerEl???
+         || this;
+        _this.slicer = new TimelineLaneSlicer();
+        _this.renderEventDrag = memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag);
+        _this.renderEventResize = memoizeRendering(_this._renderEventResize, _this._unrenderEventResize);
+        var fillRenderer = _this.fillRenderer = new TimelineLaneFillRenderer(context, bgContainerEl, timeAxis);
+        var eventRenderer = _this.eventRenderer = new TimelineLaneEventRenderer(context, fgContainerEl, timeAxis);
+        _this.mirrorRenderer = new TimelineLaneEventRenderer(context, fgContainerEl, timeAxis);
+        _this.renderBusinessHours = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'));
+        _this.renderDateSelection = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'));
+        _this.renderBgEvents = memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'));
+        _this.renderFgEvents = memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer));
+        _this.renderEventSelection = memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+        _this.timeAxis = timeAxis;
+        return _this;
+    }
+    TimelineLane.prototype.render = function (props) {
+        var slicedProps = this.slicer.sliceProps(props, props.dateProfile, this.timeAxis.tDateProfile.isTimeScale ? null : props.nextDayThreshold, this, this.timeAxis);
+        this.renderBusinessHours(slicedProps.businessHourSegs);
+        this.renderDateSelection(slicedProps.dateSelectionSegs);
+        this.renderBgEvents(slicedProps.bgEventSegs);
+        this.renderFgEvents(slicedProps.fgEventSegs);
+        this.renderEventSelection(slicedProps.eventSelection);
+        this.renderEventDrag(slicedProps.eventDrag);
+        this.renderEventResize(slicedProps.eventResize);
+    };
+    TimelineLane.prototype.destroy = function () {
+        _super.prototype.destroy.call(this);
+        this.renderBusinessHours.unrender();
+        this.renderDateSelection.unrender();
+        this.renderBgEvents.unrender();
+        this.renderFgEvents.unrender();
+        this.renderEventSelection.unrender();
+        this.renderEventDrag.unrender();
+        this.renderEventResize.unrender();
+    };
+    TimelineLane.prototype._renderEventDrag = function (state) {
+        if (state) {
+            this.eventRenderer.hideByHash(state.affectedInstances);
+            this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    TimelineLane.prototype._unrenderEventDrag = function (state) {
+        if (state) {
+            this.eventRenderer.showByHash(state.affectedInstances);
+            this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    TimelineLane.prototype._renderEventResize = function (state) {
+        if (state) {
+            // HACK. eventRenderer and fillRenderer both use these segs. would compete over seg.el
+            var segsForHighlight = state.segs.map(function (seg) {
+                return __assign({}, seg);
+            });
+            this.eventRenderer.hideByHash(state.affectedInstances);
+            this.fillRenderer.renderSegs('highlight', segsForHighlight);
+            this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    TimelineLane.prototype._unrenderEventResize = function (state) {
+        if (state) {
+            this.eventRenderer.showByHash(state.affectedInstances);
+            this.fillRenderer.unrender('highlight');
+            this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+        }
+    };
+    TimelineLane.prototype.updateSize = function (isResize) {
+        var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
+        fillRenderer.computeSizes(isResize);
+        eventRenderer.computeSizes(isResize);
+        mirrorRenderer.computeSizes(isResize);
+        fillRenderer.assignSizes(isResize);
+        eventRenderer.assignSizes(isResize);
+        mirrorRenderer.assignSizes(isResize);
+    };
+    return TimelineLane;
+}(DateComponent));
+var TimelineLaneSlicer = /** @class */ (function (_super) {
+    __extends(TimelineLaneSlicer, _super);
+    function TimelineLaneSlicer() {
+        return _super !== null && _super.apply(this, arguments) || this;
+    }
+    TimelineLaneSlicer.prototype.sliceRange = function (origRange, timeAxis) {
+        var tDateProfile = timeAxis.tDateProfile;
+        var dateProfile = timeAxis.props.dateProfile;
+        var normalRange = normalizeRange(origRange, tDateProfile, timeAxis.dateEnv);
+        var segs = [];
+        // protect against when the span is entirely in an invalid date region
+        if (timeAxis.computeDateSnapCoverage(normalRange.start) < timeAxis.computeDateSnapCoverage(normalRange.end)) {
+            // intersect the footprint's range with the grid's range
+            var slicedRange = intersectRanges(normalRange, tDateProfile.normalizedRange);
+            if (slicedRange) {
+                segs.push({
+                    start: slicedRange.start,
+                    end: slicedRange.end,
+                    isStart: slicedRange.start.valueOf() === normalRange.start.valueOf() && isValidDate(slicedRange.start, tDateProfile, dateProfile, timeAxis.view),
+                    isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf() && isValidDate(addMs(slicedRange.end, -1), tDateProfile, dateProfile, timeAxis.view)
+                });
+            }
+        }
+        return segs;
+    };
+    return TimelineLaneSlicer;
+}(Slicer));
+
+var TimelineView = /** @class */ (function (_super) {
+    __extends(TimelineView, _super);
+    function TimelineView(context, viewSpec, dateProfileGenerator, parentEl) {
+        var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+        _this.el.classList.add('fc-timeline');
+        if (_this.opt('eventOverlap') === false) {
+            _this.el.classList.add('fc-no-overlap');
+        }
+        _this.el.innerHTML = _this.renderSkeletonHtml();
+        _this.timeAxis = new TimeAxis(_this.context, _this.el.querySelector('thead .fc-time-area'), _this.el.querySelector('tbody .fc-time-area'));
+        _this.lane = new TimelineLane(_this.context, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl, _this.timeAxis);
+        context.calendar.registerInteractiveComponent(_this, {
+            el: _this.timeAxis.slats.el
+        });
+        return _this;
+    }
+    TimelineView.prototype.destroy = function () {
+        this.timeAxis.destroy();
+        this.lane.destroy();
+        _super.prototype.destroy.call(this);
+        this.calendar.unregisterInteractiveComponent(this);
+    };
+    TimelineView.prototype.renderSkeletonHtml = function () {
+        var theme = this.theme;
+        return "<table class=\"" + theme.getClass('tableGrid') + "\"> <thead class=\"fc-head\"> <tr> <td class=\"fc-time-area " + theme.getClass('widgetHeader') + "\"></td> </tr> </thead> <tbody class=\"fc-body\"> <tr> <td class=\"fc-time-area " + theme.getClass('widgetContent') + "\"></td> </tr> </tbody> </table>";
+    };
+    TimelineView.prototype.render = function (props) {
+        _super.prototype.render.call(this, props); // flags for updateSize, addScroll
+        this.timeAxis.receiveProps({
+            dateProfile: props.dateProfile
+        });
+        this.lane.receiveProps(__assign({}, props, { nextDayThreshold: this.nextDayThreshold }));
+    };
+    TimelineView.prototype.updateSize = function (isResize, totalHeight, isAuto) {
+        this.timeAxis.updateSize(isResize, totalHeight, isAuto);
+        this.lane.updateSize(isResize);
+    };
+    // Now Indicator
+    // ------------------------------------------------------------------------------------------
+    TimelineView.prototype.getNowIndicatorUnit = function (dateProfile) {
+        return this.timeAxis.getNowIndicatorUnit(dateProfile);
+    };
+    TimelineView.prototype.renderNowIndicator = function (date) {
+        this.timeAxis.renderNowIndicator(date);
+    };
+    TimelineView.prototype.unrenderNowIndicator = function () {
+        this.timeAxis.unrenderNowIndicator();
+    };
+    // Scroll System
+    // ------------------------------------------------------------------------------------------
+    TimelineView.prototype.computeDateScroll = function (duration) {
+        return this.timeAxis.computeDateScroll(duration);
+    };
+    TimelineView.prototype.applyScroll = function (scroll, isResize) {
+        _super.prototype.applyScroll.call(this, scroll, isResize); // will call applyDateScroll
+        // avoid updating stickyscroll too often
+        // TODO: repeat code as ResourceTimelineView::updateSize
+        var calendar = this.calendar;
+        if (isResize || calendar.isViewUpdated || calendar.isDatesUpdated || calendar.isEventsUpdated) {
+            this.timeAxis.updateStickyScrollers();
+        }
+    };
+    TimelineView.prototype.applyDateScroll = function (scroll) {
+        this.timeAxis.applyDateScroll(scroll);
+    };
+    TimelineView.prototype.queryScroll = function () {
+        var enhancedScroll = this.timeAxis.layout.bodyScroller.enhancedScroll;
+        return {
+            top: enhancedScroll.getScrollTop(),
+            left: enhancedScroll.getScrollLeft()
+        };
+    };
+    // Hit System
+    // ------------------------------------------------------------------------------------------
+    TimelineView.prototype.buildPositionCaches = function () {
+        this.timeAxis.slats.updateSize();
+    };
+    TimelineView.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+        var slatHit = this.timeAxis.slats.positionToHit(positionLeft);
+        if (slatHit) {
+            return {
+                component: this,
+                dateSpan: slatHit.dateSpan,
+                rect: {
+                    left: slatHit.left,
+                    right: slatHit.right,
+                    top: 0,
+                    bottom: elHeight
+                },
+                dayEl: slatHit.dayEl,
+                layer: 0
+            };
+        }
+    };
+    return TimelineView;
+}(View));
+
+var main = createPlugin({
+    defaultView: 'timelineDay',
+    views: {
+        timeline: {
+            class: TimelineView,
+            eventResizableFromStart: true // how is this consumed for TimelineView tho?
+        },
+        timelineDay: {
+            type: 'timeline',
+            duration: { days: 1 }
+        },
+        timelineWeek: {
+            type: 'timeline',
+            duration: { weeks: 1 }
+        },
+        timelineMonth: {
+            type: 'timeline',
+            duration: { months: 1 }
+        },
+        timelineYear: {
+            type: 'timeline',
+            duration: { years: 1 }
+        }
+    }
+});
+
+export default main;
+export { HeaderBodyLayout, ScrollJoiner, StickyScroller, TimeAxis, TimelineLane, TimelineView };
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/main.js b/AKPlan/static/AKPlan/fullcalendar/timeline/main.js
new file mode 100644
index 0000000000000000000000000000000000000000..f349419ba437bad07ed35a014a33230890bcfffb
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/main.js
@@ -0,0 +1,1996 @@
+/*!
+FullCalendar Timeline Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@fullcalendar/core')) :
+    typeof define === 'function' && define.amd ? define(['exports', '@fullcalendar/core'], factory) :
+    (global = global || self, factory(global.FullCalendarTimeline = {}, global.FullCalendar));
+}(this, function (exports, core) { 'use strict';
+
+    /*! *****************************************************************************
+    Copyright (c) Microsoft Corporation. All rights reserved.
+    Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+    this file except in compliance with the License. You may obtain a copy of the
+    License at http://www.apache.org/licenses/LICENSE-2.0
+
+    THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
+    WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
+    MERCHANTABLITY OR NON-INFRINGEMENT.
+
+    See the Apache Version 2.0 License for specific language governing permissions
+    and limitations under the License.
+    ***************************************************************************** */
+    /* global Reflect, Promise */
+
+    var extendStatics = function(d, b) {
+        extendStatics = Object.setPrototypeOf ||
+            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+        return extendStatics(d, b);
+    };
+
+    function __extends(d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    }
+
+    var __assign = function() {
+        __assign = Object.assign || function __assign(t) {
+            for (var s, i = 1, n = arguments.length; i < n; i++) {
+                s = arguments[i];
+                for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
+            }
+            return t;
+        };
+        return __assign.apply(this, arguments);
+    };
+
+    /*
+    A rectangular area of content that lives within a Scroller.
+    Can have "gutters", areas of dead spacing around the perimeter.
+    Also very useful for forcing a width, which a Scroller cannot do alone.
+    Has a content area that lives above a background area.
+    */
+    var ScrollerCanvas = /** @class */ (function () {
+        function ScrollerCanvas() {
+            this.gutters = {};
+            this.el = core.htmlToElement("<div class=\"fc-scroller-canvas\"> <div class=\"fc-content\"></div> <div class=\"fc-bg\"></div> </div>");
+            this.contentEl = this.el.querySelector('.fc-content');
+            this.bgEl = this.el.querySelector('.fc-bg');
+        }
+        /*
+        If falsy, resets all the gutters to 0
+        */
+        ScrollerCanvas.prototype.setGutters = function (gutters) {
+            if (!gutters) {
+                this.gutters = {};
+            }
+            else {
+                __assign(this.gutters, gutters);
+            }
+            this.updateSize();
+        };
+        ScrollerCanvas.prototype.setWidth = function (width) {
+            this.width = width;
+            this.updateSize();
+        };
+        ScrollerCanvas.prototype.setMinWidth = function (minWidth) {
+            this.minWidth = minWidth;
+            this.updateSize();
+        };
+        ScrollerCanvas.prototype.clearWidth = function () {
+            this.width = null;
+            this.minWidth = null;
+            this.updateSize();
+        };
+        ScrollerCanvas.prototype.updateSize = function () {
+            var _a = this, gutters = _a.gutters, el = _a.el;
+            // is border-box (width includes padding)
+            core.forceClassName(el, 'fc-gutter-left', gutters.left);
+            core.forceClassName(el, 'fc-gutter-right', gutters.right);
+            core.forceClassName(el, 'fc-gutter-top', gutters.top);
+            core.forceClassName(el, 'fc-gutter-bottom', gutters.bottom);
+            core.applyStyle(el, {
+                paddingLeft: gutters.left || '',
+                paddingRight: gutters.right || '',
+                paddingTop: gutters.top || '',
+                paddingBottom: gutters.bottom || '',
+                width: (this.width != null) ?
+                    this.width + (gutters.left || 0) + (gutters.right || 0) :
+                    '',
+                minWidth: (this.minWidth != null) ?
+                    this.minWidth + (gutters.left || 0) + (gutters.right || 0) :
+                    ''
+            });
+            core.applyStyle(this.bgEl, {
+                left: gutters.left || '',
+                right: gutters.right || '',
+                top: gutters.top || '',
+                bottom: gutters.bottom || ''
+            });
+        };
+        return ScrollerCanvas;
+    }());
+
+    var EnhancedScroller = /** @class */ (function (_super) {
+        __extends(EnhancedScroller, _super);
+        function EnhancedScroller(overflowX, overflowY) {
+            var _this = _super.call(this, overflowX, overflowY) || this;
+            // Scroll Events
+            // ----------------------------------------------------------------------------------------------
+            _this.reportScroll = function () {
+                if (!_this.isScrolling) {
+                    _this.reportScrollStart();
+                }
+                _this.trigger('scroll');
+                _this.isMoving = true;
+                _this.requestMovingEnd();
+            };
+            _this.reportScrollStart = function () {
+                if (!_this.isScrolling) {
+                    _this.isScrolling = true;
+                    _this.trigger('scrollStart', _this.isTouching); // created in constructor
+                }
+            };
+            // Touch Events
+            // ----------------------------------------------------------------------------------------------
+            // will fire *before* the scroll event is fired
+            _this.reportTouchStart = function () {
+                _this.isTouching = true;
+            };
+            _this.reportTouchEnd = function () {
+                if (_this.isTouching) {
+                    _this.isTouching = false;
+                    // if touch scrolling was re-enabled during a recent touch scroll
+                    // then unbind the handlers that are preventing it from happening.
+                    if (_this.isTouchScrollEnabled) {
+                        _this.unbindPreventTouchScroll(); // won't do anything if not bound
+                    }
+                    // if the user ended their touch, and the scroll area wasn't moving,
+                    // we consider this to be the end of the scroll.
+                    if (!_this.isMoving) {
+                        _this.reportScrollEnd(); // won't fire if already ended
+                    }
+                }
+            };
+            _this.isScrolling = false;
+            _this.isTouching = false;
+            _this.isMoving = false;
+            _this.isTouchScrollEnabled = true;
+            _this.requestMovingEnd = core.debounce(_this.reportMovingEnd, 500);
+            _this.canvas = new ScrollerCanvas();
+            _this.el.appendChild(_this.canvas.el);
+            _this.applyOverflow();
+            _this.bindHandlers();
+            return _this;
+        }
+        EnhancedScroller.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.unbindHandlers();
+        };
+        // Touch scroll prevention
+        // ----------------------------------------------------------------------------------------------
+        EnhancedScroller.prototype.disableTouchScroll = function () {
+            this.isTouchScrollEnabled = false;
+            this.bindPreventTouchScroll(); // will be unbound in enableTouchScroll or reportTouchEnd
+        };
+        EnhancedScroller.prototype.enableTouchScroll = function () {
+            this.isTouchScrollEnabled = true;
+            // only immediately unbind if a touch event is NOT in progress.
+            // otherwise, it will be handled by reportTouchEnd.
+            if (!this.isTouching) {
+                this.unbindPreventTouchScroll();
+            }
+        };
+        EnhancedScroller.prototype.bindPreventTouchScroll = function () {
+            if (!this.preventTouchScrollHandler) {
+                this.el.addEventListener('touchmove', (this.preventTouchScrollHandler = core.preventDefault));
+            }
+        };
+        EnhancedScroller.prototype.unbindPreventTouchScroll = function () {
+            if (this.preventTouchScrollHandler) {
+                this.el.removeEventListener('touchmove', this.preventTouchScrollHandler);
+                this.preventTouchScrollHandler = null;
+            }
+        };
+        // Handlers
+        // ----------------------------------------------------------------------------------------------
+        EnhancedScroller.prototype.bindHandlers = function () {
+            this.el.addEventListener('scroll', this.reportScroll);
+            this.el.addEventListener('touchstart', this.reportTouchStart, { passive: true });
+            this.el.addEventListener('touchend', this.reportTouchEnd);
+        };
+        EnhancedScroller.prototype.unbindHandlers = function () {
+            this.el.removeEventListener('scroll', this.reportScroll);
+            this.el.removeEventListener('touchstart', this.reportTouchStart, { passive: true });
+            this.el.removeEventListener('touchend', this.reportTouchEnd);
+        };
+        EnhancedScroller.prototype.reportMovingEnd = function () {
+            this.isMoving = false;
+            // only end the scroll if not currently touching.
+            // if touching, the scrolling will end later, on touchend.
+            if (!this.isTouching) {
+                this.reportScrollEnd();
+            }
+        };
+        EnhancedScroller.prototype.reportScrollEnd = function () {
+            if (this.isScrolling) {
+                this.trigger('scrollEnd');
+                this.isScrolling = false;
+            }
+        };
+        // Horizontal Scroll Normalization
+        // ----------------------------------------------------------------------------------------------
+        // http://stackoverflow.com/questions/24276619/better-way-to-get-the-viewport-of-a-scrollable-div-in-rtl-mode/24394376#24394376
+        // TODO: move all this to util functions
+        /*
+        If RTL, and scrolled to the left, returns NEGATIVE value (like Firefox)
+        */
+        EnhancedScroller.prototype.getScrollLeft = function () {
+            var el = this.el;
+            var direction = window.getComputedStyle(el).direction;
+            var val = el.scrollLeft;
+            if (direction === 'rtl') {
+                switch (getRtlScrollSystem()) {
+                    case 'positive':
+                        val = (val + el.clientWidth) - el.scrollWidth;
+                        break;
+                    case 'reverse':
+                        val = -val;
+                        break;
+                }
+            }
+            return val;
+        };
+        /*
+        Accepts a NEGATIVE value for when scrolled in RTL
+        */
+        EnhancedScroller.prototype.setScrollLeft = function (val) {
+            var el = this.el;
+            var direction = window.getComputedStyle(el).direction;
+            if (direction === 'rtl') {
+                switch (getRtlScrollSystem()) {
+                    case 'positive':
+                        val = (val - el.clientWidth) + el.scrollWidth;
+                        break;
+                    case 'reverse':
+                        val = -val;
+                        break;
+                }
+            }
+            el.scrollLeft = val;
+        };
+        /*
+        Always returns the number of pixels scrolled from the leftmost position (even if RTL).
+        Always positive.
+        */
+        EnhancedScroller.prototype.getScrollFromLeft = function () {
+            var el = this.el;
+            var direction = window.getComputedStyle(el).direction;
+            var val = el.scrollLeft;
+            if (direction === 'rtl') {
+                switch (getRtlScrollSystem()) {
+                    case 'negative':
+                        val = (val - el.clientWidth) + el.scrollWidth;
+                        break;
+                    case 'reverse':
+                        val = (-val - el.clientWidth) + el.scrollWidth;
+                        break;
+                }
+            }
+            return val;
+        };
+        return EnhancedScroller;
+    }(core.ScrollComponent));
+    core.EmitterMixin.mixInto(EnhancedScroller);
+    // Horizontal Scroll System Detection
+    // ----------------------------------------------------------------------------------------------
+    var _rtlScrollSystem;
+    function getRtlScrollSystem() {
+        return _rtlScrollSystem || (_rtlScrollSystem = detectRtlScrollSystem());
+    }
+    function detectRtlScrollSystem() {
+        var el = core.htmlToElement("<div style=\" position: absolute; top: -1000px; width: 1px; height: 1px; overflow: scroll; direction: rtl; font-size: 100px; \">A</div>");
+        document.body.appendChild(el);
+        var system;
+        if (el.scrollLeft > 0) {
+            system = 'positive';
+        }
+        else {
+            el.scrollLeft = 1;
+            if (el.scrollLeft > 0) {
+                system = 'reverse';
+            }
+            else {
+                system = 'negative';
+            }
+        }
+        core.removeElement(el);
+        return system;
+    }
+
+    /*
+    A Scroller, but with a wrapping div that allows "clipping" away of native scrollbars,
+    giving the appearance that there are no scrollbars.
+    */
+    var ClippedScroller = /** @class */ (function () {
+        /*
+        Received overflows can be set to 'clipped', meaning scrollbars shouldn't be visible
+        to the user, but the area should still scroll.
+        */
+        function ClippedScroller(overflowX, overflowY, parentEl) {
+            this.isHScrollbarsClipped = false;
+            this.isVScrollbarsClipped = false;
+            if (overflowX === 'clipped-scroll') {
+                overflowX = 'scroll';
+                this.isHScrollbarsClipped = true;
+            }
+            if (overflowY === 'clipped-scroll') {
+                overflowY = 'scroll';
+                this.isVScrollbarsClipped = true;
+            }
+            this.enhancedScroll = new EnhancedScroller(overflowX, overflowY);
+            parentEl.appendChild(this.el = core.createElement('div', {
+                className: 'fc-scroller-clip'
+            }));
+            this.el.appendChild(this.enhancedScroll.el);
+        }
+        ClippedScroller.prototype.destroy = function () {
+            core.removeElement(this.el);
+        };
+        ClippedScroller.prototype.updateSize = function () {
+            var enhancedScroll = this.enhancedScroll;
+            var scrollEl = enhancedScroll.el;
+            var edges = core.computeEdges(scrollEl);
+            var cssProps = { marginLeft: 0, marginRight: 0, marginTop: 0, marginBottom: 0 };
+            // give the inner scrolling div negative margins so that its scrollbars
+            // are nudged outside of the bounding box of the wrapper, which is overflow:hidden
+            if (this.isVScrollbarsClipped) {
+                cssProps.marginLeft = -edges.scrollbarLeft;
+                cssProps.marginRight = -edges.scrollbarRight;
+            }
+            if (this.isHScrollbarsClipped) {
+                cssProps.marginBottom = -edges.scrollbarBottom;
+            }
+            core.applyStyle(scrollEl, cssProps);
+            // if we are attempting to hide the scrollbars offscreen, OSX/iOS will still
+            // display the floating scrollbars. attach a className to force-hide them.
+            if ((this.isHScrollbarsClipped || (enhancedScroll.overflowX === 'hidden')) && // should never show?
+                (this.isVScrollbarsClipped || (enhancedScroll.overflowY === 'hidden')) && // should never show?
+                !( // doesn't have any scrollbar mass
+                edges.scrollbarLeft ||
+                    edges.scrollbarRight ||
+                    edges.scrollbarBottom)) {
+                scrollEl.classList.add('fc-no-scrollbars');
+            }
+            else {
+                scrollEl.classList.remove('fc-no-scrollbars');
+            }
+        };
+        ClippedScroller.prototype.setHeight = function (height) {
+            this.enhancedScroll.setHeight(height);
+        };
+        /*
+        Accounts for 'clipped' scrollbars
+        */
+        ClippedScroller.prototype.getScrollbarWidths = function () {
+            var widths = this.enhancedScroll.getScrollbarWidths();
+            if (this.isVScrollbarsClipped) {
+                widths.left = 0;
+                widths.right = 0;
+            }
+            if (this.isHScrollbarsClipped) {
+                widths.bottom = 0;
+            }
+            return widths;
+        };
+        return ClippedScroller;
+    }());
+
+    var ScrollJoiner = /** @class */ (function () {
+        function ScrollJoiner(axis, scrollers) {
+            this.axis = axis;
+            this.scrollers = scrollers;
+            for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
+                var scroller = _a[_i];
+                this.initScroller(scroller);
+            }
+        }
+        ScrollJoiner.prototype.initScroller = function (scroller) {
+            var _this = this;
+            var enhancedScroll = scroller.enhancedScroll;
+            // when the user scrolls via mousewheel, we know for sure the target
+            // scroller should be the master. capture the various x-browser events that fire.
+            var onScroll = function () {
+                _this.assignMasterScroller(scroller);
+            };
+            'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ').forEach(function (evName) {
+                enhancedScroll.el.addEventListener(evName, onScroll);
+            });
+            enhancedScroll
+                .on('scrollStart', function () {
+                if (!_this.masterScroller) {
+                    _this.assignMasterScroller(scroller);
+                }
+            })
+                .on('scroll', function () {
+                if (scroller === _this.masterScroller) {
+                    for (var _i = 0, _a = _this.scrollers; _i < _a.length; _i++) {
+                        var otherScroller = _a[_i];
+                        if (otherScroller !== scroller) {
+                            switch (_this.axis) {
+                                case 'horizontal':
+                                    otherScroller.enhancedScroll.el.scrollLeft = enhancedScroll.el.scrollLeft;
+                                    break;
+                                case 'vertical':
+                                    otherScroller.enhancedScroll.setScrollTop(enhancedScroll.getScrollTop());
+                                    break;
+                            }
+                        }
+                    }
+                }
+            })
+                .on('scrollEnd', function () {
+                if (scroller === _this.masterScroller) {
+                    _this.unassignMasterScroller();
+                }
+            });
+        };
+        ScrollJoiner.prototype.assignMasterScroller = function (scroller) {
+            this.unassignMasterScroller();
+            this.masterScroller = scroller;
+            for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
+                var otherScroller = _a[_i];
+                if (otherScroller !== scroller) {
+                    otherScroller.enhancedScroll.disableTouchScroll();
+                }
+            }
+        };
+        ScrollJoiner.prototype.unassignMasterScroller = function () {
+            if (this.masterScroller) {
+                for (var _i = 0, _a = this.scrollers; _i < _a.length; _i++) {
+                    var otherScroller = _a[_i];
+                    otherScroller.enhancedScroll.enableTouchScroll();
+                }
+                this.masterScroller = null;
+            }
+        };
+        ScrollJoiner.prototype.update = function () {
+            var allWidths = this.scrollers.map(function (scroller) { return scroller.getScrollbarWidths(); });
+            var maxLeft = 0;
+            var maxRight = 0;
+            var maxTop = 0;
+            var maxBottom = 0;
+            var widths;
+            var i;
+            for (var _i = 0, allWidths_1 = allWidths; _i < allWidths_1.length; _i++) {
+                widths = allWidths_1[_i];
+                maxLeft = Math.max(maxLeft, widths.left);
+                maxRight = Math.max(maxRight, widths.right);
+                maxTop = Math.max(maxTop, widths.top);
+                maxBottom = Math.max(maxBottom, widths.bottom);
+            }
+            for (i = 0; i < this.scrollers.length; i++) {
+                var scroller = this.scrollers[i];
+                widths = allWidths[i];
+                scroller.enhancedScroll.canvas.setGutters(this.axis === 'horizontal' ?
+                    {
+                        left: maxLeft - widths.left,
+                        right: maxRight - widths.right
+                    } :
+                    {
+                        top: maxTop - widths.top,
+                        bottom: maxBottom - widths.bottom
+                    });
+            }
+        };
+        return ScrollJoiner;
+    }());
+
+    var HeaderBodyLayout = /** @class */ (function () {
+        /*
+        verticalScroll = 'auto' | 'clipped-scroll'
+        */
+        function HeaderBodyLayout(headerContainerEl, bodyContainerEl, verticalScroll) {
+            this.headerScroller = new ClippedScroller('clipped-scroll', 'hidden', headerContainerEl);
+            this.bodyScroller = new ClippedScroller('auto', verticalScroll, bodyContainerEl);
+            this.scrollJoiner = new ScrollJoiner('horizontal', [
+                this.headerScroller,
+                this.bodyScroller
+            ]);
+        }
+        HeaderBodyLayout.prototype.destroy = function () {
+            this.headerScroller.destroy();
+            this.bodyScroller.destroy();
+        };
+        HeaderBodyLayout.prototype.setHeight = function (totalHeight, isAuto) {
+            var bodyHeight;
+            if (isAuto) {
+                bodyHeight = 'auto';
+            }
+            else {
+                bodyHeight = totalHeight - this.queryHeadHeight();
+            }
+            this.bodyScroller.setHeight(bodyHeight);
+            this.headerScroller.updateSize(); // adjusts gutters and classNames
+            this.bodyScroller.updateSize(); // adjusts gutters and classNames
+            this.scrollJoiner.update();
+        };
+        HeaderBodyLayout.prototype.queryHeadHeight = function () {
+            return this.headerScroller.enhancedScroll.canvas.contentEl.getBoundingClientRect().height;
+        };
+        return HeaderBodyLayout;
+    }());
+
+    var TimelineHeader = /** @class */ (function (_super) {
+        __extends(TimelineHeader, _super);
+        function TimelineHeader(context, parentEl) {
+            var _this = _super.call(this, context) || this;
+            parentEl.appendChild(_this.tableEl = core.createElement('table', {
+                className: _this.theme.getClass('tableGrid')
+            }));
+            return _this;
+        }
+        TimelineHeader.prototype.destroy = function () {
+            core.removeElement(this.tableEl);
+            _super.prototype.destroy.call(this);
+        };
+        TimelineHeader.prototype.render = function (props) {
+            this.renderDates(props.tDateProfile);
+        };
+        TimelineHeader.prototype.renderDates = function (tDateProfile) {
+            var _a = this, dateEnv = _a.dateEnv, theme = _a.theme;
+            var cellRows = tDateProfile.cellRows;
+            var lastRow = cellRows[cellRows.length - 1];
+            var isChrono = core.asRoughMs(tDateProfile.labelInterval) > core.asRoughMs(tDateProfile.slotDuration);
+            var oneDay = core.isSingleDay(tDateProfile.slotDuration);
+            var html = '<colgroup>';
+            // needs to be a col for each body slat. header cells will have colspans
+            for (var i = tDateProfile.slotCnt - 1; i >= 0; i--) {
+                html += '<col/>';
+            }
+            html += '</colgroup>';
+            html += '<tbody>';
+            for (var _i = 0, cellRows_1 = cellRows; _i < cellRows_1.length; _i++) {
+                var rowCells = cellRows_1[_i];
+                var isLast = rowCells === lastRow;
+                html += '<tr' + (isChrono && isLast ? ' class="fc-chrono"' : '') + '>';
+                for (var _b = 0, rowCells_1 = rowCells; _b < rowCells_1.length; _b++) {
+                    var cell = rowCells_1[_b];
+                    var headerCellClassNames = [theme.getClass('widgetHeader')];
+                    if (cell.isWeekStart) {
+                        headerCellClassNames.push('fc-em-cell');
+                    }
+                    if (oneDay) {
+                        headerCellClassNames = headerCellClassNames.concat(core.getDayClasses(cell.date, this.props.dateProfile, this.context, true) // adds "today" class and other day-based classes
+                        );
+                    }
+                    html +=
+                        '<th class="' + headerCellClassNames.join(' ') + '"' +
+                            ' data-date="' + dateEnv.formatIso(cell.date, { omitTime: !tDateProfile.isTimeScale, omitTimeZoneOffset: true }) + '"' +
+                            (cell.colspan > 1 ? ' colspan="' + cell.colspan + '"' : '') +
+                            '>' +
+                            '<div class="fc-cell-content">' +
+                            cell.spanHtml +
+                            '</div>' +
+                            '</th>';
+                }
+                html += '</tr>';
+            }
+            html += '</tbody>';
+            this.tableEl.innerHTML = html; // TODO: does this work cross-browser?
+            this.slatColEls = core.findElements(this.tableEl, 'col');
+            this.innerEls = core.findElements(this.tableEl.querySelector('tr:last-child'), // compound selector won't work because of query-root problem
+            'th .fc-cell-text');
+            core.findElements(this.tableEl.querySelectorAll('tr:not(:last-child)'), // compound selector won't work because of query-root problem
+            'th .fc-cell-text').forEach(function (innerEl) {
+                innerEl.classList.add('fc-sticky');
+            });
+        };
+        return TimelineHeader;
+    }(core.Component));
+
+    var TimelineSlats = /** @class */ (function (_super) {
+        __extends(TimelineSlats, _super);
+        function TimelineSlats(context, parentEl) {
+            var _this = _super.call(this, context) || this;
+            parentEl.appendChild(_this.el = core.createElement('div', { className: 'fc-slats' }));
+            return _this;
+        }
+        TimelineSlats.prototype.destroy = function () {
+            core.removeElement(this.el);
+            _super.prototype.destroy.call(this);
+        };
+        TimelineSlats.prototype.render = function (props) {
+            this.renderDates(props.tDateProfile);
+        };
+        TimelineSlats.prototype.renderDates = function (tDateProfile) {
+            var _a = this, theme = _a.theme, view = _a.view, dateEnv = _a.dateEnv;
+            var slotDates = tDateProfile.slotDates, isWeekStarts = tDateProfile.isWeekStarts;
+            var html = '<table class="' + theme.getClass('tableGrid') + '">' +
+                '<colgroup>';
+            for (var i = 0; i < slotDates.length; i++) {
+                html += '<col/>';
+            }
+            html += '</colgroup>';
+            html += '<tbody><tr>';
+            for (var i = 0; i < slotDates.length; i++) {
+                html += this.slatCellHtml(slotDates[i], isWeekStarts[i], tDateProfile);
+            }
+            html += '</tr></tbody></table>';
+            this.el.innerHTML = html;
+            this.slatColEls = core.findElements(this.el, 'col');
+            this.slatEls = core.findElements(this.el, 'td');
+            for (var i = 0; i < slotDates.length; i++) {
+                view.publiclyTrigger('dayRender', [
+                    {
+                        date: dateEnv.toDate(slotDates[i]),
+                        el: this.slatEls[i],
+                        view: view
+                    }
+                ]);
+            }
+            this.outerCoordCache = new core.PositionCache(this.el, this.slatEls, true, // isHorizontal
+            false // isVertical
+            );
+            // for the inner divs within the slats
+            // used for event rendering and scrollTime, to disregard slat border
+            this.innerCoordCache = new core.PositionCache(this.el, core.findChildren(this.slatEls, 'div'), true, // isHorizontal
+            false // isVertical
+            );
+        };
+        TimelineSlats.prototype.slatCellHtml = function (date, isEm, tDateProfile) {
+            var _a = this, theme = _a.theme, dateEnv = _a.dateEnv;
+            var classes;
+            if (tDateProfile.isTimeScale) {
+                classes = [];
+                classes.push(core.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval)) ?
+                    'fc-major' :
+                    'fc-minor');
+            }
+            else {
+                classes = core.getDayClasses(date, this.props.dateProfile, this.context);
+                classes.push('fc-day');
+            }
+            classes.unshift(theme.getClass('widgetContent'));
+            if (isEm) {
+                classes.push('fc-em-cell');
+            }
+            return '<td class="' + classes.join(' ') + '"' +
+                ' data-date="' + dateEnv.formatIso(date, { omitTime: !tDateProfile.isTimeScale, omitTimeZoneOffset: true }) + '"' +
+                '><div></div></td>';
+        };
+        TimelineSlats.prototype.updateSize = function () {
+            this.outerCoordCache.build();
+            this.innerCoordCache.build();
+        };
+        TimelineSlats.prototype.positionToHit = function (leftPosition) {
+            var outerCoordCache = this.outerCoordCache;
+            var tDateProfile = this.props.tDateProfile;
+            var slatIndex = outerCoordCache.leftToIndex(leftPosition);
+            if (slatIndex != null) {
+                // somewhat similar to what TimeGrid does. consolidate?
+                var slatWidth = outerCoordCache.getWidth(slatIndex);
+                var partial = this.isRtl ?
+                    (outerCoordCache.rights[slatIndex] - leftPosition) / slatWidth :
+                    (leftPosition - outerCoordCache.lefts[slatIndex]) / slatWidth;
+                var localSnapIndex = Math.floor(partial * tDateProfile.snapsPerSlot);
+                var start = this.dateEnv.add(tDateProfile.slotDates[slatIndex], core.multiplyDuration(tDateProfile.snapDuration, localSnapIndex));
+                var end = this.dateEnv.add(start, tDateProfile.snapDuration);
+                return {
+                    dateSpan: {
+                        range: { start: start, end: end },
+                        allDay: !this.props.tDateProfile.isTimeScale
+                    },
+                    dayEl: this.slatColEls[slatIndex],
+                    left: outerCoordCache.lefts[slatIndex],
+                    right: outerCoordCache.rights[slatIndex]
+                };
+            }
+            return null;
+        };
+        return TimelineSlats;
+    }(core.Component));
+
+    var MIN_AUTO_LABELS = 18; // more than `12` months but less that `24` hours
+    var MAX_AUTO_SLOTS_PER_LABEL = 6; // allows 6 10-min slots in an hour
+    var MAX_AUTO_CELLS = 200; // allows 4-days to have a :30 slot duration
+    core.config.MAX_TIMELINE_SLOTS = 1000;
+    // potential nice values for slot-duration and interval-duration
+    var STOCK_SUB_DURATIONS = [
+        { years: 1 },
+        { months: 1 },
+        { days: 1 },
+        { hours: 1 },
+        { minutes: 30 },
+        { minutes: 15 },
+        { minutes: 10 },
+        { minutes: 5 },
+        { minutes: 1 },
+        { seconds: 30 },
+        { seconds: 15 },
+        { seconds: 10 },
+        { seconds: 5 },
+        { seconds: 1 },
+        { milliseconds: 500 },
+        { milliseconds: 100 },
+        { milliseconds: 10 },
+        { milliseconds: 1 }
+    ];
+    function buildTimelineDateProfile(dateProfile, view) {
+        var dateEnv = view.dateEnv;
+        var tDateProfile = {
+            labelInterval: queryDurationOption(view, 'slotLabelInterval'),
+            slotDuration: queryDurationOption(view, 'slotDuration')
+        };
+        validateLabelAndSlot(tDateProfile, dateProfile, dateEnv); // validate after computed grid duration
+        ensureLabelInterval(tDateProfile, dateProfile, dateEnv);
+        ensureSlotDuration(tDateProfile, dateProfile, dateEnv);
+        var input = view.opt('slotLabelFormat');
+        var rawFormats = Array.isArray(input) ?
+            input
+            : (input != null) ?
+                [input]
+                :
+                    computeHeaderFormats(tDateProfile, dateProfile, dateEnv, view);
+        tDateProfile.headerFormats = rawFormats.map(function (rawFormat) {
+            return core.createFormatter(rawFormat);
+        });
+        tDateProfile.isTimeScale = Boolean(tDateProfile.slotDuration.milliseconds);
+        var largeUnit = null;
+        if (!tDateProfile.isTimeScale) {
+            var slotUnit = core.greatestDurationDenominator(tDateProfile.slotDuration).unit;
+            if (/year|month|week/.test(slotUnit)) {
+                largeUnit = slotUnit;
+            }
+        }
+        tDateProfile.largeUnit = largeUnit;
+        tDateProfile.emphasizeWeeks =
+            core.isSingleDay(tDateProfile.slotDuration) &&
+                currentRangeAs('weeks', dateProfile, dateEnv) >= 2 &&
+                !view.opt('businessHours');
+        /*
+        console.log('label interval =', timelineView.labelInterval.humanize())
+        console.log('slot duration =', timelineView.slotDuration.humanize())
+        console.log('header formats =', timelineView.headerFormats)
+        console.log('isTimeScale', timelineView.isTimeScale)
+        console.log('largeUnit', timelineView.largeUnit)
+        */
+        var rawSnapDuration = view.opt('snapDuration');
+        var snapDuration;
+        var snapsPerSlot;
+        if (rawSnapDuration) {
+            snapDuration = core.createDuration(rawSnapDuration);
+            snapsPerSlot = core.wholeDivideDurations(tDateProfile.slotDuration, snapDuration);
+            // ^ TODO: warning if not whole?
+        }
+        if (snapsPerSlot == null) {
+            snapDuration = tDateProfile.slotDuration;
+            snapsPerSlot = 1;
+        }
+        tDateProfile.snapDuration = snapDuration;
+        tDateProfile.snapsPerSlot = snapsPerSlot;
+        // more...
+        var timeWindowMs = core.asRoughMs(dateProfile.maxTime) - core.asRoughMs(dateProfile.minTime);
+        // TODO: why not use normalizeRange!?
+        var normalizedStart = normalizeDate(dateProfile.renderRange.start, tDateProfile, dateEnv);
+        var normalizedEnd = normalizeDate(dateProfile.renderRange.end, tDateProfile, dateEnv);
+        // apply minTime/maxTime
+        // TODO: View should be responsible.
+        if (tDateProfile.isTimeScale) {
+            normalizedStart = dateEnv.add(normalizedStart, dateProfile.minTime);
+            normalizedEnd = dateEnv.add(core.addDays(normalizedEnd, -1), dateProfile.maxTime);
+        }
+        tDateProfile.timeWindowMs = timeWindowMs;
+        tDateProfile.normalizedRange = { start: normalizedStart, end: normalizedEnd };
+        var slotDates = [];
+        var date = normalizedStart;
+        while (date < normalizedEnd) {
+            if (isValidDate(date, tDateProfile, dateProfile, view)) {
+                slotDates.push(date);
+            }
+            date = dateEnv.add(date, tDateProfile.slotDuration);
+        }
+        tDateProfile.slotDates = slotDates;
+        // more...
+        var snapIndex = -1;
+        var snapDiff = 0; // index of the diff :(
+        var snapDiffToIndex = [];
+        var snapIndexToDiff = [];
+        date = normalizedStart;
+        while (date < normalizedEnd) {
+            if (isValidDate(date, tDateProfile, dateProfile, view)) {
+                snapIndex++;
+                snapDiffToIndex.push(snapIndex);
+                snapIndexToDiff.push(snapDiff);
+            }
+            else {
+                snapDiffToIndex.push(snapIndex + 0.5);
+            }
+            date = dateEnv.add(date, tDateProfile.snapDuration);
+            snapDiff++;
+        }
+        tDateProfile.snapDiffToIndex = snapDiffToIndex;
+        tDateProfile.snapIndexToDiff = snapIndexToDiff;
+        tDateProfile.snapCnt = snapIndex + 1; // is always one behind
+        tDateProfile.slotCnt = tDateProfile.snapCnt / tDateProfile.snapsPerSlot;
+        // more...
+        tDateProfile.isWeekStarts = buildIsWeekStarts(tDateProfile, dateEnv);
+        tDateProfile.cellRows = buildCellRows(tDateProfile, dateEnv, view);
+        return tDateProfile;
+    }
+    /*
+    snaps to appropriate unit
+    */
+    function normalizeDate(date, tDateProfile, dateEnv) {
+        var normalDate = date;
+        if (!tDateProfile.isTimeScale) {
+            normalDate = core.startOfDay(normalDate);
+            if (tDateProfile.largeUnit) {
+                normalDate = dateEnv.startOf(normalDate, tDateProfile.largeUnit);
+            }
+        }
+        return normalDate;
+    }
+    /*
+    snaps to appropriate unit
+    */
+    function normalizeRange(range, tDateProfile, dateEnv) {
+        if (!tDateProfile.isTimeScale) {
+            range = core.computeVisibleDayRange(range);
+            if (tDateProfile.largeUnit) {
+                var dayRange = range; // preserve original result
+                range = {
+                    start: dateEnv.startOf(range.start, tDateProfile.largeUnit),
+                    end: dateEnv.startOf(range.end, tDateProfile.largeUnit)
+                };
+                // if date is partially through the interval, or is in the same interval as the start,
+                // make the exclusive end be the *next* interval
+                if (range.end.valueOf() !== dayRange.end.valueOf() || range.end <= range.start) {
+                    range = {
+                        start: range.start,
+                        end: dateEnv.add(range.end, tDateProfile.slotDuration)
+                    };
+                }
+            }
+        }
+        return range;
+    }
+    function isValidDate(date, tDateProfile, dateProfile, view) {
+        if (view.dateProfileGenerator.isHiddenDay(date)) {
+            return false;
+        }
+        else if (tDateProfile.isTimeScale) {
+            // determine if the time is within minTime/maxTime, which may have wacky values
+            var day = core.startOfDay(date);
+            var timeMs = date.valueOf() - day.valueOf();
+            var ms = timeMs - core.asRoughMs(dateProfile.minTime); // milliseconds since minTime
+            ms = ((ms % 86400000) + 86400000) % 86400000; // make negative values wrap to 24hr clock
+            return ms < tDateProfile.timeWindowMs; // before the maxTime?
+        }
+        else {
+            return true;
+        }
+    }
+    function queryDurationOption(view, name) {
+        var input = view.opt(name);
+        if (input != null) {
+            return core.createDuration(input);
+        }
+    }
+    function validateLabelAndSlot(tDateProfile, dateProfile, dateEnv) {
+        var currentRange = dateProfile.currentRange;
+        // make sure labelInterval doesn't exceed the max number of cells
+        if (tDateProfile.labelInterval) {
+            var labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.labelInterval);
+            if (labelCnt > core.config.MAX_TIMELINE_SLOTS) {
+                console.warn('slotLabelInterval results in too many cells');
+                tDateProfile.labelInterval = null;
+            }
+        }
+        // make sure slotDuration doesn't exceed the maximum number of cells
+        if (tDateProfile.slotDuration) {
+            var slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, tDateProfile.slotDuration);
+            if (slotCnt > core.config.MAX_TIMELINE_SLOTS) {
+                console.warn('slotDuration results in too many cells');
+                tDateProfile.slotDuration = null;
+            }
+        }
+        // make sure labelInterval is a multiple of slotDuration
+        if (tDateProfile.labelInterval && tDateProfile.slotDuration) {
+            var slotsPerLabel = core.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
+            if (slotsPerLabel === null || slotsPerLabel < 1) {
+                console.warn('slotLabelInterval must be a multiple of slotDuration');
+                tDateProfile.slotDuration = null;
+            }
+        }
+    }
+    function ensureLabelInterval(tDateProfile, dateProfile, dateEnv) {
+        var currentRange = dateProfile.currentRange;
+        var labelInterval = tDateProfile.labelInterval;
+        if (!labelInterval) {
+            // compute based off the slot duration
+            // find the largest label interval with an acceptable slots-per-label
+            var input = void 0;
+            if (tDateProfile.slotDuration) {
+                for (var _i = 0, STOCK_SUB_DURATIONS_1 = STOCK_SUB_DURATIONS; _i < STOCK_SUB_DURATIONS_1.length; _i++) {
+                    input = STOCK_SUB_DURATIONS_1[_i];
+                    var tryLabelInterval = core.createDuration(input);
+                    var slotsPerLabel = core.wholeDivideDurations(tryLabelInterval, tDateProfile.slotDuration);
+                    if (slotsPerLabel !== null && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
+                        labelInterval = tryLabelInterval;
+                        break;
+                    }
+                }
+                // use the slot duration as a last resort
+                if (!labelInterval) {
+                    labelInterval = tDateProfile.slotDuration;
+                }
+                // compute based off the view's duration
+                // find the largest label interval that yields the minimum number of labels
+            }
+            else {
+                for (var _a = 0, STOCK_SUB_DURATIONS_2 = STOCK_SUB_DURATIONS; _a < STOCK_SUB_DURATIONS_2.length; _a++) {
+                    input = STOCK_SUB_DURATIONS_2[_a];
+                    labelInterval = core.createDuration(input);
+                    var labelCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, labelInterval);
+                    if (labelCnt >= MIN_AUTO_LABELS) {
+                        break;
+                    }
+                }
+            }
+            tDateProfile.labelInterval = labelInterval;
+        }
+        return labelInterval;
+    }
+    function ensureSlotDuration(tDateProfile, dateProfile, dateEnv) {
+        var currentRange = dateProfile.currentRange;
+        var slotDuration = tDateProfile.slotDuration;
+        if (!slotDuration) {
+            var labelInterval = ensureLabelInterval(tDateProfile, dateProfile, dateEnv); // will compute if necessary
+            // compute based off the label interval
+            // find the largest slot duration that is different from labelInterval, but still acceptable
+            for (var _i = 0, STOCK_SUB_DURATIONS_3 = STOCK_SUB_DURATIONS; _i < STOCK_SUB_DURATIONS_3.length; _i++) {
+                var input = STOCK_SUB_DURATIONS_3[_i];
+                var trySlotDuration = core.createDuration(input);
+                var slotsPerLabel = core.wholeDivideDurations(labelInterval, trySlotDuration);
+                if (slotsPerLabel !== null && slotsPerLabel > 1 && slotsPerLabel <= MAX_AUTO_SLOTS_PER_LABEL) {
+                    slotDuration = trySlotDuration;
+                    break;
+                }
+            }
+            // only allow the value if it won't exceed the view's # of slots limit
+            if (slotDuration) {
+                var slotCnt = dateEnv.countDurationsBetween(currentRange.start, currentRange.end, slotDuration);
+                if (slotCnt > MAX_AUTO_CELLS) {
+                    slotDuration = null;
+                }
+            }
+            // use the label interval as a last resort
+            if (!slotDuration) {
+                slotDuration = labelInterval;
+            }
+            tDateProfile.slotDuration = slotDuration;
+        }
+        return slotDuration;
+    }
+    function computeHeaderFormats(tDateProfile, dateProfile, dateEnv, view) {
+        var format1;
+        var format2;
+        var labelInterval = tDateProfile.labelInterval;
+        var unit = core.greatestDurationDenominator(labelInterval).unit;
+        var weekNumbersVisible = view.opt('weekNumbers');
+        var format0 = (format1 = (format2 = null));
+        // NOTE: weekNumber computation function wont work
+        if ((unit === 'week') && !weekNumbersVisible) {
+            unit = 'day';
+        }
+        switch (unit) {
+            case 'year':
+                format0 = { year: 'numeric' }; // '2015'
+                break;
+            case 'month':
+                if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
+                    format0 = { year: 'numeric' }; // '2015'
+                }
+                format1 = { month: 'short' }; // 'Jan'
+                break;
+            case 'week':
+                if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
+                    format0 = { year: 'numeric' }; // '2015'
+                }
+                format1 = { week: 'narrow' }; // 'Wk4'
+                break;
+            case 'day':
+                if (currentRangeAs('years', dateProfile, dateEnv) > 1) {
+                    format0 = { year: 'numeric', month: 'long' }; // 'January 2014'
+                }
+                else if (currentRangeAs('months', dateProfile, dateEnv) > 1) {
+                    format0 = { month: 'long' }; // 'January'
+                }
+                if (weekNumbersVisible) {
+                    format1 = { week: 'short' }; // 'Wk 4'
+                }
+                format2 = { weekday: 'narrow', day: 'numeric' }; // 'Su 9'
+                break;
+            case 'hour':
+                if (weekNumbersVisible) {
+                    format0 = { week: 'short' }; // 'Wk 4'
+                }
+                if (currentRangeAs('days', dateProfile, dateEnv) > 1) {
+                    format1 = { weekday: 'short', day: 'numeric', month: 'numeric', omitCommas: true }; // Sat 4/7
+                }
+                format2 = {
+                    hour: 'numeric',
+                    minute: '2-digit',
+                    omitZeroMinute: true,
+                    meridiem: 'short'
+                };
+                break;
+            case 'minute':
+                // sufficiently large number of different minute cells?
+                if ((core.asRoughMinutes(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
+                    format0 = {
+                        hour: 'numeric',
+                        meridiem: 'short'
+                    };
+                    format1 = function (params) {
+                        return ':' + core.padStart(params.date.minute, 2); // ':30'
+                    };
+                }
+                else {
+                    format0 = {
+                        hour: 'numeric',
+                        minute: 'numeric',
+                        meridiem: 'short'
+                    };
+                }
+                break;
+            case 'second':
+                // sufficiently large number of different second cells?
+                if ((core.asRoughSeconds(labelInterval) / 60) >= MAX_AUTO_SLOTS_PER_LABEL) {
+                    format0 = { hour: 'numeric', minute: '2-digit', meridiem: 'lowercase' }; // '8:30 PM'
+                    format1 = function (params) {
+                        return ':' + core.padStart(params.date.second, 2); // ':30'
+                    };
+                }
+                else {
+                    format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
+                }
+                break;
+            case 'millisecond':
+                format0 = { hour: 'numeric', minute: '2-digit', second: '2-digit', meridiem: 'lowercase' }; // '8:30:45 PM'
+                format1 = function (params) {
+                    return '.' + core.padStart(params.millisecond, 3);
+                };
+                break;
+        }
+        return [].concat(format0 || [], format1 || [], format2 || []);
+    }
+    // Compute the number of the give units in the "current" range.
+    // Won't go more precise than days.
+    // Will return `0` if there's not a clean whole interval.
+    function currentRangeAs(unit, dateProfile, dateEnv) {
+        var range = dateProfile.currentRange;
+        var res = null;
+        if (unit === 'years') {
+            res = dateEnv.diffWholeYears(range.start, range.end);
+        }
+        else if (unit === 'months') {
+            res = dateEnv.diffWholeMonths(range.start, range.end);
+        }
+        else if (unit === 'weeks') {
+            res = dateEnv.diffWholeMonths(range.start, range.end);
+        }
+        else if (unit === 'days') {
+            res = core.diffWholeDays(range.start, range.end);
+        }
+        return res || 0;
+    }
+    function buildIsWeekStarts(tDateProfile, dateEnv) {
+        var slotDates = tDateProfile.slotDates, emphasizeWeeks = tDateProfile.emphasizeWeeks;
+        var prevWeekNumber = null;
+        var isWeekStarts = [];
+        for (var _i = 0, slotDates_1 = slotDates; _i < slotDates_1.length; _i++) {
+            var slotDate = slotDates_1[_i];
+            var weekNumber = dateEnv.computeWeekNumber(slotDate);
+            var isWeekStart = emphasizeWeeks && (prevWeekNumber !== null) && (prevWeekNumber !== weekNumber);
+            prevWeekNumber = weekNumber;
+            isWeekStarts.push(isWeekStart);
+        }
+        return isWeekStarts;
+    }
+    function buildCellRows(tDateProfile, dateEnv, view) {
+        var slotDates = tDateProfile.slotDates;
+        var formats = tDateProfile.headerFormats;
+        var cellRows = formats.map(function (format) { return []; }); // indexed by row,col
+        // specifically for navclicks
+        var rowUnits = formats.map(function (format) {
+            return format.getLargestUnit ? format.getLargestUnit() : null;
+        });
+        // builds cellRows and slotCells
+        for (var i = 0; i < slotDates.length; i++) {
+            var date = slotDates[i];
+            var isWeekStart = tDateProfile.isWeekStarts[i];
+            for (var row = 0; row < formats.length; row++) {
+                var format = formats[row];
+                var rowCells = cellRows[row];
+                var leadingCell = rowCells[rowCells.length - 1];
+                var isSuperRow = (formats.length > 1) && (row < (formats.length - 1)); // more than one row and not the last
+                var newCell = null;
+                if (isSuperRow) {
+                    var text = dateEnv.format(date, format);
+                    if (!leadingCell || (leadingCell.text !== text)) {
+                        newCell = buildCellObject(date, text, rowUnits[row], view);
+                    }
+                    else {
+                        leadingCell.colspan += 1;
+                    }
+                }
+                else {
+                    if (!leadingCell ||
+                        core.isInt(dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.labelInterval))) {
+                        var text = dateEnv.format(date, format);
+                        newCell = buildCellObject(date, text, rowUnits[row], view);
+                    }
+                    else {
+                        leadingCell.colspan += 1;
+                    }
+                }
+                if (newCell) {
+                    newCell.weekStart = isWeekStart;
+                    rowCells.push(newCell);
+                }
+            }
+        }
+        return cellRows;
+    }
+    function buildCellObject(date, text, rowUnit, view) {
+        var spanHtml = core.buildGotoAnchorHtml(view, {
+            date: date,
+            type: rowUnit,
+            forceOff: !rowUnit
+        }, {
+            'class': 'fc-cell-text'
+        }, core.htmlEscape(text));
+        return { text: text, spanHtml: spanHtml, date: date, colspan: 1, isWeekStart: false };
+    }
+
+    var TimelineNowIndicator = /** @class */ (function () {
+        function TimelineNowIndicator(headParent, bodyParent) {
+            this.headParent = headParent;
+            this.bodyParent = bodyParent;
+        }
+        TimelineNowIndicator.prototype.render = function (coord, isRtl) {
+            var styleProps = isRtl ? { right: -coord } : { left: coord };
+            this.headParent.appendChild(this.arrowEl = core.createElement('div', {
+                className: 'fc-now-indicator fc-now-indicator-arrow',
+                style: styleProps
+            }));
+            this.bodyParent.appendChild(this.lineEl = core.createElement('div', {
+                className: 'fc-now-indicator fc-now-indicator-line',
+                style: styleProps
+            }));
+        };
+        TimelineNowIndicator.prototype.unrender = function () {
+            if (this.arrowEl) {
+                core.removeElement(this.arrowEl);
+            }
+            if (this.lineEl) {
+                core.removeElement(this.lineEl);
+            }
+        };
+        return TimelineNowIndicator;
+    }());
+
+    var STICKY_PROP_VAL = computeStickyPropVal(); // if null, means not supported at all
+    var IS_MS_EDGE = /Edge/.test(navigator.userAgent);
+    var IS_SAFARI = STICKY_PROP_VAL === '-webkit-sticky'; // good b/c doesn't confuse chrome
+    var STICKY_CLASSNAME = 'fc-sticky';
+    /*
+    useful beyond the native position:sticky for these reasons:
+    - support in IE11
+    - nice centering support
+    */
+    var StickyScroller = /** @class */ (function () {
+        function StickyScroller(scroller, isRtl, isVertical) {
+            var _this = this;
+            this.usingRelative = null;
+            /*
+            known bug: called twice on init. problem when mixing with ScrollJoiner
+            */
+            this.updateSize = function () {
+                var els = Array.prototype.slice.call(_this.scroller.canvas.el.querySelectorAll('.' + STICKY_CLASSNAME));
+                var elGeoms = _this.queryElGeoms(els);
+                var viewportWidth = _this.scroller.el.clientWidth;
+                if (_this.usingRelative) {
+                    var elDestinations = _this.computeElDestinations(elGeoms, viewportWidth); // read before prepPositioning
+                    assignRelativePositions(els, elGeoms, elDestinations);
+                }
+                else {
+                    assignStickyPositions(els, elGeoms, viewportWidth);
+                }
+            };
+            this.scroller = scroller;
+            this.usingRelative =
+                !STICKY_PROP_VAL || // IE11
+                    (IS_MS_EDGE && isRtl) || // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/18883305/
+                    ((IS_MS_EDGE || IS_SAFARI) && isVertical); // because doesn't work with rowspan in tables, our only vertial use
+            if (this.usingRelative) {
+                scroller.on('scrollEnd', this.updateSize);
+            }
+        }
+        StickyScroller.prototype.destroy = function () {
+            this.scroller.off('scrollEnd', this.updateSize);
+        };
+        StickyScroller.prototype.queryElGeoms = function (els) {
+            var canvasOrigin = this.scroller.canvas.el.getBoundingClientRect();
+            var elGeoms = [];
+            for (var _i = 0, els_1 = els; _i < els_1.length; _i++) {
+                var el = els_1[_i];
+                var parentBound = core.translateRect(el.parentNode.getBoundingClientRect(), -canvasOrigin.left, -canvasOrigin.top);
+                var elRect = el.getBoundingClientRect();
+                var computedStyles = window.getComputedStyle(el);
+                var computedTextAlign = window.getComputedStyle(el.parentNode).textAlign; // ask the parent
+                var intendedTextAlign = computedTextAlign;
+                var naturalBound = null;
+                if (computedStyles.position !== 'sticky') {
+                    naturalBound = core.translateRect(elRect, -canvasOrigin.left - (parseFloat(computedStyles.left) || 0), // could be 'auto'
+                    -canvasOrigin.top - (parseFloat(computedStyles.top) || 0));
+                }
+                if (el.hasAttribute('data-sticky-center')) {
+                    intendedTextAlign = 'center';
+                }
+                elGeoms.push({
+                    parentBound: parentBound,
+                    naturalBound: naturalBound,
+                    elWidth: elRect.width,
+                    elHeight: elRect.height,
+                    computedTextAlign: computedTextAlign,
+                    intendedTextAlign: intendedTextAlign
+                });
+            }
+            return elGeoms;
+        };
+        StickyScroller.prototype.computeElDestinations = function (elGeoms, viewportWidth) {
+            var viewportLeft = this.scroller.getScrollFromLeft();
+            var viewportTop = this.scroller.getScrollTop();
+            var viewportRight = viewportLeft + viewportWidth;
+            return elGeoms.map(function (elGeom) {
+                var elWidth = elGeom.elWidth, elHeight = elGeom.elHeight, parentBound = elGeom.parentBound, naturalBound = elGeom.naturalBound;
+                var destLeft; // relative to canvas topleft
+                var destTop; // "
+                switch (elGeom.intendedTextAlign) {
+                    case 'left':
+                        destLeft = viewportLeft;
+                        break;
+                    case 'right':
+                        destLeft = viewportRight - elWidth;
+                        break;
+                    case 'center':
+                        destLeft = (viewportLeft + viewportRight) / 2 - elWidth / 2;
+                        break;
+                }
+                destLeft = Math.min(destLeft, parentBound.right - elWidth);
+                destLeft = Math.max(destLeft, parentBound.left);
+                destTop = viewportTop;
+                destTop = Math.min(destTop, parentBound.bottom - elHeight);
+                destTop = Math.max(destTop, naturalBound.top); // better to use natural top for upper bound
+                return { left: destLeft, top: destTop };
+            });
+        };
+        return StickyScroller;
+    }());
+    function assignRelativePositions(els, elGeoms, elDestinations) {
+        els.forEach(function (el, i) {
+            var naturalBound = elGeoms[i].naturalBound;
+            core.applyStyle(el, {
+                position: 'relative',
+                left: elDestinations[i].left - naturalBound.left,
+                top: elDestinations[i].top - naturalBound.top
+            });
+        });
+    }
+    function assignStickyPositions(els, elGeoms, viewportWidth) {
+        els.forEach(function (el, i) {
+            var stickyLeft = 0;
+            if (elGeoms[i].intendedTextAlign === 'center') {
+                stickyLeft = (viewportWidth - elGeoms[i].elWidth) / 2;
+                // needs to be forced to left?
+                if (elGeoms[i].computedTextAlign === 'center') {
+                    el.setAttribute('data-sticky-center', '') // remember for next queryElGeoms
+                    ;
+                    el.parentNode.style.textAlign = 'left';
+                }
+            }
+            core.applyStyle(el, {
+                position: STICKY_PROP_VAL,
+                left: stickyLeft,
+                right: 0,
+                top: 0
+            });
+        });
+    }
+    function computeStickyPropVal() {
+        var el = core.htmlToElement('<div style="position:-webkit-sticky;position:sticky"></div>');
+        var val = el.style.position;
+        if (val.indexOf('sticky') !== -1) {
+            return val;
+        }
+        else {
+            return null;
+        }
+    }
+
+    var TimeAxis = /** @class */ (function (_super) {
+        __extends(TimeAxis, _super);
+        function TimeAxis(context, headerContainerEl, bodyContainerEl) {
+            var _this = _super.call(this, context) || this;
+            var layout = _this.layout = new HeaderBodyLayout(headerContainerEl, bodyContainerEl, 'auto');
+            var headerEnhancedScroller = layout.headerScroller.enhancedScroll;
+            var bodyEnhancedScroller = layout.bodyScroller.enhancedScroll;
+            // needs to go after layout, which has ScrollJoiner
+            _this.headStickyScroller = new StickyScroller(headerEnhancedScroller, _this.isRtl, false); // isVertical=false
+            _this.bodyStickyScroller = new StickyScroller(bodyEnhancedScroller, _this.isRtl, false); // isVertical=false
+            _this.header = new TimelineHeader(context, headerEnhancedScroller.canvas.contentEl);
+            _this.slats = new TimelineSlats(context, bodyEnhancedScroller.canvas.bgEl);
+            _this.nowIndicator = new TimelineNowIndicator(headerEnhancedScroller.canvas.el, bodyEnhancedScroller.canvas.el);
+            return _this;
+        }
+        TimeAxis.prototype.destroy = function () {
+            this.layout.destroy();
+            this.header.destroy();
+            this.slats.destroy();
+            this.nowIndicator.unrender();
+            this.headStickyScroller.destroy();
+            this.bodyStickyScroller.destroy();
+            _super.prototype.destroy.call(this);
+        };
+        TimeAxis.prototype.render = function (props) {
+            var tDateProfile = this.tDateProfile =
+                buildTimelineDateProfile(props.dateProfile, this.view); // TODO: cache
+            this.header.receiveProps({
+                dateProfile: props.dateProfile,
+                tDateProfile: tDateProfile
+            });
+            this.slats.receiveProps({
+                dateProfile: props.dateProfile,
+                tDateProfile: tDateProfile
+            });
+        };
+        // Now Indicator
+        // ------------------------------------------------------------------------------------------
+        TimeAxis.prototype.getNowIndicatorUnit = function (dateProfile) {
+            // yuck
+            var tDateProfile = this.tDateProfile =
+                buildTimelineDateProfile(dateProfile, this.view); // TODO: cache
+            if (tDateProfile.isTimeScale) {
+                return core.greatestDurationDenominator(tDateProfile.slotDuration).unit;
+            }
+        };
+        // will only execute if isTimeScale
+        TimeAxis.prototype.renderNowIndicator = function (date) {
+            if (core.rangeContainsMarker(this.tDateProfile.normalizedRange, date)) {
+                this.nowIndicator.render(this.dateToCoord(date), this.isRtl);
+            }
+        };
+        // will only execute if isTimeScale
+        TimeAxis.prototype.unrenderNowIndicator = function () {
+            this.nowIndicator.unrender();
+        };
+        // Sizing
+        // ------------------------------------------------------------------------------------------
+        TimeAxis.prototype.updateSize = function (isResize, totalHeight, isAuto) {
+            this.applySlotWidth(this.computeSlotWidth());
+            // adjusts gutters. do after slot widths set
+            this.layout.setHeight(totalHeight, isAuto);
+            // pretty much just queries coords. do last
+            this.slats.updateSize();
+        };
+        TimeAxis.prototype.updateStickyScrollers = function () {
+            this.headStickyScroller.updateSize();
+            this.bodyStickyScroller.updateSize();
+        };
+        TimeAxis.prototype.computeSlotWidth = function () {
+            var slotWidth = this.opt('slotWidth') || '';
+            if (slotWidth === '') {
+                slotWidth = this.computeDefaultSlotWidth(this.tDateProfile);
+            }
+            return slotWidth;
+        };
+        TimeAxis.prototype.computeDefaultSlotWidth = function (tDateProfile) {
+            var maxInnerWidth = 0; // TODO: harness core's `matchCellWidths` for this
+            this.header.innerEls.forEach(function (innerEl, i) {
+                maxInnerWidth = Math.max(maxInnerWidth, innerEl.getBoundingClientRect().width);
+            });
+            var headingCellWidth = Math.ceil(maxInnerWidth) + 1; // assume no padding, and one pixel border
+            // in TimelineView.defaults we ensured that labelInterval is an interval of slotDuration
+            // TODO: rename labelDuration?
+            var slotsPerLabel = core.wholeDivideDurations(tDateProfile.labelInterval, tDateProfile.slotDuration);
+            var slotWidth = Math.ceil(headingCellWidth / slotsPerLabel);
+            var minWidth = window.getComputedStyle(this.header.slatColEls[0]).minWidth;
+            if (minWidth) {
+                minWidth = parseInt(minWidth, 10);
+                if (minWidth) {
+                    slotWidth = Math.max(slotWidth, minWidth);
+                }
+            }
+            return slotWidth;
+        };
+        TimeAxis.prototype.applySlotWidth = function (slotWidth) {
+            var _a = this, layout = _a.layout, tDateProfile = _a.tDateProfile;
+            var containerWidth = '';
+            var containerMinWidth = '';
+            var nonLastSlotWidth = '';
+            if (slotWidth !== '') {
+                slotWidth = Math.round(slotWidth);
+                containerWidth = slotWidth * tDateProfile.slotDates.length;
+                containerMinWidth = '';
+                nonLastSlotWidth = slotWidth;
+                var availableWidth = layout.bodyScroller.enhancedScroll.getClientWidth();
+                if (availableWidth > containerWidth) {
+                    containerMinWidth = availableWidth;
+                    containerWidth = '';
+                    nonLastSlotWidth = Math.floor(availableWidth / tDateProfile.slotDates.length);
+                }
+            }
+            layout.headerScroller.enhancedScroll.canvas.setWidth(containerWidth);
+            layout.headerScroller.enhancedScroll.canvas.setMinWidth(containerMinWidth);
+            layout.bodyScroller.enhancedScroll.canvas.setWidth(containerWidth);
+            layout.bodyScroller.enhancedScroll.canvas.setMinWidth(containerMinWidth);
+            if (nonLastSlotWidth !== '') {
+                this.header.slatColEls.slice(0, -1).concat(this.slats.slatColEls.slice(0, -1)).forEach(function (el) {
+                    el.style.width = nonLastSlotWidth + 'px';
+                });
+            }
+        };
+        // returned value is between 0 and the number of snaps
+        TimeAxis.prototype.computeDateSnapCoverage = function (date) {
+            var _a = this, dateEnv = _a.dateEnv, tDateProfile = _a.tDateProfile;
+            var snapDiff = dateEnv.countDurationsBetween(tDateProfile.normalizedRange.start, date, tDateProfile.snapDuration);
+            if (snapDiff < 0) {
+                return 0;
+            }
+            else if (snapDiff >= tDateProfile.snapDiffToIndex.length) {
+                return tDateProfile.snapCnt;
+            }
+            else {
+                var snapDiffInt = Math.floor(snapDiff);
+                var snapCoverage = tDateProfile.snapDiffToIndex[snapDiffInt];
+                if (core.isInt(snapCoverage)) { // not an in-between value
+                    snapCoverage += snapDiff - snapDiffInt; // add the remainder
+                }
+                else {
+                    // a fractional value, meaning the date is not visible
+                    // always round up in this case. works for start AND end dates in a range.
+                    snapCoverage = Math.ceil(snapCoverage);
+                }
+                return snapCoverage;
+            }
+        };
+        // for LTR, results range from 0 to width of area
+        // for RTL, results range from negative width of area to 0
+        TimeAxis.prototype.dateToCoord = function (date) {
+            var tDateProfile = this.tDateProfile;
+            var snapCoverage = this.computeDateSnapCoverage(date);
+            var slotCoverage = snapCoverage / tDateProfile.snapsPerSlot;
+            var slotIndex = Math.floor(slotCoverage);
+            slotIndex = Math.min(slotIndex, tDateProfile.slotCnt - 1);
+            var partial = slotCoverage - slotIndex;
+            var _a = this.slats, innerCoordCache = _a.innerCoordCache, outerCoordCache = _a.outerCoordCache;
+            if (this.isRtl) {
+                return (outerCoordCache.rights[slotIndex] -
+                    (innerCoordCache.getWidth(slotIndex) * partial)) - outerCoordCache.originClientRect.width;
+            }
+            else {
+                return (outerCoordCache.lefts[slotIndex] +
+                    (innerCoordCache.getWidth(slotIndex) * partial));
+            }
+        };
+        TimeAxis.prototype.rangeToCoords = function (range) {
+            if (this.isRtl) {
+                return { right: this.dateToCoord(range.start), left: this.dateToCoord(range.end) };
+            }
+            else {
+                return { left: this.dateToCoord(range.start), right: this.dateToCoord(range.end) };
+            }
+        };
+        // Scrolling
+        // ------------------------------------------------------------------------------------------
+        TimeAxis.prototype.computeDateScroll = function (duration) {
+            var dateEnv = this.dateEnv;
+            var dateProfile = this.props.dateProfile;
+            var left = 0;
+            if (dateProfile) {
+                left = this.dateToCoord(dateEnv.add(core.startOfDay(dateProfile.activeRange.start), // startOfDay needed?
+                duration));
+                // hack to overcome the left borders of non-first slat
+                if (!this.isRtl && left) {
+                    left += 1;
+                }
+            }
+            return { left: left };
+        };
+        TimeAxis.prototype.queryDateScroll = function () {
+            var enhancedScroll = this.layout.bodyScroller.enhancedScroll;
+            return {
+                left: enhancedScroll.getScrollLeft()
+            };
+        };
+        TimeAxis.prototype.applyDateScroll = function (scroll) {
+            // TODO: lame we have to update both. use the scrolljoiner instead maybe
+            this.layout.bodyScroller.enhancedScroll.setScrollLeft(scroll.left || 0);
+            this.layout.headerScroller.enhancedScroll.setScrollLeft(scroll.left || 0);
+        };
+        return TimeAxis;
+    }(core.Component));
+
+    var TimelineLaneEventRenderer = /** @class */ (function (_super) {
+        __extends(TimelineLaneEventRenderer, _super);
+        function TimelineLaneEventRenderer(context, masterContainerEl, timeAxis) {
+            var _this = _super.call(this, context) || this;
+            _this.masterContainerEl = masterContainerEl;
+            _this.timeAxis = timeAxis;
+            return _this;
+        }
+        TimelineLaneEventRenderer.prototype.renderSegHtml = function (seg, mirrorInfo) {
+            var view = this.context.view;
+            var eventRange = seg.eventRange;
+            var eventDef = eventRange.def;
+            var eventUi = eventRange.ui;
+            var isDraggable = view.computeEventDraggable(eventDef, eventUi);
+            var isResizableFromStart = seg.isStart && view.computeEventStartResizable(eventDef, eventUi);
+            var isResizableFromEnd = seg.isEnd && view.computeEventEndResizable(eventDef, eventUi);
+            var classes = this.getSegClasses(seg, isDraggable, isResizableFromStart || isResizableFromEnd, mirrorInfo);
+            classes.unshift('fc-timeline-event', 'fc-h-event');
+            var timeText = this.getTimeText(eventRange);
+            return '<a class="' + classes.join(' ') + '" style="' + core.cssToStr(this.getSkinCss(eventUi)) + '"' +
+                (eventDef.url ?
+                    ' href="' + core.htmlEscape(eventDef.url) + '"' :
+                    '') +
+                '>' +
+                (timeText ?
+                    '<span class="fc-time-wrap">' +
+                        '<span class="fc-time">' +
+                        core.htmlEscape(timeText) +
+                        '</span>' +
+                        '</span>'
+                    :
+                        '') +
+                '<span class="fc-title-wrap">' +
+                '<span class="fc-title fc-sticky">' +
+                (eventDef.title ? core.htmlEscape(eventDef.title) : '&nbsp;') +
+                '</span>' +
+                '</span>' +
+                (isResizableFromStart ?
+                    '<div class="fc-resizer fc-start-resizer"></div>' :
+                    '') +
+                (isResizableFromEnd ?
+                    '<div class="fc-resizer fc-end-resizer"></div>' :
+                    '') +
+                '</a>';
+        };
+        TimelineLaneEventRenderer.prototype.computeDisplayEventTime = function () {
+            return !this.timeAxis.tDateProfile.isTimeScale; // because times should be obvious via axis
+        };
+        TimelineLaneEventRenderer.prototype.computeDisplayEventEnd = function () {
+            return false;
+        };
+        // Computes a default event time formatting string if `timeFormat` is not explicitly defined
+        TimelineLaneEventRenderer.prototype.computeEventTimeFormat = function () {
+            return {
+                hour: 'numeric',
+                minute: '2-digit',
+                omitZeroMinute: true,
+                meridiem: 'narrow'
+            };
+        };
+        TimelineLaneEventRenderer.prototype.attachSegs = function (segs, mirrorInfo) {
+            if (!this.el && this.masterContainerEl) {
+                this.el = core.createElement('div', { className: 'fc-event-container' });
+                if (mirrorInfo) {
+                    this.el.classList.add('fc-mirror-container');
+                }
+                this.masterContainerEl.appendChild(this.el);
+            }
+            if (this.el) {
+                for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                    var seg = segs_1[_i];
+                    this.el.appendChild(seg.el);
+                }
+            }
+        };
+        TimelineLaneEventRenderer.prototype.detachSegs = function (segs) {
+            for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+                var seg = segs_2[_i];
+                core.removeElement(seg.el);
+            }
+        };
+        // computes AND assigns (assigns the left/right at least). bad
+        TimelineLaneEventRenderer.prototype.computeSegSizes = function (segs) {
+            var timeAxis = this.timeAxis;
+            for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) {
+                var seg = segs_3[_i];
+                var coords = timeAxis.rangeToCoords(seg); // works because Seg has start/end
+                core.applyStyle(seg.el, {
+                    left: (seg.left = coords.left),
+                    right: -(seg.right = coords.right)
+                });
+            }
+        };
+        TimelineLaneEventRenderer.prototype.assignSegSizes = function (segs) {
+            if (!this.el) {
+                return;
+            }
+            // compute seg verticals
+            for (var _i = 0, segs_4 = segs; _i < segs_4.length; _i++) {
+                var seg = segs_4[_i];
+                seg.height = core.computeHeightAndMargins(seg.el);
+            }
+            this.buildSegLevels(segs); // populates above/below props for computeOffsetForSegs
+            var totalHeight = computeOffsetForSegs(segs); // also assigns seg.top
+            core.applyStyleProp(this.el, 'height', totalHeight);
+            // assign seg verticals
+            for (var _a = 0, segs_5 = segs; _a < segs_5.length; _a++) {
+                var seg = segs_5[_a];
+                core.applyStyleProp(seg.el, 'top', seg.top);
+            }
+        };
+        TimelineLaneEventRenderer.prototype.buildSegLevels = function (segs) {
+            var segLevels = [];
+            segs = this.sortEventSegs(segs);
+            for (var _i = 0, segs_6 = segs; _i < segs_6.length; _i++) {
+                var unplacedSeg = segs_6[_i];
+                unplacedSeg.above = [];
+                // determine the first level with no collisions
+                var level = 0; // level index
+                while (level < segLevels.length) {
+                    var isLevelCollision = false;
+                    // determine collisions
+                    for (var _a = 0, _b = segLevels[level]; _a < _b.length; _a++) {
+                        var placedSeg = _b[_a];
+                        if (timeRowSegsCollide(unplacedSeg, placedSeg)) {
+                            unplacedSeg.above.push(placedSeg);
+                            isLevelCollision = true;
+                        }
+                    }
+                    if (isLevelCollision) {
+                        level += 1;
+                    }
+                    else {
+                        break;
+                    }
+                }
+                // insert into the first non-colliding level. create if necessary
+                (segLevels[level] || (segLevels[level] = []))
+                    .push(unplacedSeg);
+                // record possible colliding segments below (TODO: automated test for this)
+                level += 1;
+                while (level < segLevels.length) {
+                    for (var _c = 0, _d = segLevels[level]; _c < _d.length; _c++) {
+                        var belowSeg = _d[_c];
+                        if (timeRowSegsCollide(unplacedSeg, belowSeg)) {
+                            belowSeg.above.push(unplacedSeg);
+                        }
+                    }
+                    level += 1;
+                }
+            }
+            return segLevels;
+        };
+        return TimelineLaneEventRenderer;
+    }(core.FgEventRenderer));
+    function computeOffsetForSegs(segs) {
+        var max = 0;
+        for (var _i = 0, segs_7 = segs; _i < segs_7.length; _i++) {
+            var seg = segs_7[_i];
+            max = Math.max(max, computeOffsetForSeg(seg));
+        }
+        return max;
+    }
+    function computeOffsetForSeg(seg) {
+        if ((seg.top == null)) {
+            seg.top = computeOffsetForSegs(seg.above);
+        }
+        return seg.top + seg.height;
+    }
+    function timeRowSegsCollide(seg0, seg1) {
+        return (seg0.left < seg1.right) && (seg0.right > seg1.left);
+    }
+
+    var TimelineLaneFillRenderer = /** @class */ (function (_super) {
+        __extends(TimelineLaneFillRenderer, _super);
+        function TimelineLaneFillRenderer(context, masterContainerEl, timeAxis) {
+            var _this = _super.call(this, context) || this;
+            _this.masterContainerEl = masterContainerEl;
+            _this.timeAxis = timeAxis;
+            return _this;
+        }
+        TimelineLaneFillRenderer.prototype.attachSegs = function (type, segs) {
+            if (segs.length) {
+                var className = void 0;
+                if (type === 'businessHours') {
+                    className = 'bgevent';
+                }
+                else {
+                    className = type.toLowerCase();
+                }
+                // making a new container each time is OKAY
+                // all types of segs (background or business hours or whatever) are rendered in one pass
+                var containerEl = core.createElement('div', { className: 'fc-' + className + '-container' });
+                this.masterContainerEl.appendChild(containerEl);
+                for (var _i = 0, segs_1 = segs; _i < segs_1.length; _i++) {
+                    var seg = segs_1[_i];
+                    containerEl.appendChild(seg.el);
+                }
+                return [containerEl]; // return value
+            }
+        };
+        TimelineLaneFillRenderer.prototype.computeSegSizes = function (segs) {
+            var timeAxis = this.timeAxis;
+            for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
+                var seg = segs_2[_i];
+                var coords = timeAxis.rangeToCoords(seg);
+                seg.left = coords.left;
+                seg.right = coords.right;
+            }
+        };
+        TimelineLaneFillRenderer.prototype.assignSegSizes = function (segs) {
+            for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) {
+                var seg = segs_3[_i];
+                core.applyStyle(seg.el, {
+                    left: seg.left,
+                    right: -seg.right
+                });
+            }
+        };
+        return TimelineLaneFillRenderer;
+    }(core.FillRenderer));
+
+    var TimelineLane = /** @class */ (function (_super) {
+        __extends(TimelineLane, _super);
+        function TimelineLane(context, fgContainerEl, bgContainerEl, timeAxis) {
+            var _this = _super.call(this, context, bgContainerEl) // should el be bgContainerEl???
+             || this;
+            _this.slicer = new TimelineLaneSlicer();
+            _this.renderEventDrag = core.memoizeRendering(_this._renderEventDrag, _this._unrenderEventDrag);
+            _this.renderEventResize = core.memoizeRendering(_this._renderEventResize, _this._unrenderEventResize);
+            var fillRenderer = _this.fillRenderer = new TimelineLaneFillRenderer(context, bgContainerEl, timeAxis);
+            var eventRenderer = _this.eventRenderer = new TimelineLaneEventRenderer(context, fgContainerEl, timeAxis);
+            _this.mirrorRenderer = new TimelineLaneEventRenderer(context, fgContainerEl, timeAxis);
+            _this.renderBusinessHours = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'businessHours'), fillRenderer.unrender.bind(fillRenderer, 'businessHours'));
+            _this.renderDateSelection = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'highlight'), fillRenderer.unrender.bind(fillRenderer, 'highlight'));
+            _this.renderBgEvents = core.memoizeRendering(fillRenderer.renderSegs.bind(fillRenderer, 'bgEvent'), fillRenderer.unrender.bind(fillRenderer, 'bgEvent'));
+            _this.renderFgEvents = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer));
+            _this.renderEventSelection = core.memoizeRendering(eventRenderer.selectByInstanceId.bind(eventRenderer), eventRenderer.unselectByInstanceId.bind(eventRenderer), [_this.renderFgEvents]);
+            _this.timeAxis = timeAxis;
+            return _this;
+        }
+        TimelineLane.prototype.render = function (props) {
+            var slicedProps = this.slicer.sliceProps(props, props.dateProfile, this.timeAxis.tDateProfile.isTimeScale ? null : props.nextDayThreshold, this, this.timeAxis);
+            this.renderBusinessHours(slicedProps.businessHourSegs);
+            this.renderDateSelection(slicedProps.dateSelectionSegs);
+            this.renderBgEvents(slicedProps.bgEventSegs);
+            this.renderFgEvents(slicedProps.fgEventSegs);
+            this.renderEventSelection(slicedProps.eventSelection);
+            this.renderEventDrag(slicedProps.eventDrag);
+            this.renderEventResize(slicedProps.eventResize);
+        };
+        TimelineLane.prototype.destroy = function () {
+            _super.prototype.destroy.call(this);
+            this.renderBusinessHours.unrender();
+            this.renderDateSelection.unrender();
+            this.renderBgEvents.unrender();
+            this.renderFgEvents.unrender();
+            this.renderEventSelection.unrender();
+            this.renderEventDrag.unrender();
+            this.renderEventResize.unrender();
+        };
+        TimelineLane.prototype._renderEventDrag = function (state) {
+            if (state) {
+                this.eventRenderer.hideByHash(state.affectedInstances);
+                this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        TimelineLane.prototype._unrenderEventDrag = function (state) {
+            if (state) {
+                this.eventRenderer.showByHash(state.affectedInstances);
+                this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        TimelineLane.prototype._renderEventResize = function (state) {
+            if (state) {
+                // HACK. eventRenderer and fillRenderer both use these segs. would compete over seg.el
+                var segsForHighlight = state.segs.map(function (seg) {
+                    return __assign({}, seg);
+                });
+                this.eventRenderer.hideByHash(state.affectedInstances);
+                this.fillRenderer.renderSegs('highlight', segsForHighlight);
+                this.mirrorRenderer.renderSegs(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        TimelineLane.prototype._unrenderEventResize = function (state) {
+            if (state) {
+                this.eventRenderer.showByHash(state.affectedInstances);
+                this.fillRenderer.unrender('highlight');
+                this.mirrorRenderer.unrender(state.segs, { isDragging: true, sourceSeg: state.sourceSeg });
+            }
+        };
+        TimelineLane.prototype.updateSize = function (isResize) {
+            var _a = this, fillRenderer = _a.fillRenderer, eventRenderer = _a.eventRenderer, mirrorRenderer = _a.mirrorRenderer;
+            fillRenderer.computeSizes(isResize);
+            eventRenderer.computeSizes(isResize);
+            mirrorRenderer.computeSizes(isResize);
+            fillRenderer.assignSizes(isResize);
+            eventRenderer.assignSizes(isResize);
+            mirrorRenderer.assignSizes(isResize);
+        };
+        return TimelineLane;
+    }(core.DateComponent));
+    var TimelineLaneSlicer = /** @class */ (function (_super) {
+        __extends(TimelineLaneSlicer, _super);
+        function TimelineLaneSlicer() {
+            return _super !== null && _super.apply(this, arguments) || this;
+        }
+        TimelineLaneSlicer.prototype.sliceRange = function (origRange, timeAxis) {
+            var tDateProfile = timeAxis.tDateProfile;
+            var dateProfile = timeAxis.props.dateProfile;
+            var normalRange = normalizeRange(origRange, tDateProfile, timeAxis.dateEnv);
+            var segs = [];
+            // protect against when the span is entirely in an invalid date region
+            if (timeAxis.computeDateSnapCoverage(normalRange.start) < timeAxis.computeDateSnapCoverage(normalRange.end)) {
+                // intersect the footprint's range with the grid's range
+                var slicedRange = core.intersectRanges(normalRange, tDateProfile.normalizedRange);
+                if (slicedRange) {
+                    segs.push({
+                        start: slicedRange.start,
+                        end: slicedRange.end,
+                        isStart: slicedRange.start.valueOf() === normalRange.start.valueOf() && isValidDate(slicedRange.start, tDateProfile, dateProfile, timeAxis.view),
+                        isEnd: slicedRange.end.valueOf() === normalRange.end.valueOf() && isValidDate(core.addMs(slicedRange.end, -1), tDateProfile, dateProfile, timeAxis.view)
+                    });
+                }
+            }
+            return segs;
+        };
+        return TimelineLaneSlicer;
+    }(core.Slicer));
+
+    var TimelineView = /** @class */ (function (_super) {
+        __extends(TimelineView, _super);
+        function TimelineView(context, viewSpec, dateProfileGenerator, parentEl) {
+            var _this = _super.call(this, context, viewSpec, dateProfileGenerator, parentEl) || this;
+            _this.el.classList.add('fc-timeline');
+            if (_this.opt('eventOverlap') === false) {
+                _this.el.classList.add('fc-no-overlap');
+            }
+            _this.el.innerHTML = _this.renderSkeletonHtml();
+            _this.timeAxis = new TimeAxis(_this.context, _this.el.querySelector('thead .fc-time-area'), _this.el.querySelector('tbody .fc-time-area'));
+            _this.lane = new TimelineLane(_this.context, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl, _this.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl, _this.timeAxis);
+            context.calendar.registerInteractiveComponent(_this, {
+                el: _this.timeAxis.slats.el
+            });
+            return _this;
+        }
+        TimelineView.prototype.destroy = function () {
+            this.timeAxis.destroy();
+            this.lane.destroy();
+            _super.prototype.destroy.call(this);
+            this.calendar.unregisterInteractiveComponent(this);
+        };
+        TimelineView.prototype.renderSkeletonHtml = function () {
+            var theme = this.theme;
+            return "<table class=\"" + theme.getClass('tableGrid') + "\"> <thead class=\"fc-head\"> <tr> <td class=\"fc-time-area " + theme.getClass('widgetHeader') + "\"></td> </tr> </thead> <tbody class=\"fc-body\"> <tr> <td class=\"fc-time-area " + theme.getClass('widgetContent') + "\"></td> </tr> </tbody> </table>";
+        };
+        TimelineView.prototype.render = function (props) {
+            _super.prototype.render.call(this, props); // flags for updateSize, addScroll
+            this.timeAxis.receiveProps({
+                dateProfile: props.dateProfile
+            });
+            this.lane.receiveProps(__assign({}, props, { nextDayThreshold: this.nextDayThreshold }));
+        };
+        TimelineView.prototype.updateSize = function (isResize, totalHeight, isAuto) {
+            this.timeAxis.updateSize(isResize, totalHeight, isAuto);
+            this.lane.updateSize(isResize);
+        };
+        // Now Indicator
+        // ------------------------------------------------------------------------------------------
+        TimelineView.prototype.getNowIndicatorUnit = function (dateProfile) {
+            return this.timeAxis.getNowIndicatorUnit(dateProfile);
+        };
+        TimelineView.prototype.renderNowIndicator = function (date) {
+            this.timeAxis.renderNowIndicator(date);
+        };
+        TimelineView.prototype.unrenderNowIndicator = function () {
+            this.timeAxis.unrenderNowIndicator();
+        };
+        // Scroll System
+        // ------------------------------------------------------------------------------------------
+        TimelineView.prototype.computeDateScroll = function (duration) {
+            return this.timeAxis.computeDateScroll(duration);
+        };
+        TimelineView.prototype.applyScroll = function (scroll, isResize) {
+            _super.prototype.applyScroll.call(this, scroll, isResize); // will call applyDateScroll
+            // avoid updating stickyscroll too often
+            // TODO: repeat code as ResourceTimelineView::updateSize
+            var calendar = this.calendar;
+            if (isResize || calendar.isViewUpdated || calendar.isDatesUpdated || calendar.isEventsUpdated) {
+                this.timeAxis.updateStickyScrollers();
+            }
+        };
+        TimelineView.prototype.applyDateScroll = function (scroll) {
+            this.timeAxis.applyDateScroll(scroll);
+        };
+        TimelineView.prototype.queryScroll = function () {
+            var enhancedScroll = this.timeAxis.layout.bodyScroller.enhancedScroll;
+            return {
+                top: enhancedScroll.getScrollTop(),
+                left: enhancedScroll.getScrollLeft()
+            };
+        };
+        // Hit System
+        // ------------------------------------------------------------------------------------------
+        TimelineView.prototype.buildPositionCaches = function () {
+            this.timeAxis.slats.updateSize();
+        };
+        TimelineView.prototype.queryHit = function (positionLeft, positionTop, elWidth, elHeight) {
+            var slatHit = this.timeAxis.slats.positionToHit(positionLeft);
+            if (slatHit) {
+                return {
+                    component: this,
+                    dateSpan: slatHit.dateSpan,
+                    rect: {
+                        left: slatHit.left,
+                        right: slatHit.right,
+                        top: 0,
+                        bottom: elHeight
+                    },
+                    dayEl: slatHit.dayEl,
+                    layer: 0
+                };
+            }
+        };
+        return TimelineView;
+    }(core.View));
+
+    var main = core.createPlugin({
+        defaultView: 'timelineDay',
+        views: {
+            timeline: {
+                class: TimelineView,
+                eventResizableFromStart: true // how is this consumed for TimelineView tho?
+            },
+            timelineDay: {
+                type: 'timeline',
+                duration: { days: 1 }
+            },
+            timelineWeek: {
+                type: 'timeline',
+                duration: { weeks: 1 }
+            },
+            timelineMonth: {
+                type: 'timeline',
+                duration: { months: 1 }
+            },
+            timelineYear: {
+                type: 'timeline',
+                duration: { years: 1 }
+            }
+        }
+    });
+
+    exports.HeaderBodyLayout = HeaderBodyLayout;
+    exports.ScrollJoiner = ScrollJoiner;
+    exports.StickyScroller = StickyScroller;
+    exports.TimeAxis = TimeAxis;
+    exports.TimelineLane = TimelineLane;
+    exports.TimelineView = TimelineView;
+    exports.default = main;
+
+    Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/main.min.css b/AKPlan/static/AKPlan/fullcalendar/timeline/main.min.css
new file mode 100644
index 0000000000000000000000000000000000000000..df72784b230a20792e73e12bd5044a864697d760
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/main.min.css
@@ -0,0 +1 @@
+.fc-scroller-clip{overflow:hidden;position:relative}.fc-no-scrollbars{background:rgba(255,255,255,0)}.fc-no-scrollbars::-webkit-scrollbar{width:0;height:0}.fc-scroller-canvas{position:relative;box-sizing:border-box;min-height:100%}.fc-scroller-canvas>.fc-bg{z-index:1}.fc-scroller-canvas>.fc-content{z-index:2;position:relative;border-style:solid;border-width:0}.fc-scroller-canvas.fc-gutter-left>.fc-content{border-left-width:1px;margin-left:-1px}.fc-scroller-canvas.fc-gutter-right>.fc-content{border-right-width:1px;margin-right:-1px}.fc-scroller-canvas.fc-gutter-top>.fc-content{border-top-width:1px;margin-top:-1px}.fc-rtl .fc-timeline{direction:rtl}.fc-scrolled .fc-head .fc-scroller{z-index:2}.fc-timeline.fc-scrolled .fc-head .fc-scroller{box-shadow:0 3px 4px rgba(0,0,0,.075)}.fc-timeline .fc-body .fc-scroller{z-index:1}.fc-timeline .fc-scroller-canvas>div>div>table,.fc-timeline .fc-scroller-canvas>div>table{border-style:hidden}.fc-timeline .fc-scroller-canvas>.fc-content>.fc-rows>table{border-bottom-style:none}.fc-timeline td,.fc-timeline th{white-space:nowrap}.fc-timeline .fc-cell-content{overflow:hidden}.fc-timeline .fc-cell-text{display:inline-block;padding-left:4px;padding-right:4px}.fc-timeline th{vertical-align:middle}.fc-timeline .fc-head .fc-cell-content{padding-top:3px;padding-bottom:3px}.fc-timeline .fc-head .fc-time-area .fc-cell-content{overflow:visible}.fc-time-area col{min-width:2.2em}.fc-ltr .fc-time-area .fc-chrono th{text-align:left}.fc-rtl .fc-time-area .fc-chrono th{text-align:right}.fc-time-area .fc-slats{position:absolute;z-index:1;top:0;left:0;right:0;bottom:0}.fc-time-area .fc-slats table{height:100%}.fc-time-area .fc-slats .fc-minor{border-style:dotted}.fc-time-area .fc-slats td{border-width:0 1px}.fc-ltr .fc-time-area .fc-slats td{border-right-width:0}.fc-rtl .fc-time-area .fc-slats td{border-left-width:0}.fc-time-area .fc-bgevent-container,.fc-time-area .fc-highlight-container{position:absolute;z-index:2;top:0;bottom:0;width:0}.fc-ltr .fc-time-area .fc-bgevent-container,.fc-ltr .fc-time-area .fc-highlight-container,.fc-ltr .fc-time-area .fc-mirror-container{left:0}.fc-rtl .fc-time-area .fc-bgevent-container,.fc-rtl .fc-time-area .fc-highlight-container,.fc-rtl .fc-time-area .fc-mirror-container{right:0}.fc-time-area .fc-bgevent,.fc-time-area .fc-highlight{position:absolute;top:0;bottom:0}.fc-timeline .fc-now-indicator{z-index:3;top:0}.fc-time-area .fc-now-indicator-arrow{margin:0 -6px;border-width:6px 5px 0;border-left-color:transparent;border-right-color:transparent}.fc-time-area .fc-now-indicator-line{margin:0 -1px;bottom:0;border-left-width:1px}.fc-time-area .fc-event-container{position:relative;z-index:2;width:0}.fc-time-area .fc-mirror-container{position:absolute;z-index:3;top:0}.fc-time-area .fc-event-container{padding-bottom:8px;top:-1px}.fc-time-area tr:first-child .fc-event-container{top:0}.fc-no-overlap .fc-time-area .fc-event-container{padding-bottom:0;top:0}.fc-timeline-event{position:absolute;display:flex;border-radius:0;padding:2px 1px;margin-bottom:1px}.fc-no-overlap .fc-timeline-event{padding-top:5px;padding-bottom:5px;margin-bottom:0}.fc-ltr .fc-timeline-event{flex-direction:row;margin-right:1px}.fc-rtl .fc-timeline-event{margin-left:1px}.fc-timeline-event .fc-time-wrap{flex-shrink:0;min-width:0}.fc-timeline-event .fc-title-wrap{flex-grow:1;min-width:0}.fc-timeline-event .fc-time,.fc-timeline-event .fc-title{display:inline-block;vertical-align:top;max-width:100%;padding:0 2px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;white-space:nowrap;overflow:hidden}.fc-timeline-event .fc-time{font-weight:700}.fc-timeline-event.fc-not-end:after,.fc-timeline-event.fc-not-start:before{content:"";align-self:center;width:0;height:0;margin:0 1px;border:5px solid #000;border-top-color:transparent;border-bottom-color:transparent;opacity:.5}.fc-ltr .fc-timeline-event.fc-not-start:before,.fc-rtl .fc-timeline-event.fc-not-end:after{border-left:0}.fc-ltr .fc-timeline-event.fc-not-end:after,.fc-rtl .fc-timeline-event.fc-not-start:before{border-right:0}
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/main.min.js b/AKPlan/static/AKPlan/fullcalendar/timeline/main.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..ff54d9735fcff1b64d31896874445f4923eae9f3
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/main.min.js
@@ -0,0 +1,6 @@
+/*!
+FullCalendar Timeline Plugin v4.3.0
+Docs & License: https://fullcalendar.io/scheduler
+(c) 2019 Adam Shaw
+*/
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@fullcalendar/core")):"function"==typeof define&&define.amd?define(["exports","@fullcalendar/core"],t):t((e=e||self).FullCalendarTimeline={},e.FullCalendar)}(this,function(e,t){"use strict";var r=function(e,t){return(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])})(e,t)};function n(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var i,o=function(){return(o=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}).apply(this,arguments)},l=function(){function e(){this.gutters={},this.el=t.htmlToElement('<div class="fc-scroller-canvas"> <div class="fc-content"></div> <div class="fc-bg"></div> </div>'),this.contentEl=this.el.querySelector(".fc-content"),this.bgEl=this.el.querySelector(".fc-bg")}return e.prototype.setGutters=function(e){e?o(this.gutters,e):this.gutters={},this.updateSize()},e.prototype.setWidth=function(e){this.width=e,this.updateSize()},e.prototype.setMinWidth=function(e){this.minWidth=e,this.updateSize()},e.prototype.clearWidth=function(){this.width=null,this.minWidth=null,this.updateSize()},e.prototype.updateSize=function(){var e=this.gutters,r=this.el;t.forceClassName(r,"fc-gutter-left",e.left),t.forceClassName(r,"fc-gutter-right",e.right),t.forceClassName(r,"fc-gutter-top",e.top),t.forceClassName(r,"fc-gutter-bottom",e.bottom),t.applyStyle(r,{paddingLeft:e.left||"",paddingRight:e.right||"",paddingTop:e.top||"",paddingBottom:e.bottom||"",width:null!=this.width?this.width+(e.left||0)+(e.right||0):"",minWidth:null!=this.minWidth?this.minWidth+(e.left||0)+(e.right||0):""}),t.applyStyle(this.bgEl,{left:e.left||"",right:e.right||"",top:e.top||"",bottom:e.bottom||""})},e}(),s=function(e){function r(r,n){var i=e.call(this,r,n)||this;return i.reportScroll=function(){i.isScrolling||i.reportScrollStart(),i.trigger("scroll"),i.isMoving=!0,i.requestMovingEnd()},i.reportScrollStart=function(){i.isScrolling||(i.isScrolling=!0,i.trigger("scrollStart",i.isTouching))},i.reportTouchStart=function(){i.isTouching=!0},i.reportTouchEnd=function(){i.isTouching&&(i.isTouching=!1,i.isTouchScrollEnabled&&i.unbindPreventTouchScroll(),i.isMoving||i.reportScrollEnd())},i.isScrolling=!1,i.isTouching=!1,i.isMoving=!1,i.isTouchScrollEnabled=!0,i.requestMovingEnd=t.debounce(i.reportMovingEnd,500),i.canvas=new l,i.el.appendChild(i.canvas.el),i.applyOverflow(),i.bindHandlers(),i}return n(r,e),r.prototype.destroy=function(){e.prototype.destroy.call(this),this.unbindHandlers()},r.prototype.disableTouchScroll=function(){this.isTouchScrollEnabled=!1,this.bindPreventTouchScroll()},r.prototype.enableTouchScroll=function(){this.isTouchScrollEnabled=!0,this.isTouching||this.unbindPreventTouchScroll()},r.prototype.bindPreventTouchScroll=function(){this.preventTouchScrollHandler||this.el.addEventListener("touchmove",this.preventTouchScrollHandler=t.preventDefault)},r.prototype.unbindPreventTouchScroll=function(){this.preventTouchScrollHandler&&(this.el.removeEventListener("touchmove",this.preventTouchScrollHandler),this.preventTouchScrollHandler=null)},r.prototype.bindHandlers=function(){this.el.addEventListener("scroll",this.reportScroll),this.el.addEventListener("touchstart",this.reportTouchStart,{passive:!0}),this.el.addEventListener("touchend",this.reportTouchEnd)},r.prototype.unbindHandlers=function(){this.el.removeEventListener("scroll",this.reportScroll),this.el.removeEventListener("touchstart",this.reportTouchStart,{passive:!0}),this.el.removeEventListener("touchend",this.reportTouchEnd)},r.prototype.reportMovingEnd=function(){this.isMoving=!1,this.isTouching||this.reportScrollEnd()},r.prototype.reportScrollEnd=function(){this.isScrolling&&(this.trigger("scrollEnd"),this.isScrolling=!1)},r.prototype.getScrollLeft=function(){var e=this.el,t=window.getComputedStyle(e).direction,r=e.scrollLeft;if("rtl"===t)switch(a()){case"positive":r=r+e.clientWidth-e.scrollWidth;break;case"reverse":r=-r}return r},r.prototype.setScrollLeft=function(e){var t=this.el;if("rtl"===window.getComputedStyle(t).direction)switch(a()){case"positive":e=e-t.clientWidth+t.scrollWidth;break;case"reverse":e=-e}t.scrollLeft=e},r.prototype.getScrollFromLeft=function(){var e=this.el,t=window.getComputedStyle(e).direction,r=e.scrollLeft;if("rtl"===t)switch(a()){case"negative":r=r-e.clientWidth+e.scrollWidth;break;case"reverse":r=-r-e.clientWidth+e.scrollWidth}return r},r}(t.ScrollComponent);function a(){return i||(i=function(){var e,r=t.htmlToElement('<div style=" position: absolute; top: -1000px; width: 1px; height: 1px; overflow: scroll; direction: rtl; font-size: 100px; ">A</div>');document.body.appendChild(r),r.scrollLeft>0?e="positive":(r.scrollLeft=1,e=r.scrollLeft>0?"reverse":"negative");return t.removeElement(r),e}())}t.EmitterMixin.mixInto(s);var c=function(){function e(e,r,n){this.isHScrollbarsClipped=!1,this.isVScrollbarsClipped=!1,"clipped-scroll"===e&&(e="scroll",this.isHScrollbarsClipped=!0),"clipped-scroll"===r&&(r="scroll",this.isVScrollbarsClipped=!0),this.enhancedScroll=new s(e,r),n.appendChild(this.el=t.createElement("div",{className:"fc-scroller-clip"})),this.el.appendChild(this.enhancedScroll.el)}return e.prototype.destroy=function(){t.removeElement(this.el)},e.prototype.updateSize=function(){var e=this.enhancedScroll,r=e.el,n=t.computeEdges(r),i={marginLeft:0,marginRight:0,marginTop:0,marginBottom:0};this.isVScrollbarsClipped&&(i.marginLeft=-n.scrollbarLeft,i.marginRight=-n.scrollbarRight),this.isHScrollbarsClipped&&(i.marginBottom=-n.scrollbarBottom),t.applyStyle(r,i),!this.isHScrollbarsClipped&&"hidden"!==e.overflowX||!this.isVScrollbarsClipped&&"hidden"!==e.overflowY||n.scrollbarLeft||n.scrollbarRight||n.scrollbarBottom?r.classList.remove("fc-no-scrollbars"):r.classList.add("fc-no-scrollbars")},e.prototype.setHeight=function(e){this.enhancedScroll.setHeight(e)},e.prototype.getScrollbarWidths=function(){var e=this.enhancedScroll.getScrollbarWidths();return this.isVScrollbarsClipped&&(e.left=0,e.right=0),this.isHScrollbarsClipped&&(e.bottom=0),e},e}(),d=function(){function e(e,t){this.axis=e,this.scrollers=t;for(var r=0,n=this.scrollers;r<n.length;r++){var i=n[r];this.initScroller(i)}}return e.prototype.initScroller=function(e){var t=this,r=e.enhancedScroll,n=function(){t.assignMasterScroller(e)};"wheel mousewheel DomMouseScroll MozMousePixelScroll".split(" ").forEach(function(e){r.el.addEventListener(e,n)}),r.on("scrollStart",function(){t.masterScroller||t.assignMasterScroller(e)}).on("scroll",function(){if(e===t.masterScroller)for(var n=0,i=t.scrollers;n<i.length;n++){var o=i[n];if(o!==e)switch(t.axis){case"horizontal":o.enhancedScroll.el.scrollLeft=r.el.scrollLeft;break;case"vertical":o.enhancedScroll.setScrollTop(r.getScrollTop())}}}).on("scrollEnd",function(){e===t.masterScroller&&t.unassignMasterScroller()})},e.prototype.assignMasterScroller=function(e){this.unassignMasterScroller(),this.masterScroller=e;for(var t=0,r=this.scrollers;t<r.length;t++){var n=r[t];n!==e&&n.enhancedScroll.disableTouchScroll()}},e.prototype.unassignMasterScroller=function(){if(this.masterScroller){for(var e=0,t=this.scrollers;e<t.length;e++){t[e].enhancedScroll.enableTouchScroll()}this.masterScroller=null}},e.prototype.update=function(){for(var e,t,r=this.scrollers.map(function(e){return e.getScrollbarWidths()}),n=0,i=0,o=0,l=0,s=0,a=r;s<a.length;s++)e=a[s],n=Math.max(n,e.left),i=Math.max(i,e.right),o=Math.max(o,e.top),l=Math.max(l,e.bottom);for(t=0;t<this.scrollers.length;t++){var c=this.scrollers[t];e=r[t],c.enhancedScroll.canvas.setGutters("horizontal"===this.axis?{left:n-e.left,right:i-e.right}:{top:o-e.top,bottom:l-e.bottom})}},e}(),h=function(){function e(e,t,r){this.headerScroller=new c("clipped-scroll","hidden",e),this.bodyScroller=new c("auto",r,t),this.scrollJoiner=new d("horizontal",[this.headerScroller,this.bodyScroller])}return e.prototype.destroy=function(){this.headerScroller.destroy(),this.bodyScroller.destroy()},e.prototype.setHeight=function(e,t){var r;r=t?"auto":e-this.queryHeadHeight(),this.bodyScroller.setHeight(r),this.headerScroller.updateSize(),this.bodyScroller.updateSize(),this.scrollJoiner.update()},e.prototype.queryHeadHeight=function(){return this.headerScroller.enhancedScroll.canvas.contentEl.getBoundingClientRect().height},e}(),u=function(e){function r(r,n){var i=e.call(this,r)||this;return n.appendChild(i.tableEl=t.createElement("table",{className:i.theme.getClass("tableGrid")})),i}return n(r,e),r.prototype.destroy=function(){t.removeElement(this.tableEl),e.prototype.destroy.call(this)},r.prototype.render=function(e){this.renderDates(e.tDateProfile)},r.prototype.renderDates=function(e){for(var r=this.dateEnv,n=this.theme,i=e.cellRows,o=i[i.length-1],l=t.asRoughMs(e.labelInterval)>t.asRoughMs(e.slotDuration),s=t.isSingleDay(e.slotDuration),a="<colgroup>",c=e.slotCnt-1;c>=0;c--)a+="<col/>";a+="</colgroup>",a+="<tbody>";for(var d=0,h=i;d<h.length;d++){var u=h[d];a+="<tr"+(l&&u===o?' class="fc-chrono"':"")+">";for(var p=0,f=u;p<f.length;p++){var g=f[p],m=[n.getClass("widgetHeader")];g.isWeekStart&&m.push("fc-em-cell"),s&&(m=m.concat(t.getDayClasses(g.date,this.props.dateProfile,this.context,!0))),a+='<th class="'+m.join(" ")+'" data-date="'+r.formatIso(g.date,{omitTime:!e.isTimeScale,omitTimeZoneOffset:!0})+'"'+(g.colspan>1?' colspan="'+g.colspan+'"':"")+'><div class="fc-cell-content">'+g.spanHtml+"</div></th>"}a+="</tr>"}a+="</tbody>",this.tableEl.innerHTML=a,this.slatColEls=t.findElements(this.tableEl,"col"),this.innerEls=t.findElements(this.tableEl.querySelector("tr:last-child"),"th .fc-cell-text"),t.findElements(this.tableEl.querySelectorAll("tr:not(:last-child)"),"th .fc-cell-text").forEach(function(e){e.classList.add("fc-sticky")})},r}(t.Component),p=function(e){function r(r,n){var i=e.call(this,r)||this;return n.appendChild(i.el=t.createElement("div",{className:"fc-slats"})),i}return n(r,e),r.prototype.destroy=function(){t.removeElement(this.el),e.prototype.destroy.call(this)},r.prototype.render=function(e){this.renderDates(e.tDateProfile)},r.prototype.renderDates=function(e){for(var r=this.theme,n=this.view,i=this.dateEnv,o=e.slotDates,l=e.isWeekStarts,s='<table class="'+r.getClass("tableGrid")+'"><colgroup>',a=0;a<o.length;a++)s+="<col/>";s+="</colgroup>",s+="<tbody><tr>";for(a=0;a<o.length;a++)s+=this.slatCellHtml(o[a],l[a],e);s+="</tr></tbody></table>",this.el.innerHTML=s,this.slatColEls=t.findElements(this.el,"col"),this.slatEls=t.findElements(this.el,"td");for(a=0;a<o.length;a++)n.publiclyTrigger("dayRender",[{date:i.toDate(o[a]),el:this.slatEls[a],view:n}]);this.outerCoordCache=new t.PositionCache(this.el,this.slatEls,!0,!1),this.innerCoordCache=new t.PositionCache(this.el,t.findChildren(this.slatEls,"div"),!0,!1)},r.prototype.slatCellHtml=function(e,r,n){var i,o=this.theme,l=this.dateEnv;return n.isTimeScale?(i=[]).push(t.isInt(l.countDurationsBetween(n.normalizedRange.start,e,n.labelInterval))?"fc-major":"fc-minor"):(i=t.getDayClasses(e,this.props.dateProfile,this.context)).push("fc-day"),i.unshift(o.getClass("widgetContent")),r&&i.push("fc-em-cell"),'<td class="'+i.join(" ")+'" data-date="'+l.formatIso(e,{omitTime:!n.isTimeScale,omitTimeZoneOffset:!0})+'"><div></div></td>'},r.prototype.updateSize=function(){this.outerCoordCache.build(),this.innerCoordCache.build()},r.prototype.positionToHit=function(e){var r=this.outerCoordCache,n=this.props.tDateProfile,i=r.leftToIndex(e);if(null!=i){var o=r.getWidth(i),l=this.isRtl?(r.rights[i]-e)/o:(e-r.lefts[i])/o,s=Math.floor(l*n.snapsPerSlot),a=this.dateEnv.add(n.slotDates[i],t.multiplyDuration(n.snapDuration,s));return{dateSpan:{range:{start:a,end:this.dateEnv.add(a,n.snapDuration)},allDay:!this.props.tDateProfile.isTimeScale},dayEl:this.slatColEls[i],left:r.lefts[i],right:r.rights[i]}}return null},r}(t.Component),f=18,g=6,m=200;t.config.MAX_TIMELINE_SLOTS=1e3;var v=[{years:1},{months:1},{days:1},{hours:1},{minutes:30},{minutes:15},{minutes:10},{minutes:5},{minutes:1},{seconds:30},{seconds:15},{seconds:10},{seconds:5},{seconds:1},{milliseconds:500},{milliseconds:100},{milliseconds:10},{milliseconds:1}];function y(e,r){var n=r.dateEnv,i={labelInterval:E(r,"slotLabelInterval"),slotDuration:E(r,"slotDuration")};!function(e,r,n){var i=r.currentRange;if(e.labelInterval){var o=n.countDurationsBetween(i.start,i.end,e.labelInterval);o>t.config.MAX_TIMELINE_SLOTS&&(console.warn("slotLabelInterval results in too many cells"),e.labelInterval=null)}if(e.slotDuration){var l=n.countDurationsBetween(i.start,i.end,e.slotDuration);l>t.config.MAX_TIMELINE_SLOTS&&(console.warn("slotDuration results in too many cells"),e.slotDuration=null)}if(e.labelInterval&&e.slotDuration){var s=t.wholeDivideDurations(e.labelInterval,e.slotDuration);(null===s||s<1)&&(console.warn("slotLabelInterval must be a multiple of slotDuration"),e.slotDuration=null)}}(i,e,n),D(i,e,n),function(e,r,n){var i=r.currentRange,o=e.slotDuration;if(!o){for(var l=D(e,r,n),s=0,a=v;s<a.length;s++){var c=a[s],d=t.createDuration(c),h=t.wholeDivideDurations(l,d);if(null!==h&&h>1&&h<=g){o=d;break}}if(o){var u=n.countDurationsBetween(i.start,i.end,o);u>m&&(o=null)}o||(o=l),e.slotDuration=o}}(i,e,n);var o=r.opt("slotLabelFormat"),l=Array.isArray(o)?o:null!=o?[o]:function(e,r,n,i){var o,l,s=e.labelInterval,a=t.greatestDurationDenominator(s).unit,c=i.opt("weekNumbers"),d=o=l=null;"week"!==a||c||(a="day");switch(a){case"year":d={year:"numeric"};break;case"month":w("years",r,n)>1&&(d={year:"numeric"}),o={month:"short"};break;case"week":w("years",r,n)>1&&(d={year:"numeric"}),o={week:"narrow"};break;case"day":w("years",r,n)>1?d={year:"numeric",month:"long"}:w("months",r,n)>1&&(d={month:"long"}),c&&(o={week:"short"}),l={weekday:"narrow",day:"numeric"};break;case"hour":c&&(d={week:"short"}),w("days",r,n)>1&&(o={weekday:"short",day:"numeric",month:"numeric",omitCommas:!0}),l={hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"short"};break;case"minute":t.asRoughMinutes(s)/60>=g?(d={hour:"numeric",meridiem:"short"},o=function(e){return":"+t.padStart(e.date.minute,2)}):d={hour:"numeric",minute:"numeric",meridiem:"short"};break;case"second":t.asRoughSeconds(s)/60>=g?(d={hour:"numeric",minute:"2-digit",meridiem:"lowercase"},o=function(e){return":"+t.padStart(e.date.second,2)}):d={hour:"numeric",minute:"2-digit",second:"2-digit",meridiem:"lowercase"};break;case"millisecond":d={hour:"numeric",minute:"2-digit",second:"2-digit",meridiem:"lowercase"},o=function(e){return"."+t.padStart(e.millisecond,3)}}return[].concat(d||[],o||[],l||[])}(i,e,n,r);i.headerFormats=l.map(function(e){return t.createFormatter(e)}),i.isTimeScale=Boolean(i.slotDuration.milliseconds);var s=null;if(!i.isTimeScale){var a=t.greatestDurationDenominator(i.slotDuration).unit;/year|month|week/.test(a)&&(s=a)}i.largeUnit=s,i.emphasizeWeeks=t.isSingleDay(i.slotDuration)&&w("weeks",e,n)>=2&&!r.opt("businessHours");var c,d,h=r.opt("snapDuration");h&&(c=t.createDuration(h),d=t.wholeDivideDurations(i.slotDuration,c)),null==d&&(c=i.slotDuration,d=1),i.snapDuration=c,i.snapsPerSlot=d;var u=t.asRoughMs(e.maxTime)-t.asRoughMs(e.minTime),p=S(e.renderRange.start,i,n),f=S(e.renderRange.end,i,n);i.isTimeScale&&(p=n.add(p,e.minTime),f=n.add(t.addDays(f,-1),e.maxTime)),i.timeWindowMs=u,i.normalizedRange={start:p,end:f};for(var y=[],T=p;T<f;)b(T,i,e,r)&&y.push(T),T=n.add(T,i.slotDuration);i.slotDates=y;var R=-1,x=0,M=[],z=[];for(T=p;T<f;)b(T,i,e,r)?(R++,M.push(R),z.push(x)):M.push(R+.5),T=n.add(T,i.snapDuration),x++;return i.snapDiffToIndex=M,i.snapIndexToDiff=z,i.snapCnt=R+1,i.slotCnt=i.snapCnt/i.snapsPerSlot,i.isWeekStarts=function(e,t){for(var r=e.slotDates,n=e.emphasizeWeeks,i=null,o=[],l=0,s=r;l<s.length;l++){var a=s[l],c=t.computeWeekNumber(a),d=n&&null!==i&&i!==c;i=c,o.push(d)}return o}(i,n),i.cellRows=function(e,r,n){for(var i=e.slotDates,o=e.headerFormats,l=o.map(function(e){return[]}),s=o.map(function(e){return e.getLargestUnit?e.getLargestUnit():null}),a=0;a<i.length;a++)for(var c=i[a],d=e.isWeekStarts[a],h=0;h<o.length;h++){var u=o[h],p=l[h],f=p[p.length-1],g=o.length>1&&h<o.length-1,m=null;if(g){var v=r.format(c,u);f&&f.text===v?f.colspan+=1:m=C(c,v,s[h],n)}else if(!f||t.isInt(r.countDurationsBetween(e.normalizedRange.start,c,e.labelInterval))){var v=r.format(c,u);m=C(c,v,s[h],n)}else f.colspan+=1;m&&(m.weekStart=d,p.push(m))}return l}(i,n,r),i}function S(e,r,n){var i=e;return r.isTimeScale||(i=t.startOfDay(i),r.largeUnit&&(i=n.startOf(i,r.largeUnit))),i}function b(e,r,n,i){if(i.dateProfileGenerator.isHiddenDay(e))return!1;if(r.isTimeScale){var o=t.startOfDay(e),l=e.valueOf()-o.valueOf()-t.asRoughMs(n.minTime);return(l=(l%864e5+864e5)%864e5)<r.timeWindowMs}return!0}function E(e,r){var n=e.opt(r);if(null!=n)return t.createDuration(n)}function D(e,r,n){var i=r.currentRange,o=e.labelInterval;if(!o){var l=void 0;if(e.slotDuration){for(var s=0,a=v;s<a.length;s++){l=a[s];var c=t.createDuration(l),d=t.wholeDivideDurations(c,e.slotDuration);if(null!==d&&d<=g){o=c;break}}o||(o=e.slotDuration)}else for(var h=0,u=v;h<u.length;h++){if(l=u[h],o=t.createDuration(l),n.countDurationsBetween(i.start,i.end,o)>=f)break}e.labelInterval=o}return o}function w(e,r,n){var i=r.currentRange,o=null;return"years"===e?o=n.diffWholeYears(i.start,i.end):"months"===e?o=n.diffWholeMonths(i.start,i.end):"weeks"===e?o=n.diffWholeMonths(i.start,i.end):"days"===e&&(o=t.diffWholeDays(i.start,i.end)),o||0}function C(e,r,n,i){return{text:r,spanHtml:t.buildGotoAnchorHtml(i,{date:e,type:n,forceOff:!n},{class:"fc-cell-text"},t.htmlEscape(r)),date:e,colspan:1,isWeekStart:!1}}var T,R=function(){function e(e,t){this.headParent=e,this.bodyParent=t}return e.prototype.render=function(e,r){var n=r?{right:-e}:{left:e};this.headParent.appendChild(this.arrowEl=t.createElement("div",{className:"fc-now-indicator fc-now-indicator-arrow",style:n})),this.bodyParent.appendChild(this.lineEl=t.createElement("div",{className:"fc-now-indicator fc-now-indicator-line",style:n}))},e.prototype.unrender=function(){this.arrowEl&&t.removeElement(this.arrowEl),this.lineEl&&t.removeElement(this.lineEl)},e}(),x=-1!==(T=t.htmlToElement('<div style="position:-webkit-sticky;position:sticky"></div>').style.position).indexOf("sticky")?T:null,M=/Edge/.test(navigator.userAgent),z="-webkit-sticky"===x,k="fc-sticky",P=function(){function e(e,r,n){var i=this;this.usingRelative=null,this.updateSize=function(){var e=Array.prototype.slice.call(i.scroller.canvas.el.querySelectorAll("."+k)),r=i.queryElGeoms(e),n=i.scroller.el.clientWidth;i.usingRelative?function(e,r,n){e.forEach(function(e,i){var o=r[i].naturalBound;t.applyStyle(e,{position:"relative",left:n[i].left-o.left,top:n[i].top-o.top})})}(e,r,i.computeElDestinations(r,n)):function(e,r,n){e.forEach(function(e,i){var o=0;"center"===r[i].intendedTextAlign&&(o=(n-r[i].elWidth)/2,"center"===r[i].computedTextAlign&&(e.setAttribute("data-sticky-center",""),e.parentNode.style.textAlign="left")),t.applyStyle(e,{position:x,left:o,right:0,top:0})})}(e,r,n)},this.scroller=e,this.usingRelative=!x||M&&r||(M||z)&&n,this.usingRelative&&e.on("scrollEnd",this.updateSize)}return e.prototype.destroy=function(){this.scroller.off("scrollEnd",this.updateSize)},e.prototype.queryElGeoms=function(e){for(var r=this.scroller.canvas.el.getBoundingClientRect(),n=[],i=0,o=e;i<o.length;i++){var l=o[i],s=t.translateRect(l.parentNode.getBoundingClientRect(),-r.left,-r.top),a=l.getBoundingClientRect(),c=window.getComputedStyle(l),d=window.getComputedStyle(l.parentNode).textAlign,h=d,u=null;"sticky"!==c.position&&(u=t.translateRect(a,-r.left-(parseFloat(c.left)||0),-r.top-(parseFloat(c.top)||0))),l.hasAttribute("data-sticky-center")&&(h="center"),n.push({parentBound:s,naturalBound:u,elWidth:a.width,elHeight:a.height,computedTextAlign:d,intendedTextAlign:h})}return n},e.prototype.computeElDestinations=function(e,t){var r=this.scroller.getScrollFromLeft(),n=this.scroller.getScrollTop(),i=r+t;return e.map(function(e){var t,o,l=e.elWidth,s=e.elHeight,a=e.parentBound,c=e.naturalBound;switch(e.intendedTextAlign){case"left":t=r;break;case"right":t=i-l;break;case"center":t=(r+i)/2-l/2}return t=Math.min(t,a.right-l),t=Math.max(t,a.left),o=n,o=Math.min(o,a.bottom-s),{left:t,top:o=Math.max(o,c.top)}})},e}();var I=function(e){function r(t,r,n){var i=e.call(this,t)||this,o=i.layout=new h(r,n,"auto"),l=o.headerScroller.enhancedScroll,s=o.bodyScroller.enhancedScroll;return i.headStickyScroller=new P(l,i.isRtl,!1),i.bodyStickyScroller=new P(s,i.isRtl,!1),i.header=new u(t,l.canvas.contentEl),i.slats=new p(t,s.canvas.bgEl),i.nowIndicator=new R(l.canvas.el,s.canvas.el),i}return n(r,e),r.prototype.destroy=function(){this.layout.destroy(),this.header.destroy(),this.slats.destroy(),this.nowIndicator.unrender(),this.headStickyScroller.destroy(),this.bodyStickyScroller.destroy(),e.prototype.destroy.call(this)},r.prototype.render=function(e){var t=this.tDateProfile=y(e.dateProfile,this.view);this.header.receiveProps({dateProfile:e.dateProfile,tDateProfile:t}),this.slats.receiveProps({dateProfile:e.dateProfile,tDateProfile:t})},r.prototype.getNowIndicatorUnit=function(e){var r=this.tDateProfile=y(e,this.view);if(r.isTimeScale)return t.greatestDurationDenominator(r.slotDuration).unit},r.prototype.renderNowIndicator=function(e){t.rangeContainsMarker(this.tDateProfile.normalizedRange,e)&&this.nowIndicator.render(this.dateToCoord(e),this.isRtl)},r.prototype.unrenderNowIndicator=function(){this.nowIndicator.unrender()},r.prototype.updateSize=function(e,t,r){this.applySlotWidth(this.computeSlotWidth()),this.layout.setHeight(t,r),this.slats.updateSize()},r.prototype.updateStickyScrollers=function(){this.headStickyScroller.updateSize(),this.bodyStickyScroller.updateSize()},r.prototype.computeSlotWidth=function(){var e=this.opt("slotWidth")||"";return""===e&&(e=this.computeDefaultSlotWidth(this.tDateProfile)),e},r.prototype.computeDefaultSlotWidth=function(e){var r=0;this.header.innerEls.forEach(function(e,t){r=Math.max(r,e.getBoundingClientRect().width)});var n=Math.ceil(r)+1,i=t.wholeDivideDurations(e.labelInterval,e.slotDuration),o=Math.ceil(n/i),l=window.getComputedStyle(this.header.slatColEls[0]).minWidth;return l&&(l=parseInt(l,10))&&(o=Math.max(o,l)),o},r.prototype.applySlotWidth=function(e){var t=this.layout,r=this.tDateProfile,n="",i="",o="";if(""!==e){n=(e=Math.round(e))*r.slotDates.length,i="",o=e;var l=t.bodyScroller.enhancedScroll.getClientWidth();l>n&&(i=l,n="",o=Math.floor(l/r.slotDates.length))}t.headerScroller.enhancedScroll.canvas.setWidth(n),t.headerScroller.enhancedScroll.canvas.setMinWidth(i),t.bodyScroller.enhancedScroll.canvas.setWidth(n),t.bodyScroller.enhancedScroll.canvas.setMinWidth(i),""!==o&&this.header.slatColEls.slice(0,-1).concat(this.slats.slatColEls.slice(0,-1)).forEach(function(e){e.style.width=o+"px"})},r.prototype.computeDateSnapCoverage=function(e){var r=this.dateEnv,n=this.tDateProfile,i=r.countDurationsBetween(n.normalizedRange.start,e,n.snapDuration);if(i<0)return 0;if(i>=n.snapDiffToIndex.length)return n.snapCnt;var o=Math.floor(i),l=n.snapDiffToIndex[o];return t.isInt(l)?l+=i-o:l=Math.ceil(l),l},r.prototype.dateToCoord=function(e){var t=this.tDateProfile,r=this.computeDateSnapCoverage(e)/t.snapsPerSlot,n=Math.floor(r),i=r-(n=Math.min(n,t.slotCnt-1)),o=this.slats,l=o.innerCoordCache,s=o.outerCoordCache;return this.isRtl?s.rights[n]-l.getWidth(n)*i-s.originClientRect.width:s.lefts[n]+l.getWidth(n)*i},r.prototype.rangeToCoords=function(e){return this.isRtl?{right:this.dateToCoord(e.start),left:this.dateToCoord(e.end)}:{left:this.dateToCoord(e.start),right:this.dateToCoord(e.end)}},r.prototype.computeDateScroll=function(e){var r=this.dateEnv,n=this.props.dateProfile,i=0;return n&&(i=this.dateToCoord(r.add(t.startOfDay(n.activeRange.start),e)),!this.isRtl&&i&&(i+=1)),{left:i}},r.prototype.queryDateScroll=function(){return{left:this.layout.bodyScroller.enhancedScroll.getScrollLeft()}},r.prototype.applyDateScroll=function(e){this.layout.bodyScroller.enhancedScroll.setScrollLeft(e.left||0),this.layout.headerScroller.enhancedScroll.setScrollLeft(e.left||0)},r}(t.Component),H=function(e){function r(t,r,n){var i=e.call(this,t)||this;return i.masterContainerEl=r,i.timeAxis=n,i}return n(r,e),r.prototype.renderSegHtml=function(e,r){var n=this.context.view,i=e.eventRange,o=i.def,l=i.ui,s=n.computeEventDraggable(o,l),a=e.isStart&&n.computeEventStartResizable(o,l),c=e.isEnd&&n.computeEventEndResizable(o,l),d=this.getSegClasses(e,s,a||c,r);d.unshift("fc-timeline-event","fc-h-event");var h=this.getTimeText(i);return'<a class="'+d.join(" ")+'" style="'+t.cssToStr(this.getSkinCss(l))+'"'+(o.url?' href="'+t.htmlEscape(o.url)+'"':"")+">"+(h?'<span class="fc-time-wrap"><span class="fc-time">'+t.htmlEscape(h)+"</span></span>":"")+'<span class="fc-title-wrap"><span class="fc-title fc-sticky">'+(o.title?t.htmlEscape(o.title):"&nbsp;")+"</span></span>"+(a?'<div class="fc-resizer fc-start-resizer"></div>':"")+(c?'<div class="fc-resizer fc-end-resizer"></div>':"")+"</a>"},r.prototype.computeDisplayEventTime=function(){return!this.timeAxis.tDateProfile.isTimeScale},r.prototype.computeDisplayEventEnd=function(){return!1},r.prototype.computeEventTimeFormat=function(){return{hour:"numeric",minute:"2-digit",omitZeroMinute:!0,meridiem:"narrow"}},r.prototype.attachSegs=function(e,r){if(!this.el&&this.masterContainerEl&&(this.el=t.createElement("div",{className:"fc-event-container"}),r&&this.el.classList.add("fc-mirror-container"),this.masterContainerEl.appendChild(this.el)),this.el)for(var n=0,i=e;n<i.length;n++){var o=i[n];this.el.appendChild(o.el)}},r.prototype.detachSegs=function(e){for(var r=0,n=e;r<n.length;r++){var i=n[r];t.removeElement(i.el)}},r.prototype.computeSegSizes=function(e){for(var r=this.timeAxis,n=0,i=e;n<i.length;n++){var o=i[n],l=r.rangeToCoords(o);t.applyStyle(o.el,{left:o.left=l.left,right:-(o.right=l.right)})}},r.prototype.assignSegSizes=function(e){if(this.el){for(var r=0,n=e;r<n.length;r++){(s=n[r]).height=t.computeHeightAndMargins(s.el)}this.buildSegLevels(e);var i=L(e);t.applyStyleProp(this.el,"height",i);for(var o=0,l=e;o<l.length;o++){var s=l[o];t.applyStyleProp(s.el,"top",s.top)}}},r.prototype.buildSegLevels=function(e){for(var t=[],r=0,n=e=this.sortEventSegs(e);r<n.length;r++){var i=n[r];i.above=[];for(var o=0;o<t.length;){for(var l=!1,s=0,a=t[o];s<a.length;s++){var c=a[s];A(i,c)&&(i.above.push(c),l=!0)}if(!l)break;o+=1}for((t[o]||(t[o]=[])).push(i),o+=1;o<t.length;){for(var d=0,h=t[o];d<h.length;d++){var u=h[d];A(i,u)&&u.above.push(i)}o+=1}}return t},r}(t.FgEventRenderer);function L(e){for(var t=0,r=0,n=e;r<n.length;r++){var i=n[r];t=Math.max(t,W(i))}return t}function W(e){return null==e.top&&(e.top=L(e.above)),e.top+e.height}function A(e,t){return e.left<t.right&&e.right>t.left}var B=function(e){function r(t,r,n){var i=e.call(this,t)||this;return i.masterContainerEl=r,i.timeAxis=n,i}return n(r,e),r.prototype.attachSegs=function(e,r){if(r.length){var n=void 0;n="businessHours"===e?"bgevent":e.toLowerCase();var i=t.createElement("div",{className:"fc-"+n+"-container"});this.masterContainerEl.appendChild(i);for(var o=0,l=r;o<l.length;o++){var s=l[o];i.appendChild(s.el)}return[i]}},r.prototype.computeSegSizes=function(e){for(var t=this.timeAxis,r=0,n=e;r<n.length;r++){var i=n[r],o=t.rangeToCoords(i);i.left=o.left,i.right=o.right}},r.prototype.assignSegSizes=function(e){for(var r=0,n=e;r<n.length;r++){var i=n[r];t.applyStyle(i.el,{left:i.left,right:-i.right})}},r}(t.FillRenderer),O=function(e){function r(r,n,i,o){var l=e.call(this,r,i)||this;l.slicer=new N,l.renderEventDrag=t.memoizeRendering(l._renderEventDrag,l._unrenderEventDrag),l.renderEventResize=t.memoizeRendering(l._renderEventResize,l._unrenderEventResize);var s=l.fillRenderer=new B(r,i,o),a=l.eventRenderer=new H(r,n,o);return l.mirrorRenderer=new H(r,n,o),l.renderBusinessHours=t.memoizeRendering(s.renderSegs.bind(s,"businessHours"),s.unrender.bind(s,"businessHours")),l.renderDateSelection=t.memoizeRendering(s.renderSegs.bind(s,"highlight"),s.unrender.bind(s,"highlight")),l.renderBgEvents=t.memoizeRendering(s.renderSegs.bind(s,"bgEvent"),s.unrender.bind(s,"bgEvent")),l.renderFgEvents=t.memoizeRendering(a.renderSegs.bind(a),a.unrender.bind(a)),l.renderEventSelection=t.memoizeRendering(a.selectByInstanceId.bind(a),a.unselectByInstanceId.bind(a),[l.renderFgEvents]),l.timeAxis=o,l}return n(r,e),r.prototype.render=function(e){var t=this.slicer.sliceProps(e,e.dateProfile,this.timeAxis.tDateProfile.isTimeScale?null:e.nextDayThreshold,this,this.timeAxis);this.renderBusinessHours(t.businessHourSegs),this.renderDateSelection(t.dateSelectionSegs),this.renderBgEvents(t.bgEventSegs),this.renderFgEvents(t.fgEventSegs),this.renderEventSelection(t.eventSelection),this.renderEventDrag(t.eventDrag),this.renderEventResize(t.eventResize)},r.prototype.destroy=function(){e.prototype.destroy.call(this),this.renderBusinessHours.unrender(),this.renderDateSelection.unrender(),this.renderBgEvents.unrender(),this.renderFgEvents.unrender(),this.renderEventSelection.unrender(),this.renderEventDrag.unrender(),this.renderEventResize.unrender()},r.prototype._renderEventDrag=function(e){e&&(this.eventRenderer.hideByHash(e.affectedInstances),this.mirrorRenderer.renderSegs(e.segs,{isDragging:!0,sourceSeg:e.sourceSeg}))},r.prototype._unrenderEventDrag=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.mirrorRenderer.unrender(e.segs,{isDragging:!0,sourceSeg:e.sourceSeg}))},r.prototype._renderEventResize=function(e){if(e){var t=e.segs.map(function(e){return o({},e)});this.eventRenderer.hideByHash(e.affectedInstances),this.fillRenderer.renderSegs("highlight",t),this.mirrorRenderer.renderSegs(e.segs,{isDragging:!0,sourceSeg:e.sourceSeg})}},r.prototype._unrenderEventResize=function(e){e&&(this.eventRenderer.showByHash(e.affectedInstances),this.fillRenderer.unrender("highlight"),this.mirrorRenderer.unrender(e.segs,{isDragging:!0,sourceSeg:e.sourceSeg}))},r.prototype.updateSize=function(e){var t=this.fillRenderer,r=this.eventRenderer,n=this.mirrorRenderer;t.computeSizes(e),r.computeSizes(e),n.computeSizes(e),t.assignSizes(e),r.assignSizes(e),n.assignSizes(e)},r}(t.DateComponent),N=function(e){function r(){return null!==e&&e.apply(this,arguments)||this}return n(r,e),r.prototype.sliceRange=function(e,r){var n=r.tDateProfile,i=r.props.dateProfile,o=function(e,r,n){if(!r.isTimeScale&&(e=t.computeVisibleDayRange(e),r.largeUnit)){var i=e;((e={start:n.startOf(e.start,r.largeUnit),end:n.startOf(e.end,r.largeUnit)}).end.valueOf()!==i.end.valueOf()||e.end<=e.start)&&(e={start:e.start,end:n.add(e.end,r.slotDuration)})}return e}(e,n,r.dateEnv),l=[];if(r.computeDateSnapCoverage(o.start)<r.computeDateSnapCoverage(o.end)){var s=t.intersectRanges(o,n.normalizedRange);s&&l.push({start:s.start,end:s.end,isStart:s.start.valueOf()===o.start.valueOf()&&b(s.start,n,i,r.view),isEnd:s.end.valueOf()===o.end.valueOf()&&b(t.addMs(s.end,-1),n,i,r.view)})}return l},r}(t.Slicer),_=function(e){function t(t,r,n,i){var o=e.call(this,t,r,n,i)||this;return o.el.classList.add("fc-timeline"),!1===o.opt("eventOverlap")&&o.el.classList.add("fc-no-overlap"),o.el.innerHTML=o.renderSkeletonHtml(),o.timeAxis=new I(o.context,o.el.querySelector("thead .fc-time-area"),o.el.querySelector("tbody .fc-time-area")),o.lane=new O(o.context,o.timeAxis.layout.bodyScroller.enhancedScroll.canvas.contentEl,o.timeAxis.layout.bodyScroller.enhancedScroll.canvas.bgEl,o.timeAxis),t.calendar.registerInteractiveComponent(o,{el:o.timeAxis.slats.el}),o}return n(t,e),t.prototype.destroy=function(){this.timeAxis.destroy(),this.lane.destroy(),e.prototype.destroy.call(this),this.calendar.unregisterInteractiveComponent(this)},t.prototype.renderSkeletonHtml=function(){var e=this.theme;return'<table class="'+e.getClass("tableGrid")+'"> <thead class="fc-head"> <tr> <td class="fc-time-area '+e.getClass("widgetHeader")+'"></td> </tr> </thead> <tbody class="fc-body"> <tr> <td class="fc-time-area '+e.getClass("widgetContent")+'"></td> </tr> </tbody> </table>'},t.prototype.render=function(t){e.prototype.render.call(this,t),this.timeAxis.receiveProps({dateProfile:t.dateProfile}),this.lane.receiveProps(o({},t,{nextDayThreshold:this.nextDayThreshold}))},t.prototype.updateSize=function(e,t,r){this.timeAxis.updateSize(e,t,r),this.lane.updateSize(e)},t.prototype.getNowIndicatorUnit=function(e){return this.timeAxis.getNowIndicatorUnit(e)},t.prototype.renderNowIndicator=function(e){this.timeAxis.renderNowIndicator(e)},t.prototype.unrenderNowIndicator=function(){this.timeAxis.unrenderNowIndicator()},t.prototype.computeDateScroll=function(e){return this.timeAxis.computeDateScroll(e)},t.prototype.applyScroll=function(t,r){e.prototype.applyScroll.call(this,t,r);var n=this.calendar;(r||n.isViewUpdated||n.isDatesUpdated||n.isEventsUpdated)&&this.timeAxis.updateStickyScrollers()},t.prototype.applyDateScroll=function(e){this.timeAxis.applyDateScroll(e)},t.prototype.queryScroll=function(){var e=this.timeAxis.layout.bodyScroller.enhancedScroll;return{top:e.getScrollTop(),left:e.getScrollLeft()}},t.prototype.buildPositionCaches=function(){this.timeAxis.slats.updateSize()},t.prototype.queryHit=function(e,t,r,n){var i=this.timeAxis.slats.positionToHit(e);if(i)return{component:this,dateSpan:i.dateSpan,rect:{left:i.left,right:i.right,top:0,bottom:n},dayEl:i.dayEl,layer:0}},t}(t.View),F=t.createPlugin({defaultView:"timelineDay",views:{timeline:{class:_,eventResizableFromStart:!0},timelineDay:{type:"timeline",duration:{days:1}},timelineWeek:{type:"timeline",duration:{weeks:1}},timelineMonth:{type:"timeline",duration:{months:1}},timelineYear:{type:"timeline",duration:{years:1}}}});e.HeaderBodyLayout=h,e.ScrollJoiner=d,e.StickyScroller=P,e.TimeAxis=I,e.TimelineLane=O,e.TimelineView=_,e.default=F,Object.defineProperty(e,"__esModule",{value:!0})});
\ No newline at end of file
diff --git a/AKPlan/static/AKPlan/fullcalendar/timeline/package.json b/AKPlan/static/AKPlan/fullcalendar/timeline/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..d7105eabd221153726126ac38fd7f783da01432f
--- /dev/null
+++ b/AKPlan/static/AKPlan/fullcalendar/timeline/package.json
@@ -0,0 +1,33 @@
+{
+  "name": "@fullcalendar/timeline",
+  "version": "4.3.0",
+  "title": "FullCalendar Timeline Plugin",
+  "description": "Display events on a horizontal time axis (without resources)",
+  "keywords": [
+    "calendar",
+    "event",
+    "full-sized"
+  ],
+  "homepage": "https://fullcalendar.io/scheduler",
+  "docs": "https://fullcalendar.io/docs/timeline-view-no-resources",
+  "bugs": "https://fullcalendar.io/reporting-bugs",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/fullcalendar/fullcalendar-scheduler.git",
+    "homepage": "https://github.com/fullcalendar/fullcalendar-scheduler"
+  },
+  "license": "SEE LICENSE IN LICENSE.md",
+  "author": {
+    "name": "Adam Shaw",
+    "email": "arshaw@arshaw.com",
+    "url": "http://arshaw.com/"
+  },
+  "copyright": "2019 Adam Shaw",
+  "peerDependencies": {
+    "@fullcalendar/core": "~4.3.0"
+  },
+  "main": "main.js",
+  "module": "main.esm.js",
+  "unpkg": "main.min.js",
+  "types": "main.d.ts"
+}
diff --git a/AKPlan/templates/AKPlan/encode_events.html b/AKPlan/templates/AKPlan/encode_events.html
new file mode 100644
index 0000000000000000000000000000000000000000..c38a4652b3ebfdd164362309e3dc645d617ad1a2
--- /dev/null
+++ b/AKPlan/templates/AKPlan/encode_events.html
@@ -0,0 +1,18 @@
+{% load tz %}
+{% load tags_AKPlan %}
+
+[
+    {% for slot in slots %}
+        {% if slot.start %}
+            {
+                'title': '{{ slot.ak.name }}',
+                'start': '{{ slot.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+                'end': '{{ slot.end | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+                'resourceId': '{{ slot.room.title }}',
+                'backgroundColor': '{{ slot|highlight_change_colors }}',
+                'borderColor': '{{ slot.ak.category.color }}',
+                'url': '{% url 'submit:ak_detail' event_slug=event.slug pk=slot.ak.pk %}'
+            },
+        {% endif %}
+    {% endfor %}
+]
diff --git a/AKPlan/templates/AKPlan/encode_rooms.html b/AKPlan/templates/AKPlan/encode_rooms.html
new file mode 100644
index 0000000000000000000000000000000000000000..55138df0ead3d3db7b6959fe7be72684147baf74
--- /dev/null
+++ b/AKPlan/templates/AKPlan/encode_rooms.html
@@ -0,0 +1,15 @@
+[
+    {% for room in rooms %}
+        {
+            'id': '{{ room.title }}',
+            'title': '{{ room.title }}',
+            'parentId': '{{ room.building }}',
+        },
+    {% endfor %}
+    {% for building in buildings %}
+        {
+            'id': '{{ building }}',
+            'title': '{{ building }}',
+        },
+    {% endfor %}
+]
diff --git a/AKPlan/templates/AKPlan/plan_akslot.html b/AKPlan/templates/AKPlan/plan_akslot.html
new file mode 100644
index 0000000000000000000000000000000000000000..70514a63ce7665e09dfac113934abf306e88c103
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_akslot.html
@@ -0,0 +1,62 @@
+{% load static %}
+{% load tz %}
+{% load i18n %}
+{% get_current_language as LANGUAGE_CODE %}
+
+<link href='{% static 'AKPlan/fullcalendar/core/main.min.css' %}' rel='stylesheet' />
+<link href='{% static 'AKPlan/fullcalendar/daygrid/main.min.css' %}' rel='stylesheet' />
+<link href='{% static 'AKPlan/fullcalendar/timegrid/main.min.css' %}' rel='stylesheet' />
+
+<script src='{% static 'AKPlan/fullcalendar/core/main.min.js' %}'></script>
+{% with 'AKPlan/fullcalendar/core/locales/'|add:LANGUAGE_CODE|add:'.js' as locale_file %}
+<script src="{% static locale_file %}"></script>
+{% endwith %}
+<script src='{% static 'AKPlan/fullcalendar/daygrid/main.min.js' %}'></script>
+<script src='{% static 'AKPlan/fullcalendar/timegrid/main.min.js' %}'></script>
+<script src='{% static 'AKPlan/fullcalendar/bootstrap/main.min.js' %}'></script>
+
+<script>
+
+document.addEventListener('DOMContentLoaded', function() {
+    var calendarEl = document.getElementById('akSlotCalendar');
+
+    var calendar = new FullCalendar.Calendar(calendarEl, {
+        plugins: [ 'timeGrid', 'bootstrap' ],
+        // Adapt to timezone of the connected event
+        timeZone: '{{ ak.event.timezone }}',
+        defaultView: 'timeGrid',
+        // Adapt to user selected locale
+        locale: '{{ LANGUAGE_CODE }}',
+        // No header, not buttons
+        header: {
+            left: '',
+            center: '',
+            right: ''
+        },
+        aspectRatio: 2.5,
+        themeSystem: 'bootstrap',
+        // Only show calendar view for the dates of the connected event
+        visibleRange: {
+            start: '{{ ak.event.start | timezone:ak.event.timezone | date:"Y-m-d H:i:s" }}',
+            end: '{{ ak.event.end | timezone:ak.event.timezone  | date:"Y-m-d H:i:s"}}',
+        },
+        allDaySlot: false,
+        nowIndicator: true,
+        eventTextColor: '#fff',
+        eventColor: '#127ba3',
+        // Create entries for all scheduled slots
+        events: [
+            {% for slot in ak.akslot_set.all %}
+                {% if slot.start %}
+                    {'title': '{{ slot.room }}',
+                    'start': '{{ slot.start | timezone:ak.event.timezone | date:"Y-m-d H:i:s" }}',
+                    'end': '{{ slot.end | timezone:ak.event.timezone | date:"Y-m-d H:i:s" }}'},
+                {% endif %}
+            {% endfor %}
+        ]
+    });
+
+    calendar.render();
+});
+
+</script>
diff --git a/AKPlan/templates/AKPlan/plan_base.html b/AKPlan/templates/AKPlan/plan_base.html
new file mode 100644
index 0000000000000000000000000000000000000000..e97b7dac646f8ec53af975983d22363b6d6855c3
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_base.html
@@ -0,0 +1,27 @@
+{% extends "base.html" %}
+
+{% load fontawesome_5 %}
+{% load i18n %}
+{% load static %}
+
+{% block imports %}
+    {% get_current_language as LANGUAGE_CODE %}
+
+    <link href='{% static 'AKPlan/fullcalendar/core/main.css' %}' rel='stylesheet' />
+
+    <script src='{% static 'AKPlan/fullcalendar/core/main.js' %}'></script>
+    {% with 'AKPlan/fullcalendar/core/locales/'|add:LANGUAGE_CODE|add:'.js' as locale_file %}
+    <script src="{% static locale_file %}"></script>
+    {% endwith %}
+    <script src='{% static 'AKPlan/fullcalendar/bootstrap/main.js' %}'></script>
+
+    {% block fullcalendar %}{% endblock %}
+{% endblock imports %}
+
+{% block footer_custom %}
+    {% if event.contact_email %}
+        <h4>
+            <a href="mailto:{{ event.contact_email }}">{% fa5_icon "envelope" "far" %} {% trans "Write to organizers of this event for questions and comments" %}</a>
+        </h4>
+    {% endif %}
+{% endblock %}
diff --git a/AKPlan/templates/AKPlan/plan_breadcrumbs.html b/AKPlan/templates/AKPlan/plan_breadcrumbs.html
new file mode 100644
index 0000000000000000000000000000000000000000..c5ca3c1afc4b3ac75f0872c60e5530068b9e439f
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_breadcrumbs.html
@@ -0,0 +1,14 @@
+{% load i18n %}
+{% load tags_AKModel %}
+
+<li class="breadcrumb-item">
+    {% if 'AKDashboard'|check_app_installed %}
+        <a href="{% url 'dashboard:dashboard' %}">AKPlanning</a>
+    {% else %}
+        AKPlanning
+    {% endif %}
+</li>
+<li class="breadcrumb-item">{{ event.slug }}</li>
+<li class="breadcrumb-item">
+    <a href="{% url 'plan:plan_overview' event_slug=event.slug %}">{% trans "AK Plan" %}</a>
+</li>
diff --git a/AKPlan/templates/AKPlan/plan_detail.html b/AKPlan/templates/AKPlan/plan_detail.html
new file mode 100644
index 0000000000000000000000000000000000000000..c1ce04efcdb5699d86a4a1b4e9ff70426dbc57f6
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_detail.html
@@ -0,0 +1,53 @@
+{% extends "AKPlan/plan_base.html" %}
+
+{% load fontawesome_5 %}
+{% load i18n %}
+{% load static %}
+{% load tz %}
+
+
+{% block fullcalendar %}
+    {% get_current_language as LANGUAGE_CODE %}
+
+    <link href='{% static 'AKPlan/fullcalendar/daygrid/main.min.css' %}' rel='stylesheet' />
+    <link href='{% static 'AKPlan/fullcalendar/timegrid/main.min.css' %}' rel='stylesheet' />
+
+    <script src='{% static 'AKPlan/fullcalendar/daygrid/main.min.js' %}'></script>
+    <script src='{% static 'AKPlan/fullcalendar/timegrid/main.min.js' %}'></script>
+
+    <script>
+        document.addEventListener('DOMContentLoaded', function() {
+        var calendarEl = document.getElementById('planCalendar');
+
+        var calendar = new FullCalendar.Calendar(calendarEl, {
+            plugins: [ 'timeGrid', 'bootstrap' ],
+            // Adapt to timezone of the connected event
+            timeZone: '{{ event.timezone }}',
+            defaultView: 'timeGrid',
+            // Adapt to user selected locale
+            locale: '{{ LANGUAGE_CODE }}',
+            // No header, not buttons
+            header: {
+                left: '',
+                center: '',
+                right: ''
+            },
+            aspectRatio: 2,
+            themeSystem: 'bootstrap',
+            // Only show calendar view for the dates of the connected event
+            visibleRange: {
+                start: '{{ event.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+                end: '{{ event.end | timezone:event.timezone  | date:"Y-m-d H:i:s"}}',
+            },
+            allDaySlot: false,
+            nowIndicator: true,
+            eventTextColor: '#fff',
+            eventColor: '#127ba3',
+            // Create entries for all scheduled slots
+            events: {% block encode %}{% endblock %}
+        });
+
+        calendar.render();
+    });
+    </script>
+{% endblock %}
diff --git a/AKPlan/templates/AKPlan/plan_index.html b/AKPlan/templates/AKPlan/plan_index.html
new file mode 100644
index 0000000000000000000000000000000000000000..75b0cf86e2209921b7bc028783e0c408a11ab34f
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_index.html
@@ -0,0 +1,124 @@
+{% extends "AKPlan/plan_base.html" %}
+
+{% load fontawesome_5 %}
+{% load i18n %}
+{% load static %}
+{% load tz %}
+
+
+{% block fullcalendar %}
+    {% get_current_language as LANGUAGE_CODE %}
+
+    <link href='{% static 'AKPlan/fullcalendar/timeline/main.css' %}' rel='stylesheet' />
+    <link href='{% static 'AKPlan/fullcalendar/resource-timeline/main.css' %}' rel='stylesheet' />
+    <link href='{% static 'AKPlan/fullcalendar/resource-timeline/main.min.css' %}' rel='stylesheet' />
+
+    <script src='{% static 'AKPlan/fullcalendar/timeline/main.js' %}'></script>
+    <script src='{% static 'AKPlan/fullcalendar/resource-common/main.js' %}'></script>
+    <script src='{% static 'AKPlan/fullcalendar/resource-timeline/main.js' %}'></script>
+
+    <script>
+        document.addEventListener('DOMContentLoaded', function() {
+            var planEl = document.getElementById('planCalendar');
+
+            var plan = new FullCalendar.Calendar(planEl, {
+                plugins: ['resourceTimeline', 'bootstrap'],
+                timeZone: '{{ event.timezone }}',
+                header: {
+                    left: 'today prev,next',
+                    center: 'title',
+                    right: 'resourceTimelineDay,resourceTimelineEvent'
+                },
+                aspectRatio: 2,
+                themeSystem: 'bootstrap',
+                // Adapt to user selected locale
+                locale: '{{ LANGUAGE_CODE }}',
+                defaultView: 'resourceTimelineEvent',
+                views: {
+                    resourceTimelineDay: {
+                        type: 'resourceTimeline',
+                        buttonText: '{% trans "Day" %}',
+                        slotDuration: '01:00',
+                        scrollTime: '08:00',
+                    },
+                    resourceTimelineEvent: {
+                        type: 'resourceTimeline',
+                        visibleRange: {
+                            start: '{{ event.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+                            end: '{{ event.end | timezone:event.timezone  | date:"Y-m-d H:i:s"}}',
+                        },
+                        buttonText: '{% trans "Event" %}',
+                    }
+                },
+                editable: false,
+                allDaySlot: false,
+                nowIndicator: true,
+                eventTextColor: '#fff',
+                eventColor: '#127ba3',
+                resourceAreaWidth: '15%',
+                resourceLabelText: '{% trans "Room" %}',
+                resources: {% include "AKPlan/encode_rooms.html" %},
+                events: {% with akslots as slots %}{% include "AKPlan/encode_events.html" %}{% endwith %},
+                schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
+            });
+
+            plan.render();
+        });
+    </script>
+{% endblock %}
+
+
+{% block breadcrumbs %}
+    {% include "AKPlan/plan_breadcrumbs.html" %}
+{% endblock %}
+
+
+{% block content %}
+    <div class="float-right">
+        <ul class="nav nav-pills">
+          <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Rooms" %}</a>
+            <div class="dropdown-menu" style="">
+                {% for r in event.room_set.all %}
+                    <a class="dropdown-item" href="{% url "plan:plan_room" event_slug=event.slug pk=r.pk %}">{{ r }}</a>
+                {% endfor %}
+            </div>
+          </li>
+          <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Tracks" %}</a>
+            <div class="dropdown-menu">
+                {% for t in event.aktrack_set.all %}
+                    <a class="dropdown-item" href="{% url "plan:plan_track" event_slug=event.slug pk=t.pk %}">{{ t }}</a>
+                {% endfor %}
+            </div>
+          </li>
+          <li class="nav-item">
+            <a class="nav-link active" href="{% url 'plan:plan_wall' event_slug=event.slug %}">{% fa5_icon 'desktop' 'fas' %}&nbsp;&nbsp;{% trans "AK Wall" %}</a>
+          </li>
+        </ul>
+    </div>
+
+    <h1>Plan: {{ event }}</h1>
+
+    {% timezone event.timezone %}
+        <div class="row" style="margin-top:30px;">
+            <div class="col-md-6">
+                <h2><a name="currentAKs">{% trans "Current AKs" %}:</a></h2>
+                {% with akslots_now as slots %}
+                    {% include "AKPlan/slots_table.html" %}
+                {% endwith %}
+            </div>
+
+            <div class="col-md-6">
+                <h2><a name="currentAKs">{% trans "Next AKs" %}:</a></h2>
+                {% with akslots_next as slots %}
+                    {% include "AKPlan/slots_table.html" %}
+                {% endwith %}
+            </div>
+
+            <div class="col-md-12">
+                <div id="planCalendar" style="margin-top:30px;"></div>
+            </div>
+        </div>
+    {% endtimezone %}
+{% endblock %}
diff --git a/AKPlan/templates/AKPlan/plan_room.html b/AKPlan/templates/AKPlan/plan_room.html
new file mode 100644
index 0000000000000000000000000000000000000000..6f95656309b2790cd91d708c301d4e6a23a25ddf
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_room.html
@@ -0,0 +1,53 @@
+{% extends "AKPlan/plan_detail.html" %}
+
+{% load tz %}
+{% load i18n %}
+
+{% block breadcrumbs %}
+    {% include "AKPlan/plan_breadcrumbs.html" %}
+    <li class="breadcrumb-item">{% trans "Room" %}</li>
+    <li class="breadcrumb-item">{{ room.title }}</li>
+{% endblock %}
+
+
+{% block encode %}
+[
+    {% for slot in room.akslot_set.all %}
+        {% if slot.start %}
+            {'title': '{{ slot.ak }}',
+            'start': '{{ slot.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+            'end': '{{ slot.end | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+            'url': '{% url 'submit:ak_detail' event_slug=event.slug pk=slot.ak.pk %}',
+            'borderColor': '{{ slot.ak.track.color }}',
+            'color': '{{ slot.ak.category.color }}',
+            },
+        {% endif %}
+    {% endfor %}
+]
+{% endblock %}
+
+
+{% block content %}
+    <div class="float-right">
+        <ul class="nav nav-pills">
+          <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Rooms" %}</a>
+            <div class="dropdown-menu" style="">
+                {% for r in event.room_set.all %}
+                    <a class="dropdown-item" href="{% url "plan:plan_room" event_slug=event.slug pk=r.pk %}">{{ r }}</a>
+                {% endfor %}
+            </div>
+          </li>
+        </ul>
+    </div>
+
+    <h1>Plan: {{ event }} -- {% trans "Room" %}: {{ room }}</h1>
+
+    {% timezone event.timezone %}
+        <div class="row" style="margin-top:30px;clear:both;">
+            <div class="col-md-12">
+                <div id="planCalendar"></div>
+            </div>
+        </div>
+    {% endtimezone %}
+{% endblock %}
diff --git a/AKPlan/templates/AKPlan/plan_track.html b/AKPlan/templates/AKPlan/plan_track.html
new file mode 100644
index 0000000000000000000000000000000000000000..da45257396ef41e27b4736f5df4784f016c387b9
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_track.html
@@ -0,0 +1,53 @@
+{% extends "AKPlan/plan_detail.html" %}
+
+{% load tz %}
+{% load i18n %}
+
+{% block breadcrumbs %}
+    {% include "AKPlan/plan_breadcrumbs.html" %}
+    <li class="breadcrumb-item">{% trans "Track" %}</li>
+    <li class="breadcrumb-item">{{ track }}</li>
+{% endblock %}
+
+
+{% block encode %}
+[
+    {% for slot in slots %}
+        {% if slot.start %}
+            {'title': '{{ slot.ak }} @ {{ slot.room }}',
+            'start': '{{ slot.start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+            'end': '{{ slot.end | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+            'url': '{% url 'submit:ak_detail' event_slug=event.slug pk=slot.ak.pk %}',
+            'color': '{{ track.color }}',
+            'borderColor': '{{ slot.ak.category.color }}',
+            },
+        {% endif %}
+    {% endfor %}
+]
+{% endblock %}
+
+
+{% block content %}
+    <div class="float-right">
+        <ul class="nav nav-pills">
+          <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Tracks" %}</a>
+            <div class="dropdown-menu">
+                {% for t in event.aktrack_set.all %}
+                    <a class="dropdown-item" href="{% url "plan:plan_track" event_slug=event.slug pk=t.pk %}">{{ t }}</a>
+                {% endfor %}
+            </div>
+          </li>
+        </ul>
+    </div>
+
+    <h1>Plan: {{ event }} -- {% trans "Track" %}: {{ track }}</h1>
+
+    {% timezone event.timezone %}
+        <div class="row" style="margin-top:30px;clear: both;">
+            <div class="col-md-12">
+                <div id="planCalendar"></div>
+            </div>
+        </div>
+    {% endtimezone %}
+{% endblock %}
diff --git a/AKPlan/templates/AKPlan/plan_wall.html b/AKPlan/templates/AKPlan/plan_wall.html
new file mode 100644
index 0000000000000000000000000000000000000000..7507e88f491d6227ad847d0a6c2f81a03be30d82
--- /dev/null
+++ b/AKPlan/templates/AKPlan/plan_wall.html
@@ -0,0 +1,105 @@
+{% load static %}
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+{% load tags_AKModel %}
+{% load tz %}
+
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>{% block title %}AK Planning{% endblock %}</title>
+
+    {# Load Bootstrap CSS and JavaScript as well as font awesome #}
+    {% bootstrap_css %}
+    {% bootstrap_javascript jquery='slim' %}
+    {% fontawesome_5_static %}
+
+    <link rel="stylesheet" href="{% static 'common/css/custom.css' %}">
+
+    {% get_current_language as LANGUAGE_CODE %}
+
+    <link href='{% static 'AKPlan/fullcalendar/core/main.css' %}' rel='stylesheet' />
+    <link href='{% static 'AKPlan/fullcalendar/timeline/main.css' %}' rel='stylesheet' />
+    <link href='{% static 'AKPlan/fullcalendar/resource-timeline/main.css' %}' rel='stylesheet' />
+    <link href='{% static 'AKPlan/fullcalendar/resource-timeline/main.min.css' %}' rel='stylesheet' />
+
+    <script src='{% static 'AKPlan/fullcalendar/core/main.js' %}'></script>
+    {% with 'AKPlan/fullcalendar/core/locales/'|add:LANGUAGE_CODE|add:'.js' as locale_file %}
+    <script src="{% static locale_file %}"></script>
+    {% endwith %}
+    <script src='{% static 'AKPlan/fullcalendar/timeline/main.js' %}'></script>
+    <script src='{% static 'AKPlan/fullcalendar/resource-common/main.js' %}'></script>
+    <script src='{% static 'AKPlan/fullcalendar/resource-timeline/main.js' %}'></script>
+    <script src='{% static 'AKPlan/fullcalendar/bootstrap/main.js' %}'></script>
+
+    <script>
+        document.addEventListener('DOMContentLoaded', function() {
+            var planEl = document.getElementById('planCalendar');
+
+            var plan = new FullCalendar.Calendar(planEl, {
+                plugins: [ 'resourceTimeline', 'bootstrap'],
+                timeZone: '{{ event.timezone }}',
+                header: false,
+                themeSystem: 'bootstrap',
+                // Adapt to user selected locale
+                locale: '{{ LANGUAGE_CODE }}',
+                type: 'resourceTimeline',
+                slotDuration: '01:00',
+                defaultView: 'resourceTimeline',
+                visibleRange: {
+                    start: '{{ start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
+                    end: '{{ end | timezone:event.timezone  | date:"Y-m-d H:i:s"}}',
+                },
+                scrollTime: '{{ start | timezone:event.timezone | date:"H:i:s" }}',
+                editable: false,
+                allDaySlot: false,
+                nowIndicator: true,
+                eventTextColor: '#fff',
+                eventColor: '#127ba3',
+                height: 'parent',
+                resourceAreaWidth: '15%',
+                resourceLabelText: '{% trans "Room" %}',
+                resources: [
+                    {% for room in rooms %}
+                        {
+                            'id': '{{ room.title }}',
+                            'title': '{{ room.title }}'
+                        },
+                    {% endfor %}
+                ],
+                events: {% with akslots as slots %}{% include "AKPlan/encode_events.html" %}{% endwith %},
+                schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
+            });
+
+            plan.render();
+        });
+    </script>
+
+</head>
+<body>
+        {% timezone event.timezone %}
+            <div class="row" style="height:100vh;margin:0;padding:1vh;">
+                <div class="col-md-3">
+                    <h1>Plan: {{ event }}</h1>
+
+                    <h2><a name="currentAKs">{% trans "Current AKs" %}:</a></h2>
+                    {% with akslots_now as slots %}
+                        {% include "AKPlan/slots_table.html" %}
+                    {% endwith %}
+
+                    <h2><a name="currentAKs">{% trans "Next AKs" %}:</a></h2>
+                    {% with akslots_next as slots %}
+                        {% include "AKPlan/slots_table.html" %}
+                    {% endwith %}
+                </div>
+                <div class="col-md-9" style="height:98vh;">
+                    <div id="planCalendar"></div>
+                </div>
+            </div>
+        {% endtimezone %}
+
+</body>
+</html>
diff --git a/AKPlan/templates/AKPlan/slots_table.html b/AKPlan/templates/AKPlan/slots_table.html
new file mode 100644
index 0000000000000000000000000000000000000000..cb246dbd7c57c5e99fb20c0858b6df8299f7a2ed
--- /dev/null
+++ b/AKPlan/templates/AKPlan/slots_table.html
@@ -0,0 +1,14 @@
+{% load i18n %}
+
+
+<table class="table table-striped">
+    {% for akslot in slots %}
+    <tr>
+        <td><b><a href="{% url 'submit:ak_detail' event_slug=event.slug pk=akslot.ak.pk %}">{{ akslot.ak.name }}</a></b></td>
+        <td>{{ akslot.start | time:"H:i" }} - {{ akslot.end | time:"H:i" }}</td>
+        <td>{{ akslot.room }}</td>
+    </tr>
+    {% empty %}
+        {% trans "No AKs" %}
+    {% endfor %}
+</table>
diff --git a/AKPlan/templatetags/__init__.py b/AKPlan/templatetags/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/AKPlan/templatetags/color_gradients.py b/AKPlan/templatetags/color_gradients.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a9f96fd87eda8e405894eef648e048d81e87d32
--- /dev/null
+++ b/AKPlan/templatetags/color_gradients.py
@@ -0,0 +1,61 @@
+# gradients based on http://bsou.io/posts/color-gradients-with-python
+
+
+def hex_to_rgb(hex):
+    """
+    Convert hex color to RGB color code
+    :param hex: hex encoded color
+    :type hex: str
+    :return: rgb encoded version of given color
+    :rtype: list[int]
+    """
+    # Pass 16 to the integer function for change of base
+    return [int(hex[i:i+2], 16) for i in range(1,6,2)]
+
+
+def rgb_to_hex(rgb):
+    """
+    Convert rgb color (list) to hex encoding (str)
+    :param rgb: rgb encoded color
+    :type rgb: list[int]
+    :return: hex encoded version of given color
+    :rtype: str
+    """
+    # Components need to be integers for hex to make sense
+    rgb = [int(x) for x in rgb]
+    return "#"+"".join(["0{0:x}".format(v) if v < 16 else
+                        "{0:x}".format(v) for v in rgb])
+
+
+def linear_blend(start_hex, end_hex, position):
+    """
+    Create a linear blend between two colors and return color code on given position of the range from 0 to 1
+    :param start_hex: hex representation of start color
+    :type start_hex: str
+    :param end_hex: hex representation of end color
+    :type end_hex: str
+    :param position: position in range from 0 to 1
+    :type position: float
+    :return: hex encoded interpolated color
+    :rtype: str
+    """
+    s = hex_to_rgb(start_hex)
+    f = hex_to_rgb(end_hex)
+    blended = [int(s[j] + position * (f[j] - s[j])) for j in range(3)]
+    return rgb_to_hex(blended)
+
+
+def darken(start_hex, amount):
+    """
+    Darken the given color by the given amount (sensitivity will be cut in half)
+
+    :param start_hex: original color
+    :type start_hex: str
+    :param amount: how much to darken (1.0 -> 50% darker)
+    :type amount: float
+    :return: darker version of color
+    :rtype: str
+    """
+    start_rbg = hex_to_rgb(start_hex)
+    darker = [int(s * (1 - amount * .5)) for s in start_rbg]
+    return rgb_to_hex(darker)
diff --git a/AKPlan/templatetags/tags_AKPlan.py b/AKPlan/templatetags/tags_AKPlan.py
new file mode 100644
index 0000000000000000000000000000000000000000..5618fd57694fbfe75988bed3e9f3341f8a6c8818
--- /dev/null
+++ b/AKPlan/templatetags/tags_AKPlan.py
@@ -0,0 +1,20 @@
+from django import template
+
+from AKPlan.templatetags.color_gradients import darken
+from AKPlanning import settings
+
+register = template.Library()
+
+
+@register.filter
+def highlight_change_colors(akslot):
+    seconds_since_update = akslot.seconds_since_last_update
+
+    # Last change long ago? Use default color
+    if seconds_since_update > settings.PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS:
+        return akslot.ak.category.color
+
+    # Recent change? Calculate gradient blend between red and
+    recentness = seconds_since_update / settings.PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS
+    return darken("#b71540", recentness)
+    # return linear_blend("#b71540", "#000000", recentness)
diff --git a/AKPlan/urls.py b/AKPlan/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..d45f64c73c95cbbabf10adc6668396526e0820ec
--- /dev/null
+++ b/AKPlan/urls.py
@@ -0,0 +1,16 @@
+from django.urls import path, include
+from . import views
+
+app_name = "plan"
+
+urlpatterns = [
+    path(
+        '<slug:event_slug>/plan/',
+        include([
+            path('', views.PlanIndexView.as_view(), name='plan_overview'),
+            path('wall/', views.PlanScreenView.as_view(), name='plan_wall'),
+            path('room/<int:pk>/', views.PlanRoomView.as_view(), name='plan_room'),
+            path('track/<int:pk>/', views.PlanTrackView.as_view(), name='plan_track'),
+        ])
+    ),
+]
diff --git a/AKPlan/views.py b/AKPlan/views.py
index 60f00ef0ef347811e7b0c0921b7fda097acd9fcc..f96a972611eeca0af10b4046f6d1d1e6ba424a02 100644
--- a/AKPlan/views.py
+++ b/AKPlan/views.py
@@ -1 +1,95 @@
-# Create your views here.
+from datetime import timedelta
+
+from django.conf import settings
+from django.utils.datetime_safe import datetime
+from django.views.generic import ListView, DetailView
+
+from AKModel.models import AKSlot, Room, AKTrack
+from AKModel.views import FilterByEventSlugMixin
+
+
+class PlanIndexView(FilterByEventSlugMixin, ListView):
+    model = AKSlot
+    template_name = "AKPlan/plan_index.html"
+    context_object_name = "akslots"
+    ordering = "start"
+
+    def get_queryset(self):
+        # Ignore slots not scheduled yet
+        return super().get_queryset().filter(start__isnull=False)
+
+    def get_context_data(self, *, object_list=None, **kwargs):
+        context = super().get_context_data(object_list=object_list, **kwargs)
+
+        context["event"] = self.event
+
+        current_timestamp = datetime.now().astimezone(self.event.timezone)
+
+        context["akslots_now"] = []
+        context["akslots_next"] = []
+        rooms = set()
+        buildings = set()
+
+        # Get list of current and next slots
+        for akslot in context["akslots"]:
+            # Construct a list of all rooms used by these slots on the fly
+            if akslot.room is not None:
+                rooms.add(akslot.room)
+                # Store buildings for hierarchical view
+                if akslot.room.building != '':
+                    buildings.add(akslot.room.building)
+
+            # Recent AKs: Started but not ended yet
+            if akslot.start <= current_timestamp <= akslot.end:
+                context["akslots_now"].append(akslot)
+            # Next AKs: Not started yet, list will be filled in order until threshold is reached
+            elif akslot.start > current_timestamp:
+                if len(context["akslots_next"]) < settings.PLAN_MAX_NEXT_AKS:
+                    context["akslots_next"].append(akslot)
+
+        # Sort list of rooms by title
+        context["rooms"] = sorted(rooms, key=lambda x: x.title)
+        if settings.PLAN_SHOW_HIERARCHY:
+            context["buildings"] = sorted(buildings)
+
+        return context
+
+
+class PlanScreenView(PlanIndexView):
+    template_name = "AKPlan/plan_wall.html"
+
+    def get_queryset(self):
+        # Determine interesting range (some hours ago until some hours in the future as specified in the settings)
+        self.start = datetime.now().astimezone(self.event.timezone) - timedelta(hours=settings.PLAN_WALL_HOURS_RETROSPECT)
+        self.end = self.start + timedelta(hours=(settings.PLAN_WALL_HOURS_RETROSPECT + settings.PLAN_WALL_HOURS_FUTURE))
+
+        # Restrict AK slots to relevant ones
+        # This will automatically filter all rooms not needed for the selected range in the orginal get_context method
+        return super().get_queryset().filter(start__gt=self.start, start__lt=self.end)
+
+    def get_context_data(self, *, object_list=None, **kwargs):
+        context = super().get_context_data(object_list=object_list, **kwargs)
+        context["start"] = self.start
+        context["end"] = self.end
+        return context
+
+
+class PlanRoomView(FilterByEventSlugMixin, DetailView):
+    template_name = "AKPlan/plan_room.html"
+    model = Room
+    context_object_name = "room"
+
+
+class PlanTrackView(FilterByEventSlugMixin, DetailView):
+    template_name = "AKPlan/plan_track.html"
+    model = AKTrack
+    context_object_name = "track"
+
+    def get_context_data(self, *, object_list=None, **kwargs):
+        context = super().get_context_data(object_list=object_list, **kwargs)
+
+        context["slots"] = []
+        for ak in context["track"].ak_set.all():
+            context["slots"].extend(ak.akslot_set.all())
+
+        return context
diff --git a/AKPlanning/settings.py b/AKPlanning/settings.py
index b99554f3846f89b2a38552589e52e80af8044b53..621abaaa905f64410d9d1c9b5339ecd5fe7438d9 100644
--- a/AKPlanning/settings.py
+++ b/AKPlanning/settings.py
@@ -36,7 +36,7 @@ INSTALLED_APPS = [
     'AKDashboard.apps.AkdashboardConfig',
     'AKSubmission.apps.AksubmissionConfig',
     'AKScheduling.apps.AkschedulingConfig',
-    # 'AKPlan.apps.AkplanConfig',
+    'AKPlan.apps.AkplanConfig',
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
@@ -45,6 +45,7 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
     'bootstrap4',
     'fontawesome_5',
+    'timezone_field',
 ]
 
 MIDDLEWARE = [
@@ -156,4 +157,14 @@ FOOTER_INFO = {
     "impress_url": ""
 }
 
+# How many AKs should be visible as next AKs
+PLAN_MAX_NEXT_AKS = 10
+# Specify range of plan for screen/projector view
+PLAN_WALL_HOURS_RETROSPECT = 3
+PLAN_WALL_HOURS_FUTURE = 18
+# Should the plan use a hierarchy of buildings and rooms?
+PLAN_SHOW_HIERARCHY = True
+# For which time (in seconds) should changes of akslots be highlighted in plan?
+PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS = 2 * 60 * 60
+
 include(optional("settings/*.py"))
diff --git a/AKPlanning/urls.py b/AKPlanning/urls.py
index 76f8b94e63f478332387d177cc426bbf62bf30aa..c7c04a384a1c7f6cd822b702048b00b667c3de07 100644
--- a/AKPlanning/urls.py
+++ b/AKPlanning/urls.py
@@ -13,12 +13,19 @@ Including another URLconf
     1. Import the include() function: from django.urls import include, path
     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
 """
+from django.apps import apps
 from django.contrib import admin
 from django.urls import path, include
 
 urlpatterns = [
     path('admin/', admin.site.urls),
-    path('', include('AKSubmission.urls', namespace='submit')),
-    path('', include('AKDashboard.urls', namespace='dashboard')),
-    path('i18n/', include('django.conf.urls.i18n')),
+    path('i18n/', include('django.conf.urls.i18n'))
 ]
+
+# Load URLs dynamically (only if components are active)
+if apps.is_installed("AKSubmission"):
+    urlpatterns.append(path('', include('AKSubmission.urls', namespace='submit')))
+if apps.is_installed("AKDashboard"):
+    urlpatterns.append(path('', include('AKDashboard.urls', namespace='dashboard')))
+if apps.is_installed("AKPlan"):
+    urlpatterns.append(path('', include('AKPlan.urls', namespace='plan')))
diff --git a/AKSubmission/templates/AKSubmission/ak_detail.html b/AKSubmission/templates/AKSubmission/ak_detail.html
index 2d003de0ce71fc006187610e5078f1672525f168..02030ffc9f7e33eaa5034fd803cbd84387ad08b3 100644
--- a/AKSubmission/templates/AKSubmission/ak_detail.html
+++ b/AKSubmission/templates/AKSubmission/ak_detail.html
@@ -4,6 +4,8 @@
 {% load fontawesome_5 %}
 
 {% load tags_AKSubmission %}
+{% load tags_AKModel %}
+
 
 {% block title %}{% trans "AKs" %}: {{ ak.event.name }} - {% trans "AK" %}: {{ ak.name }}{% endblock %}
 
@@ -14,6 +16,12 @@
     <li class="breadcrumb-item active">{{ ak.name }}</li>
 {% endblock %}
 
+{% if 'AKPlan'|check_app_installed %}
+    {% block imports %}
+        {% include "AKPlan/plan_akslot.html" %}
+    {% endblock %}
+{% endif %}
+
 {% block content %}
     {% include "AKSubmission/messages.html" %}
 
@@ -55,11 +63,44 @@
                 {{ ak.reso | bool_symbol }}
             </td>
         </tr>
+        {% if ak.requirements.count > 0 %}
+            <tr>
+                <td>{% trans "Requirements" %}</td>
+                <td>
+                    {% for requirement in ak.requirements.all %}
+                        {% if forloop.counter0 > 0 %}
+                            ,&nbsp;
+                        {% endif %}
+                        {{ requirement }}
+                    {% endfor %}
+                </td>
+            </tr>
+        {% endif %}
+        {% if ak.conflicts.count > 0 %}
+            <tr>
+                <td>{% trans "Conflicting AKs" %}</td>
+                <td>
+                    {% include "AKSubmission/ak_linked_list_inline.html" with aks=ak.conflicts slug=ak.event.slug %}
+                </td>
+            </tr>
+        {% endif %}
+        {% if ak.prerequisites.count > 0 %}
+            <tr>
+                <td>{% trans "Prerequisite AKs" %}</td>
+                <td>
+                    {% include "AKSubmission/ak_linked_list_inline.html" with aks=ak.prerequisites slug=ak.event.slug %}
+                </td>
+            </tr>
+        {% endif %}
     </table>
 
     <p style="margin-top: 30px;margin-bottom: 30px;">{{ ak.description }}</p>
 
 
+    {% if 'AKPlan'|check_app_installed %}
+        <div id='akSlotCalendar' style="margin-bottom: 50px;"></div>
+    {% endif %}
+
 
     <table class="table">
         <thead>
diff --git a/AKSubmission/templates/AKSubmission/ak_linked_list_inline.html b/AKSubmission/templates/AKSubmission/ak_linked_list_inline.html
new file mode 100644
index 0000000000000000000000000000000000000000..f5b504f7c07b8d008c32bd3cdab9911d1415b22b
--- /dev/null
+++ b/AKSubmission/templates/AKSubmission/ak_linked_list_inline.html
@@ -0,0 +1,8 @@
+{% for ak in aks.all %}
+    {% if forloop.counter0 > 0 %}
+        ,&nbsp;
+    {% endif %}
+    <a href="{% url 'submit:ak_detail' event_slug=slug pk=ak.pk %}">{{ ak }}</a>
+{% empty %}
+    -
+{% endfor %}
diff --git a/AKSubmission/templates/AKSubmission/submit_new.html b/AKSubmission/templates/AKSubmission/submit_new.html
index b177d80109da5a340a1bddd612bfa4c4a449926b..682a477f5ecbaf03b04b9cc67d89a0790509dbdf 100644
--- a/AKSubmission/templates/AKSubmission/submit_new.html
+++ b/AKSubmission/templates/AKSubmission/submit_new.html
@@ -9,7 +9,7 @@
 
 {% block imports %}
     <link rel="stylesheet" href="{% static 'common/vendor/chosen-js/chosen.css' %}">
-    <link rel="stylesheet" href="{% static 'common/css/bootstrap-choosen.css' %}">
+    <link rel="stylesheet" href="{% static 'common/css/bootstrap-chosen.css' %}">
 {% endblock %}
 
 {% block breadcrumbs %}
@@ -51,4 +51,4 @@
             $('.chosen-select').chosen();
         });
     </script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/requirements.txt b/requirements.txt
index fb43f8fdb852485e172e0f923fd6e4cabe0b66c2..163de0db8e13487624d47864a2160bf2b43dc41c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,3 +2,4 @@ Django==2.2.6
 django-bootstrap4==1.0.1
 django-fontawesome-5==1.0.16
 django-split-settings==1.0.0
+django-timezone-field==4.0