Calendar refactors:

- date_helper, which was an et2_date_widget, now just a function that can handle all the different date formats in calendar
 - holiday fetch & cache moved into its own file, useable from anywhere
This commit is contained in:
nathan 2022-04-29 14:37:15 -06:00
parent f54c7f40bb
commit 813cd6924e
8 changed files with 192 additions and 255 deletions

View File

@ -544,7 +544,6 @@ class calendar_ui
$cont['owner'] = $this->owner ? (is_array($this->owner) ? $this->owner : explode(',',$this->owner) ) : $cont['owner']; $cont['owner'] = $this->owner ? (is_array($this->owner) ? $this->owner : explode(',',$this->owner) ) : $cont['owner'];
$cont['year'] = (int)Api\DateTime::to($cont['date'],'Y'); $cont['year'] = (int)Api\DateTime::to($cont['date'],'Y');
$cont['holidays'] = $this->bo->read_holidays($cont['year']);
$readonlys = array(); $readonlys = array();
$sel_options['status_filter'] = array( $sel_options['status_filter'] = array(

View File

@ -276,16 +276,6 @@ export class CalendarApp extends EgwApp
this.sidebox_hooked_templates.push(this.sidebox_et2); this.sidebox_hooked_templates.push(this.sidebox_et2);
jQuery(_et2.DOMContainer).hide(); jQuery(_et2.DOMContainer).hide();
// Set client side holiday cache for this year
// @ts-ignore
if(et2_calendar_view)
{
// @ts-ignore
et2_calendar_view.holiday_cache[content.data.year] = content.data.holidays;
delete content.data.holidays;
delete content.data.year;
}
this._setup_sidebox_filters(); this._setup_sidebox_filters();
this.state = content.data; this.state = content.data;

View File

@ -17,16 +17,17 @@
import {et2_createWidget, et2_register_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget"; import {et2_createWidget, et2_register_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget";
import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget"; import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget";
import {et2_calendar_timegrid} from "./et2_widget_timegrid"; import {et2_calendar_timegrid} from "./et2_widget_timegrid";
import {et2_calendar_view} from "./et2_widget_view";
import {et2_calendar_event} from "./et2_widget_event"; import {et2_calendar_event} from "./et2_widget_event";
import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance"; import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance";
import {et2_date} from "../../api/js/etemplate/et2_widget_date";
import {et2_IDetachedDOM, et2_IResizeable} from "../../api/js/etemplate/et2_core_interfaces"; import {et2_IDetachedDOM, et2_IResizeable} from "../../api/js/etemplate/et2_core_interfaces";
import {et2_no_init} from "../../api/js/etemplate/et2_core_common"; import {et2_no_init} from "../../api/js/etemplate/et2_core_common";
import {egw} from "../../api/js/jsapi/egw_global"; import {egw} from "../../api/js/jsapi/egw_global";
import {egwIsMobile} from "../../api/js/egw_action/egw_action_common.js"; import {egwIsMobile, sprintf} from "../../api/js/egw_action/egw_action_common.js";
import {CalendarApp} from "./app"; import {CalendarApp} from "./app";
import {sprintf} from "../../api/js/egw_action/egw_action_common.js"; import {holidays} from "../../api/js/etemplate/Et2Date/Holidays";
import {et2_calendar_view} from "./et2_widget_view";
import flatpickr from "flatpickr";
import {formatDate} from "../../api/js/etemplate/Et2Date/Et2Date";
/** /**
* Class which implements the "calendar-timegrid" XET-Tag for displaying a single days * Class which implements the "calendar-timegrid" XET-Tag for displaying a single days
@ -69,8 +70,7 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
private event_wrapper: JQuery; private event_wrapper: JQuery;
private user_spacer: JQuery; private user_spacer: JQuery;
private all_day: JQuery; private all_day: JQuery;
private _date_helper : et2_date;
private registeredUID: string = null; private registeredUID: string = null;
// Init to defaults, just in case - they will be updated from parent // Init to defaults, just in case - they will be updated from parent
@ -118,11 +118,6 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
.appendTo(this.div); .appendTo(this.div);
this.setDOMNode(this.div[0]); this.setDOMNode(this.div[0]);
// Used for its date calculations - note this is a datetime, parent
// uses just a date
this._date_helper = et2_createWidget('date-time', {}, null);
this._date_helper.loadingFinished();
} }
doLoadingFinished( ) doLoadingFinished( )
@ -158,10 +153,6 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
this.title = null; this.title = null;
this.user_spacer = null; this.user_spacer = null;
// date_helper has no parent, so we must explicitly remove it
this._date_helper.destroy();
this._date_helper = null;
egw.dataUnregisterUID(this.registeredUID,null,this); egw.dataUnregisterUID(this.registeredUID,null,this);
} }
@ -225,9 +216,9 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
return this.date; return this.date;
} }
get date_helper(): et2_date date_helper(value)
{ {
return this._date_helper; return (<et2_calendar_view>this.getParent()).date_helper(value);
} }
/** /**
@ -249,28 +240,16 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
{ {
force_redraw = false; force_redraw = false;
} }
if(!this.getParent() || !this.getParent().date_helper) if(!this.getParent())
{ {
egw.debug('warn', 'Day col widget "' + this.id + '" is missing its parent.'); egw.debug('warn', 'Day col widget "' + this.id + '" is missing its parent.');
return false; return false;
} }
if(typeof _date === "object")
{
this.getParent().date_helper.set_value(_date);
}
else if(typeof _date === "string")
{
// Need a new date to avoid invalid month/date combinations when setting
// month then day. Use a string to avoid browser timezone.
this.getParent().date_helper.set_value(_date.substring(0,4)+'-'+(_date.substring(4,6))+'-'+_date.substring(6,8)+'T00:00:00Z');
}
this.date = new Date(this.getParent().date_helper.getValue()); this.date = (<et2_calendar_view>this.getParent()).date_helper(_date);
// Keep internal option in Ymd format, it gets passed around in this format // Keep internal option in Ymd format, it gets passed around in this format
const new_date = "" + this.getParent().date_helper.get_year() + const new_date = formatDate(this.date, {dateFormat: "Ymd"});
sprintf("%02d", this.getParent().date_helper.get_month()) +
sprintf("%02d", this.getParent().date_helper.get_date());
// Set label // Set label
if(!this.options.label) if(!this.options.label)
@ -278,9 +257,9 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
// Add timezone offset back in, or formatDate will lose those hours // Add timezone offset back in, or formatDate will lose those hours
const formatDate = new Date(this.date.valueOf() + this.date.getTimezoneOffset() * 60 * 1000); const formatDate = new Date(this.date.valueOf() + this.date.getTimezoneOffset() * 60 * 1000);
this.title.html('<span class="long_date">'+jQuery.datepicker.formatDate('DD',formatDate)+ this.title.html('<span class="long_date">' + flatpickr.formatDate(formatDate, 'l') +
'</span><span class="short_date">'+jQuery.datepicker.formatDate('D',formatDate)+'</span>'+ '</span><span class="short_date">' + flatpickr.formatDate(formatDate, 'D') + '</span>' +
jQuery.datepicker.formatDate('d',formatDate)); flatpickr.formatDate(formatDate, 'd'));
} }
this.title this.title
.attr("data-date", new_date) .attr("data-date", new_date)
@ -472,7 +451,7 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
); );
// Holidays and birthdays // Holidays and birthdays
let holidays = await et2_calendar_view.get_holidays(this.options.date.substring(0, 4)); let fetched_holidays = await holidays(this.options.date.substring(0, 4));
const holiday_list = []; const holiday_list = [];
let holiday_pref = (egw.preference('birthdays_as_events', 'calendar') || []); let holiday_pref = (egw.preference('birthdays_as_events', 'calendar') || []);
if(typeof holiday_pref === 'string') if(typeof holiday_pref === 'string')
@ -490,27 +469,24 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
const birthdays_as_events = egwIsMobile() || holiday_pref.indexOf('birthday') >= 0; const birthdays_as_events = egwIsMobile() || holiday_pref.indexOf('birthday') >= 0;
if(holidays && holidays[this.options.date]) if(fetched_holidays && fetched_holidays[this.options.date])
{ {
holidays = holidays[this.options.date]; fetched_holidays = fetched_holidays[this.options.date];
for(let i = 0; i < holidays.length; i++) for(let i = 0; i < fetched_holidays.length; i++)
{ {
if (typeof holidays[i]['birthyear'] !== 'undefined') if(typeof fetched_holidays[i]['birthyear'] !== 'undefined')
{ {
// Show birthdays as events on mobile or by preference // Show birthdays as events on mobile or by preference
if(birthdays_as_events) if(birthdays_as_events)
{ {
// Create event // Create event
this.getParent().date_helper.set_value(this.options.date.substring(0,4)+'-'+ var event = et2_createWidget('calendar-event', {
(this.options.date.substring(4,6))+'-'+this.options.date.substring(6,8)+ id: 'event_' + fetched_holidays[i].name,
'T00:00:00Z');
var event = et2_createWidget('calendar-event',{
id:'event_'+holidays[i].name,
value: { value: {
title: holidays[i].name, title: fetched_holidays[i].name,
whole_day: true, whole_day: true,
whole_day_on_top: true, whole_day_on_top: true,
start: new Date(this.getParent().date_helper.get_value()), start: (<et2_calendar_view>this.getParent()).date_helper(this.options.date),
end: this.options.date, end: this.options.date,
owner: this.options.owner, owner: this.options.owner,
participants: this.options.owner, participants: this.options.owner,
@ -528,7 +504,7 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
//If the birthdays are already displayed as event, don't //If the birthdays are already displayed as event, don't
//show them in the caption //show them in the caption
this.title.addClass('calendar_calBirthday'); this.title.addClass('calendar_calBirthday');
holiday_list.push(holidays[i]['name']); holiday_list.push(fetched_holidays[i]['name']);
} }
} }
else else
@ -537,16 +513,13 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
if(holidays_as_events) if(holidays_as_events)
{ {
// Create event // Create event
this.getParent().date_helper.set_value(this.options.date.substring(0,4)+'-'+ var event = et2_createWidget('calendar-event', {
(this.options.date.substring(4,6))+'-'+this.options.date.substring(6,8)+ id: 'event_' + fetched_holidays[i].name,
'T00:00:00Z');
var event = et2_createWidget('calendar-event',{
id:'event_'+holidays[i].name,
value: { value: {
title: holidays[i].name, title: fetched_holidays[i].name,
whole_day: true, whole_day: true,
whole_day_on_top: true, whole_day_on_top: true,
start: new Date(this.getParent().date_helper.get_value()), start: (<et2_calendar_view>this.getParent()).date_helper(this.options.date),
end: this.options.date, end: this.options.date,
owner: this.options.owner, owner: this.options.owner,
participants: this.options.owner, participants: this.options.owner,
@ -562,13 +535,13 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache
else else
{ {
this.title.addClass('calendar_calHoliday'); this.title.addClass('calendar_calHoliday');
this.title.attr('data-holiday', holidays[i]['name']); this.title.attr('data-holiday', fetched_holidays[i]['name']);
//If the birthdays are already displayed as event, don't //If the birthdays are already displayed as event, don't
//show them in the caption //show them in the caption
if (!this.options.display_holiday_as_event) if (!this.options.display_holiday_as_event)
{ {
holiday_list.push(holidays[i]['name']); holiday_list.push(fetched_holidays[i]['name']);
} }
} }
} }

View File

@ -515,29 +515,30 @@ export class et2_calendar_event extends et2_valueWidget implements et2_IDetached
*/ */
_tooltip() _tooltip()
{ {
if (!this.div || !this.options.value || !this.options.value.app_id) return ''; if(!this.div || !this.options.value || !this.options.value.app_id)
{
return '';
}
const border = this.div.css('borderTopColor'); const border = this.div.css('borderTopColor');
const bg_color = this.div.css('background-color'); const bg_color = this.div.css('background-color');
const header_color = this.title.css('color'); const header_color = this.title.css('color');
const timespan = this._get_timespan(this.options.value); const timespan = this._get_timespan(this.options.value);
const parent = this.getParent() instanceof et2_calendar_daycol ? (<et2_calendar_daycol>this.getParent()) : (<et2_calendar_planner_row>this.getParent()); const parent = this.getParent() instanceof et2_calendar_daycol ? (<et2_calendar_daycol>this.getParent()) : (<et2_calendar_planner_row>this.getParent());
parent.date_helper.set_value(this.options.value.start.valueOf ? new Date(this.options.value.start) : this.options.value.start); const start = parent.date_helper(this.options.value.start);
const start = parent.date_helper.input_date.val(); const end = parent.date_helper(this.options.value.end);
parent.date_helper.set_value(this.options.value.end.valueOf ? new Date(this.options.value.end) : this.options.value.end);
const end = parent.date_helper.input_date.val();
const times = !this.options.value.multiday ? const times = !this.options.value.multiday ?
'<span class="calendar_calEventLabel">' + this.egw().lang('Time') + '</span>:' + timespan : '<span class="calendar_calEventLabel">' + this.egw().lang('Time') + '</span>:' + timespan :
'<span class="calendar_calEventLabel">' + this.egw().lang('Start') + '</span>:' + start + ' ' + '<span class="calendar_calEventLabel">' + this.egw().lang('Start') + '</span>:' + start + ' ' +
'<span class="calendar_calEventLabel">' + this.egw().lang('End') + '</span>:' + end; '<span class="calendar_calEventLabel">' + this.egw().lang('End') + '</span>:' + end;
let cat_label: (string | string[]) = ''; let cat_label : (string | string[]) = '';
if (this.options.value.category) if(this.options.value.category)
{ {
const cat = et2_createWidget('select-cat', {'readonly': true}, this); const cat = et2_createWidget('select-cat', {'readonly': true}, this);
cat.set_value(this.options.value.category); cat.set_value(this.options.value.category);
cat_label = this.options.value.category.indexOf(',') <= 0 ? cat.span.text() : []; cat_label = this.options.value.category.indexOf(',') <= 0 ? cat.span.text() : [];
if (typeof cat_label != 'string') if (typeof cat_label != 'string')
{ {
cat.span.children().each(function () cat.span.children().each(function ()
@ -847,13 +848,11 @@ export class et2_calendar_event extends et2_valueWidget implements et2_IDetached
// Use dates as objects // Use dates as objects
if (typeof event.start !== 'object') if (typeof event.start !== 'object')
{ {
parent.date_helper.set_value(event.start); event.start = parent.date_helper(event.start);
event.start = new Date(parent.date_helper.getValue());
} }
if (typeof event.end !== 'object') if (typeof event.end !== 'object')
{ {
parent.date_helper.set_value(event.end); event.end = parent.date_helper(event.end)
event.end = new Date(parent.date_helper.getValue());
} }
// We need minutes for durations // We need minutes for durations

View File

@ -15,7 +15,7 @@
/calendar/js/et2_widget_event.js; /calendar/js/et2_widget_event.js;
*/ */
import {et2_register_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget"; import {et2_createWidget, et2_register_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget";
import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance"; import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance";
import {et2_calendar_view} from "./et2_widget_view"; import {et2_calendar_view} from "./et2_widget_view";
import {et2_action_object_impl} from "../../api/js/etemplate/et2_core_DOMWidget"; import {et2_action_object_impl} from "../../api/js/etemplate/et2_core_DOMWidget";
@ -31,6 +31,8 @@ import {CalendarApp} from "./app";
import {sprintf} from "../../api/js/egw_action/egw_action_common.js"; import {sprintf} from "../../api/js/egw_action/egw_action_common.js";
import {et2_dataview_grid} from "../../api/js/etemplate/et2_dataview_view_grid"; import {et2_dataview_grid} from "../../api/js/etemplate/et2_dataview_view_grid";
import {et2_selectbox} from "../../api/js/etemplate/et2_widget_selectbox"; import {et2_selectbox} from "../../api/js/etemplate/et2_widget_selectbox";
import {formatDate} from "../../api/js/etemplate/Et2Date/Et2Date";
import {holidays} from "../../api/js/etemplate/Et2Date/Holidays";
/** /**
* Class which implements the "calendar-planner" XET-Tag for displaying a longer * Class which implements the "calendar-planner" XET-Tag for displaying a longer
@ -1324,21 +1326,23 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
*/ */
async day_class_holiday(date, holiday_list, days?) async day_class_holiday(date, holiday_list, days?)
{ {
if(!date) return ''; if(!date)
{
return '';
}
// Holidays and birthdays // Holidays and birthdays
const holidays = await et2_calendar_view.get_holidays(date.getUTCFullYear()); const fetched = await holidays(date.getUTCFullYear());
var day_class = ''; var day_class = '';
// Pass a string rather than the date object, to make sure it doesn't get changed // Pass a string rather than the date object, to make sure it doesn't get changed
this.date_helper.set_value(date.toJSON()); let date_key = formatDate(this.date_helper(date.toJSON()), {dateFormat: "Ymd"});
var date_key = ''+this.date_helper.get_year() + sprintf('%02d',this.date_helper.get_month()) + sprintf('%02d',this.date_helper.get_date()); if(fetched && fetched[date_key])
if (holidays && holidays[date_key])
{ {
const dates = holidays[date_key]; const dates = fetched[date_key];
for(var i = 0; i < dates.length; i++) for(var i = 0; i < dates.length; i++)
{ {
if (typeof dates[i]['birthyear'] !== 'undefined') if(typeof dates[i]['birthyear'] !== 'undefined')
{ {
day_class += ' calendar_calBirthday '; day_class += ' calendar_calBirthday ';
if(typeof days == 'undefined' || days <= 21) if(typeof days == 'undefined' || days <= 21)
@ -1805,8 +1809,7 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
var event_widget = planner.getWidgetById(event_data.widget_id); var event_widget = planner.getWidgetById(event_data.widget_id);
if(event_widget) if(event_widget)
{ {
event_widget._parent.date_helper.set_value(drop_date); event_widget.options.value.start = event_widget._parent.date_helper(drop_date);
event_widget.options.value.start = new Date(event_widget._parent.date_helper.getValue());
// Leave the helper there until the update is done // Leave the helper there until the update is done
var loading = ui.helper.clone().appendTo(ui.helper.parent()); var loading = ui.helper.clone().appendTo(ui.helper.parent());
@ -2393,6 +2396,7 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
var rel_time = 0; var rel_time = 0;
var day_header = jQuery('.calendar_plannerScaleDay', this.headers); var day_header = jQuery('.calendar_plannerScaleDay', this.headers);
let date;
// Simple math, the x is offset from start date // Simple math, the x is offset from start date
if(this.options.group_by !== 'month' && ( if(this.options.group_by !== 'month' && (
@ -2400,8 +2404,8 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
this.options.show_weekend || day_header.length === 0 this.options.show_weekend || day_header.length === 0
)) ))
{ {
rel_time = (new Date(this.options.end_date) - new Date(this.options.start_date))*rel_x/1000; rel_time = (new Date(this.options.end_date) - new Date(this.options.start_date)) * rel_x / 1000;
this.date_helper.set_value(this.options.start_date.toJSON()); date = this.date_helper(this.options.start_date.toJSON());
} }
// Not so simple math, need to account for missing days // Not so simple math, need to account for missing days
else if(this.options.group_by !== 'month' && !this.options.show_weekend) else if(this.options.group_by !== 'month' && !this.options.show_weekend)
@ -2416,10 +2420,10 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
// Use day, and find time in that day // Use day, and find time in that day
if(day && day.dataset && day.dataset.date) if(day && day.dataset && day.dataset.date)
{ {
this.date_helper.set_value(day.dataset.date); date = this.date_helper(day.dataset.date);
rel_time = ((x - jQuery(day).position().left) / jQuery(day).outerWidth(true)) * 24*60; rel_time = ((x - jQuery(day).position().left) / jQuery(day).outerWidth(true)) * 24 * 60;
this.date_helper.set_minutes(Math.round(rel_time/interval) * interval); date.setUTCMinutes(Math.round(rel_time / interval) * interval);
return new Date(this.date_helper.getValue()); return date;
} }
return false; return false;
} }
@ -2465,11 +2469,11 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
{ {
// Not sure where the extra -1 and +2 are coming from, but it makes it work out // Not sure where the extra -1 and +2 are coming from, but it makes it work out
// in FF & Chrome // in FF & Chrome
rel_x = Math.min((x-row_widget.rows.offset().left-1)/(row_widget.rows.width()+2),1); rel_x = Math.min((x - row_widget.rows.offset().left - 1) / (row_widget.rows.width() + 2), 1);
// 2678400 is the number of seconds in 31 days // 2678400 is the number of seconds in 31 days
rel_time = (2678400)*rel_x; rel_time = (2678400) * rel_x;
this.date_helper.set_value(row_widget.options.start_date.toJSON()); date = this.date_helper(row_widget.options.start_date.toJSON());
} }
else else
{ {
@ -2478,9 +2482,9 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta
} }
if(rel_time < 0) return false; if(rel_time < 0) return false;
this.date_helper.set_minutes(Math.round(rel_time / (60 * interval))*interval); date.setUTCMinutes(Math.round(rel_time / (60 * interval)) * interval);
return new Date(this.date_helper.getValue()); return date;
} }
/** /**

View File

@ -18,13 +18,13 @@
import {et2_createWidget, et2_register_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget"; import {et2_createWidget, et2_register_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget";
import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget"; import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget";
import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance"; import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance";
import {et2_date} from "../../api/js/etemplate/et2_widget_date";
import {et2_action_object_impl} from "../../api/js/etemplate/et2_core_DOMWidget"; import {et2_action_object_impl} from "../../api/js/etemplate/et2_core_DOMWidget";
import {et2_calendar_planner} from "./et2_widget_planner"; import {et2_calendar_planner} from "./et2_widget_planner";
import {egw_getObjectManager, egwActionObject} from "../../api/js/egw_action/egw_action.js"; import {egw_getObjectManager, egwActionObject} from "../../api/js/egw_action/egw_action.js";
import {EGW_AI_DRAG_OUT, EGW_AI_DRAG_OVER} from "../../api/js/egw_action/egw_action_constants.js"; import {EGW_AI_DRAG_OUT, EGW_AI_DRAG_OVER} from "../../api/js/egw_action/egw_action_constants.js";
import {et2_IResizeable} from "../../api/js/etemplate/et2_core_interfaces"; import {et2_IResizeable} from "../../api/js/etemplate/et2_core_interfaces";
import {egw} from "../../api/js/jsapi/egw_global"; import {egw} from "../../api/js/jsapi/egw_global";
import {et2_calendar_view} from "./et2_widget_view";
/** /**
* Class for one row of a planner * Class for one row of a planner
@ -50,7 +50,6 @@ export class et2_calendar_planner_row extends et2_valueWidget implements et2_IRe
private div: JQuery; private div: JQuery;
private title: JQuery; private title: JQuery;
private rows: JQuery; private rows: JQuery;
private _date_helper: et2_date;
private _cached_rows: any[]; private _cached_rows: any[];
private _row_height = 20; private _row_height = 20;
private _actionObject: egwActionObject; private _actionObject: egwActionObject;
@ -76,10 +75,6 @@ export class et2_calendar_planner_row extends et2_valueWidget implements et2_IRe
this.setDOMNode(this.div[0]); this.setDOMNode(this.div[0]);
// Used for its date calculations
this._date_helper = <et2_date>et2_createWidget('date-time', {}, null);
this._date_helper.loadingFinished();
this.set_start_date(this.options.start_date); this.set_start_date(this.options.start_date);
this.set_end_date(this.options.end_date); this.set_end_date(this.options.end_date);
@ -102,10 +97,6 @@ export class et2_calendar_planner_row extends et2_valueWidget implements et2_IRe
destroy( ) destroy( )
{ {
super.destroy(); super.destroy();
// date_helper has no parent, so we must explicitly remove it
this._date_helper.destroy();
this._date_helper = null;
} }
getDOMNode(_sender) getDOMNode(_sender)
@ -494,9 +485,9 @@ export class et2_calendar_planner_row extends et2_valueWidget implements et2_IRe
} }
} }
get date_helper(): et2_date date_helper(value)
{ {
return this._date_helper; return (<et2_calendar_view>this.getParent()).date_helper(value);
} }
/** /**
@ -625,13 +616,11 @@ export class et2_calendar_planner_row extends et2_valueWidget implements et2_IRe
const event = this._children[n].options.value || false; const event = this._children[n].options.value || false;
if(typeof event.start !== 'object') if(typeof event.start !== 'object')
{ {
this._date_helper.set_value(event.start); event.start = this.date_helper(event.start);
event.start = new Date(this._date_helper.getValue());
} }
if(typeof event.end !== 'object') if(typeof event.end !== 'object')
{ {
this._date_helper.set_value(event.end); event.end = this.date_helper(event.end);
event.end = new Date(this._date_helper.getValue());
} }
if(typeof event['start_m'] === 'undefined') if(typeof event['start_m'] === 'undefined')
{ {

View File

@ -26,8 +26,8 @@ import {et2_calendar_event} from "./et2_widget_event";
import {egw_getObjectManager, egwActionObject} from "../../api/js/egw_action/egw_action.js"; import {egw_getObjectManager, egwActionObject} from "../../api/js/egw_action/egw_action.js";
import {et2_compileLegacyJS} from "../../api/js/etemplate/et2_core_legacyJSFunctions"; import {et2_compileLegacyJS} from "../../api/js/etemplate/et2_core_legacyJSFunctions";
import {Et2Dialog} from "../../api/js/etemplate/Et2Dialog/Et2Dialog"; import {Et2Dialog} from "../../api/js/etemplate/Et2Dialog/Et2Dialog";
import {sprintf} from "../../api/js/egw_action/egw_action_common.js";
import {EGW_AI_DRAG_OUT, EGW_AI_DRAG_OVER} from "../../api/js/egw_action/egw_action_constants.js"; import {EGW_AI_DRAG_OUT, EGW_AI_DRAG_OVER} from "../../api/js/egw_action/egw_action_constants.js";
import {formatDate} from "../../api/js/etemplate/Et2Date/Et2Date";
/** /**
* Class which implements the "calendar-timegrid" XET-Tag for displaying a span of days * Class which implements the "calendar-timegrid" XET-Tag for displaying a span of days
@ -463,7 +463,8 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
if(typeof dropEnd != 'undefined' && dropEnd) if(typeof dropEnd != 'undefined' && dropEnd)
{ {
var drop_date = dropEnd.date||false; var drop_date = dropEnd.date || false;
let target_date;
var event_data = timegrid._get_event_info(ui.draggable); var event_data = timegrid._get_event_info(ui.draggable);
var event_widget = timegrid.getWidgetById(event_data.widget_id); var event_widget = timegrid.getWidgetById(event_data.widget_id);
@ -475,8 +476,8 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
if(event_widget) if(event_widget)
{ {
// Send full string to avoid rollover between months using set_month() // Send full string to avoid rollover between months using set_month()
event_widget._parent.date_helper.set_value( target_date = event_widget._parent.date_helper(
drop_date.substring(0,4)+'-'+drop_date.substring(4,6)+'-'+drop_date.substring(6,8)+ drop_date.substring(0, 4) + '-' + drop_date.substring(4, 6) + '-' + drop_date.substring(6, 8) +
'T00:00:00Z' 'T00:00:00Z'
); );
@ -484,20 +485,20 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
// Make sure whole day events stay as whole day events by ignoring drop time // Make sure whole day events stay as whole day events by ignoring drop time
if(event_data.app == 'calendar' && event_widget.options.value.whole_day) if(event_data.app == 'calendar' && event_widget.options.value.whole_day)
{ {
event_widget._parent.date_helper.set_hours(0); target_date.setUTCHours(0);
event_widget._parent.date_helper.set_minutes(0); target_date.setUTCMinutes(0);
} }
else if (timegrid.options.granularity === 0) else if (timegrid.options.granularity === 0)
{ {
// List, not time grid - keep time // List, not time grid - keep time
event_widget._parent.date_helper.set_hours(event_widget.options.value.start.getUTCHours()); target_date.setUTCHours(event_widget.options.value.start.getUTCHours());
event_widget._parent.date_helper.set_minutes(event_widget.options.value.start.getUTCMinutes()); target_date.setUTCMinutes(event_widget.options.value.start.getUTCMinutes());
} }
else else
{ {
// Non-whole day events, and integrated apps, can change // Non-whole day events, and integrated apps, can change
event_widget._parent.date_helper.set_hours(dropEnd.whole_day ? 0 : dropEnd.hour||0); target_date.setUTCHours(dropEnd.whole_day ? 0 : dropEnd.hour || 0);
event_widget._parent.date_helper.set_minutes(dropEnd.whole_day ? 0 : dropEnd.minute||0); target_date.setUTCMinutes(dropEnd.whole_day ? 0 : dropEnd.minute || 0);
} }
// Leave the helper there until the update is done // Leave the helper there until the update is done
@ -531,7 +532,7 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
// If it is an integrated infolog event we need to edit infolog entry // If it is an integrated infolog event we need to edit infolog entry
egw().json('stylite_infolog_calendar_integration::ajax_moveInfologEvent', egw().json('stylite_infolog_calendar_integration::ajax_moveInfologEvent',
[event_data.app_id, event_widget._parent.date_helper.getValue()||false,duration], [event_data.app_id, target_date || false, duration],
function() {loading.remove();} function() {loading.remove();}
).sendRequest(true); ).sendRequest(true);
} }
@ -550,7 +551,7 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
// Send the update // Send the update
var _send = function(series_instance) var _send = function(series_instance)
{ {
var start = new Date(event_widget._parent.date_helper.getValue()); var start = new Date(target_date);
egw().json('calendar.calendar_uiforms.ajax_moveEvent', [ egw().json('calendar.calendar_uiforms.ajax_moveEvent', [
button_id==='series' ? event_data.id : event_data.app_id,event_data.owner, button_id==='series' ? event_data.id : event_data.app_id,event_data.owner,
@ -1164,24 +1165,27 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
_calculate_day_list( start_date, end_date, show_weekend) _calculate_day_list( start_date, end_date, show_weekend)
{ {
var day_list = []; let day_list = [];
if(!start_date || !end_date)
{
return day_list;
}
this.date_helper.set_value(end_date); let end = this.date_helper(end_date).getTime();
var end = this.date_helper.date.getTime(); let i = 1;
var i = 1; let start = this.date_helper(start_date);
this.date_helper.set_value(new Date(start_date));
do do
{ {
if(show_weekend || !show_weekend && [0,6].indexOf(this.date_helper.date.getUTCDay()) === -1 || end_date === start_date) if(show_weekend || !show_weekend && [0, 6].indexOf(this.date_helper.date.getUTCDay()) === -1 || end_date === start_date)
{ {
day_list.push(''+this.date_helper.get_year() + sprintf('%02d',this.date_helper.get_month()) + sprintf('%02d',this.date_helper.get_date())); day_list.push(formatDate(start, {dateFormat: "Ymd"}));
} }
this.date_helper.set_date(this.date_helper.get_date()+1); start.setDate(start.getDate() + 1);
} }
// Limit it to 14 days to avoid infinite loops in case something is mis-set, // Limit it to 14 days to avoid infinite loops in case something is mis-set,
// though the limit is more based on how wide the screen is // though the limit is more based on how wide the screen is
while(end >= this.date_helper.date.getTime() && i++ <= 14); while(end >= start && i++ <= 14);
return day_list; return day_list;
} }
@ -2051,18 +2055,16 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
} }
// Format date // Format date
this.date_helper.set_year(start.date.substring(0,4)); let date = this.date_helper(start.date);
this.date_helper.set_month(start.date.substring(4,6));
this.date_helper.set_date(start.date.substring(6,8));
if(start.hour) if(start.hour)
{ {
this.date_helper.set_hours(start.hour); date.setUTCHours(start.hour);
} }
if(start.minute) if(start.minute)
{ {
this.date_helper.set_minutes(start.minute); date.setUTCMinutes(start.minute);
} }
start.date = this.date_helper.get_value(); start.date = date;
this.gridHover.css('cursor', 'ns-resize'); this.gridHover.css('cursor', 'ns-resize');
@ -2075,18 +2077,16 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
var end = jQuery.extend({}, timegrid.gridHover[0].dataset); var end = jQuery.extend({}, timegrid.gridHover[0].dataset);
if(end.date) if(end.date)
{ {
timegrid.date_helper.set_year(end.date.substring(0,4)); let date = timegrid.date_helper(end.date);
timegrid.date_helper.set_month(end.date.substring(4,6));
timegrid.date_helper.set_date(end.date.substring(6,8));
if(end.hour) if(end.hour)
{ {
timegrid.date_helper.set_hours(end.hour); date.setUTCHours(end.hour);
} }
if(end.minute) if(end.minute)
{ {
timegrid.date_helper.set_minutes(end.minute); date.setUTCMinutes(end.minute);
} }
timegrid.drag_create.end.date = timegrid.date_helper.get_value(); timegrid.drag_create.end.date = date;
} }
try try
{ {
@ -2113,18 +2113,16 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet
var end = jQuery.extend({}, this.gridHover[0].dataset); var end = jQuery.extend({}, this.gridHover[0].dataset);
if(end.date) if(end.date)
{ {
this.date_helper.set_year(end.date.substring(0,4)); let date = this.date_helper(end.date);
this.date_helper.set_month(end.date.substring(4,6));
this.date_helper.set_date(end.date.substring(6,8));
if(end.hour) if(end.hour)
{ {
this.date_helper.set_hours(end.hour); date.setUTCMinutes(end.hour);
} }
if(end.minute) if(end.minute)
{ {
this.date_helper.set_minutes(end.minute); date.setUTCMinutes(end.minute);
} }
end.date = this.date_helper.get_value(); end.date = date;
} }
this.div.off('mousemove.dragcreate'); this.div.off('mousemove.dragcreate');
this.gridHover.css('cursor', ''); this.gridHover.css('cursor', '');

View File

@ -15,9 +15,8 @@
import {et2_createWidget, et2_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget"; import {et2_createWidget, et2_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget";
import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget"; import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget";
import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance"; import {ClassWithAttributes} from "../../api/js/etemplate/et2_core_inheritance";
import {et2_date} from "../../api/js/etemplate/et2_widget_date";
import {et2_calendar_event} from "./et2_widget_event"; import {et2_calendar_event} from "./et2_widget_event";
import {sprintf} from "../../api/js/egw_action/egw_action_common.js"; import {formatDate} from "../../api/js/etemplate/Et2Date/Et2Date";
/** /**
* Parent class for the various calendar views to reduce copied code * Parent class for the various calendar views to reduce copied code
@ -47,7 +46,6 @@ export class et2_calendar_view extends et2_valueWidget
} }
}; };
protected dataStorePrefix: string = 'calendar'; protected dataStorePrefix: string = 'calendar';
protected _date_helper: et2_date;
protected loader: JQuery; protected loader: JQuery;
protected div: JQuery; protected div: JQuery;
protected now_div: JQuery; protected now_div: JQuery;
@ -68,11 +66,6 @@ export class et2_calendar_view extends et2_valueWidget
// Call the inherited constructor // Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_calendar_view._attributes, _child || {})); super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_calendar_view._attributes, _child || {}));
// Used for its date calculations
this._date_helper = <et2_date>et2_createWidget('date-time', {}, null);
this._date_helper.loadingFinished();
this.loader = jQuery('<div class="egw-loading-prompt-container ui-front loading"></div>'); this.loader = jQuery('<div class="egw-loading-prompt-container ui-front loading"></div>');
this.now_div = jQuery('<div class="calendar_now"/>'); this.now_div = jQuery('<div class="calendar_now"/>');
this.update_timer = null; this.update_timer = null;
@ -90,10 +83,6 @@ export class et2_calendar_view extends et2_valueWidget
destroy() { destroy() {
super.destroy(); super.destroy();
// date_helper has no parent, so we must explicitly remove it
this._date_helper.destroy();
this._date_helper = null;
// Stop the invalidate timer // Stop the invalidate timer
if(this.update_timer) if(this.update_timer)
{ {
@ -175,22 +164,8 @@ export class et2_calendar_view extends et2_valueWidget
new_date = new Date(); new_date = new Date();
} }
// Use date widget's existing functions to deal let old_date = this.options.start_date;
if(typeof new_date === "object" || typeof new_date === "string" && new_date.length > 8) this.options.start_date = this.date_helper(new_date)
{
this._date_helper.set_value(new_date);
}
else if(typeof new_date === "string")
{
this._date_helper.set_year(new_date.substring(0, 4));
// Avoid overflow into next month, since we re-use date_helper
this._date_helper.set_date(1);
this._date_helper.set_month(new_date.substring(4, 6));
this._date_helper.set_date(new_date.substring(6, 8));
}
var old_date = this.options.start_date;
this.options.start_date = new Date(this._date_helper.getValue());
if(old_date !== this.options.start_date && this.isAttached()) if(old_date !== this.options.start_date && this.isAttached())
{ {
@ -216,22 +191,9 @@ export class et2_calendar_view extends et2_valueWidget
{ {
new_date = new Date(); new_date = new Date();
} }
// Use date widget's existing functions to deal
if(typeof new_date === "object" || typeof new_date === "string" && new_date.length > 8)
{
this._date_helper.set_value(new_date);
}
else if(typeof new_date === "string")
{
this._date_helper.set_year(new_date.substring(0, 4));
// Avoid overflow into next month, since we re-use date_helper
this._date_helper.set_date(1);
this._date_helper.set_month(new_date.substring(4, 6));
this._date_helper.set_date(new_date.substring(6, 8));
}
var old_date = this.options.end_date; let old_date = this.options.end_date;
this.options.end_date = new Date(this._date_helper.getValue()); this.options.end_date = this.date_helper(new_date);
if(old_date !== this.options.end_date && this.isAttached()) if(old_date !== this.options.end_date && this.isAttached())
{ {
@ -344,15 +306,74 @@ export class et2_calendar_view extends et2_valueWidget
// None of the above changed anything, hide the loader // None of the above changed anything, hide the loader
if(!this.update_timer) if(!this.update_timer)
{ {
window.setTimeout(jQuery.proxy(function() {this.loader.hide();},this),200); window.setTimeout(jQuery.proxy(function() {this.loader.hide();}, this), 200);
} }
} }
get date_helper(): et2_date
/**
* Parse something that we think is a date into an actual date.
*
* The passed value could be a Date, or a string in an unknown format, or a timestamp
*
* @param {Date | string | number} _value
*/
date_helper(_value : Date | string | number)
{ {
return this._date_helper; if(_value === null || _value === "" || _value === undefined || _value === 0)
{
return undefined;
}
if(_value instanceof Date)
{
// Return a copy, because often modifications are made after
return new Date(_value.getTime());
}
let date = new Date();
if(typeof _value === "string" && _value.length == 8)
{
// Ymd format: 20000101
date.setFullYear(parseInt(_value.substring(0, 4)));
// Avoid overflow into next month since it already has a value
date.setDate(1);
date.setMonth(parseInt(_value.substring(4, 6)) - 1);
date.setDate(parseInt(_value.substring(6, 8)));
date.setUTCHours(0);
date.setUTCMinutes(0);
date.setUTCSeconds(0);
}
// Check for full timestamp
else if(typeof _value == 'string' && _value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(?:\.\d{3})?(?:Z|[+-](\d{2})\:(\d{2})|)$/))
{
date = new Date(_value);
}
// timestamp in usertime
else if(typeof _value == 'number' || typeof _value == 'string' && !isNaN(<number><unknown>_value) && _value[0] != '+' && _value[0] != '-')
{
date = new Date((typeof _value == "number" ? _value : parseInt(_value)) * 1000);
}
// @ts-ignore
else if(typeof _value == 'object' && typeof _value.date !== "undefined")
{
// @ts-ignore
return this.date_helper(_value.date);
}
// @ts-ignore
else if(typeof _value == 'object' && typeof _value.valueOf !== "undefined")
{
date = _value;
}
else
{
// string starting with + or - --> add/substract number of seconds from current value
date.setTime(date.getTime() + 1000 * parseInt("" + _value));
}
return date;
} }
_createNamespace() {
_createNamespace()
{
return true; return true;
} }
@ -369,10 +390,7 @@ export class et2_calendar_view extends et2_valueWidget
var tempDate = new Date(); var tempDate = new Date();
var now = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate(),tempDate.getHours(),tempDate.getMinutes()-tempDate.getTimezoneOffset(),0); var now = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate(),tempDate.getHours(),tempDate.getMinutes()-tempDate.getTimezoneOffset(),0);
// Use date widget's existing functions to deal now = this.date_helper(now.toJSON());
this._date_helper.set_value(now.toJSON());
now = new Date(this._date_helper.getValue());
if(this.get_start_date() <= now && this.get_end_date() >= now) if(this.get_start_date() <= now && this.get_end_date() >= now)
{ {
return now; return now;
@ -535,16 +553,13 @@ export class et2_calendar_view extends et2_valueWidget
} }
if(!this.drag_create.event) if(!this.drag_create.event)
{ {
this._date_helper.set_value(this.drag_create.start.date);
var value = jQuery.extend({}, var value = jQuery.extend({},
this.drag_create.start, this.drag_create.start,
this.drag_create.end, this.drag_create.end,
{ {
start: this.drag_create.start.date, start: this.drag_create.start.date,
end: this.drag_create.end && this.drag_create.end.date || this.drag_create.start.date, end: this.drag_create.end && this.drag_create.end.date || this.drag_create.start.date,
date: ""+this._date_helper.get_year()+ date: "" + formatDate(this.date_helper(this.drag_create.start.date), {dateFormat: "Ymd"}),
sprintf("%02d",this._date_helper.get_month())+
sprintf("%02d",this._date_helper.get_date()),
title: '', title: '',
description: '', description: '',
owner: this.options.owner, owner: this.options.owner,
@ -687,34 +702,4 @@ export class et2_calendar_view extends et2_valueWidget
* Cache to map owner & resource IDs to names, helps cut down on server requests * Cache to map owner & resource IDs to names, helps cut down on server requests
*/ */
static owner_name_cache = {}; static owner_name_cache = {};
public static holiday_cache = {};
/**
* Fetch and cache a list of the year's holidays
*
* @param {et2_calendar_timegrid} widget
* @param {string|numeric} year
* @returns Promise<{[key: string]: Array<object>}>|{[key: string]: Array<object>}
*/
static get_holidays(year) : Promise<{[key: string]: Array<object>}>|{[key: string]: Array<object>}
{
// Loaded in an iframe or something
var view = egw.window.et2_calendar_view ? egw.window.et2_calendar_view : this;
// No country selected causes error, so skip if it's missing
if(!view || !egw.preference('country','common')) return {};
if (typeof view.holiday_cache[year] === 'undefined')
{
// Fetch with json instead of jsonq because there may be more than
// one widget listening for the response by the time it gets back,
// and we can't do that when it's queued.
view.holiday_cache[year] = window.fetch(
egw.link('/calendar/holidays.php', {year: year})
).then((response) => {
return view.holiday_cache[year] = response.json();
});
}
return view.holiday_cache[year];
}
} }