scripts/django_cradmin/utilities/Calendar.js
//NOTE: This file was originally converted from coffeescript to ES6 using decaffeinate. If you see something weird,
// please rewrite it to something readable :)
/**
Get an array of the short names for all the weekdays
in the current locale, in the the correct order for the
current locale.
*/
let getWeekdaysShortForCurrentLocale = function() {
let weekdays = [];
let weekdaysWithSundayFirst = moment.weekdaysShort();
let firstDayOfWeek = moment.localeData().firstDayOfWeek();
for (let index = firstDayOfWeek; index <= firstDayOfWeek + 6; index++) {
if (index > 6) {
index = Math.abs(7 - index);
}
let weekday = weekdaysWithSundayFirst[index];
weekdays.push(weekday);
}
return weekdays;
};
class Month {
constructor(firstDayOfMonth) {
this.firstDayOfMonth = firstDayOfMonth;
this.lastDayOfMonth = this.firstDayOfMonth.clone().add({
days: this.firstDayOfMonth.daysInMonth() - 1
});
}
getDaysInMonth() {
return this.firstDayOfMonth.daysInMonth();
}
}
class CalendarDay {
constructor(momentObject, isInCurrentMonth, isDisabled, nowMomentObject) {
this.momentObject = momentObject;
this.isInCurrentMonth = isInCurrentMonth;
this.nowMomentObject = nowMomentObject;
this._isDisabled = isDisabled;
}
getNumberInMonth() {
return this.momentObject.format('D');
}
isToday() {
return this.momentObject.isSame(this.nowMomentObject, 'day');
}
isDisabled() {
return this._isDisabled;
}
}
class CalendarWeek {
constructor() {
this.calendarDays = [];
}
addDay(calendarDay) {
return this.calendarDays.push(calendarDay);
}
getDayCount() {
return this.calendarDays.length;
}
prettyOneLineFormat() {
let formattedDays = [];
for (let calendarDay of this.calendarDays) {
let formattedDay = calendarDay.momentObject.format('DD');
if (calendarDay.isInCurrentMonth) {
formattedDay = ` ${formattedDay} `;
} else {
formattedDay = `(${formattedDay})`;
}
formattedDays.push(formattedDay);
}
return formattedDays.join(' ');
}
}
class CalendarMonth {
constructor(calendarCoordinator, momentObject){
this.calendarCoordinator = calendarCoordinator;
this.changeMonth(momentObject);
}
changeMonth(momentObject) {
let firstDayOfMonthMomentObject = momentObject.clone().set({
date: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0
});
this.month = new Month(firstDayOfMonthMomentObject);
this.calendarWeeks = [new CalendarWeek()];
this.currentWeekIndex = 0;
this.daysPerWeek = 7;
this.totalWeeks = 6;
this.currentDayCount = 0;
this.lastDay = null;
return this._build();
}
_buildPrefixedDays() {
if (this.month.firstDayOfMonth.weekday() > 0) {
let momentObject;
for (let index = 1; index <= this.month.firstDayOfMonth.weekday(); index++) {
momentObject = this.month.firstDayOfMonth.clone().subtract({
days: index
});
this._addMomentObject(momentObject, false);
}
}
}
_buildSuffixedDays() {
let totalDayCount = this.totalWeeks * this.daysPerWeek;
return (() => {
let result = [];
while (this.currentDayCount < totalDayCount) {
let momentObject = this.lastDay.momentObject.clone().add({
days: 1
});
result.push(this._addMomentObject(momentObject, false));
}
return result;
})();
}
_buildDaysBelongingInMonth() {
let momentObject;
for (let dayIndex = 1; dayIndex <= this.month.getDaysInMonth(); dayIndex ++) {
momentObject = this.month.firstDayOfMonth.clone().date(dayIndex);
this._addMomentObject(momentObject, true);
}
}
_build(momentFirstDayOfMonth) {
this._buildPrefixedDays();
this._buildDaysBelongingInMonth();
return this._buildSuffixedDays();
}
_addMomentObject(momentObject, isInCurrentMonth) {
let week = this.calendarWeeks[this.currentWeekIndex];
if (week.getDayCount() >= this.daysPerWeek) {
this.calendarWeeks.push(new CalendarWeek());
this.currentWeekIndex += 1;
week = this.calendarWeeks[this.currentWeekIndex];
}
let isDisabled = !this.calendarCoordinator.momentObjectIsAllowed(momentObject);
let calendarDay = new CalendarDay(momentObject, isInCurrentMonth, isDisabled,
this.calendarCoordinator.nowMomentObject);
week.addDay(calendarDay);
this.currentDayCount += 1;
return this.lastDay = calendarDay;
}
prettyprint() {
let rowFormatted;
for (week in this.calendarWeeks) {
rowFormatted = [];
console.log(week.prettyOneLineFormat());
}
}
}
/**
Coordinates the common calendar data no matter what kind of
view we present.
*/
export class CalendarCoordinator {
constructor({selectedMomentObject,
minimumDatetime,
maximumDatetime,
nowMomentObject}) {
// We operate with two momentObjects:
// - selectedMomentObject: This is the actual moment object
// that the user has selected. This can be null if the user
// has not selected a value yet.
// - shownMomentObject: This reflects the value shown on the screen
// at any given moment (E.g.: It changes each time a user changes
// the day, month, hour, etc). This is never ``null``.
this.selectedMomentObject = selectedMomentObject;
this.minimumDatetime = minimumDatetime;
this.maximumDatetime = maximumDatetime;
this.nowMomentObject = nowMomentObject;
if (this.selectedMomentObject != null) {
this.shownMomentObject = this.selectedMomentObject.clone();
} else {
// We set this to start the date picker on the current date
this.setToNow();
// If the current time is not allowed, pick the first allowed value
if (!this.momentObjectIsAllowed(this.shownMomentObject)) {
this.shownMomentObject = this.minimumDatetime.clone();
}
}
}
selectShownValue() {
return this.selectedMomentObject = this.shownMomentObject.clone();
}
clearSelectedMomentObject() {
return this.selectedMomentObject = null;
}
momentObjectIsAllowed(momentObject, ignoreTime=true) {
let isAllowed = true;
if (this.minimumDatetime != null) {
let { minimumDatetime } = this;
if (ignoreTime) {
minimumDatetime = minimumDatetime.clone().set({
hour: 0,
minute: 0,
second: 0
});
}
isAllowed = !momentObject.isBefore(minimumDatetime);
}
if (isAllowed && (this.maximumDatetime != null)) {
let { maximumDatetime } = this;
if (ignoreTime) {
maximumDatetime = maximumDatetime.clone().set({
hour: 23,
minute: 59,
second: 59
});
}
isAllowed = !momentObject.isAfter(maximumDatetime);
}
return isAllowed;
}
todayIsValidValue() {
return this.momentObjectIsAllowed(this.nowMomentObject);
}
nowIsValidValue() {
return this.momentObjectIsAllowed(this.nowMomentObject, false);
}
shownDateIsToday() {
return this.shownMomentObject.isSame(this.nowMomentObject, 'day');
}
shownDateIsTodayAndNowIsValid() {
return this.shownDateIsToday() && this.nowIsValidValue();
}
setToNow() {
return this.shownMomentObject = this.nowMomentObject.clone();
}
}
/**
Coordinates the common calendar data for a month-view.
*/
export class MonthlyCalendarCoordinator {
constructor({calendarCoordinator,
yearselectValues,
hourselectValues,
minuteselectValues,
yearFormat,
monthFormat,
dayOfMonthSelectFormat,
dayOfMonthTableCellFormat,
hourFormat,
minuteFormat}) {
this.calendarCoordinator = calendarCoordinator;
this.yearselectValues = yearselectValues;
this.hourselectValues = hourselectValues;
this.minuteselectValues = minuteselectValues;
this.yearFormat = yearFormat;
this.monthFormat = monthFormat;
this.dayOfMonthSelectFormat = dayOfMonthSelectFormat;
this.dayOfMonthTableCellFormat = dayOfMonthTableCellFormat;
this.hourFormat = hourFormat;
this.minuteFormat = minuteFormat;
this.dayobjects = null; // Updated in @_changeSelectedDate()
this._initWeekdays();
this._initMonthObjects();
this._initYearObjects();
this._initHourObjects();
this._initMinuteObjects();
this._changeSelectedDate();
}
_sortConfigObjectsByValue(configObjects) {
let compareFunction = function(a, b) {
if (a.value < b.value) {
return -1;
}
if (a.value > b.value) {
return 1;
}
return 0;
};
return configObjects.sort(compareFunction);
}
_initWeekdays() {
return this.shortWeekdays = getWeekdaysShortForCurrentLocale();
}
_initYearObjects() {
let selectedYearValue = this.calendarCoordinator.shownMomentObject.year();
let hasSelectedYearValue = false;
let formatMomentObject = this.calendarCoordinator.shownMomentObject.clone().set({
month: 0,
date: 0,
hour: 0,
minute: 0,
second: 0
});
this._yearsMap = {};
this.yearselectConfig = [];
for (let year of this.yearselectValues) {
var label = formatMomentObject.set({
year
}).format(this.yearFormat);
var yearConfig = {
value: year,
label
};
this.yearselectConfig.push(yearConfig);
this._yearsMap[year] = yearConfig;
if (year === selectedYearValue) {
hasSelectedYearValue = true;
}
}
if (!hasSelectedYearValue) {
// Since we do not include all years in the yearList, we need
// to handle the case when the given datetimes year is not in the
// list. We handle this by adding it to the end of the list.
var label = formatMomentObject.set({
year: selectedYearValue
}).format(this.yearFormat);
var yearConfig = {
value: selectedYearValue,
label
};
this.yearselectConfig.push(yearConfig);
this._yearsMap[yearConfig.value] = yearConfig;
// Sort the config to put the newly added config in
// its natural place.
return this._sortConfigObjectsByValue(this.yearselectConfig);
}
}
_initMonthObjects() {
let label;
let monthObject;
this.monthselectConfig = [];
this._monthsMap = {};
let formatMomentObject = this.calendarCoordinator.shownMomentObject.clone().set({
month: 0,
date: 0,
hour: 0,
minute: 0,
second: 0
});
for (let monthnumber = 0; monthnumber < 12; monthnumber++) {
label = formatMomentObject.set({
month: monthnumber
}).format(this.monthFormat);
monthObject = {
value: monthnumber,
label
};
this.monthselectConfig.push(monthObject);
this._monthsMap[monthnumber] = monthObject;
}
}
_initHourObjects() {
let selectedHourValue = this.calendarCoordinator.shownMomentObject.hour();
let hasSelectedHourValue = false;
let formatMomentObject = this.calendarCoordinator.shownMomentObject.clone().set({
minute: 0,
second: 0
});
this._hoursMap = {};
this.hourselectConfig = [];
for (let hour of this.hourselectValues) {
var label = formatMomentObject.set({
hour
}).format(this.hourFormat);
var hourConfig = {
value: hour,
label
};
this.hourselectConfig.push(hourConfig);
this._hoursMap[hourConfig.value] = hourConfig;
if (hourConfig.value === selectedHourValue) {
hasSelectedHourValue = true;
}
}
if (!hasSelectedHourValue) {
// Since we do not include all hours in the hourList, we need
// to handle the case when the given datetimes hour is not in the
// list. We handle this by adding it to the end of the list.
var label = formatMomentObject.set({
hour: selectedHourValue
}).format(this.hourFormat);
var hourConfig = {
value: selectedHourValue,
label
};
this.hourselectConfig.push(hourConfig);
this._hoursMap[hourConfig.value] = hourConfig;
return this._sortConfigObjectsByValue(this.hourselectConfig);
}
}
_initMinuteObjects() {
let selectedMinuteValue = this.calendarCoordinator.shownMomentObject.minute();
let hasSelectedMinuteValue = false;
let formatMomentObject = this.calendarCoordinator.shownMomentObject.clone().set({
second: 0
});
this._minutesMap = {};
this.minuteselectConfig = [];
for (let minute of this.minuteselectValues) {
var label = formatMomentObject.set({
minute
}).format(this.minuteFormat);
var minuteConfig = {
value: minute,
label
};
this.minuteselectConfig.push(minuteConfig);
this._minutesMap[minuteConfig.value] = minuteConfig;
if (minuteConfig.value === selectedMinuteValue) {
hasSelectedMinuteValue = true;
}
}
if (!hasSelectedMinuteValue) {
// Since we do not include all minutes in the minuteList, we need
// to handle the case when the given datetimes minute is not in the
// list. We handle this by adding it to the end of the list.
var label = formatMomentObject.set({
minute: selectedMinuteValue
}).format(this.minuteFormat);
var minuteConfig = {
value: selectedMinuteValue,
label
};
this.minuteselectConfig.push(minuteConfig);
this._minutesMap[minuteConfig.value] = minuteConfig;
return this._sortConfigObjectsByValue(this.minuteselectConfig);
}
}
_setCurrentYear() {
let currentYearNumber = this.calendarMonth.month.firstDayOfMonth.year();
this.currentYearObject = this._yearsMap[currentYearNumber];
if (this.currentYearObject == null) {
console.warn(`The given year, ${currentYearNumber} is not one of the available choices`);
}
}
_setCurrentMonth() {
let currentMonthNumber = this.calendarMonth.month.firstDayOfMonth.month();
this.currentMonthObject = this._monthsMap[currentMonthNumber];
if (this.currentMonthObject == null) {
console.warn(`The given month number, ${currentMonthNumber} is not one of the available choices`);
}
}
_setCurrentHour() {
let currentHourNumber = this.calendarCoordinator.shownMomentObject.hour();
this.currentHourObject = this._hoursMap[currentHourNumber];
if (this.currentHourObject == null) {
console.warn(`The given hour, ${currentHourNumber} is not one of the available choices`);
}
}
_setCurrentMinute() {
let currentMinuteNumber = this.calendarCoordinator.shownMomentObject.minute();
this.currentMinuteObject = this._minutesMap[currentMinuteNumber];
if (this.currentMinuteObject == null) {
console.warn(`The given minute, ${currentMinuteNumber} is not one of the available choices`);
}
}
_updateDayObjects() {
let label;
let dayNumberObject;
let formatMomentObject = this.calendarCoordinator.shownMomentObject.clone().set({
hour: 0,
minute: 0,
second: 0
});
this.dayobjects = [];
for (let daynumber = 1; index <= this.calendarMonth.month.getDaysInMonth(); index++) {
label = formatMomentObject.set({
date: daynumber
}).format(this.dayOfMonthSelectFormat);
dayNumberObject = {
value: daynumber,
label: label
};
this.dayobjects.push(dayNumberObject);
}
}
/*
Change month to the month containing the given momentObject,
and select the date.
As long as you change ``@calendarCoordinator.shownMomentObject``, this
will update everything to mirror the change (selected day, month, year, ...).
*/
_changeSelectedDate() {
this.calendarMonth = new CalendarMonth(this.calendarCoordinator, this.calendarCoordinator.shownMomentObject);
this._setCurrentYear();
this._setCurrentMonth();
this._setCurrentHour();
this._setCurrentMinute();
this._updateDayObjects();
return this.currentDayObject = this.dayobjects[this.calendarCoordinator.shownMomentObject.date()-1];
}
handleDayChange(momentObject) {
this.calendarCoordinator.shownMomentObject = momentObject.clone().set({
hour: this.currentHourObject.value,
minute: this.currentMinuteObject.value
});
return this._changeSelectedDate();
}
handleCurrentDayObjectChange() {
let momentObject = moment({
year: this.currentYearObject.value,
month: this.currentMonthObject.value,
day: this.currentDayObject.value
});
return this.handleDayChange(momentObject);
}
handleCalendarDayChange(calendarDay) {
return this.handleDayChange(calendarDay.momentObject);
}
handleCurrentMonthChange() {
this.calendarCoordinator.shownMomentObject.set({
month: this.currentMonthObject.value
});
return this._changeSelectedDate();
}
handleCurrentYearChange() {
this.calendarCoordinator.shownMomentObject.set({
year: this.currentYearObject.value
});
return this._changeSelectedDate();
}
handleCurrentHourChange() {
this.calendarCoordinator.shownMomentObject.set({
hour: this.currentHourObject.value
});
return this._changeSelectedDate();
}
handleCurrentMinuteChange() {
this.calendarCoordinator.shownMomentObject.set({
minute: this.currentMinuteObject.value
});
return this._changeSelectedDate();
}
handleFocusOnCalendarDay(calendarDay) {
return this.lastFocusedMomentObject = calendarDay.momentObject;
}
getLastFocusedMomentObject() {
if (this.lastFocusedMomentObject != null) {
return this.lastFocusedMomentObject;
} else {
return this.calendarCoordinator.shownMomentObject;
}
}
getDayOfMonthLabelForTableCell(calendarDay) {
return calendarDay.momentObject.format(this.dayOfMonthTableCellFormat);
}
setToToday() {
return this.handleDayChange(this.calendarCoordinator.nowMomentObject.clone());
}
}