From 813cd6924e329e53ddc4276896248eb610f0ba20 Mon Sep 17 00:00:00 2001 From: nathan Date: Fri, 29 Apr 2022 14:37:15 -0600 Subject: [PATCH] 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 --- calendar/inc/class.calendar_ui.inc.php | 1 - calendar/js/app.ts | 10 -- calendar/js/et2_widget_daycol.ts | 87 +++++--------- calendar/js/et2_widget_event.ts | 47 ++++---- calendar/js/et2_widget_planner.ts | 46 ++++---- calendar/js/et2_widget_planner_row.ts | 21 +--- calendar/js/et2_widget_timegrid.ts | 80 +++++++------ calendar/js/et2_widget_view.ts | 155 +++++++++++-------------- 8 files changed, 192 insertions(+), 255 deletions(-) diff --git a/calendar/inc/class.calendar_ui.inc.php b/calendar/inc/class.calendar_ui.inc.php index b5beacffda..3c4ddfb61d 100644 --- a/calendar/inc/class.calendar_ui.inc.php +++ b/calendar/inc/class.calendar_ui.inc.php @@ -544,7 +544,6 @@ class calendar_ui $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['holidays'] = $this->bo->read_holidays($cont['year']); $readonlys = array(); $sel_options['status_filter'] = array( diff --git a/calendar/js/app.ts b/calendar/js/app.ts index 8859ed60ca..d0c47c5b49 100644 --- a/calendar/js/app.ts +++ b/calendar/js/app.ts @@ -276,16 +276,6 @@ export class CalendarApp extends EgwApp this.sidebox_hooked_templates.push(this.sidebox_et2); 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.state = content.data; diff --git a/calendar/js/et2_widget_daycol.ts b/calendar/js/et2_widget_daycol.ts index c837f85df6..0a27146bb6 100644 --- a/calendar/js/et2_widget_daycol.ts +++ b/calendar/js/et2_widget_daycol.ts @@ -17,16 +17,17 @@ 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_calendar_timegrid} from "./et2_widget_timegrid"; -import {et2_calendar_view} from "./et2_widget_view"; import {et2_calendar_event} from "./et2_widget_event"; 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_no_init} from "../../api/js/etemplate/et2_core_common"; 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 {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 @@ -69,8 +70,7 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache private event_wrapper: JQuery; private user_spacer: JQuery; private all_day: JQuery; - - private _date_helper : et2_date; + private registeredUID: string = null; // 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); 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( ) @@ -158,10 +153,6 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache this.title = 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); } @@ -225,9 +216,9 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache return this.date; } - get date_helper(): et2_date + date_helper(value) { - return this._date_helper; + return (this.getParent()).date_helper(value); } /** @@ -249,28 +240,16 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache { 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.'); 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 = (this.getParent()).date_helper(_date); // Keep internal option in Ymd format, it gets passed around in this format - const new_date = "" + this.getParent().date_helper.get_year() + - sprintf("%02d", this.getParent().date_helper.get_month()) + - sprintf("%02d", this.getParent().date_helper.get_date()); + const new_date = formatDate(this.date, {dateFormat: "Ymd"}); // Set 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 const formatDate = new Date(this.date.valueOf() + this.date.getTimezoneOffset() * 60 * 1000); - this.title.html(''+jQuery.datepicker.formatDate('DD',formatDate)+ - ''+jQuery.datepicker.formatDate('D',formatDate)+''+ - jQuery.datepicker.formatDate('d',formatDate)); + this.title.html('' + flatpickr.formatDate(formatDate, 'l') + + '' + flatpickr.formatDate(formatDate, 'D') + '' + + flatpickr.formatDate(formatDate, 'd')); } this.title .attr("data-date", new_date) @@ -472,7 +451,7 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache ); // 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 = []; let holiday_pref = (egw.preference('birthdays_as_events', 'calendar') || []); 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; - if(holidays && holidays[this.options.date]) + if(fetched_holidays && fetched_holidays[this.options.date]) { - holidays = holidays[this.options.date]; - for(let i = 0; i < holidays.length; i++) + fetched_holidays = fetched_holidays[this.options.date]; + 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 if(birthdays_as_events) { // Create event - this.getParent().date_helper.set_value(this.options.date.substring(0,4)+'-'+ - (this.options.date.substring(4,6))+'-'+this.options.date.substring(6,8)+ - 'T00:00:00Z'); - var event = et2_createWidget('calendar-event',{ - id:'event_'+holidays[i].name, + var event = et2_createWidget('calendar-event', { + id: 'event_' + fetched_holidays[i].name, value: { - title: holidays[i].name, + title: fetched_holidays[i].name, whole_day: true, whole_day_on_top: true, - start: new Date(this.getParent().date_helper.get_value()), + start: (this.getParent()).date_helper(this.options.date), end: this.options.date, owner: 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 //show them in the caption this.title.addClass('calendar_calBirthday'); - holiday_list.push(holidays[i]['name']); + holiday_list.push(fetched_holidays[i]['name']); } } else @@ -537,16 +513,13 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache if(holidays_as_events) { // Create event - this.getParent().date_helper.set_value(this.options.date.substring(0,4)+'-'+ - (this.options.date.substring(4,6))+'-'+this.options.date.substring(6,8)+ - 'T00:00:00Z'); - var event = et2_createWidget('calendar-event',{ - id:'event_'+holidays[i].name, + var event = et2_createWidget('calendar-event', { + id: 'event_' + fetched_holidays[i].name, value: { - title: holidays[i].name, + title: fetched_holidays[i].name, whole_day: true, whole_day_on_top: true, - start: new Date(this.getParent().date_helper.get_value()), + start: (this.getParent()).date_helper(this.options.date), end: this.options.date, owner: this.options.owner, participants: this.options.owner, @@ -562,13 +535,13 @@ export class et2_calendar_daycol extends et2_valueWidget implements et2_IDetache else { 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 //show them in the caption if (!this.options.display_holiday_as_event) { - holiday_list.push(holidays[i]['name']); + holiday_list.push(fetched_holidays[i]['name']); } } } diff --git a/calendar/js/et2_widget_event.ts b/calendar/js/et2_widget_event.ts index 3e7cc3001b..70f64159f0 100644 --- a/calendar/js/et2_widget_event.ts +++ b/calendar/js/et2_widget_event.ts @@ -515,29 +515,30 @@ export class et2_calendar_event extends et2_valueWidget implements et2_IDetached */ _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 bg_color = this.div.css('background-color'); - const header_color = this.title.css('color'); - const timespan = this._get_timespan(this.options.value); - const parent = this.getParent() instanceof et2_calendar_daycol ? (this.getParent()) : (this.getParent()); + const border = this.div.css('borderTopColor'); + const bg_color = this.div.css('background-color'); + const header_color = this.title.css('color'); + const timespan = this._get_timespan(this.options.value); + const parent = this.getParent() instanceof et2_calendar_daycol ? (this.getParent()) : (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.input_date.val(); - 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 start = parent.date_helper(this.options.value.start); + const end = parent.date_helper(this.options.value.end); - const times = !this.options.value.multiday ? - '' + this.egw().lang('Time') + ':' + timespan : - '' + this.egw().lang('Start') + ':' + start + ' ' + - '' + this.egw().lang('End') + ':' + end; - let cat_label: (string | string[]) = ''; - if (this.options.value.category) - { - const cat = et2_createWidget('select-cat', {'readonly': true}, this); - cat.set_value(this.options.value.category); - cat_label = this.options.value.category.indexOf(',') <= 0 ? cat.span.text() : []; + const times = !this.options.value.multiday ? + '' + this.egw().lang('Time') + ':' + timespan : + '' + this.egw().lang('Start') + ':' + start + ' ' + + '' + this.egw().lang('End') + ':' + end; + let cat_label : (string | string[]) = ''; + if(this.options.value.category) + { + const cat = et2_createWidget('select-cat', {'readonly': true}, this); + cat.set_value(this.options.value.category); + cat_label = this.options.value.category.indexOf(',') <= 0 ? cat.span.text() : []; if (typeof cat_label != 'string') { cat.span.children().each(function () @@ -847,13 +848,11 @@ export class et2_calendar_event extends et2_valueWidget implements et2_IDetached // Use dates as objects if (typeof event.start !== 'object') { - parent.date_helper.set_value(event.start); - event.start = new Date(parent.date_helper.getValue()); + event.start = parent.date_helper(event.start); } if (typeof event.end !== 'object') { - parent.date_helper.set_value(event.end); - event.end = new Date(parent.date_helper.getValue()); + event.end = parent.date_helper(event.end) } // We need minutes for durations diff --git a/calendar/js/et2_widget_planner.ts b/calendar/js/et2_widget_planner.ts index 1115cd16e8..e0c22178a1 100644 --- a/calendar/js/et2_widget_planner.ts +++ b/calendar/js/et2_widget_planner.ts @@ -15,7 +15,7 @@ /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 {et2_calendar_view} from "./et2_widget_view"; 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 {et2_dataview_grid} from "../../api/js/etemplate/et2_dataview_view_grid"; 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 @@ -1324,21 +1326,23 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta */ async day_class_holiday(date, holiday_list, days?) { - if(!date) return ''; + if(!date) + { + return ''; + } // Holidays and birthdays - const holidays = await et2_calendar_view.get_holidays(date.getUTCFullYear()); + const fetched = await holidays(date.getUTCFullYear()); var day_class = ''; // Pass a string rather than the date object, to make sure it doesn't get changed - this.date_helper.set_value(date.toJSON()); - var date_key = ''+this.date_helper.get_year() + sprintf('%02d',this.date_helper.get_month()) + sprintf('%02d',this.date_helper.get_date()); - if (holidays && holidays[date_key]) + let date_key = formatDate(this.date_helper(date.toJSON()), {dateFormat: "Ymd"}); + if(fetched && fetched[date_key]) { - const dates = holidays[date_key]; + const dates = fetched[date_key]; for(var i = 0; i < dates.length; i++) { - if (typeof dates[i]['birthyear'] !== 'undefined') + if(typeof dates[i]['birthyear'] !== 'undefined') { day_class += ' calendar_calBirthday '; 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); if(event_widget) { - event_widget._parent.date_helper.set_value(drop_date); - event_widget.options.value.start = new Date(event_widget._parent.date_helper.getValue()); + event_widget.options.value.start = event_widget._parent.date_helper(drop_date); // Leave the helper there until the update is done 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 day_header = jQuery('.calendar_plannerScaleDay', this.headers); + let date; // Simple math, the x is offset from start date 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 )) { - 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()); + rel_time = (new Date(this.options.end_date) - new Date(this.options.start_date)) * rel_x / 1000; + date = this.date_helper(this.options.start_date.toJSON()); } // Not so simple math, need to account for missing days 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 if(day && day.dataset && day.dataset.date) { - this.date_helper.set_value(day.dataset.date); - rel_time = ((x - jQuery(day).position().left) / jQuery(day).outerWidth(true)) * 24*60; - this.date_helper.set_minutes(Math.round(rel_time/interval) * interval); - return new Date(this.date_helper.getValue()); + date = this.date_helper(day.dataset.date); + rel_time = ((x - jQuery(day).position().left) / jQuery(day).outerWidth(true)) * 24 * 60; + date.setUTCMinutes(Math.round(rel_time / interval) * interval); + return date; } 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 // 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 - rel_time = (2678400)*rel_x; - this.date_helper.set_value(row_widget.options.start_date.toJSON()); + rel_time = (2678400) * rel_x; + date = this.date_helper(row_widget.options.start_date.toJSON()); } else { @@ -2478,9 +2482,9 @@ export class et2_calendar_planner extends et2_calendar_view implements et2_IDeta } 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; } /** diff --git a/calendar/js/et2_widget_planner_row.ts b/calendar/js/et2_widget_planner_row.ts index 7cb0c2bd84..8c5264e3e9 100644 --- a/calendar/js/et2_widget_planner_row.ts +++ b/calendar/js/et2_widget_planner_row.ts @@ -18,13 +18,13 @@ 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 {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_calendar_planner} from "./et2_widget_planner"; 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 {et2_IResizeable} from "../../api/js/etemplate/et2_core_interfaces"; import {egw} from "../../api/js/jsapi/egw_global"; +import {et2_calendar_view} from "./et2_widget_view"; /** * 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 title: JQuery; private rows: JQuery; - private _date_helper: et2_date; private _cached_rows: any[]; private _row_height = 20; private _actionObject: egwActionObject; @@ -76,10 +75,6 @@ export class et2_calendar_planner_row extends et2_valueWidget implements et2_IRe this.setDOMNode(this.div[0]); - // Used for its date calculations - this._date_helper = et2_createWidget('date-time', {}, null); - this._date_helper.loadingFinished(); - this.set_start_date(this.options.start_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( ) { super.destroy(); - - // date_helper has no parent, so we must explicitly remove it - this._date_helper.destroy(); - this._date_helper = null; } 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 (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; if(typeof event.start !== 'object') { - this._date_helper.set_value(event.start); - event.start = new Date(this._date_helper.getValue()); + event.start = this.date_helper(event.start); } if(typeof event.end !== 'object') { - this._date_helper.set_value(event.end); - event.end = new Date(this._date_helper.getValue()); + event.end = this.date_helper(event.end); } if(typeof event['start_m'] === 'undefined') { diff --git a/calendar/js/et2_widget_timegrid.ts b/calendar/js/et2_widget_timegrid.ts index 932b0a6a26..ae58917669 100644 --- a/calendar/js/et2_widget_timegrid.ts +++ b/calendar/js/et2_widget_timegrid.ts @@ -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 {et2_compileLegacyJS} from "../../api/js/etemplate/et2_core_legacyJSFunctions"; 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 {formatDate} from "../../api/js/etemplate/Et2Date/Et2Date"; /** * 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) { - 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_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) { // Send full string to avoid rollover between months using set_month() - event_widget._parent.date_helper.set_value( - drop_date.substring(0,4)+'-'+drop_date.substring(4,6)+'-'+drop_date.substring(6,8)+ + target_date = event_widget._parent.date_helper( + drop_date.substring(0, 4) + '-' + drop_date.substring(4, 6) + '-' + drop_date.substring(6, 8) + '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 if(event_data.app == 'calendar' && event_widget.options.value.whole_day) { - event_widget._parent.date_helper.set_hours(0); - event_widget._parent.date_helper.set_minutes(0); + target_date.setUTCHours(0); + target_date.setUTCMinutes(0); } else if (timegrid.options.granularity === 0) { // List, not time grid - keep time - event_widget._parent.date_helper.set_hours(event_widget.options.value.start.getUTCHours()); - event_widget._parent.date_helper.set_minutes(event_widget.options.value.start.getUTCMinutes()); + target_date.setUTCHours(event_widget.options.value.start.getUTCHours()); + target_date.setUTCMinutes(event_widget.options.value.start.getUTCMinutes()); } else { // Non-whole day events, and integrated apps, can change - event_widget._parent.date_helper.set_hours(dropEnd.whole_day ? 0 : dropEnd.hour||0); - event_widget._parent.date_helper.set_minutes(dropEnd.whole_day ? 0 : dropEnd.minute||0); + target_date.setUTCHours(dropEnd.whole_day ? 0 : dropEnd.hour || 0); + target_date.setUTCMinutes(dropEnd.whole_day ? 0 : dropEnd.minute || 0); } // 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 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();} ).sendRequest(true); } @@ -550,7 +551,7 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet // Send the update 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', [ 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) { - var day_list = []; + let day_list = []; + if(!start_date || !end_date) + { + return day_list; + } - this.date_helper.set_value(end_date); - var end = this.date_helper.date.getTime(); - var i = 1; - this.date_helper.set_value(new Date(start_date)); + let end = this.date_helper(end_date).getTime(); + let i = 1; + let start = this.date_helper(start_date); 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, - // though the limit is more based on how wide the screen is - while(end >= this.date_helper.date.getTime() && i++ <= 14); + // 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 + while(end >= start && i++ <= 14); return day_list; } @@ -2051,18 +2055,16 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet } // Format date - this.date_helper.set_year(start.date.substring(0,4)); - this.date_helper.set_month(start.date.substring(4,6)); - this.date_helper.set_date(start.date.substring(6,8)); + let date = this.date_helper(start.date); if(start.hour) { - this.date_helper.set_hours(start.hour); + date.setUTCHours(start.hour); } 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'); @@ -2075,18 +2077,16 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet var end = jQuery.extend({}, timegrid.gridHover[0].dataset); if(end.date) { - timegrid.date_helper.set_year(end.date.substring(0,4)); - timegrid.date_helper.set_month(end.date.substring(4,6)); - timegrid.date_helper.set_date(end.date.substring(6,8)); + let date = timegrid.date_helper(end.date); if(end.hour) { - timegrid.date_helper.set_hours(end.hour); + date.setUTCHours(end.hour); } 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 { @@ -2113,18 +2113,16 @@ export class et2_calendar_timegrid extends et2_calendar_view implements et2_IDet var end = jQuery.extend({}, this.gridHover[0].dataset); if(end.date) { - this.date_helper.set_year(end.date.substring(0,4)); - this.date_helper.set_month(end.date.substring(4,6)); - this.date_helper.set_date(end.date.substring(6,8)); + let date = this.date_helper(end.date); if(end.hour) { - this.date_helper.set_hours(end.hour); + date.setUTCMinutes(end.hour); } 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.gridHover.css('cursor', ''); diff --git a/calendar/js/et2_widget_view.ts b/calendar/js/et2_widget_view.ts index 1c9915b352..c902c13860 100644 --- a/calendar/js/et2_widget_view.ts +++ b/calendar/js/et2_widget_view.ts @@ -15,9 +15,8 @@ import {et2_createWidget, et2_widget, WidgetConfig} from "../../api/js/etemplate/et2_core_widget"; import {et2_valueWidget} from "../../api/js/etemplate/et2_core_valueWidget"; 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 {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 @@ -47,7 +46,6 @@ export class et2_calendar_view extends et2_valueWidget } }; protected dataStorePrefix: string = 'calendar'; - protected _date_helper: et2_date; protected loader: JQuery; protected div: JQuery; protected now_div: JQuery; @@ -68,11 +66,6 @@ export class et2_calendar_view extends et2_valueWidget // Call the inherited constructor super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_calendar_view._attributes, _child || {})); - - // Used for its date calculations - this._date_helper = et2_createWidget('date-time', {}, null); - this._date_helper.loadingFinished(); - this.loader = jQuery('
'); this.now_div = jQuery('
'); this.update_timer = null; @@ -90,10 +83,6 @@ export class et2_calendar_view extends et2_valueWidget 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 if(this.update_timer) { @@ -175,22 +164,8 @@ export class et2_calendar_view extends et2_valueWidget 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.start_date; - this.options.start_date = new Date(this._date_helper.getValue()); + let old_date = this.options.start_date; + this.options.start_date = this.date_helper(new_date) 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(); } - // 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; - this.options.end_date = new Date(this._date_helper.getValue()); + let old_date = this.options.end_date; + this.options.end_date = this.date_helper(new_date); 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 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(_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; } @@ -369,10 +390,7 @@ export class et2_calendar_view extends et2_valueWidget var tempDate = new Date(); 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 - this._date_helper.set_value(now.toJSON()); - - now = new Date(this._date_helper.getValue()); + now = this.date_helper(now.toJSON()); if(this.get_start_date() <= now && this.get_end_date() >= now) { return now; @@ -535,16 +553,13 @@ export class et2_calendar_view extends et2_valueWidget } if(!this.drag_create.event) { - this._date_helper.set_value(this.drag_create.start.date); var value = jQuery.extend({}, this.drag_create.start, this.drag_create.end, { start: 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()+ - sprintf("%02d",this._date_helper.get_month())+ - sprintf("%02d",this._date_helper.get_date()), + date: "" + formatDate(this.date_helper(this.drag_create.start.date), {dateFormat: "Ymd"}), title: '', description: '', 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 */ 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}>|{[key: string]: Array} - */ - static get_holidays(year) : Promise<{[key: string]: Array}>|{[key: string]: Array} - { - // 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]; - } } \ No newline at end of file