summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/calendar/calendar_model.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/web/static/src/js/views/calendar/calendar_model.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/web/static/src/js/views/calendar/calendar_model.js')
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_model.js777
1 files changed, 777 insertions, 0 deletions
diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js
new file mode 100644
index 00000000..93999bee
--- /dev/null
+++ b/addons/web/static/src/js/views/calendar/calendar_model.js
@@ -0,0 +1,777 @@
+odoo.define('web.CalendarModel', function (require) {
+"use strict";
+
+var AbstractModel = require('web.AbstractModel');
+var Context = require('web.Context');
+var core = require('web.core');
+var fieldUtils = require('web.field_utils');
+var session = require('web.session');
+
+var _t = core._t;
+
+function dateToServer (date) {
+ return date.clone().utc().locale('en').format('YYYY-MM-DD HH:mm:ss');
+}
+
+return AbstractModel.extend({
+ /**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+ this.end_date = null;
+ var week_start = _t.database.parameters.week_start;
+ // calendar uses index 0 for Sunday but Odoo stores it as 7
+ this.week_start = week_start !== undefined && week_start !== false ? week_start % 7 : moment().startOf('week').day();
+ this.week_stop = this.week_start + 6;
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Transform fullcalendar event object to OpenERP Data object
+ */
+ calendarEventToRecord: function (event) {
+ // Normalize event_end without changing fullcalendars event.
+ var data = {'name': event.title};
+ var start = event.start.clone();
+ var end = event.end && event.end.clone();
+
+ // Set end date if not existing
+ if (!end || end.diff(start) < 0) { // undefined or invalid end date
+ if (event.allDay) {
+ end = start.clone();
+ } else {
+ // in week mode or day mode, convert allday event to event
+ end = start.clone().add(2, 'h');
+ }
+ } else if (event.allDay) {
+ // For an "allDay", FullCalendar gives the end day as the
+ // next day at midnight (instead of 23h59).
+ end.add(-1, 'days');
+ }
+
+ var isDateEvent = this.fields[this.mapping.date_start].type === 'date';
+ // An "allDay" event without the "all_day" option is not considered
+ // as a 24h day. It's just a part of the day (by default: 7h-19h).
+ if (event.allDay) {
+ if (!this.mapping.all_day && !isDateEvent) {
+ if (event.r_start) {
+ start.hours(event.r_start.hours())
+ .minutes(event.r_start.minutes())
+ .seconds(event.r_start.seconds())
+ .utc();
+ end.hours(event.r_end.hours())
+ .minutes(event.r_end.minutes())
+ .seconds(event.r_end.seconds())
+ .utc();
+ } else {
+ // default hours in the user's timezone
+ start.hours(7);
+ end.hours(19);
+ }
+ start.add(-this.getSession().getTZOffset(start), 'minutes');
+ end.add(-this.getSession().getTZOffset(end), 'minutes');
+ }
+ } else {
+ start.add(-this.getSession().getTZOffset(start), 'minutes');
+ end.add(-this.getSession().getTZOffset(end), 'minutes');
+ }
+
+ if (this.mapping.all_day) {
+ if (event.record) {
+ data[this.mapping.all_day] =
+ (this.data.scale !== 'month' && event.allDay) ||
+ event.record[this.mapping.all_day] &&
+ end.diff(start) < 10 ||
+ false;
+ } else {
+ data[this.mapping.all_day] = event.allDay;
+ }
+ }
+
+ data[this.mapping.date_start] = start;
+ if (this.mapping.date_stop) {
+ data[this.mapping.date_stop] = end;
+ }
+
+ if (this.mapping.date_delay) {
+ if (this.data.scale !== 'month' || (this.data.scale === 'month' && !event.drop)) {
+ data[this.mapping.date_delay] = (end.diff(start) <= 0 ? end.endOf('day').diff(start) : end.diff(start)) / 1000 / 3600;
+ }
+ }
+
+ return data;
+ },
+ /**
+ * @param {Object} filter
+ * @returns {boolean}
+ */
+ changeFilter: function (filter) {
+ var Filter = this.data.filters[filter.fieldName];
+ if (filter.value === 'all') {
+ Filter.all = filter.active;
+ }
+ var f = _.find(Filter.filters, function (f) {
+ return f.value === filter.value;
+ });
+ if (f) {
+ if (f.active !== filter.active) {
+ f.active = filter.active;
+ } else {
+ return false;
+ }
+ } else if (filter.active) {
+ Filter.filters.push({
+ value: filter.value,
+ active: true,
+ });
+ }
+ return true;
+ },
+ /**
+ * @param {OdooEvent} event
+ */
+ createRecord: function (event) {
+ var data = this.calendarEventToRecord(event.data.data);
+ for (var k in data) {
+ if (data[k] && data[k]._isAMomentObject) {
+ data[k] = dateToServer(data[k]);
+ }
+ }
+ return this._rpc({
+ model: this.modelName,
+ method: 'create',
+ args: [data],
+ context: event.data.options.context,
+ });
+ },
+ /**
+ * @todo I think this is dead code
+ *
+ * @param {any} ids
+ * @param {any} model
+ * @returns
+ */
+ deleteRecords: function (ids, model) {
+ return this._rpc({
+ model: model,
+ method: 'unlink',
+ args: [ids],
+ context: session.user_context, // todo: combine with view context
+ });
+ },
+ /**
+ * @override
+ * @returns {Object}
+ */
+ __get: function () {
+ return _.extend({}, this.data, {
+ fields: this.fields
+ });
+ },
+ /**
+ * @override
+ * @param {any} params
+ * @returns {Promise}
+ */
+ __load: function (params) {
+ var self = this;
+ this.modelName = params.modelName;
+ this.fields = params.fields;
+ this.fieldNames = params.fieldNames;
+ this.fieldsInfo = params.fieldsInfo;
+ this.mapping = params.mapping;
+ this.mode = params.mode; // one of month, week or day
+ this.scales = params.scales; // one of month, week or day
+ this.scalesInfo = params.scalesInfo;
+
+ // Check whether the date field is editable (i.e. if the events can be
+ // dragged and dropped)
+ this.editable = params.editable;
+ this.creatable = params.creatable;
+
+ // display more button when there are too much event on one day
+ this.eventLimit = params.eventLimit;
+
+ // fields to display color, e.g.: user_id.partner_id
+ this.fieldColor = params.fieldColor;
+ if (!this.preloadPromise) {
+ this.preloadPromise = new Promise(function (resolve, reject) {
+ Promise.all([
+ self._rpc({model: self.modelName, method: 'check_access_rights', args: ["write", false]}),
+ self._rpc({model: self.modelName, method: 'check_access_rights', args: ["create", false]})
+ ]).then(function (result) {
+ var write = result[0];
+ var create = result[1];
+ self.write_right = write;
+ self.create_right = create;
+ resolve();
+ }).guardedCatch(reject);
+ });
+ }
+
+ this.data = {
+ domain: params.domain,
+ context: params.context,
+ // get in arch the filter to display in the sidebar and the field to read
+ filters: params.filters,
+ };
+
+ this.setDate(params.initialDate);
+ // Use mode attribute in xml file to specify zoom timeline (day,week,month)
+ // by default month.
+ this.setScale(params.mode);
+
+ _.each(this.data.filters, function (filter) {
+ if (filter.avatar_field && !filter.avatar_model) {
+ filter.avatar_model = self.modelName;
+ }
+ });
+
+ return this.preloadPromise.then(this._loadCalendar.bind(this));
+ },
+ /**
+ * Move the current date range to the next period
+ */
+ next: function () {
+ this.setDate(this.data.target_date.clone().add(1, this.data.scale));
+ },
+ /**
+ * Move the current date range to the previous period
+ */
+ prev: function () {
+ this.setDate(this.data.target_date.clone().add(-1, this.data.scale));
+ },
+ /**
+ * @override
+ * @param {Object} [params.context]
+ * @param {Array} [params.domain]
+ * @returns {Promise}
+ */
+ __reload: function (handle, params) {
+ if (params.domain) {
+ this.data.domain = params.domain;
+ }
+ if (params.context) {
+ this.data.context = params.context;
+ }
+ return this._loadCalendar();
+ },
+ /**
+ * @param {Moment} start. in local TZ
+ */
+ setDate: function (start) {
+ // keep highlight/target_date in localtime
+ this.data.highlight_date = this.data.target_date = start.clone();
+ this.data.start_date = this.data.end_date = start;
+ switch (this.data.scale) {
+ case 'year': {
+ const yearStart = this.data.start_date.clone().startOf('year');
+ let yearStartDay = this.week_start;
+ if (yearStart.day() < yearStartDay) {
+ // the 1st of January is before our week start (e.g. week start is Monday, and
+ // 01/01 is Sunday), so we go one week back
+ yearStartDay -= 7;
+ }
+ this.data.start_date = yearStart.day(yearStartDay).startOf('day');
+ this.data.end_date = this.data.end_date.clone()
+ .endOf('year').day(this.week_stop).endOf('day');
+ break;
+ }
+ case 'month':
+ var monthStart = this.data.start_date.clone().startOf('month');
+
+ var monthStartDay;
+ if (monthStart.day() >= this.week_start) {
+ // the month's first day is after our week start
+ // Then we are in the right week
+ monthStartDay = this.week_start;
+ } else {
+ // The month's first day is before our week start
+ // Then we should go back to the the previous week
+ monthStartDay = this.week_start - 7;
+ }
+
+ this.data.start_date = monthStart.day(monthStartDay).startOf('day');
+ this.data.end_date = this.data.start_date.clone().add(5, 'week').day(this.week_stop).endOf('day');
+ break;
+ case 'week':
+ var weekStart = this.data.start_date.clone().startOf('week');
+ var weekStartDay = this.week_start;
+ if (this.data.start_date.day() < this.week_start) {
+ // The week's first day is after our current day
+ // Then we should go back to the previous week
+ weekStartDay -= 7;
+ }
+ this.data.start_date = this.data.start_date.clone().day(weekStartDay).startOf('day');
+ this.data.end_date = this.data.end_date.clone().day(weekStartDay + 6).endOf('day');
+ break;
+ default:
+ this.data.start_date = this.data.start_date.clone().startOf('day');
+ this.data.end_date = this.data.end_date.clone().endOf('day');
+ }
+ // We have set start/stop datetime as definite begin/end boundaries of a period (month, week, day)
+ // in local TZ (what is the begining of the week *I am* in ?)
+ // The following code:
+ // - converts those to UTC using our homemade method (testable)
+ // - sets the moment UTC flag to true, to ensure compatibility with third party libs
+ var manualUtcDateStart = this.data.start_date.clone().add(-this.getSession().getTZOffset(this.data.start_date), 'minutes');
+ var formattedUtcDateStart = manualUtcDateStart.format('YYYY-MM-DDTHH:mm:ss') + 'Z';
+ this.data.start_date = moment.utc(formattedUtcDateStart);
+
+ var manualUtcDateEnd = this.data.end_date.clone().add(-this.getSession().getTZOffset(this.data.start_date), 'minutes');
+ var formattedUtcDateEnd = manualUtcDateEnd.format('YYYY-MM-DDTHH:mm:ss') + 'Z';
+ this.data.end_date = moment.utc(formattedUtcDateEnd);
+ },
+ /**
+ * @param {string} scale the scale to set
+ */
+ setScale: function (scale) {
+ if (!_.contains(this.scales, scale)) {
+ scale = "week";
+ }
+ this.data.scale = scale;
+ this.setDate(this.data.target_date);
+ },
+ /**
+ * Move the current date range to the period containing today
+ */
+ today: function () {
+ this.setDate(moment(new Date()));
+ },
+ /**
+ * @param {Object} record
+ * @param {integer} record.id
+ * @returns {Promise}
+ */
+ updateRecord: function (record) {
+ // Cannot modify actual name yet
+ var data = _.omit(this.calendarEventToRecord(record), 'name');
+ for (var k in data) {
+ if (data[k] && data[k]._isAMomentObject) {
+ data[k] = dateToServer(data[k]);
+ }
+ }
+ var context = new Context(this.data.context, {from_ui: true});
+ return this._rpc({
+ model: this.modelName,
+ method: 'write',
+ args: [[parseInt(record.id, 10)], data],
+ context: context
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Converts this.data.filters into a domain
+ *
+ * @private
+ * @returns {Array}
+ */
+ _getFilterDomain: function () {
+ // List authorized values for every field
+ // fields with an active 'all' filter are skipped
+ var authorizedValues = {};
+ var avoidValues = {};
+
+ _.each(this.data.filters, function (filter) {
+ // Skip 'all' filters because they do not affect the domain
+ if (filter.all) return;
+
+ // Loop over subfilters to complete authorizedValues
+ _.each(filter.filters, function (f) {
+ if (filter.write_model) {
+ if (!authorizedValues[filter.fieldName])
+ authorizedValues[filter.fieldName] = [];
+
+ if (f.active) {
+ authorizedValues[filter.fieldName].push(f.value);
+ }
+ } else {
+ if (!f.active) {
+ if (!avoidValues[filter.fieldName])
+ avoidValues[filter.fieldName] = [];
+
+ avoidValues[filter.fieldName].push(f.value);
+ }
+ }
+ });
+ });
+
+ // Compute the domain
+ var domain = [];
+ for (var field in authorizedValues) {
+ domain.push([field, 'in', authorizedValues[field]]);
+ }
+ for (var field in avoidValues) {
+ if (avoidValues[field].length > 0) {
+ domain.push([field, 'not in', avoidValues[field]]);
+ }
+ }
+
+ return domain;
+ },
+ /**
+ * @private
+ * @returns {Object}
+ */
+ _getFullCalendarOptions: function () {
+ var format12Hour = {
+ hour: 'numeric',
+ minute: '2-digit',
+ omitZeroMinute: true,
+ meridiem: 'short'
+ };
+ var format24Hour = {
+ hour: 'numeric',
+ minute: '2-digit',
+ hour12: false,
+ };
+ return {
+ defaultView: this.scalesInfo[this.mode || 'week'],
+ header: false,
+ selectable: this.creatable && this.create_right,
+ selectMirror: true,
+ editable: this.editable,
+ droppable: true,
+ navLinks: false,
+ eventLimit: this.eventLimit, // allow "more" link when too many events
+ snapMinutes: 15,
+ longPressDelay: 500,
+ eventResizableFromStart: true,
+ nowIndicator: true,
+ weekNumbers: true,
+ weekNumbersWithinDays: true,
+ weekNumberCalculation: function (date) {
+ // Since FullCalendar v4 ISO 8601 week date is preferred so we force the old system
+ return moment(date).week();
+ },
+ weekLabel: _t("Week"),
+ allDayText: _t("All day"),
+ monthNames: moment.months(),
+ monthNamesShort: moment.monthsShort(),
+ dayNames: moment.weekdays(),
+ dayNamesShort: moment.weekdaysShort(),
+ firstDay: this.week_start,
+ slotLabelFormat: _t.database.parameters.time_format.search("%H") !== -1 ? format24Hour : format12Hour,
+ allDaySlot: this.mapping.all_day || this.fields[this.mapping.date_start].type === 'date',
+ };
+ },
+ /**
+ * Return a domain from the date range
+ *
+ * @private
+ * @returns {Array} A domain containing datetimes start and stop in UTC
+ * those datetimes are formatted according to server's standards
+ */
+ _getRangeDomain: function () {
+ // Build OpenERP Domain to filter object by this.mapping.date_start field
+ // between given start, end dates.
+ var domain = [[this.mapping.date_start, '<=', dateToServer(this.data.end_date)]];
+ if (this.mapping.date_stop) {
+ domain.push([this.mapping.date_stop, '>=', dateToServer(this.data.start_date)]);
+ } else if (!this.mapping.date_delay) {
+ domain.push([this.mapping.date_start, '>=', dateToServer(this.data.start_date)]);
+ }
+ return domain;
+ },
+ /**
+ * @private
+ * @returns {Promise}
+ */
+ _loadCalendar: function () {
+ var self = this;
+ this.data.fc_options = this._getFullCalendarOptions();
+
+ var defs = _.map(this.data.filters, this._loadFilter.bind(this));
+
+ return Promise.all(defs).then(function () {
+ return self._rpc({
+ model: self.modelName,
+ method: 'search_read',
+ context: self.data.context,
+ fields: self.fieldNames,
+ domain: self.data.domain.concat(self._getRangeDomain()).concat(self._getFilterDomain())
+ })
+ .then(function (events) {
+ self._parseServerData(events);
+ self.data.data = _.map(events, self._recordToCalendarEvent.bind(self));
+ return Promise.all([
+ self._loadColors(self.data, self.data.data),
+ self._loadRecordsToFilters(self.data, self.data.data)
+ ]);
+ });
+ });
+ },
+ /**
+ * @private
+ * @param {any} element
+ * @param {any} events
+ * @returns {Promise}
+ */
+ _loadColors: function (element, events) {
+ if (this.fieldColor) {
+ var fieldName = this.fieldColor;
+ _.each(events, function (event) {
+ var value = event.record[fieldName];
+ event.color_index = _.isArray(value) ? value[0] % 30 : value % 30;
+ });
+ this.model_color = this.fields[fieldName].relation || element.model;
+ }
+ return Promise.resolve();
+ },
+ /**
+ * @private
+ * @param {any} filter
+ * @returns {Promise}
+ */
+ _loadFilter: function (filter) {
+ if (!filter.write_model) {
+ return Promise.resolve();
+ }
+
+ var field = this.fields[filter.fieldName];
+ return this._rpc({
+ model: filter.write_model,
+ method: 'search_read',
+ domain: [["user_id", "=", session.uid]],
+ fields: [filter.write_field],
+ })
+ .then(function (res) {
+ var records = _.map(res, function (record) {
+ var _value = record[filter.write_field];
+ var value = _.isArray(_value) ? _value[0] : _value;
+ var f = _.find(filter.filters, function (f) {return f.value === value;});
+ var formater = fieldUtils.format[_.contains(['many2many', 'one2many'], field.type) ? 'many2one' : field.type];
+ return {
+ 'id': record.id,
+ 'value': value,
+ 'label': formater(_value, field),
+ 'active': !f || f.active,
+ };
+ });
+ records.sort(function (f1,f2) {
+ return _.string.naturalCmp(f2.label, f1.label);
+ });
+
+ // add my profile
+ if (field.relation === 'res.partner' || field.relation === 'res.users') {
+ var value = field.relation === 'res.partner' ? session.partner_id : session.uid;
+ var me = _.find(records, function (record) {
+ return record.value === value;
+ });
+ if (me) {
+ records.splice(records.indexOf(me), 1);
+ } else {
+ var f = _.find(filter.filters, function (f) {return f.value === value;});
+ me = {
+ 'value': value,
+ 'label': session.name + _t(" [Me]"),
+ 'active': !f || f.active,
+ };
+ }
+ records.unshift(me);
+ }
+ // add all selection
+ records.push({
+ 'value': 'all',
+ 'label': field.relation === 'res.partner' || field.relation === 'res.users' ? _t("Everybody's calendars") : _t("Everything"),
+ 'active': filter.all,
+ });
+
+ filter.filters = records;
+ });
+ },
+ /**
+ * @private
+ * @param {any} element
+ * @param {any} events
+ * @returns {Promise}
+ */
+ _loadRecordsToFilters: function (element, events) {
+ var self = this;
+ var new_filters = {};
+ var to_read = {};
+ var defs = [];
+ var color_filter = {};
+
+ _.each(this.data.filters, function (filter, fieldName) {
+ var field = self.fields[fieldName];
+
+ new_filters[fieldName] = filter;
+ if (filter.write_model) {
+ if (field.relation === self.model_color) {
+ _.each(filter.filters, function (f) {
+ f.color_index = f.value;
+ });
+ }
+ return;
+ }
+
+ _.each(filter.filters, function (filter) {
+ filter.display = !filter.active;
+ });
+
+ var fs = [];
+ var undefined_fs = [];
+ _.each(events, function (event) {
+ var data = event.record[fieldName];
+ if (!_.contains(['many2many', 'one2many'], field.type)) {
+ data = [data];
+ } else {
+ to_read[field.relation] = (to_read[field.relation] || []).concat(data);
+ }
+ _.each(data, function (_value) {
+ var value = _.isArray(_value) ? _value[0] : _value;
+ var f = {
+ 'color_index': self.model_color === (field.relation || element.model) ? value % 30 : false,
+ 'value': value,
+ 'label': fieldUtils.format[field.type](_value, field) || _t("Undefined"),
+ 'avatar_model': field.relation || element.model,
+ };
+ // if field used as color does not have value then push filter in undefined_fs,
+ // such filters should come last in filter list with Undefined string, later merge it with fs
+ value ? fs.push(f) : undefined_fs.push(f);
+ });
+ });
+ _.each(_.union(fs, undefined_fs), function (f) {
+ var f1 = _.findWhere(filter.filters, _.omit(f, 'color_index'));
+ if (f1) {
+ f1.display = true;
+ } else {
+ f.display = f.active = true;
+ filter.filters.push(f);
+ }
+ });
+
+ if (filter.color_model && filter.field_color) {
+ var ids = filter.filters.reduce((acc, f) => {
+ if (!f.color_index && f.value) {
+ acc.push(f.value);
+ }
+ return acc;
+ }, []);
+ if (!color_filter[filter.color_model]) {
+ color_filter[filter.color_model] = {};
+ }
+ if (ids.length) {
+ defs.push(self._rpc({
+ model: filter.color_model,
+ method: 'read',
+ args: [_.uniq(ids), [filter.field_color]],
+ })
+ .then(function (res) {
+ _.each(res, function (c) {
+ color_filter[filter.color_model][c.id] = c[filter.field_color];
+ });
+ }));
+ }
+ }
+ });
+
+ _.each(to_read, function (ids, model) {
+ defs.push(self._rpc({
+ model: model,
+ method: 'name_get',
+ args: [_.uniq(ids)],
+ })
+ .then(function (res) {
+ to_read[model] = _.object(res);
+ }));
+ });
+ return Promise.all(defs).then(function () {
+ _.each(self.data.filters, function (filter) {
+ if (filter.write_model) {
+ return;
+ }
+ if (filter.filters.length && (filter.filters[0].avatar_model in to_read)) {
+ _.each(filter.filters, function (f) {
+ f.label = to_read[f.avatar_model][f.value];
+ });
+ }
+ if (filter.color_model && filter.field_color) {
+ _.each(filter.filters, function (f) {
+ if (!f.color_index) {
+ f.color_index = color_filter[filter.color_model] && color_filter[filter.color_model][f.value];
+ }
+ });
+ }
+ });
+ });
+ },
+ /**
+ * parse the server values to javascript framwork
+ *
+ * @private
+ * @param {Object} data the server data to parse
+ */
+ _parseServerData: function (data) {
+ var self = this;
+ _.each(data, function(event) {
+ _.each(self.fieldNames, function (fieldName) {
+ event[fieldName] = self._parseServerValue(self.fields[fieldName], event[fieldName]);
+ });
+ });
+ },
+ /**
+ * Transform OpenERP event object to fullcalendar event object
+ *
+ * @private
+ * @param {Object} evt
+ */
+ _recordToCalendarEvent: function (evt) {
+ var date_start;
+ var date_stop;
+ var date_delay = evt[this.mapping.date_delay] || 1.0,
+ all_day = this.fields[this.mapping.date_start].type === 'date' ||
+ this.mapping.all_day && evt[this.mapping.all_day] || false,
+ the_title = '',
+ attendees = [];
+
+ if (!all_day) {
+ date_start = evt[this.mapping.date_start].clone();
+ date_stop = this.mapping.date_stop ? evt[this.mapping.date_stop].clone() : null;
+ } else {
+ date_start = evt[this.mapping.date_start].clone().startOf('day');
+ date_stop = this.mapping.date_stop ? evt[this.mapping.date_stop].clone().startOf('day') : null;
+ }
+
+ if (!date_stop && date_delay) {
+ date_stop = date_start.clone().add(date_delay,'hours');
+ }
+
+ if (!all_day) {
+ date_start.add(this.getSession().getTZOffset(date_start), 'minutes');
+ date_stop.add(this.getSession().getTZOffset(date_stop), 'minutes');
+ }
+
+ if (this.mapping.all_day && evt[this.mapping.all_day]) {
+ date_stop.add(1, 'days');
+ }
+ var r = {
+ 'record': evt,
+ 'start': date_start.local(true).toDate(),
+ 'end': date_stop.local(true).toDate(),
+ 'r_start': date_start.clone().local(true).toDate(),
+ 'r_end': date_stop.clone().local(true).toDate(),
+ 'title': the_title,
+ 'allDay': all_day,
+ 'id': evt.id,
+ 'attendees':attendees,
+ };
+
+ if (!(this.mapping.all_day && evt[this.mapping.all_day]) && this.data.scale === 'month' && this.fields[this.mapping.date_start].type !== 'date') {
+ r.showTime = true;
+ }
+
+ return r;
+ },
+});
+
+});