diff --git a/addons/calendar/static/src/js/calendar_view.js b/addons/calendar/static/src/js/calendar_view.js index 1717deb7899..6c6f8ea0d54 100644 --- a/addons/calendar/static/src/js/calendar_view.js +++ b/addons/calendar/static/src/js/calendar_view.js @@ -25,7 +25,7 @@ var AttendeeCalendarPopover = CalendarPopover.extend({ _.each(this.fields.attendee_status.selection, function (selection) { self.statusInfo[selection[0]] = {text: selection[1], color: self.statusColors[selection[0]]}; }); - this.selectedStatusInfo = this.statusInfo[this.event.record.attendee_status]; + this.selectedStatusInfo = this.statusInfo[this.event.extendedProps.record.attendee_status]; } }, @@ -37,7 +37,7 @@ var AttendeeCalendarPopover = CalendarPopover.extend({ * @return {boolean} */ isCurrentPartnerAttendee() { - return this.event.record.partner_ids.includes(session.partner_id); + return this.event.extendedProps.record.partner_ids.includes(session.partner_id); }, /** * @override @@ -70,7 +70,7 @@ var AttendeeCalendarPopover = CalendarPopover.extend({ * @return {boolean} */ _isEventPrivate() { - return this.event.record.privacy === 'private'; + return this.event.extendedProps.record.privacy === 'private'; }, //-------------------------------------------------------------------------- @@ -90,7 +90,7 @@ var AttendeeCalendarPopover = CalendarPopover.extend({ method: 'change_attendee_status', args: [this.event.id, selectedStatus], }).then(function () { - self.event.record.attendee_status = selectedStatus; // FIXEME: Maybe we have to reload view + self.event.extendedProps.record.attendee_status = selectedStatus; // FIXEME: Maybe we have to reload view self.$('.o-calendar-attendee-status-text').text(self.statusInfo[selectedStatus].text); self.$('.o-calendar-attendee-status-icon').removeClass(_.values(self.statusColors).join(' ')).addClass(self.statusInfo[selectedStatus].color); }); diff --git a/addons/web/static/lib/fullcalendar/LICENSE.txt b/addons/web/static/lib/fullcalendar/LICENSE.txt index eafaf97ec40..2149cfbeff4 100644 --- a/addons/web/static/lib/fullcalendar/LICENSE.txt +++ b/addons/web/static/lib/fullcalendar/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2015 Adam Shaw +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 diff --git a/addons/web/static/lib/fullcalendar/bootstrap/main.css b/addons/web/static/lib/fullcalendar/bootstrap/main.css new file mode 100644 index 00000000000..22b33f56ba0 --- /dev/null +++ b/addons/web/static/lib/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; +} \ No newline at end of file diff --git a/addons/web/static/lib/fullcalendar/bootstrap/main.js b/addons/web/static/lib/fullcalendar/bootstrap/main.js new file mode 100644 index 00000000000..5f966890cf7 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/bootstrap/main.js @@ -0,0 +1,91 @@ +/*! +FullCalendar Bootstrap Plugin v4.4.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/addons/web/static/lib/fullcalendar/core/locales-all.js b/addons/web/static/lib/fullcalendar/core/locales-all.js new file mode 100644 index 00000000000..e6c77df2c09 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/core/locales-all.js @@ -0,0 +1,1405 @@ +(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: "az", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Əvvəl", + next: "Sonra", + today: "Bu Gün", + month: "Ay", + week: "Həftə", + day: "Gün", + list: "Gündəm" + }, + weekLabel: "Həftə", + allDayText: "Bütün Gün", + eventLimitText: function (n) { + return "+ daha çox " + n; + }, + noEventsMessage: "Göstərmək üçün hadisə yoxdur" + }; + + var _m9 = { + 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 _m10 = { + 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 _m11 = { + 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 _m12 = { + 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 _m13 = { + 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 _m14 = { + 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 _m15 = { + 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 _m16 = { + code: "en-au", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + var _m17 = { + code: "en-gb", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + var _m18 = { + code: "en-nz", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + } + }; + + var _m19 = { + 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
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + var _m20 = { + 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
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + var _m21 = { + 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 _m22 = { + 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
osoa", + eventLimitText: "gehiago", + noEventsMessage: "Ez dago ekitaldirik erakusteko" + }; + + var _m23 = { + 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 _m24 = { + 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 _m25 = { + 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
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + var _m26 = { + 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
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + var _m27 = { + 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: "Planning" + }, + weekLabel: "Sem.", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + var _m28 = { + 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
o día", + eventLimitText: "máis", + noEventsMessage: "Non hai eventos para amosar" + }; + + var _m29 = { + code: "he", + dir: 'rtl', + buttonText: { + prev: "הקודם", + next: "הבא", + today: "היום", + month: "חודש", + week: "שבוע", + day: "יום", + list: "סדר יום" + }, + allDayText: "כל היום", + eventLimitText: "אחר", + noEventsMessage: "אין אירועים להצגה", + weekLabel: "שבוע" + }; + + var _m30 = { + 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 _m31 = { + 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 _m32 = { + 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 _m33 = { + 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
penuh", + eventLimitText: "lebih", + noEventsMessage: "Tidak ada acara untuk ditampilkan" + }; + + var _m34 = { + 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
daginn", + eventLimitText: "meira", + noEventsMessage: "Engir viðburðir til að sýna" + }; + + var _m35 = { + 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
giorno", + eventLimitText: function (n) { + return "+altri " + n; + }, + noEventsMessage: "Non ci sono eventi da visualizzare" + }; + + var _m36 = { + code: "ja", + buttonText: { + prev: "前", + next: "次", + today: "今日", + month: "月", + week: "週", + day: "日", + list: "予定リスト" + }, + weekLabel: "週", + allDayText: "終日", + eventLimitText: function (n) { + return "他 " + n + " 件"; + }, + noEventsMessage: "表示する予定はありません" + }; + + var _m37 = { + code: "ka", + week: { + dow: 1, + doy: 7 + }, + buttonText: { + prev: "წინა", + next: "შემდეგი", + today: "დღეს", + month: "თვე", + week: "კვირა", + day: "დღე", + list: "დღის წესრიგი" + }, + weekLabel: "კვ", + allDayText: "მთელი დღე", + eventLimitText: function (n) { + return "+ კიდევ " + n; + }, + noEventsMessage: "ღონისძიებები არ არის" + }; + + var _m38 = { + 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 _m39 = { + code: "ko", + buttonText: { + prev: "이전달", + next: "다음달", + today: "오늘", + month: "월", + week: "주", + day: "일", + list: "일정목록" + }, + weekLabel: "주", + allDayText: "종일", + eventLimitText: "개", + noEventsMessage: "일정이 없습니다" + }; + + var _m40 = { + 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 _m41 = { + 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 _m42 = { + 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 _m43 = { + code: "mk", + buttonText: { + prev: "претходно", + next: "следно", + today: "Денес", + month: "Месец", + week: "Недела", + day: "Ден", + list: "График" + }, + weekLabel: "Сед", + allDayText: "Цел ден", + eventLimitText: function (n) { + return "+повеќе " + n; + }, + noEventsMessage: "Нема настани за прикажување" + }; + + var _m44 = { + 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 _m45 = { + 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 _m46 = { + 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 _m47 = { + 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 _m48 = { + 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 _m49 = { + code: "pt-br", + buttonText: { + prev: "Anterior", + next: "Próximo", + today: "Hoje", + month: "Mês", + week: "Semana", + day: "Dia", + list: "Lista" + }, + weekLabel: "Sm", + allDayText: "dia inteiro", + eventLimitText: function (n) { + return "mais +" + n; + }, + noEventsMessage: "Não há eventos para mostrar" + }; + + var _m50 = { + 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 _m51 = { + 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 _m52 = { + 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 _m53 = { + 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 _m54 = { + 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 _m55 = { + 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ë
ditën", + eventLimitText: function (n) { + return "+më tepër " + n; + }, + noEventsMessage: "Nuk ka evente për të shfaqur" + }; + + var _m56 = { + 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 _m57 = { + 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 _m58 = { + 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 _m59 = { + code: "th", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "ก่อนหน้า", + next: "ถัดไป", + prevYear: 'ปีก่อนหน้า', + nextYear: 'ปีถัดไป', + year: 'ปี', + today: "วันนี้", + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "กำหนดการ" + }, + weekLabel: "สัปดาห์", + allDayText: "ตลอดวัน", + eventLimitText: "เพิ่มเติม", + noEventsMessage: "ไม่มีกิจกรรมที่จะแสดง" + }; + + var _m60 = { + 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 _m61 = { + code: "ug", + buttonText: { + month: "ئاي", + week: "ھەپتە", + day: "كۈن", + list: "كۈنتەرتىپ" + }, + allDayText: "پۈتۈن كۈن" + }; + + var _m62 = { + 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 _m63 = { + code: "uz", + buttonText: { + month: "Oy", + week: "Xafta", + day: "Kun", + list: "Kun tartibi" + }, + allDayText: "Kun bo'yi", + eventLimitText: function (n) { + return "+ yana " + n; + }, + noEventsMessage: "Ko'rsatish uchun voqealar yo'q" + }; + + var _m64 = { + 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 _m65 = { + 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 _m66 = { + 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, _m64, _m65, _m66 + ]; + + return _rollupPluginMultiEntry_entryPoint; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/af.js b/addons/web/static/lib/fullcalendar/core/locales/af.js new file mode 100644 index 00000000000..ee9f9f74756 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar-dz.js b/addons/web/static/lib/fullcalendar/core/locales/ar-dz.js new file mode 100644 index 00000000000..201eb171ae6 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar-kw.js b/addons/web/static/lib/fullcalendar/core/locales/ar-kw.js new file mode 100644 index 00000000000..94c69001466 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar-ly.js b/addons/web/static/lib/fullcalendar/core/locales/ar-ly.js new file mode 100644 index 00000000000..e1c8aeb07bc --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar-ma.js b/addons/web/static/lib/fullcalendar/core/locales/ar-ma.js new file mode 100644 index 00000000000..00cc7c67906 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar-sa.js b/addons/web/static/lib/fullcalendar/core/locales/ar-sa.js new file mode 100644 index 00000000000..0361f6d8701 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar-tn.js b/addons/web/static/lib/fullcalendar/core/locales/ar-tn.js new file mode 100644 index 00000000000..57a07f8f514 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ar.js b/addons/web/static/lib/fullcalendar/core/locales/ar.js new file mode 100644 index 00000000000..f789afd151e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/az.js b/addons/web/static/lib/fullcalendar/core/locales/az.js new file mode 100644 index 00000000000..fbd2dc35cf8 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/core/locales/az.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.az = factory())); +}(this, function () { 'use strict'; + + var az = { + code: "az", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "Əvvəl", + next: "Sonra", + today: "Bu Gün", + month: "Ay", + week: "Həftə", + day: "Gün", + list: "Gündəm" + }, + weekLabel: "Həftə", + allDayText: "Bütün Gün", + eventLimitText: function (n) { + return "+ daha çox " + n; + }, + noEventsMessage: "Göstərmək üçün hadisə yoxdur" + }; + + return az; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/bg.js b/addons/web/static/lib/fullcalendar/core/locales/bg.js new file mode 100644 index 00000000000..e7343a6c5ab --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/bs.js b/addons/web/static/lib/fullcalendar/core/locales/bs.js new file mode 100644 index 00000000000..d96b8adb3d9 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ca.js b/addons/web/static/lib/fullcalendar/core/locales/ca.js new file mode 100644 index 00000000000..d2d3e2aa770 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/cs.js b/addons/web/static/lib/fullcalendar/core/locales/cs.js new file mode 100644 index 00000000000..2624e3607df --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/da.js b/addons/web/static/lib/fullcalendar/core/locales/da.js new file mode 100644 index 00000000000..73d15592990 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/de.js b/addons/web/static/lib/fullcalendar/core/locales/de.js new file mode 100644 index 00000000000..ab5a815a0cc --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/el.js b/addons/web/static/lib/fullcalendar/core/locales/el.js new file mode 100644 index 00000000000..cdc10a66e4a --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/en-au.js b/addons/web/static/lib/fullcalendar/core/locales/en-au.js new file mode 100644 index 00000000000..be10bfb66ea --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/en-gb.js b/addons/web/static/lib/fullcalendar/core/locales/en-gb.js new file mode 100644 index 00000000000..8a4a84e6b42 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/en-nz.js b/addons/web/static/lib/fullcalendar/core/locales/en-nz.js new file mode 100644 index 00000000000..df56c145506 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/es-us.js b/addons/web/static/lib/fullcalendar/core/locales/es-us.js new file mode 100644 index 00000000000..1efa89a4d18 --- /dev/null +++ b/addons/web/static/lib/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
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + return esUs; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/es.js b/addons/web/static/lib/fullcalendar/core/locales/es.js new file mode 100644 index 00000000000..bfd9af4c626 --- /dev/null +++ b/addons/web/static/lib/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
el día", + eventLimitText: "más", + noEventsMessage: "No hay eventos para mostrar" + }; + + return es; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/et.js b/addons/web/static/lib/fullcalendar/core/locales/et.js new file mode 100644 index 00000000000..c44fcaec9e5 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/eu.js b/addons/web/static/lib/fullcalendar/core/locales/eu.js new file mode 100644 index 00000000000..91903aaaf17 --- /dev/null +++ b/addons/web/static/lib/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
osoa", + eventLimitText: "gehiago", + noEventsMessage: "Ez dago ekitaldirik erakusteko" + }; + + return eu; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/fa.js b/addons/web/static/lib/fullcalendar/core/locales/fa.js new file mode 100644 index 00000000000..031fc7b304f --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/fi.js b/addons/web/static/lib/fullcalendar/core/locales/fi.js new file mode 100644 index 00000000000..3912845cf75 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/fr-ca.js b/addons/web/static/lib/fullcalendar/core/locales/fr-ca.js new file mode 100644 index 00000000000..d554c1408f8 --- /dev/null +++ b/addons/web/static/lib/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
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + return frCa; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/fr-ch.js b/addons/web/static/lib/fullcalendar/core/locales/fr-ch.js new file mode 100644 index 00000000000..358b8bf311a --- /dev/null +++ b/addons/web/static/lib/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
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + return frCh; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/fr.js b/addons/web/static/lib/fullcalendar/core/locales/fr.js new file mode 100644 index 00000000000..4760a71a9be --- /dev/null +++ b/addons/web/static/lib/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: "Planning" + }, + weekLabel: "Sem.", + allDayHtml: "Toute la
journée", + eventLimitText: "en plus", + noEventsMessage: "Aucun événement à afficher" + }; + + return fr; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/gl.js b/addons/web/static/lib/fullcalendar/core/locales/gl.js new file mode 100644 index 00000000000..721a6a89b25 --- /dev/null +++ b/addons/web/static/lib/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
o día", + eventLimitText: "máis", + noEventsMessage: "Non hai eventos para amosar" + }; + + return gl; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/he.js b/addons/web/static/lib/fullcalendar/core/locales/he.js new file mode 100644 index 00000000000..3521d9e335b --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/hi.js b/addons/web/static/lib/fullcalendar/core/locales/hi.js new file mode 100644 index 00000000000..15348e697f7 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/hr.js b/addons/web/static/lib/fullcalendar/core/locales/hr.js new file mode 100644 index 00000000000..295b4856634 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/hu.js b/addons/web/static/lib/fullcalendar/core/locales/hu.js new file mode 100644 index 00000000000..2f0fe8acb6b --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/id.js b/addons/web/static/lib/fullcalendar/core/locales/id.js new file mode 100644 index 00000000000..b742e80dda7 --- /dev/null +++ b/addons/web/static/lib/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
penuh", + eventLimitText: "lebih", + noEventsMessage: "Tidak ada acara untuk ditampilkan" + }; + + return id; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/is.js b/addons/web/static/lib/fullcalendar/core/locales/is.js new file mode 100644 index 00000000000..dd569bce72f --- /dev/null +++ b/addons/web/static/lib/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
daginn", + eventLimitText: "meira", + noEventsMessage: "Engir viðburðir til að sýna" + }; + + return is; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/it.js b/addons/web/static/lib/fullcalendar/core/locales/it.js new file mode 100644 index 00000000000..39a2829e522 --- /dev/null +++ b/addons/web/static/lib/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
giorno", + eventLimitText: function (n) { + return "+altri " + n; + }, + noEventsMessage: "Non ci sono eventi da visualizzare" + }; + + return it; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/ja.js b/addons/web/static/lib/fullcalendar/core/locales/ja.js new file mode 100644 index 00000000000..eb4245b2a07 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ka.js b/addons/web/static/lib/fullcalendar/core/locales/ka.js new file mode 100644 index 00000000000..b971c033f81 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/kk.js b/addons/web/static/lib/fullcalendar/core/locales/kk.js new file mode 100644 index 00000000000..5b19b99d510 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ko.js b/addons/web/static/lib/fullcalendar/core/locales/ko.js new file mode 100644 index 00000000000..ffe985d6c5d --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/lb.js b/addons/web/static/lib/fullcalendar/core/locales/lb.js new file mode 100644 index 00000000000..b9b17e3ec1f --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/lt.js b/addons/web/static/lib/fullcalendar/core/locales/lt.js new file mode 100644 index 00000000000..ec641b7500e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/lv.js b/addons/web/static/lib/fullcalendar/core/locales/lv.js new file mode 100644 index 00000000000..5453630df11 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/mk.js b/addons/web/static/lib/fullcalendar/core/locales/mk.js new file mode 100644 index 00000000000..6729fa63d39 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ms.js b/addons/web/static/lib/fullcalendar/core/locales/ms.js new file mode 100644 index 00000000000..7205ecc7275 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/nb.js b/addons/web/static/lib/fullcalendar/core/locales/nb.js new file mode 100644 index 00000000000..6464461c648 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/nl.js b/addons/web/static/lib/fullcalendar/core/locales/nl.js new file mode 100644 index 00000000000..c91b5e55fdd --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/nn.js b/addons/web/static/lib/fullcalendar/core/locales/nn.js new file mode 100644 index 00000000000..a5cdd16268e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/pl.js b/addons/web/static/lib/fullcalendar/core/locales/pl.js new file mode 100644 index 00000000000..0a22e69c436 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/pt-br.js b/addons/web/static/lib/fullcalendar/core/locales/pt-br.js new file mode 100644 index 00000000000..bfa023dd665 --- /dev/null +++ b/addons/web/static/lib/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: "Lista" + }, + weekLabel: "Sm", + allDayText: "dia inteiro", + eventLimitText: function (n) { + return "mais +" + n; + }, + noEventsMessage: "Não há eventos para mostrar" + }; + + return ptBr; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/pt.js b/addons/web/static/lib/fullcalendar/core/locales/pt.js new file mode 100644 index 00000000000..5c54d8d401e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ro.js b/addons/web/static/lib/fullcalendar/core/locales/ro.js new file mode 100644 index 00000000000..e8992f27628 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ru.js b/addons/web/static/lib/fullcalendar/core/locales/ru.js new file mode 100644 index 00000000000..77e0308e2c4 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/sk.js b/addons/web/static/lib/fullcalendar/core/locales/sk.js new file mode 100644 index 00000000000..3513a64ad3b --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/sl.js b/addons/web/static/lib/fullcalendar/core/locales/sl.js new file mode 100644 index 00000000000..323355359ed --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/sq.js b/addons/web/static/lib/fullcalendar/core/locales/sq.js new file mode 100644 index 00000000000..0d43a522094 --- /dev/null +++ b/addons/web/static/lib/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ë
ditën", + eventLimitText: function (n) { + return "+më tepër " + n; + }, + noEventsMessage: "Nuk ka evente për të shfaqur" + }; + + return sq; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/sr-cyrl.js b/addons/web/static/lib/fullcalendar/core/locales/sr-cyrl.js new file mode 100644 index 00000000000..ba0d0dfa31e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/sr.js b/addons/web/static/lib/fullcalendar/core/locales/sr.js new file mode 100644 index 00000000000..23e5c9b23fe --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/sv.js b/addons/web/static/lib/fullcalendar/core/locales/sv.js new file mode 100644 index 00000000000..a887060ba4e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/th.js b/addons/web/static/lib/fullcalendar/core/locales/th.js new file mode 100644 index 00000000000..faeaee2143c --- /dev/null +++ b/addons/web/static/lib/fullcalendar/core/locales/th.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.th = factory())); +}(this, function () { 'use strict'; + + var th = { + code: "th", + week: { + dow: 1, + doy: 4 // The week that contains Jan 4th is the first week of the year. + }, + buttonText: { + prev: "ก่อนหน้า", + next: "ถัดไป", + prevYear: 'ปีก่อนหน้า', + nextYear: 'ปีถัดไป', + year: 'ปี', + today: "วันนี้", + month: "เดือน", + week: "สัปดาห์", + day: "วัน", + list: "กำหนดการ" + }, + weekLabel: "สัปดาห์", + allDayText: "ตลอดวัน", + eventLimitText: "เพิ่มเติม", + noEventsMessage: "ไม่มีกิจกรรมที่จะแสดง" + }; + + return th; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/tr.js b/addons/web/static/lib/fullcalendar/core/locales/tr.js new file mode 100644 index 00000000000..48458982fb6 --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/ug.js b/addons/web/static/lib/fullcalendar/core/locales/ug.js new file mode 100644 index 00000000000..f13a5c28604 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/core/locales/ug.js @@ -0,0 +1,20 @@ +(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.ug = factory())); +}(this, function () { 'use strict'; + + var ug = { + code: "ug", + buttonText: { + month: "ئاي", + week: "ھەپتە", + day: "كۈن", + list: "كۈنتەرتىپ" + }, + allDayText: "پۈتۈن كۈن" + }; + + return ug; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/uk.js b/addons/web/static/lib/fullcalendar/core/locales/uk.js new file mode 100644 index 00000000000..de33f250c3e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/uz.js b/addons/web/static/lib/fullcalendar/core/locales/uz.js new file mode 100644 index 00000000000..24089756dcf --- /dev/null +++ b/addons/web/static/lib/fullcalendar/core/locales/uz.js @@ -0,0 +1,24 @@ +(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.uz = factory())); +}(this, function () { 'use strict'; + + var uz = { + code: "uz", + buttonText: { + month: "Oy", + week: "Xafta", + day: "Kun", + list: "Kun tartibi" + }, + allDayText: "Kun bo'yi", + eventLimitText: function (n) { + return "+ yana " + n; + }, + noEventsMessage: "Ko'rsatish uchun voqealar yo'q" + }; + + return uz; + +})); diff --git a/addons/web/static/lib/fullcalendar/core/locales/vi.js b/addons/web/static/lib/fullcalendar/core/locales/vi.js new file mode 100644 index 00000000000..167ce11d74b --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/zh-cn.js b/addons/web/static/lib/fullcalendar/core/locales/zh-cn.js new file mode 100644 index 00000000000..4debbb9e60e --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/locales/zh-tw.js b/addons/web/static/lib/fullcalendar/core/locales/zh-tw.js new file mode 100644 index 00000000000..bc14dcd4bca --- /dev/null +++ b/addons/web/static/lib/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/addons/web/static/lib/fullcalendar/core/main.css b/addons/web/static/lib/fullcalendar/core/main.css new file mode 100644 index 00000000000..0bbe9a35b31 --- /dev/null +++ b/addons/web/static/lib/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 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 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 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 */ + } +} \ No newline at end of file diff --git a/addons/web/static/lib/fullcalendar/core/main.js b/addons/web/static/lib/fullcalendar/core/main.js new file mode 100644 index 00000000000..ed160ddf423 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/core/main.js @@ -0,0 +1,8740 @@ +/*! +FullCalendar Core Package v4.4.0 +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 = { + '= 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' + } + }, '
'); + 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 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(context, segs, isMirror) { + var calendar = context.calendar, view = context.view; + if (calendar.hasPublicHandlers('eventRender')) { + segs = segs.filter(function (seg) { + var custom = calendar.publiclyTrigger('eventRender', [ + { + event: new EventApi(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); + } + // triggers + function triggerRenderedSegs(context, segs, isMirrors) { + var calendar = context.calendar, view = context.view; + if (calendar.hasPublicHandlers('eventPositioned')) { + for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) { + var seg = segs_2[_i]; + calendar.publiclyTriggerAfterSizing('eventPositioned', [ + { + event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance), + isMirror: isMirrors, + isStart: seg.isStart, + isEnd: seg.isEnd, + el: seg.el, + view: view + } + ]); + } + } + if (!calendar.state.loadingLevel) { // avoid initial empty state while pending + calendar.afterSizingTriggers._eventsPositioned = [null]; // fire once + } + } + function triggerWillRemoveSegs(context, segs, isMirrors) { + var calendar = context.calendar, view = context.view; + for (var _i = 0, segs_3 = segs; _i < segs_3.length; _i++) { + var seg = segs_3[_i]; + calendar.trigger('eventElRemove', seg.el); + } + if (calendar.hasPublicHandlers('eventDestroy')) { + for (var _a = 0, segs_4 = segs; _a < segs_4.length; _a++) { + var seg = segs_4[_a]; + calendar.publiclyTrigger('eventDestroy', [ + { + event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance), + isMirror: isMirrors, + el: seg.el, + view: view + } + ]); + } + } + } + // is-interactable + function computeEventDraggable(context, eventDef, eventUi) { + var calendar = context.calendar, view = context.view; + var transformers = 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, view); + } + return val; + } + function computeEventStartResizable(context, eventDef, eventUi) { + return eventUi.durationEditable && context.options.eventResizableFromStart; + } + function computeEventEndResizable(context, eventDef, eventUi) { + return eventUi.durationEditable; + } + + // 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, '&') + .replace(//g, '>') + .replace(/'/g, ''') + .replace(/"/g, '"') + .replace(/\n/g, '
'); + } + // 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
tag or a non-clickable 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(allOptions, dateEnv, gotoOptions, attrs, innerHtml) { + 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 && allOptions.navLinks) { + return '' + + innerHtml + + ''; + } + else { + return '' + + innerHtml + + ''; + } + } + function getAllDayHtml(allOptions) { + return allOptions.allDayHtml || htmlEscape(allOptions.allDayText); + } + // Computes HTML classNames for a single-day element + function getDayClasses(date, dateProfile, context, noThemeHighlight) { + var calendar = context.calendar, options = context.options, 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 (options.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 ComponentContext = /** @class */ (function () { + function ComponentContext(calendar, theme, dateEnv, options, view) { + this.calendar = calendar; + this.theme = theme; + this.dateEnv = dateEnv; + this.options = options; + this.view = view; + this.isRtl = options.dir === 'rtl'; + this.eventOrderSpecs = parseFieldSpecs(options.eventOrder); + this.nextDayThreshold = createDuration(options.nextDayThreshold); + } + ComponentContext.prototype.extend = function (options, view) { + return new ComponentContext(this.calendar, this.theme, this.dateEnv, options || this.options, view || this.view); + }; + return ComponentContext; + }()); + var Component = /** @class */ (function () { + function Component() { + this.uid = String(guid++); + } + Component.addEqualityFuncs = function (newFuncs) { + this.prototype.equalityFuncs = __assign({}, this.prototype.equalityFuncs, newFuncs); + }; + Component.prototype.receiveProps = function (props, context) { + var oldContext = this.context; + this.context = context; + if (!oldContext) { + this.firstContext(context); + } + var _a = recycleProps(this.props || {}, props, this.equalityFuncs), anyChanges = _a.anyChanges, comboProps = _a.comboProps; + this.props = comboProps; + if (anyChanges) { + if (oldContext) { + this.beforeUpdate(); + } + this.render(comboProps, context); + if (oldContext) { + this.afterUpdate(); + } + } + }; + Component.prototype.render = function (props, context) { + }; + Component.prototype.firstContext = function (context) { + }; + Component.prototype.beforeUpdate = function () { + }; + Component.prototype.afterUpdate = function () { + }; + // 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(el) { + var _this = _super.call(this) || this; + _this.el = el; + return _this; + } + DateComponent.prototype.destroy = function () { + _super.prototype.destroy.call(this); + removeElement(this.el); + }; + // 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.context.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 calendar = this.context.calendar; + var dateProfile = this.props.dateProfile; // HACK + if (dateProfile && // HACK for DayTile + !rangeContainsRange(dateProfile.validRange, selection.range)) { + return false; + } + return isDateSelectionValid(selection, calendar); + }; + // 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) { + if (!Object.keys(updates).length && !removals.length) { + return; + } + 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 || + eventSource.isFetching || // always cancel outdated in-progress fetches + 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 // also serves as a marker that at least one fetch has completed + }), _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(extraClassName) { + var _this = _super.call(this) || 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.context, 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('

 

')); // 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 = ""; + buttonAriaAttr = ' aria-label="' + buttonName + '"'; + } + buttonEl = htmlToElement(// type="button" so that it doesn't submit a form + ''); + 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 theme = this.context.theme; + var className = 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(el) { + var _this = _super.call(this) || this; + _this.elClassNames = []; + _this.renderSkeleton = memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton); + _this.renderToolbars = memoizeRendering(_this._renderToolbars, _this._unrenderToolbars, [_this.renderSkeleton]); + _this.buildComponentContext = memoize(buildComponentContext); + _this.buildViewPropTransformers = memoize(buildViewPropTransformers); + _this.el = el; + _this.computeTitle = memoize(computeTitle); + _this.parseBusinessHours = memoize(function (input) { + return parseBusinessHours(input, _this.context.calendar); + }); + return _this; + } + CalendarComponent.prototype.render = function (props, context) { + this.freezeHeight(); + var title = this.computeTitle(props.dateProfile, props.viewSpec.options); + this.renderSkeleton(context); + this.renderToolbars(props.viewSpec, props.dateProfile, props.currentDate, title); + this.renderView(props, title); + this.updateSize(); + this.thawHeight(); + }; + CalendarComponent.prototype.destroy = function () { + if (this.header) { + this.header.destroy(); + } + if (this.footer) { + this.footer.destroy(); + } + this.renderSkeleton.unrender(); // will call destroyView + _super.prototype.destroy.call(this); + }; + CalendarComponent.prototype._renderSkeleton = function (context) { + this.updateElClassNames(context); + prependToElement(this.el, this.contentEl = createElement('div', { className: 'fc-view-container' })); + var calendar = context.calendar; + for (var _i = 0, _a = calendar.pluginSystem.hooks.viewContainerModifiers; _i < _a.length; _i++) { + var modifyViewContainer = _a[_i]; + modifyViewContainer(this.contentEl, calendar); + } + }; + CalendarComponent.prototype._unrenderSkeleton = function () { + // weird to have this here + if (this.view) { + this.savedScroll = this.view.queryScroll(); + this.view.destroy(); + this.view = null; + } + removeElement(this.contentEl); + this.removeElClassNames(); + }; + CalendarComponent.prototype.removeElClassNames = function () { + var classList = this.el.classList; + for (var _i = 0, _a = this.elClassNames; _i < _a.length; _i++) { + var className = _a[_i]; + classList.remove(className); + } + this.elClassNames = []; + }; + CalendarComponent.prototype.updateElClassNames = function (context) { + this.removeElClassNames(); + var theme = context.theme, options = context.options; + this.elClassNames = [ + 'fc', + 'fc-' + options.dir, + theme.getClass('widget') + ]; + var classList = this.el.classList; + for (var _i = 0, _a = this.elClassNames; _i < _a.length; _i++) { + var className = _a[_i]; + classList.add(className); + } + }; + CalendarComponent.prototype._renderToolbars = function (viewSpec, dateProfile, currentDate, title) { + var _a = this, context = _a.context, header = _a.header, footer = _a.footer; + var options = context.options, calendar = context.calendar; + var headerLayout = options.header; + var footerLayout = options.footer; + var dateProfileGenerator = this.props.dateProfileGenerator; + var now = 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 (!header) { + header = this.header = new Toolbar('fc-header-toolbar'); + prependToElement(this.el, header.el); + } + header.receiveProps(__assign({ layout: headerLayout }, toolbarProps), context); + } + else if (header) { + header.destroy(); + header = this.header = null; + } + if (footerLayout) { + if (!footer) { + footer = this.footer = new Toolbar('fc-footer-toolbar'); + appendToElement(this.el, footer.el); + } + footer.receiveProps(__assign({ layout: footerLayout }, toolbarProps), context); + } + else if (footer) { + footer.destroy(); + footer = this.footer = null; + } + }; + CalendarComponent.prototype._unrenderToolbars = function () { + if (this.header) { + this.header.destroy(); + this.header = null; + } + if (this.footer) { + this.footer.destroy(); + this.footer = null; + } + }; + CalendarComponent.prototype.renderView = function (props, title) { + var view = this.view; + var _a = this.context, calendar = _a.calendar, options = _a.options; + var viewSpec = props.viewSpec, dateProfileGenerator = props.dateProfileGenerator; + if (!view || view.viewSpec !== viewSpec) { + if (view) { + view.destroy(); + } + view = this.view = new viewSpec['class'](viewSpec, this.contentEl); + if (this.savedScroll) { + view.addScroll(this.savedScroll, true); + this.savedScroll = null; + } + } + view.title = title; // for the API + var viewProps = { + dateProfileGenerator: dateProfileGenerator, + 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(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, options)); + } + view.receiveProps(viewProps, this.buildComponentContext(this.context, viewSpec, view)); + }; + // Sizing + // ----------------------------------------------------------------------------------------------------------------- + CalendarComponent.prototype.updateSize = function (isResize) { + if (isResize === void 0) { isResize = false; } + var view = this.view; + if (!view) { + return; // why? + } + 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.context.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.context.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' }; + } + } + } + // build a context scoped to the view + function buildComponentContext(context, viewSpec, view) { + return context.extend(viewSpec.options, view); + } + // 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 _a = component.context, calendar = _a.calendar, view = _a.view; + var seg = getElSeg(segEl); + if (seg && // might be the
surrounding the more link + component.isValidSegDownEl(ev.target)) { + // our way to simulate a link click for elements that can't be 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 : ''; + calendar.publiclyTrigger('eventClick', [ + { + el: segEl, + event: new EventApi(component.context.calendar, seg.eventRange.def, seg.eventRange.instance), + jsEvent: ev, + view: 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); + // how to make sure component already has context? + component.context.calendar.on('eventElRemove', _this.handleEventElRemove); + return _this; + } + EventHovering.prototype.destroy = function () { + this.removeHoverListeners(); + this.component.context.calendar.off('eventElRemove', this.handleEventElRemove); + }; + EventHovering.prototype.triggerEvent = function (publicEvName, ev, segEl) { + var component = this.component; + var _a = component.context, calendar = _a.calendar, view = _a.view; + var seg = getElSeg(segEl); + if (!ev || component.isValidSegDownEl(ev.target)) { + calendar.publiclyTrigger(publicEvName, [ + { + el: segEl, + event: new EventApi(calendar, seg.eventRange.def, seg.eventRange.instance), + jsEvent: ev, + view: 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.buildComponentContext = memoize(buildComponentContext$1); + 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.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.component = new CalendarComponent(this.el); + this.renderableEventStore = createEmptyEventStore(); + this.bindHandlers(); + this.executeRender(); + } + else { + this.requestRerender(); + } + }; + 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) { + if (oldState.eventStore) { + this.isEventsUpdated = true; + } + } + if (oldState.dateProfile !== newState.dateProfile) { + 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) { + 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 () { + this.needsRerender = true; + 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 () { + // clear these BEFORE the render so that new values will accumulate during render + this.needsRerender = false; + this.isRendering = true; + this.renderComponent(); + 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 () { + var _a = this, state = _a.state, component = _a.component; + var viewType = state.viewType; + var viewSpec = this.viewSpecs[viewType]; + 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); + component.receiveProps(__assign({}, state, { viewSpec: viewSpec, dateProfileGenerator: this.dateProfileGenerators[viewType], dateProfile: state.dateProfile, eventStore: renderableEventStore, eventUiBases: eventUiBases, dateSelection: state.dateSelection, eventSelection: state.eventSelection, eventDrag: state.eventDrag, eventResize: state.eventResize }), this.buildComponentContext(this.theme, this.dateEnv, this.optionsManager.computed)); + 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.batchRendering(function () { + if (anyDifficultOptions) { + if (isTimeZoneDirty) { + _this.dispatch({ + type: 'CHANGE_TIMEZONE', + oldDateEnv: oldDateEnv + }); + } + /* HACK + has the same effect as calling this.requestRerender() + 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 buildComponentContext$1(theme, dateEnv, options) { + return new ComponentContext(this, theme, dateEnv, options, null); + } + 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(viewSpec, parentEl) { + var _this = _super.call(this, createElement('div', { className: 'fc-view fc-' + viewSpec.type + '-view' })) || 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.type = viewSpec.type; + parentEl.appendChild(_this.el); + _this.initialize(); + return _this; + } + View.prototype.initialize = function () { + }; + Object.defineProperty(View.prototype, "activeStart", { + // Date Setting/Unsetting + // ----------------------------------------------------------------------------------------------------------------- + get: function () { + return this.context.dateEnv.toDate(this.props.dateProfile.activeRange.start); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(View.prototype, "activeEnd", { + get: function () { + return this.context.dateEnv.toDate(this.props.dateProfile.activeRange.end); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(View.prototype, "currentStart", { + get: function () { + return this.context.dateEnv.toDate(this.props.dateProfile.currentRange.start); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(View.prototype, "currentEnd", { + get: function () { + return this.context.dateEnv.toDate(this.props.dateProfile.currentRange.end); + }, + enumerable: true, + configurable: true + }); + // General Rendering + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.render = function (props, context) { + 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.beforeUpdate = function () { + this.addScroll(this.queryScroll()); + }; + 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.context.calendar; + if (isResize) { + this.addScroll(this.queryScroll()); // NOTE: same code as in beforeUpdate + } + 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); + } + // NOTE: popScroll is called by CalendarComponent + }; + View.prototype.updateBaseSize = function (isResize, viewHeight, isAuto) { + }; + // Date Rendering + // ----------------------------------------------------------------------------------------------------------------- + View.prototype.renderDatesWrap = function (dateProfile) { + this.renderDates(dateProfile); + this.addScroll({ + duration: createDuration(this.context.options.scrollTime) + }); + }; + 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.context.nextDayThreshold : null).fg; + }; + // 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 + // USAGE: must be called manually from subclasses' render methods! don't need to call stopNowIndicator tho + View.prototype.startNowIndicator = function (dateProfile, dateProfileGenerator) { + var _this = this; + var _a = this.context, calendar = _a.calendar, dateEnv = _a.dateEnv, options = _a.options; + var unit; + var update; + var delay; // ms wait value + if (options.nowIndicator && !this.initialNowDate) { + unit = this.getNowIndicatorUnit(dateProfile, dateProfileGenerator); + if (unit) { + update = this.updateNowIndicator.bind(this); + this.initialNowDate = 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.nowIndicatorTimeoutID) { + clearTimeout(this.nowIndicatorTimeoutID); + this.nowIndicatorTimeoutID = null; + } + if (this.nowIndicatorIntervalID) { + clearInterval(this.nowIndicatorIntervalID); + this.nowIndicatorIntervalID = null; + } + if (this.isNowIndicatorRendered) { + this.unrenderNowIndicator(); + this.isNowIndicatorRendered = false; + } + }; + View.prototype.getNowIndicatorUnit = function (dateProfile, dateProfileGenerator) { + // 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, isForced) { + if (isForced) { + scroll.isForced = isForced; + } + __assign(this.queuedScroll || (this.queuedScroll = {}), scroll); + }; + View.prototype.popScroll = function (isResize) { + this.applyQueuedScroll(isResize); + this.queuedScroll = null; + }; + View.prototype.applyQueuedScroll = function (isResize) { + if (this.queuedScroll) { + 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, isForced = scroll.isForced; + if (duration != null && !isForced) { + 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() { + this.segs = []; + this.isSizeDirty = false; + } + FgEventRenderer.prototype.renderSegs = function (context, segs, mirrorInfo) { + this.context = context; + 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; + triggerRenderedSegs(this.context, this.segs, Boolean(mirrorInfo)); + }; + FgEventRenderer.prototype.unrender = function (context, _segs, mirrorInfo) { + triggerWillRemoveSegs(this.context, 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, 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.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 + }); + } + + /* + TODO: when refactoring this class, make a new FillRenderer instance for each `type` + */ + var FillRenderer = /** @class */ (function () { + function FillRenderer() { + this.fillSegTag = 'div'; + this.dirtySizeFlags = {}; + this.containerElsByType = {}; + this.segsByType = {}; + } + FillRenderer.prototype.getSegsByType = function (type) { + return this.segsByType[type] || []; + }; + FillRenderer.prototype.renderSegs = function (type, context, segs) { + var _a; + this.context = context; + 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') { + triggerRenderedSegs(context, 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, context) { + var segs = this.segsByType[type]; + if (segs) { + if (type === 'bgEvent') { + triggerWillRemoveSegs(context, 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, 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) + '"' : '') + + '>'; + }; + 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 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 '' + + ' 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(options, dateEnv, { date: dateMarker, forceOff: !datesRepDistinctDays || colCnt === 1 }, innerHtml) : + // if not valid, display text, but no link + innerHtml) + + ''; + } + + var DayHeader = /** @class */ (function (_super) { + __extends(DayHeader, _super); + function DayHeader(parentEl) { + var _this = _super.call(this) || this; + _this.renderSkeleton = memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton); + _this.parentEl = parentEl; + return _this; + } + DayHeader.prototype.render = function (props, context) { + var dates = props.dates, datesRepDistinctDays = props.datesRepDistinctDays; + var parts = []; + this.renderSkeleton(context); + if (props.renderIntroHtml) { + parts.push(props.renderIntroHtml()); + } + var colHeadFormat = createFormatter(context.options.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, context)); + } + if (context.isRtl) { + parts.reverse(); + } + this.thead.innerHTML = '' + parts.join('') + ''; + }; + DayHeader.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderSkeleton.unrender(); + }; + DayHeader.prototype._renderSkeleton = function (context) { + var theme = context.theme; + var parentEl = this.parentEl; + parentEl.innerHTML = ''; // because might be nbsp + parentEl.appendChild(this.el = htmlToElement('
' + + '' + + '' + + '
' + + '
')); + this.thead = this.el.querySelector('thead'); + }; + DayHeader.prototype._unrenderSkeleton = function () { + removeElement(this.el); + }; + 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, calendar, component) { + var extraArgs = []; + for (var _i = 5; _i < arguments.length; _i++) { + extraArgs[_i - 5] = 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, calendar, 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, calendar, component) { + var extraArgs = []; + for (var _i = 5; _i < arguments.length; _i++) { + extraArgs[_i - 5] = arguments[_i]; + } + if (!businessHours) { + return []; + } + return this._sliceEventStore.apply(this, [expandRecurring(businessHours, computeActiveRange(dateProfile, Boolean(nextDayThreshold)), 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.context.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.4.0'; + + exports.Calendar = Calendar; + exports.Component = Component; + exports.ComponentContext = ComponentContext; + 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.computeEventDraggable = computeEventDraggable; + exports.computeEventEndResizable = computeEventEndResizable; + exports.computeEventStartResizable = computeEventStartResizable; + 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/addons/web/static/lib/fullcalendar/css/fullcalendar.css b/addons/web/static/lib/fullcalendar/css/fullcalendar.css deleted file mode 100644 index 0fa5d9ea419..00000000000 --- a/addons/web/static/lib/fullcalendar/css/fullcalendar.css +++ /dev/null @@ -1,1402 +0,0 @@ -/*! - * FullCalendar v3.0.1 Stylesheet - * Docs & License: http://fullcalendar.io/ - * (c) 2016 Adam Shaw - */ - - -.fc { - direction: ltr; - text-align: left; -} - -.fc-rtl { - text-align: right; -} - -body .fc { /* extra precedence to overcome jqui */ - font-size: 1em; -} - - -/* 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, /* for gutter border */ -.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 .fc-popover .fc-header .fc-close { - color: #666; -} - -.fc-unthemed .fc-today { - background: #fcf8e3; -} - -.fc-highlight { /* when user is selecting cells */ - background: #bce8f1; - opacity: .3; -} - -.fc-bgevent { /* default look for background events */ - background: rgb(143, 223, 130); - opacity: .3; -} - -.fc-nonbusiness { /* default look for non-business-hours areas */ - /* will inherit .fc-bgevent's styles */ - background: #d7d7d7; -} - - -/* Icons (inline elements with styled text that mock arrow icons) ---------------------------------------------------------------------------------------------------*/ - -.fc-icon { - display: inline-block; - height: 1em; - line-height: 1em; - font-size: 1em; - text-align: center; - overflow: hidden; - font-family: "Courier New", Courier, monospace; - - /* don't allow browser text-selection */ - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - -/* -Acceptable font-family overrides for individual icons: - "Arial", sans-serif - "Times New Roman", serif - -NOTE: use percentage font sizes or else old IE chokes -*/ - -.fc-icon:after { - position: relative; -} - -.fc-icon-left-single-arrow:after { - content: "\02039"; - font-weight: bold; - font-size: 200%; - top: -7%; -} - -.fc-icon-right-single-arrow:after { - content: "\0203A"; - font-weight: bold; - font-size: 200%; - top: -7%; -} - -.fc-icon-left-double-arrow:after { - content: "\000AB"; - font-size: 160%; - top: -7%; -} - -.fc-icon-right-double-arrow:after { - content: "\000BB"; - font-size: 160%; - top: -7%; -} - -.fc-icon-left-triangle:after { - content: "\25C4"; - font-size: 125%; - top: 3%; -} - -.fc-icon-right-triangle:after { - content: "\25BA"; - font-size: 125%; - top: 3%; -} - -.fc-icon-down-triangle:after { - content: "\25BC"; - font-size: 125%; - top: 2%; -} - -.fc-icon-x:after { - content: "\000D7"; - font-size: 200%; - top: 6%; -} - - -/* Buttons (styled
' - ) - .click(function(ev) { - // don't process clicks for disabled buttons - if (!button.hasClass(tm + '-state-disabled')) { - - buttonClick(ev); - - // after the click action, if the button becomes the "active" tab, or disabled, - // it should never have a hover class, so remove it now. - if ( - button.hasClass(tm + '-state-active') || - button.hasClass(tm + '-state-disabled') - ) { - button.removeClass(tm + '-state-hover'); - } - } - }) - .mousedown(function() { - // the *down* effect (mouse pressed in). - // only on buttons that are not the "active" tab, or disabled - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-down'); - }) - .mouseup(function() { - // undo the *down* effect - button.removeClass(tm + '-state-down'); - }) - .hover( - function() { - // the *hover* effect. - // only on buttons that are not the "active" tab, or disabled - button - .not('.' + tm + '-state-active') - .not('.' + tm + '-state-disabled') - .addClass(tm + '-state-hover'); - }, - function() { - // undo the *hover* effect - button - .removeClass(tm + '-state-hover') - .removeClass(tm + '-state-down'); // if mouseleave happens before mouseup - } - ); - - groupChildren = groupChildren.add(button); - } - } - }); - - if (isOnlyButtons) { - groupChildren - .first().addClass(tm + '-corner-left').end() - .last().addClass(tm + '-corner-right').end(); - } - - if (groupChildren.length > 1) { - groupEl = $('
'); - if (isOnlyButtons) { - groupEl.addClass('fc-button-group'); - } - groupEl.append(groupChildren); - sectionEl.append(groupEl); - } - else { - sectionEl.append(groupChildren); // 1 or 0 children - } - }); - } - - return sectionEl; - } - - - function updateTitle(text) { - if (el) { - el.find('h2').text(text); - } - } - - - function activateButton(buttonName) { - if (el) { - el.find('.fc-' + buttonName + '-button') - .addClass(tm + '-state-active'); - } - } - - - function deactivateButton(buttonName) { - if (el) { - el.find('.fc-' + buttonName + '-button') - .removeClass(tm + '-state-active'); - } - } - - - function disableButton(buttonName) { - if (el) { - el.find('.fc-' + buttonName + '-button') - .prop('disabled', true) - .addClass(tm + '-state-disabled'); - } - } - - - function enableButton(buttonName) { - if (el) { - el.find('.fc-' + buttonName + '-button') - .prop('disabled', false) - .removeClass(tm + '-state-disabled'); - } - } - - - function getViewsWithButtons() { - return viewsWithButtons; - } - -} - -;; - -FC.sourceNormalizers = []; -FC.sourceFetchers = []; - -var ajaxDefaults = { - dataType: 'json', - cache: false -}; - -var eventGUID = 1; - - -function EventManager() { // assumed to be a calendar - var t = this; - - - // exports - t.isFetchNeeded = isFetchNeeded; - t.fetchEvents = fetchEvents; - t.fetchEventSources = fetchEventSources; - t.getEventSources = getEventSources; - t.getEventSourceById = getEventSourceById; - t.getEventSourcesByMatchArray = getEventSourcesByMatchArray; - t.getEventSourcesByMatch = getEventSourcesByMatch; - t.addEventSource = addEventSource; - t.removeEventSource = removeEventSource; - t.removeEventSources = removeEventSources; - t.updateEvent = updateEvent; - t.renderEvent = renderEvent; - t.removeEvents = removeEvents; - t.clientEvents = clientEvents; - t.mutateEvent = mutateEvent; - t.normalizeEventDates = normalizeEventDates; - t.normalizeEventTimes = normalizeEventTimes; - - - // imports - var reportEvents = t.reportEvents; - - - // locals - var stickySource = { events: [] }; - var sources = [ stickySource ]; - var rangeStart, rangeEnd; - var pendingSourceCnt = 0; // outstanding fetch requests, max one per source - var cache = []; // holds events that have already been expanded - - - $.each( - (t.options.events ? [ t.options.events ] : []).concat(t.options.eventSources || []), - function(i, sourceInput) { - var source = buildEventSource(sourceInput); - if (source) { - sources.push(source); - } - } - ); - - - - /* Fetching - -----------------------------------------------------------------------------*/ - - - // start and end are assumed to be unzoned - function isFetchNeeded(start, end) { - return !rangeStart || // nothing has been fetched yet? - start < rangeStart || end > rangeEnd; // is part of the new range outside of the old range? - } - - - function fetchEvents(start, end) { - rangeStart = start; - rangeEnd = end; - fetchEventSources(sources, 'reset'); - } - - - // expects an array of event source objects (the originals, not copies) - // `specialFetchType` is an optimization parameter that affects purging of the event cache. - function fetchEventSources(specificSources, specialFetchType) { - var i, source; - - if (specialFetchType === 'reset') { - cache = []; - } - else if (specialFetchType !== 'add') { - cache = excludeEventsBySources(cache, specificSources); - } - - for (i = 0; i < specificSources.length; i++) { - source = specificSources[i]; - - // already-pending sources have already been accounted for in pendingSourceCnt - if (source._status !== 'pending') { - pendingSourceCnt++; - } - - source._fetchId = (source._fetchId || 0) + 1; - source._status = 'pending'; - } - - for (i = 0; i < specificSources.length; i++) { - source = specificSources[i]; - - tryFetchEventSource(source, source._fetchId); - } - } - - - // fetches an event source and processes its result ONLY if it is still the current fetch. - // caller is responsible for incrementing pendingSourceCnt first. - function tryFetchEventSource(source, fetchId) { - _fetchEventSource(source, function(eventInputs) { - var isArraySource = $.isArray(source.events); - var i, eventInput; - var abstractEvent; - - if ( - // is this the source's most recent fetch? - // if not, rely on an upcoming fetch of this source to decrement pendingSourceCnt - fetchId === source._fetchId && - // event source no longer valid? - source._status !== 'rejected' - ) { - source._status = 'resolved'; - - if (eventInputs) { - for (i = 0; i < eventInputs.length; i++) { - eventInput = eventInputs[i]; - - if (isArraySource) { // array sources have already been convert to Event Objects - abstractEvent = eventInput; - } - else { - abstractEvent = buildEventFromInput(eventInput, source); - } - - if (abstractEvent) { // not false (an invalid event) - cache.push.apply( - cache, - expandEvent(abstractEvent) // add individual expanded events to the cache - ); - } - } - } - - decrementPendingSourceCnt(); - } - }); - } - - - function rejectEventSource(source) { - var wasPending = source._status === 'pending'; - - source._status = 'rejected'; - - if (wasPending) { - decrementPendingSourceCnt(); - } - } - - - function decrementPendingSourceCnt() { - pendingSourceCnt--; - if (!pendingSourceCnt) { - reportEvents(cache); - } - } - - - function _fetchEventSource(source, callback) { - var i; - var fetchers = FC.sourceFetchers; - var res; - - for (i=0; i= eventStart && innerSpan.end <= eventEnd; -}; - - -// Returns a list of events that the given event should be compared against when being considered for a move to -// the specified span. Attached to the Calendar's prototype because EventManager is a mixin for a Calendar. -Calendar.prototype.getPeerEvents = function(span, event) { - var cache = this.getEventCache(); - var peerEvents = []; - var i, otherEvent; - - for (i = 0; i < cache.length; i++) { - otherEvent = cache[i]; - if ( - !event || - event._id !== otherEvent._id // don't compare the event to itself or other related [repeating] events - ) { - peerEvents.push(otherEvent); - } - } - - return peerEvents; -}; - - -// updates the "backup" properties, which are preserved in order to compute diffs later on. -function backupEventDates(event) { - event._allDay = event.allDay; - event._start = event.start.clone(); - event._end = event.end ? event.end.clone() : null; -} - - -/* Overlapping / Constraining ------------------------------------------------------------------------------------------*/ - - -// Determines if the given event can be relocated to the given span (unzoned start/end with other misc data) -Calendar.prototype.isEventSpanAllowed = function(span, event) { - var source = event.source || {}; - - var constraint = firstDefined( - event.constraint, - source.constraint, - this.options.eventConstraint - ); - - var overlap = firstDefined( - event.overlap, - source.overlap, - this.options.eventOverlap - ); - - return this.isSpanAllowed(span, constraint, overlap, event) && - (!this.options.eventAllow || this.options.eventAllow(span, event) !== false); -}; - - -// Determines if an external event can be relocated to the given span (unzoned start/end with other misc data) -Calendar.prototype.isExternalSpanAllowed = function(eventSpan, eventLocation, eventProps) { - var eventInput; - var event; - - // note: very similar logic is in View's reportExternalDrop - if (eventProps) { - eventInput = $.extend({}, eventProps, eventLocation); - event = this.expandEvent( - this.buildEventFromInput(eventInput) - )[0]; - } - - if (event) { - return this.isEventSpanAllowed(eventSpan, event); - } - else { // treat it as a selection - - return this.isSelectionSpanAllowed(eventSpan); - } -}; - - -// Determines the given span (unzoned start/end with other misc data) can be selected. -Calendar.prototype.isSelectionSpanAllowed = function(span) { - return this.isSpanAllowed(span, this.options.selectConstraint, this.options.selectOverlap) && - (!this.options.selectAllow || this.options.selectAllow(span) !== false); -}; - - -// Returns true if the given span (caused by an event drop/resize or a selection) is allowed to exist -// according to the constraint/overlap settings. -// `event` is not required if checking a selection. -Calendar.prototype.isSpanAllowed = function(span, constraint, overlap, event) { - var constraintEvents; - var anyContainment; - var peerEvents; - var i, peerEvent; - var peerOverlap; - - // the range must be fully contained by at least one of produced constraint events - if (constraint != null) { - - // not treated as an event! intermediate data structure - // TODO: use ranges in the future - constraintEvents = this.constraintToEvents(constraint); - if (constraintEvents) { // not invalid - - anyContainment = false; - for (i = 0; i < constraintEvents.length; i++) { - if (this.spanContainsSpan(constraintEvents[i], span)) { - anyContainment = true; - break; - } - } - - if (!anyContainment) { - return false; - } - } - } - - peerEvents = this.getPeerEvents(span, event); - - for (i = 0; i < peerEvents.length; i++) { - peerEvent = peerEvents[i]; - - // there needs to be an actual intersection before disallowing anything - if (this.eventIntersectsRange(peerEvent, span)) { - - // evaluate overlap for the given range and short-circuit if necessary - if (overlap === false) { - return false; - } - // if the event's overlap is a test function, pass the peer event in question as the first param - else if (typeof overlap === 'function' && !overlap(peerEvent, event)) { - return false; - } - - // if we are computing if the given range is allowable for an event, consider the other event's - // EventObject-specific or Source-specific `overlap` property - if (event) { - peerOverlap = firstDefined( - peerEvent.overlap, - (peerEvent.source || {}).overlap - // we already considered the global `eventOverlap` - ); - if (peerOverlap === false) { - return false; - } - // if the peer event's overlap is a test function, pass the subject event as the first param - if (typeof peerOverlap === 'function' && !peerOverlap(event, peerEvent)) { - return false; - } - } - } - } - - return true; -}; - - -// Given an event input from the API, produces an array of event objects. Possible event inputs: -// 'businessHours' -// An event ID (number or string) -// An object with specific start/end dates or a recurring event (like what businessHours accepts) -Calendar.prototype.constraintToEvents = function(constraintInput) { - - if (constraintInput === 'businessHours') { - return this.getCurrentBusinessHourEvents(); - } - - if (typeof constraintInput === 'object') { - if (constraintInput.start != null) { // needs to be event-like input - return this.expandEvent(this.buildEventFromInput(constraintInput)); - } - else { - return null; // invalid - } - } - - return this.clientEvents(constraintInput); // probably an ID -}; - - -// Does the event's date range intersect with the given range? -// start/end already assumed to have stripped zones :( -Calendar.prototype.eventIntersectsRange = function(event, range) { - var eventStart = event.start.clone().stripZone(); - var eventEnd = this.getEventEnd(event).stripZone(); - - return range.start < eventEnd && range.end > eventStart; -}; - - -/* Business Hours ------------------------------------------------------------------------------------------*/ - -var BUSINESS_HOUR_EVENT_DEFAULTS = { - id: '_fcBusinessHours', // will relate events from different calls to expandEvent - start: '09:00', - end: '17:00', - dow: [ 1, 2, 3, 4, 5 ], // monday - friday - rendering: 'inverse-background' - // classNames are defined in businessHoursSegClasses -}; - -// Return events objects for business hours within the current view. -// Abuse of our event system :( -Calendar.prototype.getCurrentBusinessHourEvents = function(wholeDay) { - return this.computeBusinessHourEvents(wholeDay, this.options.businessHours); -}; - -// Given a raw input value from options, return events objects for business hours within the current view. -Calendar.prototype.computeBusinessHourEvents = function(wholeDay, input) { - if (input === true) { - return this.expandBusinessHourEvents(wholeDay, [ {} ]); - } - else if ($.isPlainObject(input)) { - return this.expandBusinessHourEvents(wholeDay, [ input ]); - } - else if ($.isArray(input)) { - return this.expandBusinessHourEvents(wholeDay, input, true); - } - else { - return []; - } -}; - -// inputs expected to be an array of objects. -// if ignoreNoDow is true, will ignore entries that don't specify a day-of-week (dow) key. -Calendar.prototype.expandBusinessHourEvents = function(wholeDay, inputs, ignoreNoDow) { - var view = this.getView(); - var events = []; - var i, input; - - for (i = 0; i < inputs.length; i++) { - input = inputs[i]; - - if (ignoreNoDow && !input.dow) { - continue; - } - - // give defaults. will make a copy - input = $.extend({}, BUSINESS_HOUR_EVENT_DEFAULTS, input); - - // if a whole-day series is requested, clear the start/end times - if (wholeDay) { - input.start = null; - input.end = null; - } - - events.push.apply(events, // append - this.expandEvent( - this.buildEventFromInput(input), - view.start, - view.end - ) - ); - } - - return events; -}; - -;; - -/* An abstract class for the "basic" 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 BasicView = FC.BasicView = View.extend({ - - scroller: null, - - dayGridClass: DayGrid, // class the dayGrid will be instantiated from (overridable by subclasses) - dayGrid: null, // the main subcomponent that does most of the heavy lifting - - dayNumbersVisible: false, // display day numbers on each day cell? - colWeekNumbersVisible: false, // display week numbers along the side? - cellWeekNumbersVisible: false, // display week numbers in day cell? - - weekNumberWidth: null, // width of all the week-number cells running down the side - - headContainerEl: null, // div that hold's the dayGrid's rendered date header - headRowEl: null, // the fake row element of the day-of-week header - - - initialize: function() { - this.dayGrid = this.instantiateDayGrid(); - - this.scroller = new Scroller({ - overflowX: 'hidden', - overflowY: 'auto' - }); - }, - - - // Generates the DayGrid object this view needs. Draws from this.dayGridClass - instantiateDayGrid: function() { - // generate a subclass on the fly with BasicView-specific behavior - // TODO: cache this subclass - var subclass = this.dayGridClass.extend(basicDayGridMethods); - - return new subclass(this); - }, - - - // Sets the display range and computes all necessary dates - setRange: function(range) { - View.prototype.setRange.call(this, range); // call the super-method - - this.dayGrid.breakOnWeeks = /year|month|week/.test(this.intervalUnit); // do before setRange - this.dayGrid.setRange(range); - }, - - - // Compute the value to feed into setRange. Overrides superclass. - computeRange: function(date) { - var range = View.prototype.computeRange.call(this, date); // get value from the super-method - - // year and month views should be aligned with weeks. this is already done for week - if (/year|month/.test(range.intervalUnit)) { - range.start.startOf('week'); - range.start = this.skipHiddenDays(range.start); - - // make end-of-week if not already - if (range.end.weekday()) { - range.end.add(1, 'week').startOf('week'); - range.end = this.skipHiddenDays(range.end, -1, true); // exclusively move backwards - } - } - - return range; - }, - - - // Renders the view into `this.el`, which should already be assigned - renderDates: function() { - - this.dayNumbersVisible = this.dayGrid.rowCnt > 1; // TODO: make grid responsible - if (this.opt('weekNumbers')) { - if (this.opt('weekNumbersWithinDays')) { - this.cellWeekNumbersVisible = true; - this.colWeekNumbersVisible = false; - } - else { - this.cellWeekNumbersVisible = false; - this.colWeekNumbersVisible = true; - }; - } - this.dayGrid.numbersVisible = this.dayNumbersVisible || - this.cellWeekNumbersVisible || this.colWeekNumbersVisible; - - this.el.addClass('fc-basic-view').html(this.renderSkeletonHtml()); - this.renderHead(); - - this.scroller.render(); - var dayGridContainerEl = this.scroller.el.addClass('fc-day-grid-container'); - var dayGridEl = $('
').appendTo(dayGridContainerEl); - this.el.find('.fc-body > tr > td').append(dayGridContainerEl); - - this.dayGrid.setElement(dayGridEl); - this.dayGrid.renderDates(this.hasRigidRows()); - }, - - - // render the day-of-week headers - renderHead: function() { - this.headContainerEl = - this.el.find('.fc-head-container') - .html(this.dayGrid.renderHeadHtml()); - this.headRowEl = this.headContainerEl.find('.fc-row'); - }, - - - // Unrenders the content of the view. Since we haven't separated skeleton rendering from date rendering, - // always completely kill the dayGrid's rendering. - unrenderDates: function() { - this.dayGrid.unrenderDates(); - this.dayGrid.removeElement(); - this.scroller.destroy(); - }, - - - renderBusinessHours: function() { - this.dayGrid.renderBusinessHours(); - }, - - - unrenderBusinessHours: function() { - this.dayGrid.unrenderBusinessHours(); - }, - - - // Builds the HTML skeleton for the view. - // The day-grid component will render inside of a container defined by this HTML. - renderSkeletonHtml: function() { - return '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
'; - }, - - - // Generates an HTML attribute string for setting the width of the week number column, if it is known - weekNumberStyleAttr: function() { - if (this.weekNumberWidth !== null) { - return 'style="width:' + this.weekNumberWidth + 'px"'; - } - return ''; - }, - - - // Determines whether each row should have a constant height - hasRigidRows: function() { - var eventLimit = this.opt('eventLimit'); - return eventLimit && typeof eventLimit !== 'number'; - }, - - - /* Dimensions - ------------------------------------------------------------------------------------------------------------------*/ - - - // Refreshes the horizontal dimensions of the view - updateWidth: function() { - if (this.colWeekNumbersVisible) { - // Make sure all week number cells running down the side have the same width. - // Record the width for cells created later. - this.weekNumberWidth = matchCellWidths( - this.el.find('.fc-week-number') - ); - } - }, - - - // Adjusts the vertical dimensions of the view to the specified values - setHeight: function(totalHeight, isAuto) { - var eventLimit = this.opt('eventLimit'); - var scrollerHeight; - var scrollbarWidths; - - // reset all heights to be natural - this.scroller.clear(); - uncompensateScroll(this.headRowEl); - - this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed - - // is the event limit a constant level number? - if (eventLimit && typeof eventLimit === 'number') { - this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after - } - - // distribute the height to the rows - // (totalHeight is a "recommended" value if isAuto) - scrollerHeight = this.computeScrollerHeight(totalHeight); - this.setGridHeight(scrollerHeight, isAuto); - - // is the event limit dynamically calculated? - if (eventLimit && typeof eventLimit !== 'number') { - this.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? - - compensateScroll(this.headRowEl, scrollbarWidths); - - // doing the scrollbar compensation might have created text overflow which created more height. redo - scrollerHeight = this.computeScrollerHeight(totalHeight); - 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 - computeScrollerHeight: function(totalHeight) { - return totalHeight - - subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller - }, - - - // Sets the height of just the DayGrid component in this view - setGridHeight: function(height, isAuto) { - 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 - ------------------------------------------------------------------------------------------------------------------*/ - - - queryScroll: function() { - return this.scroller.getScrollTop(); - }, - - - setScroll: function(top) { - this.scroller.setScrollTop(top); - }, - - - /* Hit Areas - ------------------------------------------------------------------------------------------------------------------*/ - // forward all hit-related method calls to dayGrid - - - prepareHits: function() { - this.dayGrid.prepareHits(); - }, - - - releaseHits: function() { - this.dayGrid.releaseHits(); - }, - - - queryHit: function(left, top) { - return this.dayGrid.queryHit(left, top); - }, - - - getHitSpan: function(hit) { - return this.dayGrid.getHitSpan(hit); - }, - - - getHitEl: function(hit) { - return this.dayGrid.getHitEl(hit); - }, - - - /* Events - ------------------------------------------------------------------------------------------------------------------*/ - - - // Renders the given events onto the view and populates the segments array - renderEvents: function(events) { - this.dayGrid.renderEvents(events); - - this.updateHeight(); // must compensate for events that overflow the row - }, - - - // Retrieves all segment objects that are rendered in the view - getEventSegs: function() { - return this.dayGrid.getEventSegs(); - }, - - - // Unrenders all event elements and clears internal segment data - unrenderEvents: function() { - this.dayGrid.unrenderEvents(); - - // we DON'T need to call updateHeight() because - // a renderEvents() call always happens after this, which will eventually call updateHeight() - }, - - - /* Dragging (for both events and external elements) - ------------------------------------------------------------------------------------------------------------------*/ - - - // A returned value of `true` signals that a mock "helper" event has been rendered. - renderDrag: function(dropLocation, seg) { - return this.dayGrid.renderDrag(dropLocation, seg); - }, - - - unrenderDrag: function() { - this.dayGrid.unrenderDrag(); - }, - - - /* Selection - ------------------------------------------------------------------------------------------------------------------*/ - - - // Renders a visual indication of a selection - renderSelection: function(span) { - this.dayGrid.renderSelection(span); - }, - - - // Unrenders a visual indications of a selection - unrenderSelection: function() { - this.dayGrid.unrenderSelection(); - } - -}); - - -// Methods that will customize the rendering behavior of the BasicView's dayGrid -var basicDayGridMethods = { - - - // Generates the HTML that will go before the day-of week header cells - renderHeadIntroHtml: function() { - var view = this.view; - - if (view.colWeekNumbersVisible) { - return '' + - '' + - '' + // needed for matchCellWidths - htmlEscape(view.opt('weekNumberTitle')) + - '' + - ''; - } - - return ''; - }, - - - // Generates the HTML that will go before content-skeleton cells that display the day/week numbers - renderNumberIntroHtml: function(row) { - var view = this.view; - var weekStart = this.getCellDate(row, 0); - - if (view.colWeekNumbersVisible) { - return '' + - '' + - view.buildGotoAnchorHtml( // aside from link, important for matchCellWidths - { date: weekStart, type: 'week', forceOff: this.colCnt === 1 }, - weekStart.format('w') // inner HTML - ) + - ''; - } - - return ''; - }, - - - // Generates the HTML that goes before the day bg cells for each day-row - renderBgIntroHtml: function() { - var view = this.view; - - if (view.colWeekNumbersVisible) { - return ''; - } - - return ''; - }, - - - // Generates the HTML that goes before every other type of row generated by DayGrid. - // Affects helper-skeleton and highlight-skeleton rows. - renderIntroHtml: function() { - var view = this.view; - - if (view.colWeekNumbersVisible) { - return ''; - } - - return ''; - } - -}; - -;; - -/* A month view with day cells running in rows (one-per-week) and columns -----------------------------------------------------------------------------------------------------------------------*/ - -var MonthView = FC.MonthView = BasicView.extend({ - - // Produces information about what range to display - computeRange: function(date) { - var range = BasicView.prototype.computeRange.call(this, date); // get value from super-method - var rowCnt; - - // ensure 6 weeks - if (this.isFixedWeeks()) { - rowCnt = Math.ceil(range.end.diff(range.start, 'weeks', true)); // could be partial weeks due to hiddenDays - range.end.add(6 - rowCnt, 'weeks'); - } - - return range; - }, - - - // Overrides the default BasicView behavior to have special multi-week auto-height logic - setGridHeight: function(height, isAuto) { - - // if auto, make the height of each row the height that it would be if there were 6 weeks - if (isAuto) { - height *= this.rowCnt / 6; - } - - distributeHeight(this.dayGrid.rowEls, height, !isAuto); // if auto, don't compensate for height-hogging rows - }, - - - isFixedWeeks: function() { - return this.opt('fixedWeekCount'); - } - -}); - -;; - -fcViews.basic = { - 'class': BasicView -}; - -fcViews.basicDay = { - type: 'basic', - duration: { days: 1 } -}; - -fcViews.basicWeek = { - type: 'basic', - duration: { weeks: 1 } -}; - -fcViews.month = { - 'class': MonthView, - duration: { months: 1 }, // important for prev/next - defaults: { - fixedWeekCount: true - } -}; -;; - -/* An abstract class for all agenda-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 AgendaView = FC.AgendaView = View.extend({ - - scroller: null, - - timeGridClass: TimeGrid, // class used to instantiate the timeGrid. subclasses can override - timeGrid: null, // the main time-grid subcomponent of this view - - dayGridClass: DayGrid, // class used to instantiate the dayGrid. subclasses can override - dayGrid: null, // the "all-day" subcomponent. if all-day is turned off, this will be null - - axisWidth: null, // the width of the time axis running down the side - - headContainerEl: null, // div that hold's the timeGrid's rendered date header - noScrollRowEls: null, // set of fake row elements that must compensate when scroller has scrollbars - - // when the time-grid isn't tall enough to occupy the given height, we render an
underneath - bottomRuleEl: null, - - - initialize: function() { - this.timeGrid = this.instantiateTimeGrid(); - - if (this.opt('allDaySlot')) { // should we display the "all-day" area? - this.dayGrid = this.instantiateDayGrid(); // the all-day subcomponent of this view - } - - this.scroller = new Scroller({ - overflowX: 'hidden', - overflowY: 'auto' - }); - }, - - - // Instantiates the TimeGrid object this view needs. Draws from this.timeGridClass - instantiateTimeGrid: function() { - var subclass = this.timeGridClass.extend(agendaTimeGridMethods); - - return new subclass(this); - }, - - - // Instantiates the DayGrid object this view might need. Draws from this.dayGridClass - instantiateDayGrid: function() { - var subclass = this.dayGridClass.extend(agendaDayGridMethods); - - return new subclass(this); - }, - - - /* Rendering - ------------------------------------------------------------------------------------------------------------------*/ - - - // Sets the display range and computes all necessary dates - setRange: function(range) { - View.prototype.setRange.call(this, range); // call the super-method - - this.timeGrid.setRange(range); - if (this.dayGrid) { - this.dayGrid.setRange(range); - } - }, - - - // Renders the view into `this.el`, which has already been assigned - renderDates: function() { - - this.el.addClass('fc-agenda-view').html(this.renderSkeletonHtml()); - this.renderHead(); - - this.scroller.render(); - var timeGridWrapEl = this.scroller.el.addClass('fc-time-grid-container'); - var timeGridEl = $('
').appendTo(timeGridWrapEl); - this.el.find('.fc-body > tr > td').append(timeGridWrapEl); - - this.timeGrid.setElement(timeGridEl); - this.timeGrid.renderDates(); - - // the
that sometimes displays under the time-grid - this.bottomRuleEl = $('
') - .appendTo(this.timeGrid.el); // inject it into the time-grid - - if (this.dayGrid) { - this.dayGrid.setElement(this.el.find('.fc-day-grid')); - this.dayGrid.renderDates(); - - // have the day-grid extend it's coordinate area over the
dividing the two grids - this.dayGrid.bottomCoordPadding = this.dayGrid.el.next('hr').outerHeight(); - } - - this.noScrollRowEls = this.el.find('.fc-row:not(.fc-scroller *)'); // fake rows not within the scroller - }, - - - // render the day-of-week headers - renderHead: function() { - this.headContainerEl = - this.el.find('.fc-head-container') - .html(this.timeGrid.renderHeadHtml()); - }, - - - // Unrenders the content of the view. Since we haven't separated skeleton rendering from date rendering, - // always completely kill each grid's rendering. - unrenderDates: function() { - this.timeGrid.unrenderDates(); - this.timeGrid.removeElement(); - - if (this.dayGrid) { - this.dayGrid.unrenderDates(); - this.dayGrid.removeElement(); - } - - this.scroller.destroy(); - }, - - - // Builds the HTML skeleton for the view. - // The day-grid and time-grid components will render inside containers defined by this HTML. - renderSkeletonHtml: function() { - return '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
' + - (this.dayGrid ? - '
' + - '
' : - '' - ) + - '
'; - }, - - - // Generates an HTML attribute string for setting the width of the axis, if it is known - axisStyleAttr: function() { - if (this.axisWidth !== null) { - return 'style="width:' + this.axisWidth + 'px"'; - } - return ''; - }, - - - /* Business Hours - ------------------------------------------------------------------------------------------------------------------*/ - - - renderBusinessHours: function() { - this.timeGrid.renderBusinessHours(); - - if (this.dayGrid) { - this.dayGrid.renderBusinessHours(); - } - }, - - - unrenderBusinessHours: function() { - this.timeGrid.unrenderBusinessHours(); - - if (this.dayGrid) { - this.dayGrid.unrenderBusinessHours(); - } - }, - - - /* Now Indicator - ------------------------------------------------------------------------------------------------------------------*/ - - - getNowIndicatorUnit: function() { - return this.timeGrid.getNowIndicatorUnit(); - }, - - - renderNowIndicator: function(date) { - this.timeGrid.renderNowIndicator(date); - }, - - - unrenderNowIndicator: function() { - this.timeGrid.unrenderNowIndicator(); - }, - - - /* Dimensions - ------------------------------------------------------------------------------------------------------------------*/ - - - updateSize: function(isResize) { - this.timeGrid.updateSize(isResize); - - View.prototype.updateSize.call(this, isResize); // call the super-method - }, - - - // Refreshes the horizontal dimensions of the view - updateWidth: function() { - // make all axis cells line up, and record the width so newly created axis cells will have it - this.axisWidth = matchCellWidths(this.el.find('.fc-axis')); - }, - - - // Adjusts the vertical dimensions of the view to the specified values - setHeight: function(totalHeight, isAuto) { - var eventLimit; - var scrollerHeight; - var scrollbarWidths; - - // reset all dimensions back to the original state - this.bottomRuleEl.hide(); // .show() will be called later if this
is necessary - this.scroller.clear(); // sets height to 'auto' and clears overflow - uncompensateScroll(this.noScrollRowEls); - - // 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 = AGENDA_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(totalHeight); - this.scroller.setHeight(scrollerHeight); - scrollbarWidths = this.scroller.getScrollbarWidths(); - - if (scrollbarWidths.left || scrollbarWidths.right) { // using scrollbars? - - // make the all-day and header rows lines up - compensateScroll(this.noScrollRowEls, 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(totalHeight); - 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.bottomRuleEl.show(); - } - } - }, - - - // given a desired total height of the view, returns what the height of the scroller should be - computeScrollerHeight: function(totalHeight) { - return totalHeight - - 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 - computeInitialScroll: function() { - var scrollTime = moment.duration(this.opt('scrollTime')); - var top = this.timeGrid.computeTimeTop(scrollTime); - - // 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; - }, - - - queryScroll: function() { - return this.scroller.getScrollTop(); - }, - - - setScroll: function(top) { - this.scroller.setScrollTop(top); - }, - - - /* Hit Areas - ------------------------------------------------------------------------------------------------------------------*/ - // forward all hit-related method calls to the grids (dayGrid might not be defined) - - - prepareHits: function() { - this.timeGrid.prepareHits(); - if (this.dayGrid) { - this.dayGrid.prepareHits(); - } - }, - - - releaseHits: function() { - this.timeGrid.releaseHits(); - if (this.dayGrid) { - this.dayGrid.releaseHits(); - } - }, - - - queryHit: function(left, top) { - var hit = this.timeGrid.queryHit(left, top); - - if (!hit && this.dayGrid) { - hit = this.dayGrid.queryHit(left, top); - } - - return hit; - }, - - - getHitSpan: function(hit) { - // TODO: hit.component is set as a hack to identify where the hit came from - return hit.component.getHitSpan(hit); - }, - - - getHitEl: function(hit) { - // TODO: hit.component is set as a hack to identify where the hit came from - return hit.component.getHitEl(hit); - }, - - - /* Events - ------------------------------------------------------------------------------------------------------------------*/ - - - // Renders events onto the view and populates the View's segment array - renderEvents: function(events) { - var dayEvents = []; - var timedEvents = []; - var daySegs = []; - var timedSegs; - var i; - - // separate the events into all-day and timed - for (i = 0; i < events.length; i++) { - if (events[i].allDay) { - dayEvents.push(events[i]); - } - else { - timedEvents.push(events[i]); - } - } - - // render the events in the subcomponents - timedSegs = this.timeGrid.renderEvents(timedEvents); - if (this.dayGrid) { - daySegs = this.dayGrid.renderEvents(dayEvents); - } - - // the all-day area is flexible and might have a lot of events, so shift the height - this.updateHeight(); - }, - - - // Retrieves all segment objects that are rendered in the view - getEventSegs: function() { - return this.timeGrid.getEventSegs().concat( - this.dayGrid ? this.dayGrid.getEventSegs() : [] - ); - }, - - - // Unrenders all event elements and clears internal segment data - unrenderEvents: function() { - - // unrender the events in the subcomponents - this.timeGrid.unrenderEvents(); - if (this.dayGrid) { - this.dayGrid.unrenderEvents(); - } - - // we DON'T need to call updateHeight() because - // a renderEvents() call always happens after this, which will eventually call updateHeight() - }, - - - /* Dragging (for events and external elements) - ------------------------------------------------------------------------------------------------------------------*/ - - - // A returned value of `true` signals that a mock "helper" event has been rendered. - renderDrag: function(dropLocation, seg) { - if (dropLocation.start.hasTime()) { - return this.timeGrid.renderDrag(dropLocation, seg); - } - else if (this.dayGrid) { - return this.dayGrid.renderDrag(dropLocation, seg); - } - }, - - - unrenderDrag: function() { - this.timeGrid.unrenderDrag(); - if (this.dayGrid) { - this.dayGrid.unrenderDrag(); - } - }, - - - /* Selection - ------------------------------------------------------------------------------------------------------------------*/ - - - // Renders a visual indication of a selection - renderSelection: function(span) { - if (span.start.hasTime() || span.end.hasTime()) { - this.timeGrid.renderSelection(span); - } - else if (this.dayGrid) { - this.dayGrid.renderSelection(span); - } - }, - - - // Unrenders a visual indications of a selection - unrenderSelection: function() { - this.timeGrid.unrenderSelection(); - if (this.dayGrid) { - this.dayGrid.unrenderSelection(); - } - } - -}); - - -// Methods that will customize the rendering behavior of the AgendaView's timeGrid -// TODO: move into TimeGrid -var agendaTimeGridMethods = { - - - // Generates the HTML that will go before the day-of week header cells - renderHeadIntroHtml: function() { - var view = this.view; - var weekText; - - if (view.opt('weekNumbers')) { - weekText = this.start.format(view.opt('smallWeekFormat')); - - return '' + - '' + - view.buildGotoAnchorHtml( // aside from link, important for matchCellWidths - { date: this.start, type: 'week', forceOff: this.colCnt > 1 }, - htmlEscape(weekText) // inner HTML - ) + - ''; - } - else { - return ''; - } - }, - - - // Generates the HTML that goes before the bg of the TimeGrid slot area. Long vertical column. - renderBgIntroHtml: function() { - var view = this.view; - - return ''; - }, - - - // Generates the HTML that goes before all other types of cells. - // Affects content-skeleton, helper-skeleton, highlight-skeleton for both the time-grid and day-grid. - renderIntroHtml: function() { - var view = this.view; - - return ''; - } - -}; - - -// Methods that will customize the rendering behavior of the AgendaView's dayGrid -var agendaDayGridMethods = { - - - // Generates the HTML that goes before the all-day cells - renderBgIntroHtml: function() { - var view = this.view; - - return '' + - '' + - '' + // needed for matchCellWidths - view.getAllDayHtml() + - '' + - ''; - }, - - - // Generates the HTML that goes before all other types of cells. - // Affects content-skeleton, helper-skeleton, highlight-skeleton for both the time-grid and day-grid. - renderIntroHtml: function() { - var view = this.view; - - return ''; - } - -}; - -;; - -var AGENDA_ALL_DAY_EVENT_LIMIT = 5; - -// 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 } -]; - -fcViews.agenda = { - 'class': AgendaView, - defaults: { - allDaySlot: true, - slotDuration: '00:30:00', - minTime: '00:00:00', - maxTime: '24:00:00', - slotEventOverlap: true // a bad name. confused with overlap/constraint system - } -}; - -fcViews.agendaDay = { - type: 'agenda', - duration: { days: 1 } -}; - -fcViews.agendaWeek = { - type: 'agenda', - duration: { weeks: 1 } -}; -;; - -/* -Responsible for the scroller, and forwarding event-related actions into the "grid" -*/ -var ListView = View.extend({ - - grid: null, - scroller: null, - - initialize: function() { - this.grid = new ListViewGrid(this); - this.scroller = new Scroller({ - overflowX: 'hidden', - overflowY: 'auto' - }); - }, - - setRange: function(range) { - View.prototype.setRange.call(this, range); // super - - this.grid.setRange(range); // needs to process range-related options - }, - - renderSkeleton: function() { - this.el.addClass( - 'fc-list-view ' + - this.widgetContentClass - ); - - this.scroller.render(); - this.scroller.el.appendTo(this.el); - - this.grid.setElement(this.scroller.scrollEl); - }, - - unrenderSkeleton: function() { - this.scroller.destroy(); // will remove the Grid too - }, - - setHeight: function(totalHeight, isAuto) { - this.scroller.setHeight(this.computeScrollerHeight(totalHeight)); - }, - - computeScrollerHeight: function(totalHeight) { - return totalHeight - - subtractInnerElHeight(this.el, this.scroller.el); // everything that's NOT the scroller - }, - - renderEvents: function(events) { - this.grid.renderEvents(events); - }, - - unrenderEvents: function() { - this.grid.unrenderEvents(); - }, - - isEventResizable: function(event) { - return false; - }, - - isEventDraggable: function(event) { - return false; - } - -}); - -/* -Responsible for event rendering and user-interaction. -Its "el" is the inner-content of the above view's scroller. -*/ -var ListViewGrid = Grid.extend({ - - segSelector: '.fc-list-item', // which elements accept event actions - hasDayInteractions: false, // no day selection or day clicking - - // slices by day - spanToSegs: function(span) { - var view = this.view; - var dayStart = view.start.clone().time(0); // timed, so segs get times! - var dayIndex = 0; - var seg; - var segs = []; - - while (dayStart < view.end) { - - seg = intersectRanges(span, { - start: dayStart, - end: dayStart.clone().add(1, 'day') - }); - - if (seg) { - seg.dayIndex = dayIndex; - segs.push(seg); - } - - dayStart.add(1, 'day'); - dayIndex++; - - // detect when span won't go fully into the next day, - // and mutate the latest seg to the be the end. - if ( - seg && !seg.isEnd && span.end.hasTime() && - span.end < dayStart.clone().add(this.view.nextDayThreshold) - ) { - seg.end = span.end.clone(); - seg.isEnd = true; - break; - } - } - - return segs; - }, - - // like "4:00am" - computeEventTimeFormat: function() { - return this.view.opt('mediumTimeFormat'); - }, - - // for events with a url, the whole should be clickable, - // but it's impossible to wrap with an tag. simulate this. - handleSegClick: function(seg, ev) { - var url; - - Grid.prototype.handleSegClick.apply(this, arguments); // super. might prevent the default action - - // not clicking on or within an with an href - if (!$(ev.target).closest('a[href]').length) { - url = seg.event.url; - if (url && !ev.isDefaultPrevented()) { // jsEvent not cancelled in handler - window.location.href = url; // simulate link click - } - } - }, - - // returns list of foreground segs that were actually rendered - renderFgSegs: function(segs) { - segs = this.renderFgSegEls(segs); // might filter away hidden events - - if (!segs.length) { - this.renderEmptyMessage(); - } - else { - this.renderSegList(segs); - } - - return segs; - }, - - renderEmptyMessage: function() { - this.el.html( - '
' + // TODO: try less wraps - '
' + - '
' + - htmlEscape(this.view.opt('noEventsMessage')) + - '
' + - '
' + - '
' - ); - }, - - // render the event segments in the view - renderSegList: function(allSegs) { - var segsByDay = this.groupSegsByDay(allSegs); // sparse array - var dayIndex; - var daySegs; - var i; - var tableEl = $('
'); - var tbodyEl = tableEl.find('tbody'); - - for (dayIndex = 0; dayIndex < segsByDay.length; dayIndex++) { - daySegs = segsByDay[dayIndex]; - if (daySegs) { // sparse array, so might be undefined - - // append a day header - tbodyEl.append(this.dayHeaderHtml( - this.view.start.clone().add(dayIndex, 'days') - )); - - this.sortEventSegs(daySegs); - - for (i = 0; i < daySegs.length; i++) { - tbodyEl.append(daySegs[i].el); // append event row - } - } - } - - this.el.empty().append(tableEl); - }, - - // Returns a sparse array of arrays, segs grouped by their dayIndex - groupSegsByDay: function(segs) { - var segsByDay = []; // sparse array - var i, 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 - dayHeaderHtml: function(dayDate) { - var view = this.view; - var mainFormat = view.opt('listDayFormat'); - var altFormat = view.opt('listDayAltFormat'); - - return '' + - '' + - (mainFormat ? - view.buildGotoAnchorHtml( - dayDate, - { 'class': 'fc-list-heading-main' }, - htmlEscape(dayDate.format(mainFormat)) // inner HTML - ) : - '') + - (altFormat ? - view.buildGotoAnchorHtml( - dayDate, - { 'class': 'fc-list-heading-alt' }, - htmlEscape(dayDate.format(altFormat)) // inner HTML - ) : - '') + - '' + - ''; - }, - - // generates the HTML for a single event row - fgSegHtml: function(seg) { - var view = this.view; - var classes = [ 'fc-list-item' ].concat(this.getSegCustomClasses(seg)); - var bgColor = this.getSegBackgroundColor(seg); - var event = seg.event; - var url = event.url; - var timeHtml; - - if (event.allDay) { - timeHtml = view.getAllDayHtml(); - } - else if (view.isMultiDayEvent(event)) { // if the event appears to span more than one day - if (seg.isStart || seg.isEnd) { // outer segment that probably lasts part of the day - timeHtml = htmlEscape(this.getEventTimeText(seg)); - } - else { // inner segment that lasts the whole day - timeHtml = view.getAllDayHtml(); - } - } - else { - // Display the normal time text for the *event's* times - timeHtml = htmlEscape(this.getEventTimeText(event)); - } - - if (url) { - classes.push('fc-has-url'); - } - - return '' + - (this.displayEventTime ? - '' + - (timeHtml || '') + - '' : - '') + - '' + - '' + - '' + - '' + - '' + - htmlEscape(seg.event.title || '') + - '
' + - '' + - ''; - } - -}); - -;; - -fcViews.list = { - 'class': ListView, - buttonTextKey: 'list', // what to lookup in locale files - defaults: { - buttonText: 'list', // text to display for English - listDayFormat: 'LL', // like "January 1, 2016" - noEventsMessage: 'No events to display' - } -}; - -fcViews.listDay = { - type: 'list', - duration: { days: 1 }, - defaults: { - listDayFormat: 'dddd' // day-of-week is all we need. full date is probably in header - } -}; - -fcViews.listWeek = { - type: 'list', - duration: { weeks: 1 }, - defaults: { - listDayFormat: 'dddd', // day-of-week is more important - listDayAltFormat: 'LL' - } -}; - -fcViews.listMonth = { - type: 'list', - duration: { month: 1 }, - defaults: { - listDayAltFormat: 'dddd' // day-of-week is nice-to-have - } -}; - -fcViews.listYear = { - type: 'list', - duration: { year: 1 }, - defaults: { - listDayAltFormat: 'dddd' // day-of-week is nice-to-have - } -}; - -;; - -return FC; // export for Node/CommonJS -}); \ No newline at end of file diff --git a/addons/web/static/lib/fullcalendar/js/gcal.js b/addons/web/static/lib/fullcalendar/js/gcal.js deleted file mode 100644 index dfe6fa90591..00000000000 --- a/addons/web/static/lib/fullcalendar/js/gcal.js +++ /dev/null @@ -1,180 +0,0 @@ -/*! - * FullCalendar v3.0.1 Google Calendar Plugin - * Docs & License: http://fullcalendar.io/ - * (c) 2016 Adam Shaw - */ - -(function(factory) { - if (typeof define === 'function' && define.amd) { - define([ 'jquery' ], factory); - } - else if (typeof exports === 'object') { // Node/CommonJS - module.exports = factory(require('jquery')); - } - else { - factory(jQuery); - } -})(function($) { - - -var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars'; -var FC = $.fullCalendar; -var applyAll = FC.applyAll; - - -FC.sourceNormalizers.push(function(sourceOptions) { - var googleCalendarId = sourceOptions.googleCalendarId; - var url = sourceOptions.url; - var match; - - // if the Google Calendar ID hasn't been explicitly defined - if (!googleCalendarId && url) { - - // 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)) { - googleCalendarId = url; - } - // try to scrape it out of a V1 or V3 API feed URL - else if ( - (match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) || - (match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url)) - ) { - googleCalendarId = decodeURIComponent(match[1]); - } - - if (googleCalendarId) { - sourceOptions.googleCalendarId = googleCalendarId; - } - } - - - if (googleCalendarId) { // is this a Google Calendar? - - // make each Google Calendar source uneditable by default - if (sourceOptions.editable == null) { - sourceOptions.editable = false; - } - - // We want removeEventSource to work, but it won't know about the googleCalendarId primitive. - // Shoehorn it into the url, which will function as the unique primitive. Won't cause side effects. - // This hack is obsolete since 2.2.3, but keep it so this plugin file is compatible with old versions. - sourceOptions.url = googleCalendarId; - } -}); - - -FC.sourceFetchers.push(function(sourceOptions, start, end, timezone) { - if (sourceOptions.googleCalendarId) { - return transformOptions(sourceOptions, start, end, timezone, this); // `this` is the calendar - } -}); - - -function transformOptions(sourceOptions, start, end, timezone, calendar) { - var url = API_BASE + '/' + encodeURIComponent(sourceOptions.googleCalendarId) + '/events?callback=?'; // jsonp - var apiKey = sourceOptions.googleCalendarApiKey || calendar.options.googleCalendarApiKey; - var success = sourceOptions.success; - var data; - var timezoneArg; // populated when a specific timezone. escaped to Google's liking - - function reportError(message, apiErrorObjs) { - var errorObjs = apiErrorObjs || [ { message: message } ]; // to be passed into error handlers - - // call error handlers - (sourceOptions.googleCalendarError || $.noop).apply(calendar, errorObjs); - (calendar.options.googleCalendarError || $.noop).apply(calendar, errorObjs); - - // print error to debug console - FC.warn.apply(null, [ message ].concat(apiErrorObjs || [])); - } - - if (!apiKey) { - reportError("Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/"); - return {}; // an empty source to use instead. won't fetch anything. - } - - // The API expects an ISO8601 datetime with a time and timezone part. - // Since the calendar's timezone offset isn't always known, request the date in UTC and pad it by a day on each - // side, guaranteeing we will receive all events in the desired range, albeit a superset. - // .utc() will set a zone and give it a 00:00:00 time. - if (!start.hasZone()) { - start = start.clone().utc().add(-1, 'day'); - } - if (!end.hasZone()) { - end = end.clone().utc().add(1, 'day'); - } - - // when sending timezone names to Google, only accepts underscores, not spaces - if (timezone && timezone != 'local') { - timezoneArg = timezone.replace(' ', '_'); - } - - data = $.extend({}, sourceOptions.data || {}, { - key: apiKey, - timeMin: start.format(), - timeMax: end.format(), - timeZone: timezoneArg, - singleEvents: true, - maxResults: 9999 - }); - - return $.extend({}, sourceOptions, { - googleCalendarId: null, // prevents source-normalizing from happening again - url: url, - data: data, - startParam: false, // `false` omits this parameter. we already included it above - endParam: false, // same - timezoneParam: false, // same - success: function(data) { - var events = []; - var successArgs; - var successRes; - - if (data.error) { - reportError('Google Calendar API: ' + data.error.message, data.error.errors); - } - else if (data.items) { - $.each(data.items, function(i, entry) { - var url = entry.htmlLink || null; - - // make the URLs for each event show times in the correct timezone - if (timezoneArg && url !== null) { - url = injectQsComponent(url, 'ctz=' + timezoneArg); - } - - events.push({ - id: entry.id, - title: entry.summary, - start: entry.start.dateTime || entry.start.date, // try timed. will fall back to all-day - end: entry.end.dateTime || entry.end.date, // same - url: url, - location: entry.location, - description: entry.description - }); - }); - - // call the success handler(s) and allow it to return a new events array - successArgs = [ events ].concat(Array.prototype.slice.call(arguments, 1)); // forward other jq args - successRes = applyAll(success, this, successArgs); - if ($.isArray(successRes)) { - return successRes; - } - } - - return events; - } - }); -} - - -// Injects a string like "arg=value" into the querystring of a URL -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; - }); -} - - -}); diff --git a/addons/web/static/lib/fullcalendar/list/main.css b/addons/web/static/lib/fullcalendar/list/main.css new file mode 100644 index 00000000000..1a9b8701260 --- /dev/null +++ b/addons/web/static/lib/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 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; +} \ No newline at end of file diff --git a/addons/web/static/lib/fullcalendar/list/main.js b/addons/web/static/lib/fullcalendar/list/main.js new file mode 100644 index 00000000000..1f72dee2296 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/list/main.js @@ -0,0 +1,356 @@ +/*! +FullCalendar List View Plugin v4.4.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) || 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, theme = _a.theme, options = _a.options; + 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(options); + } + 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(options); + } + } + 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 '' + + (this.displayEventTime ? + '' + + (timeHtml || '') + + '' : + '') + + '' + + '' + + '' + + '' + + '' + + core.htmlEscape(eventDef.title || '') + + '' + + '' + + ''; + }; + // 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(viewSpec, parentEl) { + var _this = _super.call(this, viewSpec, parentEl) || this; + _this.computeDateVars = core.memoize(computeDateVars); + _this.eventStoreToSegs = core.memoize(_this._eventStoreToSegs); + _this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton); + var eventRenderer = _this.eventRenderer = new ListEventRenderer(_this); + _this.renderContent = core.memoizeRendering(eventRenderer.renderSegs.bind(eventRenderer), eventRenderer.unrender.bind(eventRenderer), [_this.renderSkeleton]); + return _this; + } + ListView.prototype.firstContext = function (context) { + context.calendar.registerInteractiveComponent(this, { + el: this.el + // TODO: make aware that it doesn't do Hits + }); + }; + ListView.prototype.render = function (props, context) { + _super.prototype.render.call(this, props, context); + var _a = this.computeDateVars(props.dateProfile), dayDates = _a.dayDates, dayRanges = _a.dayRanges; + this.dayDates = dayDates; + this.renderSkeleton(context); + this.renderContent(context, this.eventStoreToSegs(props.eventStore, props.eventUiBases, dayRanges)); + }; + ListView.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderSkeleton.unrender(); + this.renderContent.unrender(); + this.context.calendar.unregisterInteractiveComponent(this); + }; + ListView.prototype._renderSkeleton = function (context) { + var theme = context.theme; + this.el.classList.add('fc-list-view'); + var listViewClassNames = (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 + }; + ListView.prototype._unrenderSkeleton = function () { + // TODO: remove classNames + this.scroller.destroy(); // will remove the Grid too + }; + 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.context.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.context, 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 = + '
' + // TODO: try less wraps + '
' + + '
' + + core.htmlEscape(this.context.options.noEventsMessage) + + '
' + + '
' + + '
'; + }; + // called by ListEventRenderer + ListView.prototype.renderSegList = function (allSegs) { + var theme = this.context.theme; + var segsByDay = this.groupSegsByDay(allSegs); // sparse array + var dayIndex; + var daySegs; + var i; + var tableEl = core.htmlToElement('
'); + 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 _a = this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options; + var mainFormat = core.createFormatter(options.listDayFormat); // TODO: cache + var altFormat = core.createFormatter(options.listDayAltFormat); // TODO: cache + return core.createElement('tr', { + className: 'fc-list-heading', + 'data-date': dateEnv.formatIso(dayDate, { omitTime: true }) + }, '' + + (mainFormat ? + core.buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-main' }, core.htmlEscape(dateEnv.format(dayDate, mainFormat)) // inner HTML + ) : + '') + + (altFormat ? + core.buildGotoAnchorHtml(options, dateEnv, dayDate, { 'class': 'fc-list-heading-alt' }, core.htmlEscape(dateEnv.format(dayDate, altFormat)) // inner HTML + ) : + '') + + ''); + }; + 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/addons/web/static/lib/fullcalendar/luxon/main.js b/addons/web/static/lib/fullcalendar/luxon/main.js new file mode 100644 index 00000000000..88e3976c7ae --- /dev/null +++ b/addons/web/static/lib/fullcalendar/luxon/main.js @@ -0,0 +1,170 @@ +/*! +FullCalendar Luxon Plugin v4.4.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/addons/web/static/lib/fullcalendar/moment-timezone/main.js b/addons/web/static/lib/fullcalendar/moment-timezone/main.js new file mode 100644 index 00000000000..b4d19bd030e --- /dev/null +++ b/addons/web/static/lib/fullcalendar/moment-timezone/main.js @@ -0,0 +1,64 @@ +/*! +FullCalendar Moment Timezone Plugin v4.4.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/addons/web/static/lib/fullcalendar/moment/main.js b/addons/web/static/lib/fullcalendar/moment/main.js new file mode 100644 index 00000000000..96edc5cd15e --- /dev/null +++ b/addons/web/static/lib/fullcalendar/moment/main.js @@ -0,0 +1,110 @@ +/*! +FullCalendar Moment Plugin v4.4.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/addons/web/static/lib/fullcalendar/rrule/main.js b/addons/web/static/lib/fullcalendar/rrule/main.js new file mode 100644 index 00000000000..450650ab148 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/rrule/main.js @@ -0,0 +1,128 @@ +/*! +FullCalendar RRule Plugin v4.4.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/addons/web/static/lib/fullcalendar/timegrid/main.css b/addons/web/static/lib/fullcalendar/timegrid/main.css new file mode 100644 index 00000000000..4b8bcd6e2cc --- /dev/null +++ b/addons/web/static/lib/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
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; +} \ No newline at end of file diff --git a/addons/web/static/lib/fullcalendar/timegrid/main.js b/addons/web/static/lib/fullcalendar/timegrid/main.js new file mode 100644 index 00000000000..086625fac52 --- /dev/null +++ b/addons/web/static/lib/fullcalendar/timegrid/main.js @@ -0,0 +1,1401 @@ +/*! +FullCalendar Time Grid Plugin v4.4.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) || this; + _this.timeGrid = timeGrid; + return _this; + } + TimeGridEventRenderer.prototype.renderSegs = function (context, segs, mirrorInfo) { + _super.prototype.renderSegs.call(this, context, segs, mirrorInfo); + // TODO: dont do every time. memoize + this.fullTimeFormat = core.createFormatter({ + hour: 'numeric', + minute: '2-digit', + separator: this.context.options.defaultRangeSeparator + }); + }; + // 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 eventRange = seg.eventRange; + var eventDef = eventRange.def; + var eventUi = eventRange.ui; + var allDay = eventDef.allDay; + var isDraggable = core.computeEventDraggable(this.context, eventDef, eventUi); + var isResizableFromStart = seg.isStart && core.computeEventStartResizable(this.context, eventDef, eventUi); + var isResizableFromEnd = seg.isEnd && core.computeEventEndResizable(this.context, 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 '' + + '
' + + (timeText ? + '
' + + '' + core.htmlEscape(timeText) + '' + + '
' : + '') + + (eventDef.title ? + '
' + + core.htmlEscape(eventDef.title) + + '
' : + '') + + '
' + + /* TODO: write CSS for this + (isResizableFromStart ? + '
' : + '' + ) + + */ + (isResizableFromEnd ? + '
' : + '') + + '
'; + }; + // 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.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.context.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) || 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(el, renderProps) { + var _this = _super.call(this, el) || this; + _this.isSlatSizesDirty = false; + _this.isColSizesDirty = false; + _this.processOptions = core.memoize(_this._processOptions); + _this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton); + _this.renderSlats = core.memoizeRendering(_this._renderSlats, null, [_this.renderSkeleton]); + _this.renderColumns = core.memoizeRendering(_this._renderColumns, _this._unrenderColumns, [_this.renderSkeleton]); + _this.renderProps = renderProps; + var renderColumns = _this.renderColumns; + var eventRenderer = _this.eventRenderer = new TimeGridEventRenderer(_this); + var fillRenderer = _this.fillRenderer = new TimeGridFillRenderer(_this); + _this.mirrorRenderer = new TimeGridMirrorRenderer(_this); + _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]); + return _this; + } + /* Options + ------------------------------------------------------------------------------------------------------------------*/ + // Parses various options into properties of this object + // MUST have context already set + TimeGrid.prototype._processOptions = function (options) { + var slotDuration = options.slotDuration, snapDuration = options.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 = options.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 = options.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, context) { + this.processOptions(context.options); + var cells = props.cells; + this.colCnt = cells.length; + this.renderSkeleton(context.theme); + this.renderSlats(props.dateProfile); + this.renderColumns(props.cells, props.dateProfile); + this.renderBusinessHours(context, props.businessHourSegs); + this.renderDateSelection(props.dateSelectionSegs); + this.renderFgEvents(context, props.fgEventSegs); + this.renderBgEvents(context, 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(); + this.renderSkeleton.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._renderSkeleton = function (theme) { + var el = this.el; + el.innerHTML = + '
' + + '
' + + ''; + this.rootBgContainerEl = el.querySelector('.fc-bg'); + this.slatContainerEl = el.querySelector('.fc-slats'); + this.bottomRuleEl = el.querySelector('.fc-divider'); + }; + TimeGrid.prototype._renderSlats = function (dateProfile) { + var theme = this.context.theme; + this.slatContainerEl.innerHTML = + '' + + this.renderSlatRowHtml(dateProfile) + + '
'; + 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.context, 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 = + '' + + (isLabeled ? + '' + // for matchCellWidths + core.htmlEscape(dateEnv.format(slotDate, this.labelFormat)) + + '' : + '') + + ''; + html += + '' + + (!isRtl ? axisHtml : '') + + '' + + (isRtl ? axisHtml : '') + + ''; + slotTime = core.addDurations(slotTime, this.slotDuration); + slotIterator = core.addDurations(slotIterator, this.slotDuration); + } + return html; + }; + TimeGrid.prototype._renderColumns = function (cells, dateProfile) { + var _a = this.context, calendar = _a.calendar, view = _a.view, isRtl = _a.isRtl, theme = _a.theme, dateEnv = _a.dateEnv; + var bgRow = new daygrid.DayBgRow(this.context); + this.rootBgContainerEl.innerHTML = + '' + + bgRow.renderHtml({ + cells: cells, + dateProfile: dateProfile, + renderIntroHtml: this.renderProps.renderBgIntroHtml + }) + + '
'; + this.colEls = core.findElements(this.el, '.fc-day, .fc-disabled-day'); + for (var col = 0; col < this.colCnt; col++) { + calendar.publiclyTrigger('dayRender', [ + { + date: dateEnv.toDate(cells[col].date), + el: this.colEls[col], + view: view + } + ]); + } + if (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 isRtl = this.context.isRtl; + var parts = []; + var skeletonEl; + parts.push(this.renderProps.renderIntroHtml()); + for (var i = 0; i < this.colCnt; i++) { + parts.push('' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + ''); + } + if (isRtl) { + parts.reverse(); + } + skeletonEl = this.contentSkeletonEl = core.htmlToElement('
' + + '' + + '' + parts.join('') + '' + + '
' + + '
'); + 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 (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 options = this.context.options; + var eventMinHeight = options.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 dateEnv = this.context.dateEnv; + var _a = this, 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(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg }); + } + else { + this.fillRenderer.renderSegs('highlight', this.context, state.segs); + } + } + }; + TimeGrid.prototype._unrenderEventDrag = function (state) { + if (state) { + this.eventRenderer.showByHash(state.affectedInstances); + if (state.isEvent) { + this.mirrorRenderer.unrender(this.context, state.segs, { isDragging: true, sourceSeg: state.sourceSeg }); + } + else { + this.fillRenderer.unrender('highlight', this.context); + } + } + }; + /* Event Resize Visualization + ------------------------------------------------------------------------------------------------------------------*/ + TimeGrid.prototype._renderEventResize = function (state) { + if (state) { + this.eventRenderer.hideByHash(state.affectedInstances); + this.mirrorRenderer.renderSegs(this.context, state.segs, { isResizing: true, sourceSeg: state.sourceSeg }); + } + }; + TimeGrid.prototype._unrenderEventResize = function (state) { + if (state) { + this.eventRenderer.showByHash(state.affectedInstances); + this.mirrorRenderer.unrender(this.context, 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.context.options.selectMirror) { + this.mirrorRenderer.renderSegs(this.context, segs, { isSelecting: true }); + } + else { + this.fillRenderer.renderSegs('highlight', this.context, segs); + } + } + }; + TimeGrid.prototype._unrenderDateSelection = function (segs) { + if (segs) { + if (this.context.options.selectMirror) { + this.mirrorRenderer.unrender(this.context, segs, { isSelecting: true }); + } + else { + this.fillRenderer.unrender('highlight', this.context); + } + } + }; + 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 AbstractTimeGridView = /** @class */ (function (_super) { + __extends(AbstractTimeGridView, _super); + function AbstractTimeGridView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.splitter = new AllDaySplitter(); + _this.renderSkeleton = core.memoizeRendering(_this._renderSkeleton, _this._unrenderSkeleton); + /* Header Render Methods + ------------------------------------------------------------------------------------------------------------------*/ + // Generates the HTML that will go before the day-of week header cells + _this.renderHeadIntroHtml = function () { + var _a = _this.context, theme = _a.theme, dateEnv = _a.dateEnv, options = _a.options; + var range = _this.props.dateProfile.renderRange; + var dayCnt = core.diffDays(range.start, range.end); + var weekText; + if (options.weekNumbers) { + weekText = dateEnv.format(range.start, WEEK_HEADER_FORMAT); + return '' + + '' + + core.buildGotoAnchorHtml(// aside from link, important for matchCellWidths + options, dateEnv, { date: range.start, type: 'week', forceOff: dayCnt > 1 }, core.htmlEscape(weekText) // inner HTML + ) + + ''; + } + else { + return ''; + } + }; + /* 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.context.theme; + return ''; + }; + // 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 ''; + }; + /* Day Grid Render Methods + ------------------------------------------------------------------------------------------------------------------*/ + // Generates the HTML that goes before the all-day cells + _this.renderDayGridBgIntroHtml = function () { + var _a = _this.context, theme = _a.theme, options = _a.options; + return '' + + '' + + '' + // needed for matchCellWidths + core.getAllDayHtml(options) + + '' + + ''; + }; + // 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 ''; + }; + return _this; + } + AbstractTimeGridView.prototype.render = function (props, context) { + _super.prototype.render.call(this, props, context); + this.renderSkeleton(context); + }; + AbstractTimeGridView.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.renderSkeleton.unrender(); + }; + AbstractTimeGridView.prototype._renderSkeleton = function (context) { + 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(timeGridEl, { + renderBgIntroHtml: this.renderTimeGridBgIntroHtml, + renderIntroHtml: this.renderTimeGridIntroHtml + }); + if (context.options.allDaySlot) { // should we display the "all-day" area? + this.dayGrid = new daygrid.DayGrid(// the all-day subcomponent of this view + 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
dividing the two grids + var dividerEl = this.el.querySelector('.fc-divider'); + this.dayGrid.bottomCoordPadding = dividerEl.getBoundingClientRect().height; + } + }; + AbstractTimeGridView.prototype._unrenderSkeleton = function () { + this.el.classList.remove('fc-timeGrid-view'); + 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. + AbstractTimeGridView.prototype.renderSkeletonHtml = function () { + var _a = this.context, theme = _a.theme, options = _a.options; + return '' + + '' + + (options.columnHeader ? + '' + + '' + + '' + + '' + + '' : + '') + + '' + + '' + + '' + + '' + + '' + + '
 
' + + (options.allDaySlot ? + '
' + + '
' : + '') + + '
'; + }; + /* Now Indicator + ------------------------------------------------------------------------------------------------------------------*/ + AbstractTimeGridView.prototype.getNowIndicatorUnit = function () { + return this.timeGrid.getNowIndicatorUnit(); + }; + // subclasses should implement + // renderNowIndicator(date: DateMarker) { + // } + AbstractTimeGridView.prototype.unrenderNowIndicator = function () { + this.timeGrid.unrenderNowIndicator(); + }; + /* Dimensions + ------------------------------------------------------------------------------------------------------------------*/ + AbstractTimeGridView.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 + AbstractTimeGridView.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
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.context.options.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 + AbstractTimeGridView.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 + AbstractTimeGridView.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 }; + }; + AbstractTimeGridView.prototype.queryDateScroll = function () { + return { top: this.scroller.getScrollTop() }; + }; + AbstractTimeGridView.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 + AbstractTimeGridView.prototype.axisStyleAttr = function () { + if (this.axisWidth != null) { + return 'style="width:' + this.axisWidth + 'px"'; + } + return ''; + }; + return AbstractTimeGridView; + }(core.View)); + AbstractTimeGridView.prototype.usesMinMaxTime = true; // indicates that minTime/maxTime affects rendering + + var SimpleTimeGrid = /** @class */ (function (_super) { + __extends(SimpleTimeGrid, _super); + function SimpleTimeGrid(timeGrid) { + var _this = _super.call(this, timeGrid.el) || this; + _this.buildDayRanges = core.memoize(buildDayRanges); + _this.slicer = new TimeGridSlicer(); + _this.timeGrid = timeGrid; + return _this; + } + SimpleTimeGrid.prototype.firstContext = function (context) { + context.calendar.registerInteractiveComponent(this, { + el: this.timeGrid.el + }); + }; + SimpleTimeGrid.prototype.destroy = function () { + _super.prototype.destroy.call(this); + this.context.calendar.unregisterInteractiveComponent(this); + }; + SimpleTimeGrid.prototype.render = function (props, context) { + var dateEnv = this.context.dateEnv; + var dateProfile = props.dateProfile, dayTable = props.dayTable; + var dayRanges = this.dayRanges = this.buildDayRanges(dayTable, dateProfile, dateEnv); + this.timeGrid.receiveProps(__assign({}, this.slicer.sliceProps(props, dateProfile, null, context.calendar, this.timeGrid, dayRanges), { dateProfile: dateProfile, cells: dayTable.cells[0] }), context); + }; + 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 = /** @class */ (function (_super) { + __extends(TimeGridView, _super); + function TimeGridView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.buildDayTable = core.memoize(buildDayTable); + return _this; + } + TimeGridView.prototype.render = function (props, context) { + _super.prototype.render.call(this, props, context); // for flags for updateSize. also _renderSkeleton/_unrenderSkeleton + var _a = this.props, dateProfile = _a.dateProfile, dateProfileGenerator = _a.dateProfileGenerator; + var nextDayThreshold = context.nextDayThreshold; + var dayTable = this.buildDayTable(dateProfile, dateProfileGenerator); + var splitProps = this.splitter.splitProps(props); + if (this.header) { + this.header.receiveProps({ + dateProfile: dateProfile, + dates: dayTable.headerDates, + datesRepDistinctDays: true, + renderIntroHtml: this.renderHeadIntroHtml + }, context); + } + this.simpleTimeGrid.receiveProps(__assign({}, splitProps['timed'], { dateProfile: dateProfile, + dayTable: dayTable }), context); + if (this.simpleDayGrid) { + this.simpleDayGrid.receiveProps(__assign({}, splitProps['allDay'], { dateProfile: dateProfile, + dayTable: dayTable, + nextDayThreshold: nextDayThreshold, isRigid: false }), context); + } + this.startNowIndicator(dateProfile, dateProfileGenerator); + }; + TimeGridView.prototype._renderSkeleton = function (context) { + _super.prototype._renderSkeleton.call(this, context); + if (context.options.columnHeader) { + this.header = new core.DayHeader(this.el.querySelector('.fc-head-container')); + } + this.simpleTimeGrid = new SimpleTimeGrid(this.timeGrid); + if (this.dayGrid) { + this.simpleDayGrid = new daygrid.SimpleDayGrid(this.dayGrid); + } + }; + TimeGridView.prototype._unrenderSkeleton = function () { + _super.prototype._unrenderSkeleton.call(this); + if (this.header) { + this.header.destroy(); + } + this.simpleTimeGrid.destroy(); + if (this.simpleDayGrid) { + this.simpleDayGrid.destroy(); + } + }; + TimeGridView.prototype.renderNowIndicator = function (date) { + this.simpleTimeGrid.renderNowIndicator(date); + }; + return TimeGridView; + }(AbstractTimeGridView)); + 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, + 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 = AbstractTimeGridView; + exports.TimeGrid = TimeGrid; + exports.TimeGridSlicer = TimeGridSlicer; + exports.TimeGridView = TimeGridView; + exports.buildDayRanges = buildDayRanges; + exports.buildDayTable = buildDayTable; + exports.default = main; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js index 7ed57ceafa9..78fa6fdaf5e 100644 --- a/addons/web/static/src/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/js/views/calendar/calendar_model.js @@ -356,7 +356,7 @@ return AbstractModel.extend({ return this._rpc({ model: this.modelName, method: 'write', - args: [[record.id], data], + args: [[parseInt(record.id, 10)], data], context: context }); }, @@ -419,11 +419,22 @@ return AbstractModel.extend({ * @returns {Object} */ _getFullCalendarOptions: function () { + var format12Hour = { + hour: 'numeric', + minute: '2-digit', + omitZeroMinute: true, + meridiem: 'short' + }; + var format24Hour = { + hour: 'numeric', + minute: '2-digit', + hour12: false, + }; return { - defaultView: (this.mode === "month")? "month" : ((this.mode === "week")? "agendaWeek" : ((this.mode === "day")? "agendaDay" : "agendaWeek")), + defaultView: (this.mode === "month")? "dayGridMonth" : ((this.mode === "week")? "timeGridWeek" : ((this.mode === "day")? "timeGridDay" : "timeGridWeek")), header: false, selectable: this.creatable && this.create_right, - selectHelper: true, + selectMirror: true, editable: this.editable, droppable: true, navLinks: false, @@ -434,14 +445,18 @@ return AbstractModel.extend({ nowIndicator: true, weekNumbers: true, weekNumbersWithinDays: true, - weekNumberTitle: _t("Week") + " ", + weekNumberCalculation: function (date) { + // Since FullCalendar v4 ISO 8601 week date is preferred so we force the old system + return moment(date).week(); + }, + weekLabel: _t("Week"), allDayText: _t("All day"), monthNames: moment.months(), monthNamesShort: moment.monthsShort(), dayNames: moment.weekdays(), dayNamesShort: moment.weekdaysShort(), firstDay: this.week_start, - slotLabelFormat: _t.database.parameters.time_format.search("%H") != -1 ? 'H:mm': 'h(:mm)a', + slotLabelFormat: _t.database.parameters.time_format.search("%H") !== -1 ? format24Hour : format12Hour, }; }, /** @@ -733,10 +748,10 @@ return AbstractModel.extend({ } var r = { 'record': evt, - 'start': date_start, - 'end': date_stop, - 'r_start': date_start.clone(), - 'r_end': date_stop.clone(), + 'start': date_start.local(true).toDate(), + 'end': date_stop.local(true).toDate(), + 'r_start': date_start.clone().local(true).toDate(), + 'r_end': date_stop.clone().local(true).toDate(), 'title': the_title, 'allDay': all_day, 'id': evt.id, @@ -754,8 +769,8 @@ return AbstractModel.extend({ // allow to resize in month mode r.reset_allday = r.allDay; r.allDay = true; - r.start = date_start.format('YYYY-MM-DD'); - r.end = date_stop.startOf('day').format('YYYY-MM-DD'); + r.start = date_start.toDate(); + r.end = date_stop.startOf('day').toDate(); r.showTime = true; } diff --git a/addons/web/static/src/js/views/calendar/calendar_popover.js b/addons/web/static/src/js/views/calendar/calendar_popover.js index 9b4af4bb065..8bfadbc7c4d 100644 --- a/addons/web/static/src/js/views/calendar/calendar_popover.js +++ b/addons/web/static/src/js/views/calendar/calendar_popover.js @@ -112,7 +112,7 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi var field = { name: fieldName, string: displayFieldInfo.attrs.string || fieldInfo.string, - value: self.event.record[fieldName], + value: self.event.extendedProps.record[fieldName], type: fieldInfo.type, }; if (field.type === 'selection') { @@ -199,7 +199,7 @@ var CalendarPopover = Widget.extend(WidgetAdapterMixin, StandaloneFieldManagerMi ev.preventDefault(); this.trigger_up('edit_event', { id: this.event.id, - title: this.event.record.display_name, + title: this.event.extendedProps.record.display_name, }); }, /** diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js index 7f281e474c9..0ad1c4c0c6b 100644 --- a/addons/web/static/src/js/views/calendar/calendar_renderer.js +++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js @@ -16,9 +16,9 @@ var _t = core._t; var qweb = core.qweb; var scales = { - day: 'agendaDay', - week: 'agendaWeek', - month: 'month' + day: 'timeGridDay', + week: 'timeGridWeek', + month: 'dayGridMonth' }; var SidebarFilterM2O = relational_fields.FieldMany2One.extend({ @@ -78,6 +78,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, { { mode: 'edit', attrs: { + string: _t(self.fields[self.fieldName].string), placeholder: "+ " + _.str.sprintf(_t("Add %s"), self.title), can_create: false }, @@ -192,6 +193,7 @@ return AbstractRenderer.extend({ this.hideDate = params.hideDate; this.hideTime = params.hideTime; this.canDelete = params.canDelete; + this._isInDOM = false; }, /** * @override @@ -209,22 +211,29 @@ return AbstractRenderer.extend({ * @override */ on_attach_callback: function () { + this._super(...arguments); + this._isInDOM = true; if (config.device.isMobile) { this.$el.height($(window).height() - this.$el.offset().top); } - var scrollTop = this.$calendar.find('.fc-scroller').scrollTop(); - if (scrollTop) { - this.$calendar.fullCalendar('reinitView'); - } else { - this.$calendar.fullCalendar('render'); - } + // BUG Test ???? + // this.$el.height($(window).height() - this.$el.offset().top); + this.calendar.render(); + this._renderCalendar(); + }, + /** + * Called when the field is detached from the DOM. + */ + on_detach_callback: function () { + this._super(...arguments); + this._isInDOM = false; }, /** * @override */ destroy: function () { - if (this.$calendar) { - this.$calendar.fullCalendar('destroy'); + if (this.calendar) { + this.calendar.destroy(); } if (this.$small_calendar) { this.$small_calendar.datepicker('destroy'); @@ -296,9 +305,9 @@ return AbstractRenderer.extend({ * @override */ getLocalState: function () { - var $fcScroller = this.$calendar.find('.fc-scroller'); + var fcScroller = this.calendarElement.querySelector('.fc-scroller'); return { - scrollPosition: $fcScroller.scrollTop(), + scrollPosition: fcScroller.scrollTop, }; }, /** @@ -306,8 +315,8 @@ return AbstractRenderer.extend({ */ setLocalState: function (localState) { if (localState.scrollPosition) { - var $fcScroller = this.$calendar.find('.fc-scroller'); - $fcScroller.scrollTop(localState.scrollPosition); + var fcScroller = this.calendarElement.querySelector('.fc-scroller'); + fcScroller.scrollTop = localState.scrollPosition; } }, @@ -325,14 +334,15 @@ return AbstractRenderer.extend({ var self = this; var touchStartX; var touchEndX; - this.$calendar.on('touchstart', function (event) { - touchStartX = event.originalEvent.touches[0].pageX; + this.calendarElement.addEventListener('touchstart', function (event) { + self.isSwipeEnabled = true; + touchStartX = event.touches[0].pageX; }); - this.$calendar.on('touchend', function (event) { - touchEndX = event.originalEvent.changedTouches[0].pageX; + this.calendarElement.addEventListener('touchend', function (event) { if (!self.isSwipeEnabled) { return; } + touchEndX = event.changedTouches[0].pageX; if (touchStartX - touchEndX > 100) { self.trigger_up('next'); } else if (touchStartX - touchEndX < -100) { @@ -340,6 +350,35 @@ return AbstractRenderer.extend({ } }); }, + /** + * Convert the new format of Event from FullCalendar V4 to a Event FullCalendar V3 + * @param fc4Event + * @return {Object} FullCalendar V3 Object Event + * @private + */ + _convertEventToFC3Event: function (fc4Event) { + var event = fc4Event; + if (!moment.isMoment(fc4Event.start)) { + event = { + id: fc4Event.id, + title: fc4Event.title, + start: moment(fc4Event.start).utcOffset(0, true), + end: fc4Event.end && moment(fc4Event.end).utcOffset(0, true), + allDay: fc4Event.allDay, + color: fc4Event.color, + }; + if (fc4Event.extendedProps) { + event = Object.assign({}, event, { + r_start: fc4Event.extendedProps.r_start && moment(fc4Event.extendedProps.r_start).utcOffset(0, true), + r_end: fc4Event.extendedProps.r_end && moment(fc4Event.extendedProps.r_end).utcOffset(0, true), + reset_allday: fc4Event.extendedProps.reset_allday, + record: fc4Event.extendedProps.record, + attendees: fc4Event.extendedProps.attendees, + }); + } + } + return event; + }, /** * @param {any} event * @returns {string} the html for the rendered event @@ -347,8 +386,8 @@ return AbstractRenderer.extend({ _eventRender: function (event) { var qweb_context = { event: event, - record: event.record, - color: this.getColor(event.color_index), + record: event.extendedProps.record, + color: this.getColor(event.extendedProps.color_index), }; this.qweb_context = qweb_context; if (_.isEmpty(qweb_context.record)) { @@ -379,43 +418,50 @@ return AbstractRenderer.extend({ _initCalendar: function () { var self = this; - this.$calendar = this.$(".o_calendar_widget"); - - // This seems like a workaround but apparently passing the locale - // in the options is not enough. We should initialize it beforehand + this.calendarElement = this.$(".o_calendar_widget")[0]; var locale = moment.locale(); - $.fullCalendar.locale(locale); - //Documentation here : http://arshaw.com/fullcalendar/docs/ var fc_options = $.extend({}, this.state.fc_options, { - eventDrop: function (event) { + plugins: [ + 'moment', + 'interaction', + 'dayGrid', + 'timeGrid' + ], + eventDrop: function (eventDropInfo) { + var event = self._convertEventToFC3Event(eventDropInfo.event); self.trigger_up('dropRecord', event); }, - eventResize: function (event) { + eventResize: function (eventResizeInfo) { + self.isSwipeEnabled = false; self._unselectEvent(); + var event = self._convertEventToFC3Event(eventResizeInfo.event); self.trigger_up('updateRecord', event); }, - eventClick: function (eventData, ev) { + eventClick: function (eventClickInfo) { + var eventData = eventClickInfo.event; self._unselectEvent(); - self.$calendar.find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_highlight'); - self._renderEventPopover(eventData, $(ev.currentTarget)); + $(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_highlight'); + self._renderEventPopover(eventData, $(eventClickInfo.el)); }, - select: function (startDate, endDate) { - self.isSwipeEnabled = false; + select: function (selectionInfo) { // Clicking on the view, dispose any visible popover. Otherwise create a new event. if (self.$('.o_cw_popover').length) { self._unselectEvent(); } else { - var data = {start: startDate, end: endDate}; + var data = {start: selectionInfo.start, end: selectionInfo.end}; if (self.state.context.default_name) { data.title = self.state.context.default_name; } - self.trigger_up('openCreate', data); + self.trigger_up('openCreate', self._convertEventToFC3Event(data)); } - self.$calendar.fullCalendar('unselect'); + self.calendar.unselect(); }, - eventRender: function (event, element, view) { + eventRender: function (info) { self.isSwipeEnabled = false; + var event = info.event; + var element = $(info.el); + var view = info.view; var $render = $(self._eventRender(event)); element.find('.fc-content').html($render.html()); element.addClass($render.attr('class')); @@ -426,17 +472,17 @@ return AbstractRenderer.extend({ element.find('.fc-content').after($('
', {class: 'fc-bg'})); } - if (view.name === 'month' && event.record) { - var start = event.r_start || event.start; - var end = event.r_end || event.end; + if (view.type === 'dayGridMonth' && event.extendedProps.record) { + var start = event.extendedProps.r_start || event.start; + var end = event.extendedProps.r_end || event.end; // Detect if the event occurs in just one day // note: add & remove 1 min to avoid issues with 00:00 - var isSameDayEvent = start.clone().add(1, 'minute').isSame(end.clone().subtract(1, 'minute'), 'day'); - if (!event.record.allday && isSameDayEvent) { + var isSameDayEvent = moment(start).clone().add(1, 'minute').isSame(moment(end).clone().subtract(1, 'minute'), 'day'); + if (!event.extendedProps.record.allday && isSameDayEvent) { // For month view: do not show background for non allday, single day events element.addClass('o_cw_nobg'); - if (event.showTime && !self.hideTime) { - const displayTime = start.format(self._getDbTimeFormat()); + if (event.extendedProps.showTime && !self.hideTime) { + const displayTime = moment(start).clone().format(self._getDbTimeFormat()); element.find('.fc-content .fc-time').text(displayTime); } } @@ -447,32 +493,36 @@ return AbstractRenderer.extend({ self.trigger_up('edit_event', {id: event.id}); }); }, - eventAfterAllRender: function () { - self.isSwipeEnabled = true; + eventPositioned: function (){ + self.isSwipeEnabled = false; }, - viewRender: function (view) { - // compute mode from view.name which is either 'month', 'agendaWeek' or 'agendaDay' - var mode = view.name === 'month' ? 'month' : (view.name === 'agendaWeek' ? 'week' : 'day'); + datesRender: function (info) { + // compute mode from view.type which is either 'dayGridMonth', 'timeGridWeek' or 'timeGridDay' + var mode = info.view.type === 'dayGridMonth' ? 'month' : (info.view.type === 'timeGridWeek' ? 'week' : 'day'); self.trigger_up('viewUpdated', { mode: mode, - title: view.title, + title: info.view.title, }); }, // Add/Remove a class on hover to style multiple days events. // The css ":hover" selector can't be used because these events // are rendered using multiple elements. - eventMouseover: function (eventData) { - self.$calendar.find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_hover'); + eventMouseEnter: function (mouseEnterInfo) { + $(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseEnterInfo.event.id)).addClass('o_cw_custom_hover'); }, - eventMouseout: function (eventData) { - self.$calendar.find(_.str.sprintf('[data-event-id=%s]', eventData.id)).removeClass('o_cw_custom_hover'); + eventMouseLeave: function (mouseLeaveInfo) { + if (!mouseLeaveInfo.event.id) { + return; + } + $(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseLeaveInfo.event.id)).removeClass('o_cw_custom_hover'); }, - eventDragStart: function (eventData) { - self.$calendar.find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_hover'); + eventDragStart: function (mouseDragInfo) { + $(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseDragInfo.event.id)).addClass('o_cw_custom_hover'); self._unselectEvent(); }, - eventResizeStart: function (eventData) { - self.$calendar.find(_.str.sprintf('[data-event-id=%s]', eventData.id)).addClass('o_cw_custom_hover'); + eventResizeStart: function (mouseResizeInfo) { + self.isSwipeEnabled = false; + $(self.calendarElement).find(_.str.sprintf('[data-event-id=%s]', mouseResizeInfo.event.id)).addClass('o_cw_custom_hover'); self._unselectEvent(); }, eventLimitClick: function () { @@ -483,23 +533,23 @@ return AbstractRenderer.extend({ self._render(); }, views: { - day: { - columnFormat: 'LL' + timeGridDay: { + columnHeaderFormat: 'LL' }, - week: { - columnFormat: 'ddd D' + timeGridWeek: { + columnHeaderFormat: 'ddd D' }, - month: { - columnFormat: config.device.isMobile ? 'ddd' : 'dddd' + dayGridMonth: { + columnHeaderFormat: config.device.isMobile ? 'ddd' : 'dddd' } }, height: 'parent', unselectAuto: false, - isRTL: _t.database.parameters.direction === "rtl", - locale: locale, // reset locale when fullcalendar has already been instanciated before now + dir: _t.database.parameters.direction, + locale: locale, }); - this.$calendar.fullCalendar(fc_options); + this.calendar = new FullCalendar.Calendar(this.calendarElement, fcOptions); }, /** * Initialize the mini calendar in the sidebar @@ -551,22 +601,10 @@ return AbstractRenderer.extend({ * @returns {Promise} */ _render: function () { - var $calendar = this.$calendar; - var $fc_view = $calendar.find('.fc-view'); - var scrollPosition = $fc_view.scrollLeft(); - - $fc_view.scrollLeft(0); - $calendar.fullCalendar('unselect'); - - if (scales[this.state.scale] !== $calendar.data('fullCalendar').getView().type) { - $calendar.fullCalendar('changeView', scales[this.state.scale]); + this.$('.o_calendar_view')[0].prepend(this.calendarElement); + if (this._isInDOM) { + this._renderCalendar(); } - - if (this.target_date !== this.state.target_date.toString()) { - $calendar.fullCalendar('gotoDate', moment(this.state.target_date)); - this.target_date = this.state.target_date.toString(); - } - this.$small_calendar.datepicker("setDate", this.state.highlight_date.toDate()) .find('.o_selected_range') .removeClass('o_color o_selected_range'); @@ -581,23 +619,41 @@ return AbstractRenderer.extend({ $a.not('.ui-state-active').addClass('o_color'); }); - $fc_view.scrollLeft(scrollPosition); - - this._unselectEvent(); var filterProm = this._renderFilters(); - this._renderEvents(); - this.$calendar.prependTo(this.$('.o_calendar_view')); return Promise.all([filterProm, this._super.apply(this, arguments)]); }, + /** + * Render the specific code for the FullCalendar when it's in the DOM + * + * @private + */ + _renderCalendar() { + this.calendar.unselect(); + + if (scales[this.state.scale] !== this.calendar.view.type) { + this.calendar.changeView(scales[this.state.scale]); + } + + if (this.target_date !== this.state.target_date.toString()) { + this.calendar.gotoDate(moment(this.state.target_date).toDate()); + this.target_date = this.state.target_date.toString(); + } + + this._unselectEvent(); + this._renderEvents(); + // this._scrollToScrollTime(); + }, /** * Render all events * * @private */ _renderEvents: function () { - this.$calendar.fullCalendar('removeEvents'); - this.$calendar.fullCalendar('addEventSource', this.state.data); + this.calendar.getEvents().forEach(function(event) { + event.remove(); + }); + this.calendar.addEventSource(this.state.data); }, /** * Render all filters @@ -612,7 +668,7 @@ return AbstractRenderer.extend({ filter.destroy(); }); if (this.state.fullWidth) { - return; + return Promise.resolve(); } return this._renderFiltersOneByOne(); }, @@ -699,12 +755,12 @@ return AbstractRenderer.extend({ canDelete: this.canDelete, }; - var start = moment(eventData.r_start || eventData.start); - var end = moment(eventData.r_end || eventData.end); + var start = moment((eventData.extendedProps && eventData.extendedProps.r_start) || eventData.start); + var end = moment((eventData.extendedProps && eventData.extendedProps.r_end) || eventData.end); var isSameDayEvent = start.clone().add(1, 'minute').isSame(end.clone().subtract(1, 'minute'), 'day'); // Do not display timing if the event occur across multiple days. Otherwise use user's timing preferences - if (!this.hideTime && !eventData.record.allday && isSameDayEvent) { + if (!this.hideTime && !eventData.extendedProps.record.allday && isSameDayEvent) { var dbTimeFormat = this._getDbTimeFormat(); context.eventTime.time = start.clone().format(dbTimeFormat) + ' - ' + end.clone().format(dbTimeFormat); @@ -729,9 +785,9 @@ return AbstractRenderer.extend({ context.eventDate.date = isSameDayEvent ? start.clone().format('dddd, LL') : start.clone().format('LL') + ' - ' + end.clone().format('LL'); } - if (eventData.record.allday && isSameDayEvent) { + if (eventData.extendedProps.record.allday && isSameDayEvent) { context.eventDate.duration = _t("All day"); - } else if (eventData.record.allday && !isSameDayEvent) { + } else if (eventData.extendedProps.record.allday && !isSameDayEvent) { var daysLocaleData = moment.localeData(); var days = moment.duration(end.diff(start)).days(); context.eventDate.duration = daysLocaleData.relativeTime(days, true, 'dd'); @@ -756,8 +812,8 @@ return AbstractRenderer.extend({ }, trigger: 'manual', html: true, - title: eventData.record.display_name, - template: qweb.render('CalendarView.event.popover.placeholder', {color: this.getColor(eventData.color_index)}), + title: eventData.extendedProps.record.display_name, + template: qweb.render('CalendarView.event.popover.placeholder', {color: this.getColor(eventData.extendedProps.color_index)}), container: eventData.allDay ? '.fc-view' : '.fc-scroller', } }, @@ -781,6 +837,14 @@ return AbstractRenderer.extend({ }).popover('show'); }); }, + /** + * Scroll to the time set in the FullCalendar parameter + * @private + */ + _scrollToScrollTime: function () { + var scrollTime = this.calendar.getOption('scrollTime'); + this.calendar.scrollToTime(scrollTime); + }, //-------------------------------------------------------------------------- // Handlers @@ -812,7 +876,7 @@ return AbstractRenderer.extend({ */ _onDeleteEvent: function (event) { this._unselectEvent(); - this.trigger_up('deleteRecord', {id: event.data.id}); + this.trigger_up('deleteRecord', {id: parseInt(event.data.id, 10)}); }, }); diff --git a/addons/web/static/src/js/views/calendar/calendar_view.js b/addons/web/static/src/js/views/calendar/calendar_view.js index dfdb41a54e0..881012952b7 100644 --- a/addons/web/static/src/js/views/calendar/calendar_view.js +++ b/addons/web/static/src/js/views/calendar/calendar_view.js @@ -22,8 +22,20 @@ var fieldsToGather = [ var CalendarView = AbstractView.extend({ display_name: _lt('Calendar'), icon: 'fa-calendar', - jsLibs: ['/web/static/lib/fullcalendar/js/fullcalendar.js'], - cssLibs: ['/web/static/lib/fullcalendar/css/fullcalendar.css'], + jsLibs: [ + '/web/static/lib/fullcalendar/core/main.js', + '/web/static/lib/fullcalendar/interaction/main.js', + '/web/static/lib/fullcalendar/moment/main.js', + '/web/static/lib/fullcalendar/daygrid/main.js', + '/web/static/lib/fullcalendar/timegrid/main.js', + '/web/static/lib/fullcalendar/list/main.js' + ], + cssLibs: [ + '/web/static/lib/fullcalendar/core/main.css', + '/web/static/lib/fullcalendar/daygrid/main.css', + '/web/static/lib/fullcalendar/timegrid/main.css', + '/web/static/lib/fullcalendar/list/main.css' + ], config: _.extend({}, AbstractView.prototype.config, { Model: CalendarModel, Controller: CalendarController, diff --git a/addons/web/static/src/scss/web_calendar.scss b/addons/web/static/src/scss/web_calendar.scss index 97c2ef44f5c..cb98cc47129 100644 --- a/addons/web/static/src/scss/web_calendar.scss +++ b/addons/web/static/src/scss/web_calendar.scss @@ -90,7 +90,7 @@ $o-cw-filter-avatar-size: 20px; background: gray('400'); } - .fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton { + .fc-timeGrid-view .fc-day-grid .fc-row .fc-content-skeleton { padding: .5em; } @@ -107,7 +107,7 @@ $o-cw-filter-avatar-size: 20px; // ==================================== // ====== Both Day and Week agenda - .fc-agendaDay-view, .fc-agendaWeek-view { + .fc-timeGridDay-view, .fc-timeGridWeek-view { .fc-axis { padding-left: $o-horizontal-padding; } @@ -167,7 +167,7 @@ $o-cw-filter-avatar-size: 20px; box-shadow: inset 0 1px 0 gray('400'); } - .fc-day.fc-widget-content.fc-today.fc-state-highlight { + .fc-day.fc-widget-content.fc-today { border-left-color: rgba($o-cw-color-today, 0.3); border-right-color: rgba($o-cw-color-today, 0.3); background: rgba($o-cw-color-today, 0.05 ); @@ -177,7 +177,7 @@ $o-cw-filter-avatar-size: 20px; // Prevent events with similar color to visually overlap each other box-shadow: 0 0 0 1px white; - &.fc-v-event { + &.fc-event:not(.fc-h-event) { border-width: 3px 0 0; &.fc-not-start { @@ -197,7 +197,7 @@ $o-cw-filter-avatar-size: 20px; } // ====== Day only - .fc-agendaDay-view .fc-event { + .fc-timeGridDay-view .fc-event { padding: 10px; font-size: 14px; @@ -208,13 +208,13 @@ $o-cw-filter-avatar-size: 20px; } // ====== Week only - .fc-agendaWeek-view { + .fc-timeGridWeek-view { .fc-now-indicator { left: $o-horizontal-padding; } // Expand tiny events on hover/select - .fc-v-event.o_cw_custom_highlight, .fc-v-event.o_cw_custom_hover { + .fc-event:not(.fc-h-event).o_cw_custom_highlight, .fc-event:not(.fc-h-event).o_cw_custom_hover { transition: margin .1s .3s, left .1s .3s, right .1s .3s; margin: 0!important; right: 1px!important; @@ -223,7 +223,7 @@ $o-cw-filter-avatar-size: 20px; } // ====== Month only - .fc-month-view { + .fc-dayGridMonth-view { padding-left: $o-horizontal-padding; .fc-widget-header { @@ -319,11 +319,11 @@ $o-cw-filter-avatar-size: 20px; } // ====== RTL layout(s) &.fc-rtl { - .fc-agendaWeek-view .fc-event { + .fc-timeGrid-view .fc-event { border-width: 0 3px 0 0; } - .fc-month-view .fc-event { + .fc-dayGridMonth-view .fc-event { border-width: 0 3px 0 0; &.fc-not-start { diff --git a/addons/web/static/tests/helpers/test_utils_create.js b/addons/web/static/tests/helpers/test_utils_create.js index 8130e74ba03..494a0aa2436 100644 --- a/addons/web/static/tests/helpers/test_utils_create.js +++ b/addons/web/static/tests/helpers/test_utils_create.js @@ -106,7 +106,12 @@ odoo.define('web.test_utils_create', function (require) { return calendar; } const viewElements = [...document.getElementById('qunit-fixture').children]; + // prepend reset the scrollTop to zero so we restore it manually + let fcScroller = document.querySelector('.fc-scroller'); + const scrollPosition = fcScroller.scrollTop; viewElements.forEach(el => document.body.prepend(el)); + fcScroller = document.querySelector('.fc-scroller'); + fcScroller.scrollTop = scrollPosition; const destroy = calendar.destroy; calendar.destroy = () => { diff --git a/addons/web/static/tests/views/calendar_tests.js b/addons/web/static/tests/views/calendar_tests.js index 5865d4be80b..1a375cbf5f5 100644 --- a/addons/web/static/tests/views/calendar_tests.js +++ b/addons/web/static/tests/views/calendar_tests.js @@ -351,7 +351,7 @@ QUnit.module('Views', { }, }); - assert.ok(calendar.$('.fc-month-view').length, "should display in month mode"); + assert.ok(calendar.$('.fc-dayGridMonth-view').length, "should display in month mode"); // click on an existing event to open the formViewDialog @@ -654,7 +654,7 @@ QUnit.module('Views', { assert.strictEqual($newevent.find('.o_event_title').text(), "new event", "should display the new event with title"); - assert.deepEqual($newevent.data('fcSeg').event.record, + assert.deepEqual($newevent[0].fcSeg.eventRange.def.extendedProps.record, { display_name: "new event", start: fieldUtils.parse.datetime("2016-12-13 06:00:00", this.data.event.fields.start, {isUTC: true}), @@ -972,7 +972,7 @@ QUnit.module('Views', { assert.strictEqual($newevent.find('.o_event_title').text(), "new event", "should display the new event with title"); - assert.deepEqual($newevent.data('fcSeg').event.record, + assert.deepEqual($newevent[0].fcSeg.eventRange.def.extendedProps.record, { display_name: "new event", start: fieldUtils.parse.datetime("2016-12-13 06:00:00", this.data.event.fields.start, {isUTC: true}), @@ -1083,7 +1083,7 @@ QUnit.module('Views', { assert.strictEqual($newevent.find('.o_event_title').text(), "new event", "should display the new event with title"); - assert.deepEqual($newevent.data('fcSeg').event.record, + assert.deepEqual($newevent[0].fcSeg.eventRange.def.extendedProps.record, { display_name: "new event", start: fieldUtils.parse.datetime("2016-12-13 06:00:00", this.data.event.fields.start, {isUTC: true}), @@ -1256,7 +1256,7 @@ QUnit.module('Views', { assert.strictEqual($newevent.find('.o_event_title').text(), "new event", "should display the new event with title"); - assert.deepEqual($newevent.data('fcSeg').event.record, + assert.deepEqual($newevent[0].fcSeg.eventRange.def.extendedProps.record, { display_name: "new event", start: fieldUtils.parse.datetime("2016-12-13 06:00:00", this.data.event.fields.start, {isUTC: true}), @@ -1373,7 +1373,7 @@ QUnit.module('Views', { assert.hasAttrValue($newevent.parent(), 'colspan', "2", "should appear over two days."); - assert.deepEqual($newevent.data('fcSeg').event.record, + assert.deepEqual($newevent[0].fcSeg.eventRange.def.extendedProps.record, { display_name: "new event", start: fieldUtils.parse.datetime("2016-12-14 00:00:00", this.data.event.fields.start, {isUTC: true}), @@ -1552,7 +1552,7 @@ QUnit.module('Views', { assert.hasAttrValue($newevent.parent(), 'colspan', "2", "should appear over two days."); - assert.deepEqual($newevent.data('fcSeg').event.record, { + assert.deepEqual($newevent[0].fcSeg.eventRange.def.extendedProps.record, { display_name: "new event", start: fieldUtils.parse.datetime("2016-12-14 05:00:00", this.data.event.fields.start, {isUTC: true}), stop: fieldUtils.parse.datetime("2016-12-15 17:00:00", this.data.event.fields.stop, {isUTC: true}), @@ -1588,25 +1588,25 @@ QUnit.module('Views', { }, }); - assert.containsOnce(calendar, '.fc-agendaWeek-view', "should be in week mode"); + assert.containsOnce(calendar, '.fc-timeGridWeek-view', "should be in week mode"); assert.containsN(calendar, '.fc-event', 9, "should display 9 events on the week (4 event + 5 days event)"); await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(19)')); // Clicking on a day in another week should switch to the other week view - assert.containsOnce(calendar, '.fc-agendaWeek-view', "should be in week mode"); + assert.containsOnce(calendar, '.fc-timeGridWeek-view', "should be in week mode"); assert.containsN(calendar, '.fc-event', 4, "should display 4 events on the week (1 event + 3 days event)"); // Clicking on a day in the same week should switch to that particular day view await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); - assert.containsOnce(calendar, '.fc-agendaDay-view', "should be in day mode"); + assert.containsOnce(calendar, '.fc-timeGridDay-view', "should be in day mode"); assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the day"); // Clicking on the same day should toggle between day, month and week views await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); - assert.containsOnce(calendar, '.fc-month-view', "should be in month mode"); + assert.containsOnce(calendar, '.fc-dayGridMonth-view', "should be in month mode"); assert.containsN(calendar, '.fc-event', 7, "should display 7 events on the month (event 5 is on multiple weeks and generates to .fc-event)"); await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); - assert.containsOnce(calendar, '.fc-agendaWeek-view', "should be in week mode"); + assert.containsOnce(calendar, '.fc-timeGridWeek-view', "should be in week mode"); assert.containsN(calendar, '.fc-event', 4, "should display 4 events on the week (1 event + 3 days event)"); await testUtils.dom.click(calendar.$('.o_calendar_mini a:contains(18)')); - assert.containsOnce(calendar, '.fc-agendaDay-view', "should be in day mode"); + assert.containsOnce(calendar, '.fc-timeGridDay-view', "should be in day mode"); assert.containsN(calendar, '.fc-event', 2, "should display 2 events on the day"); calendar.destroy(); @@ -2459,7 +2459,7 @@ QUnit.module('Views', { }); assert.containsOnce(calendar, '.fc-day-grid .fc-event-container', "should be one event in the all day row"); - assert.strictEqual(calendar.model.data.data[0].r_start.date(), 14, + assert.strictEqual(moment(calendar.model.data.data[0].r_start).date(), 14, "the date should be 14"); calendar.destroy(); }); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 20bf8b43205..e28cc6d28dd 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -607,8 +607,17 @@ - - + + + + + + + + + + +