"use strict"; /* * Egroupware * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api * @link http://www.egroupware.org * @author Nathan Gray * @version $Id$ */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.et2_calendar_daycol = void 0; /*egw:uses et2_core_valueWidget; /calendar/js/et2_widget_event.js; */ var et2_core_widget_1 = require("../../api/js/etemplate/et2_core_widget"); var et2_core_valueWidget_1 = require("../../api/js/etemplate/et2_core_valueWidget"); var et2_widget_timegrid_1 = require("./et2_widget_timegrid"); var et2_widget_view_1 = require("./et2_widget_view"); var et2_widget_event_1 = require("./et2_widget_event"); var et2_core_inheritance_1 = require("../../api/js/etemplate/et2_core_inheritance"); /** * Class which implements the "calendar-timegrid" XET-Tag for displaying a single days * * This widget is responsible mostly for positioning its events * */ var et2_calendar_daycol = /** @class */ (function (_super) { __extends(et2_calendar_daycol, _super); /** * Constructor */ function et2_calendar_daycol(_parent, _attrs, _child) { var _this = // Call the inherited constructor _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_calendar_daycol._attributes, _child || {})) || this; _this.registeredUID = null; // Init to defaults, just in case - they will be updated from parent _this.display_settings = { wd_start: 60 * 9, wd_end: 60 * 17, granularity: 30, rowsToDisplay: 10, rowHeight: 20, // Percentage; not yet available titleHeight: 2.0 }; // Main container _this.div = jQuery(document.createElement("div")) .addClass("calendar_calDayCol") .css('width', _this.options.width) .css('left', _this.options.left); _this.header = jQuery(document.createElement('div')) .addClass("calendar_calDayColHeader") .css('width', _this.options.width) .css('left', _this.options.left); _this.title = jQuery(document.createElement('div')) .addClass('et2_clickable et2_link') .appendTo(_this.header); _this.user_spacer = jQuery(document.createElement('div')) .addClass("calendar_calDayColHeader_spacer") .appendTo(_this.header); _this.all_day = jQuery(document.createElement('div')) .addClass("calendar_calDayColAllDay") .css('max-height', (egw.preference('limit_all_day_lines', 'calendar') || 3) * 1.4 + 'em') .appendTo(_this.header); _this.event_wrapper = jQuery(document.createElement('div')) .addClass("event_wrapper") .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(); return _this; } et2_calendar_daycol.prototype.doLoadingFinished = function () { var result = _super.prototype.doLoadingFinished.call(this); // Parent will have everything we need, just load it from there if (this.getParent() && this.getParent().options.owner) { this.set_owner(this.getParent().options.owner); } if (this.title.text() === '' && this.options.date && this.getParent() && this.getParent().instanceOf(et2_widget_timegrid_1.et2_calendar_timegrid)) { // Forces an update var date = this.options.date; this.options.date = ''; this.set_date(date); } return result; }; et2_calendar_daycol.prototype.destroy = function () { _super.prototype.destroy.call(this); this.div.off(); this.header.off().remove(); this.title.off(); this.div = null; this.header = null; 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); }; et2_calendar_daycol.prototype.getDOMNode = function (sender) { if (!sender || sender === this) return this.div[0]; if (sender.instanceOf && sender.instanceOf(et2_widget_event_1.et2_calendar_event)) { if (this.display_settings.granularity === 0) { return this.event_wrapper[0]; } if (sender.options.value.whole_day_on_top || sender.options.value.whole_day && sender.options.value.non_blocking === true) { return this.all_day[0]; } return this.div[0]; } }; /** * Draw the individual divs for clicking to add an event */ et2_calendar_daycol.prototype._draw = function () { // Remove any existing jQuery('.calendar_calAddEvent', this.div).remove(); // Grab real values from parent if (this.getParent() && this.getParent().instanceOf(et2_widget_timegrid_1.et2_calendar_timegrid)) { this.display_settings.wd_start = 60 * this.getParent().options.day_start; this.display_settings.wd_end = 60 * this.getParent().options.day_end; this.display_settings.granularity = this.getParent().options.granularity; var header = this.getParent().dayHeader.children(); // Figure out insert index var idx = 0; var siblings = this.getParent().getDOMNode(this).childNodes; while (idx < siblings.length && siblings[idx] != this.getDOMNode()) { idx++; } // Stick header in the right place if (idx == 0) { this.getParent().dayHeader.prepend(this.header); } else if (header.length) { header.eq(Math.min(header.length, idx) - 1).after(this.header); } } this.div.attr('data-date', this.options.date); }; et2_calendar_daycol.prototype.getDate = function () { return this.date; }; Object.defineProperty(et2_calendar_daycol.prototype, "date_helper", { get: function () { return this._date_helper; }, enumerable: false, configurable: true }); /** * Set the date * * @param {string|Date} _date New date * @param {Object[]} events =false List of event data to be displayed, or false to * automatically fetch data from content array * @param {boolean} force_redraw =false Redraw even if the date is the same. * Used for when new data is available. */ et2_calendar_daycol.prototype.set_date = function (_date, events, force_redraw) { if (typeof events === 'undefined' || !events) { events = false; } if (typeof force_redraw === 'undefined' || !force_redraw) { force_redraw = false; } if (!this.getParent() || !this.getParent().date_helper) { 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()); // Keep internal option in Ymd format, it gets passed around in this format var new_date = "" + this.getParent().date_helper.get_year() + sprintf("%02d", this.getParent().date_helper.get_month()) + sprintf("%02d", this.getParent().date_helper.get_date()); // Set label if (!this.options.label) { // Add timezone offset back in, or formatDate will lose those hours var 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 .attr("data-date", new_date) .toggleClass('et2_label', !!this.options.label); this.header .attr('data-date', new_date) .attr('data-whole_day', true); // Avoid redrawing if date is the same if (new_date === this.options.date && this.display_settings.granularity === this.getParent().options.granularity && !force_redraw) { return; } var cache_id = CalendarApp._daywise_cache_id(new_date, this.options.owner); if (this.options.date && this.registeredUID && cache_id !== this.registeredUID) { egw.dataUnregisterUID(this.registeredUID, null, this); // Remove existing events while (this._children.length > 0) { var node = this._children[this._children.length - 1]; this.removeChild(node); node.destroy(); } } this.options.date = new_date; // Set holiday and today classes this.day_class_holiday(); // Update all the little boxes this._draw(); // Register for updates on events for this day if (this.registeredUID !== cache_id) { this.registeredUID = cache_id; egw.dataRegisterUID(this.registeredUID, this._data_callback, this, this.getInstanceManager().execId, this.id); } }; /** * Set the owner of this day * * @param {number|number[]|string|string[]} _owner - Owner ID, which can * be an account ID, a resource ID (as defined in calendar_bo, not * necessarily an entry from the resource app), or a list containing a * combination of both. */ et2_calendar_daycol.prototype.set_owner = function (_owner) { this.title .attr("data-owner", _owner); this.header.attr('data-owner', _owner); this.div.attr('data-owner', _owner); // Simple comparison, both numbers if (_owner === this.options.owner) return; // More complicated comparison, one or the other is an array if ((typeof _owner == 'object' || typeof this.options.owner == 'object') && _owner.toString() == this.options.owner.toString()) { return; } this.options.owner = typeof _owner !== 'object' ? [_owner] : _owner; var cache_id = CalendarApp._daywise_cache_id(this.options.date, _owner); if (this.options.date && this.registeredUID && cache_id !== this.registeredUID) { egw.dataUnregisterUID(this.registeredUID, null, this); } if (this.registeredUID !== cache_id) { this.registeredUID = cache_id; egw.dataRegisterUID(this.registeredUID, this._data_callback, this, this.getInstanceManager().execId, this.id); } }; et2_calendar_daycol.prototype.set_class = function (classnames) { this.header.removeClass(this.class); _super.prototype.set_class.call(this, classnames); this.header.addClass(classnames); }; /** * Callback used when the daywise data changes * * Events should update themselves when their data changes, here we are * dealing with a change in which events are displayed on this day. * * @param {String[]} event_ids * @returns {undefined} */ et2_calendar_daycol.prototype._data_callback = function (event_ids) { var events = []; if (event_ids == null || typeof event_ids.length == 'undefined') event_ids = []; for (var i = 0; i < event_ids.length; i++) { var event_1 = egw.dataGetUIDdata('calendar::' + event_ids[i]); event_1 = event_1 && event_1.data || false; if (event_1 && event_1.date && et2_widget_event_1.et2_calendar_event.owner_check(event_1, this) && (event_1.date === this.options.date || // Accept multi-day events new Date(event_1.start) <= this.date //&& new Date(event.end) >= this.date )) { events.push(event_1); } else if (event_1) { // Got an ID that doesn't belong event_ids.splice(i--, 1); } } if (!this.div.is(":visible")) { // Not visible, defer the layout or it all winds up at the top // Cancel any existing listener & bind jQuery(this.getInstanceManager().DOMContainer.parentNode) .off('show.' + CalendarApp._daywise_cache_id(this.options.date, this.options.owner)) .one('show.' + CalendarApp._daywise_cache_id(this.options.date, this.options.owner), function () { this._update_events(events); }.bind(this)); return; } if (!this.getParent().disabled) this._update_events(events); }; et2_calendar_daycol.prototype.set_label = function (label) { this.options.label = label; this.title.text(label); this.title.toggleClass('et2_clickable et2_link', label === ''); }; et2_calendar_daycol.prototype.set_left = function (left) { if (this.div) { this.div.css('left', left); } }; et2_calendar_daycol.prototype.set_width = function (width) { this.options.width = width; if (this.div) { this.div.outerWidth(this.options.width); this.header.outerWidth(this.options.width); } }; /** * Applies class for today, and any holidays for current day */ et2_calendar_daycol.prototype.day_class_holiday = function () { this.title // Remove all special day classes .removeClass('calendar_calToday calendar_calBirthday calendar_calHoliday') // Except this one... .addClass("et2_clickable et2_link"); this.title.attr('data-holiday', ''); // Set today class - note +1 when dealing with today, as months in JS are 0-11 var today = new Date(); today.setUTCMinutes(today.getUTCMinutes() - today.getTimezoneOffset()); this.title.toggleClass("calendar_calToday", this.options.date === '' + today.getUTCFullYear() + sprintf("%02d", today.getUTCMonth() + 1) + sprintf("%02d", today.getUTCDate())); // Holidays and birthdays var holidays = et2_widget_view_1.et2_calendar_view.get_holidays(this, this.options.date.substring(0, 4)); var holiday_list = []; var holiday_pref = (egw.preference('birthdays_as_events', 'calendar') || []); if (typeof holiday_pref === 'string') { holiday_pref = holiday_pref.split(','); } else { holiday_pref = jQuery.extend([], holiday_pref); } // Show holidays as events on mobile or by preference var holidays_as_events = egwIsMobile() || egw.preference('birthdays_as_events', 'calendar') === true || holiday_pref.indexOf('holiday') >= 0; var birthdays_as_events = egwIsMobile() || holiday_pref.indexOf('birthday') >= 0; if (holidays && holidays[this.options.date]) { holidays = holidays[this.options.date]; for (var i = 0; i < holidays.length; i++) { if (typeof 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, value: { title: holidays[i].name, whole_day: true, whole_day_on_top: true, start: new Date(this.getParent().date_helper.get_value()), end: this.options.date, owner: this.options.owner, participants: this.options.owner, app: 'calendar', class: 'calendar_calBirthday' }, readonly: true, class: 'calendar_calBirthday' }, this); event.doLoadingFinished(); event._update(); } if (!egwIsMobile()) { //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']); } } else { // Show holidays as events on mobile 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, value: { title: holidays[i].name, whole_day: true, whole_day_on_top: true, start: new Date(this.getParent().date_helper.get_value()), end: this.options.date, owner: this.options.owner, participants: this.options.owner, app: 'calendar', class: 'calendar_calHoliday' }, readonly: true, class: 'calendar_calHoliday' }, this); event.doLoadingFinished(); event._update(); } else { this.title.addClass('calendar_calHoliday'); this.title.attr('data-holiday', 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']); } } } } } this.title.attr('title', holiday_list.join(', ')); }; /** * Load the event data for this day and create event widgets for each. * * If event information is not provided, it will be pulled from the content array. * * @param {Object[]} [_events] Array of event information, one per event. */ et2_calendar_daycol.prototype._update_events = function (_events) { var c; var events = _events || this.getArrayMgr('content').getEntry(this.options.date) || []; // Remove extra events while (this._children.length > 0) { var node = this._children[this._children.length - 1]; this.removeChild(node); node.destroy(); } // Make sure children are in cronological order, or columns are backwards events.sort(function (a, b) { var start = new Date(a.start) - new Date(b.start); var end = new Date(a.end) - new Date(b.end); // Whole day events sorted by ID, normal events by start / end time if (a.whole_day && b.whole_day) { return (a.app_id - b.app_id); } else if (a.whole_day || b.whole_day) { return a.whole_day ? -1 : 1; } return start ? start : end; }); for (c = 0; c < events.length; c++) { // Create event var event = et2_createWidget('calendar-event', { id: 'event_' + events[c].id, value: events[c] }, this); } // Seperate loop so column sorting finds all children in the right place var child_length = this._children.length; for (c = 0; c < events.length && c < child_length; c++) { var event_2 = this.getWidgetById('event_' + events[c].id); if (!event_2) continue; if (this.isInTree()) { event_2.doLoadingFinished(); } } // Show holidays as events on mobile or by preference if (egwIsMobile() || egw.preference('birthdays_as_events', 'calendar')) { this.day_class_holiday(); } // Apply styles to hidden events this._out_of_view(); }; /** * Apply styles for out-of-view and partially hidden events * * There are 3 different states or modes of display: * * - 'Normal' - When showing events positioned by time, the indicator is just * a bar colored by the last category color. On hover it shows either the * title of a single event or "x event(s)" if more than one are hidden. * Clicking adjusts the current view to show the earliest / latest hidden * event * * - Fixed - When showing events positioned by time but in a fixed-height * week (not auto-sized to fit screen) the indicator is the same as sized. * On hover it shows the titles of the hidden events, clicking changes * the view to the selected day. * * - GridList - When showing just a list, the indicator shows "x event(s)", * and on hover shows the category color, title & time. Clicking changes * the view to the selected day, and opens the event for editing. */ et2_calendar_daycol.prototype._out_of_view = function () { // Reset this.header.children('.hiddenEventBefore').remove(); this.div.children('.hiddenEventAfter').remove(); this.event_wrapper.css('overflow', 'visible'); this.all_day.removeClass('overflown'); jQuery('.calendar_calEventBody', this.div).css({ 'padding-top': '', 'margin-top': '' }); var timegrid = this.getParent(); // elem is jquery div of event function isHidden(elem) { // Add an extra 5px top and bottom to include events just on the // edge of visibility var docViewTop = timegrid.scrolling.scrollTop() + 5, docViewBottom = docViewTop + (this.display_settings.granularity === 0 ? this.event_wrapper.height() : timegrid.scrolling.height() - 10), elemTop = elem.position().top, elemBottom = elemTop + elem.outerHeight(true); if ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)) { // Entirely visible return false; } var visible = { hidden: elemTop > docViewTop ? 'bottom' : 'top', completely: false }; visible.completely = visible.hidden == 'top' ? elemBottom < docViewTop : elemTop > docViewBottom; return visible; } // In gridlist view, we can quickly check if we need it at all if (this.display_settings.granularity === 0 && this._children.length) { jQuery('div.calendar_calEvent', this.div).show(0); if (Math.ceil(this.div.height() / this._children[0].div.height()) > this._children.length) { return; } } // Check all day overflow this.all_day.toggleClass('overflown', this.all_day[0].scrollHeight - this.all_day.innerHeight() > 5); // Check each event this.iterateOver(function (event) { // Skip whole day events and events missing value if (this.display_settings.granularity && ((!event.options || !event.options.value || event.options.value.whole_day_on_top))) { return; } // Reset event.title.css({ 'top': '', 'background-color': '' }); event.body.css({ 'padding-top': '', 'margin-top': '' }); var hidden = isHidden.call(this, event.div); var day = this; if (!hidden) { return; } // Only top is hidden, move label // Bottom hidden is fine if (hidden.hidden === 'top' && !hidden.completely && !event.div.hasClass('calendar_calEventSmall')) { var title_height = event.title.outerHeight(); event.title.css({ 'top': timegrid.scrolling.scrollTop() - event.div.position().top, 'background-color': 'transparent' }); event.body.css({ 'padding-top': timegrid.scrolling.scrollTop() - event.div.position().top + title_height, 'margin-top': -title_height }); } // Too many in gridlist view, show indicator else if (this.display_settings.granularity === 0 && hidden) { if (jQuery('.hiddenEventAfter', this.div).length == 0) { this.event_wrapper.css('overflow', 'hidden'); } this._hidden_indicator(event, false, function () { app.calendar.update_state({ view: 'day', date: day.date }); }); // Avoid partially visible events // We need to hide all, or the next row will be visible event.div.hide(0); } // Completely out of view, show indicator else if (hidden.completely) { this._hidden_indicator(event, hidden.hidden == 'top', false); } }, this, et2_widget_event_1.et2_calendar_event); }; /** * Show an indicator that there are hidden events * * The indicator works 3 different ways, depending on if the day can be * scrolled, is fixed, or if in gridview. * * @see _out_of_view() * * @param {et2_calendar_event} event Event we're creating the indicator for * @param {boolean} top Events hidden at the top (true) or bottom (false) * @param {function} [onclick] Callback for when user clicks on the indicator */ et2_calendar_daycol.prototype._hidden_indicator = function (event, top, onclick) { var indicator = null; var day = this; var timegrid = this.getParent(); var fixed_height = timegrid.div.hasClass('calendar_calTimeGridFixed'); // Event is before the displayed times if (top) { // Create if not already there if (jQuery('.hiddenEventBefore', this.header).length === 0) { indicator = jQuery('
') .appendTo(this.header) .attr('data-hidden_count', 1); if (!fixed_height) { indicator .text(event.options.value.title) .on('click', typeof onclick === 'function' ? onclick : function () { jQuery('.calendar_calEvent', day.div).first()[0].scrollIntoView(); return false; }); } } else { indicator = jQuery('.hiddenEventBefore', this.header); indicator.attr('data-hidden_count', parseInt(indicator.attr('data-hidden_count')) + 1); if (!fixed_height) { indicator.text(day.egw().lang('%1 event(s) %2', indicator.attr('data-hidden_count'), '')); } } } // Event is after displayed times else { indicator = jQuery('.hiddenEventAfter', this.div); // Create if not already there if (indicator.length === 0) { indicator = jQuery('
') .attr('data-hidden_count', 0) .appendTo(this.div); if (!fixed_height) { indicator .on('click', typeof onclick === 'function' ? onclick : function () { jQuery('.calendar_calEvent', day.div).last()[0].scrollIntoView(false); // Better re-run this to clean up day._out_of_view(); return false; }); } else { indicator .on('mouseover', function () { indicator.css({ 'height': (indicator.attr('data-hidden_count') * 1.2) + 'em', 'margin-top': -(indicator.attr('data-hidden_count') * 1.2) + 'em' }); }) .on('mouseout', function () { indicator.css({ 'height': '', 'margin-top': '' }); }); } } var count = parseInt(indicator.attr('data-hidden_count')) + 1; indicator.attr('data-hidden_count', count); if (this.display_settings.granularity === 0) { indicator.append(event.div.clone()); indicator.attr('data-hidden_label', day.egw().lang('%1 event(s) %2', indicator.attr('data-hidden_count'), '')); } else if (!fixed_height) { indicator.text(day.egw().lang('%1 event(s) %2', indicator.attr('data-hidden_count'), '')); } indicator.css('top', timegrid.scrolling.height() + timegrid.scrolling.scrollTop() - indicator.innerHeight()); } // Show different stuff for fixed height if (fixed_height) { indicator .append("
" + event.options.value.title + "
"); } // Match color to the event if (indicator !== null) { // Avoid white, which is hard to see // Use border-bottom-color, Firefox doesn't give a value with border-color var color = jQuery.Color(event.div.css('background-color')).toString() !== jQuery.Color('white').toString() ? event.div.css('background-color') : event.div.css('border-bottom-color'); if (color !== 'rgba(0, 0, 0, 0)') { indicator.css('border-color', color); } } }; /** * Sort a day's events into minimally overlapping columns * * @returns {Array[]} Events sorted into columns */ et2_calendar_daycol.prototype._spread_events = function () { if (!this.date) return []; var day_start = this.date.valueOf() / 1000; var dst_check = new Date(this.date); dst_check.setUTCHours(12); // if daylight saving is switched on or off, correct $day_start // gives correct times after 2am, times between 0am and 2am are wrong var daylight_diff = day_start + 12 * 60 * 60 - (dst_check.valueOf() / 1000); if (daylight_diff) { day_start -= daylight_diff; } var eventCols = [], col_ends = []; // Make sure children are in cronological order, or columns are backwards this._children.sort(function (a, b) { var start = new Date(a.options.value.start) - new Date(b.options.value.start); var end = new Date(a.options.value.end) - new Date(b.options.value.end); // Whole day events sorted by ID, normal events by start / end time if (a.options.value.whole_day && b.options.value.whole_day) { // Longer duration comes first so we have nicer bars across the top var duration = (new Date(b.options.value.end) - new Date(b.options.value.start)) - (new Date(a.options.value.end) - new Date(a.options.value.start)); return duration ? duration : (a.options.value.app_id - b.options.value.app_id); } else if (a.options.value.whole_day || b.options.value.whole_day) { return a.options.value.whole_day ? -1 : 1; } return start ? start : end; }); for (var i = 0; i < this._children.length; i++) { var event_3 = this._children[i].options.value || false; if (!event_3) continue; if (event_3.date && event_3.date != this.options.date && // Multi-day events date may be different (new Date(event_3.start) >= this.date || new Date(event_3.end) < this.date)) { // Still have a child event that has changed date (DnD) this._children[i].destroy(); this.removeChild(this._children[i]); continue; } var c = 0; event_3['multiday'] = false; if (typeof event_3.start !== 'object') { event_3.start = new Date(event_3.start); } if (typeof event_3.end !== 'object') { event_3.end = new Date(event_3.end); } event_3['start_m'] = parseInt(String((event_3.start.valueOf() / 1000 - day_start) / 60), 10); if (event_3['start_m'] < 0) { event_3['start_m'] = 0; event_3['multiday'] = true; } event_3['end_m'] = parseInt(String((event_3.end.valueOf() / 1000 - day_start) / 60), 10); if (event_3['end_m'] >= 24 * 60) { event_3['end_m'] = 24 * 60 - 1; event_3['multiday'] = true; } if (!event_3.start.getUTCHours() && !event_3.start.getUTCMinutes() && event_3.end.getUTCHours() == 23 && event_3.end.getUTCMinutes() == 59) { event_3.whole_day_on_top = (event_3.non_blocking && event_3.non_blocking != '0'); } if (!event_3['whole_day_on_top']) { for (c = 0; event_3['start_m'] < col_ends[c]; ++c) ; col_ends[c] = event_3['end_m']; } if (typeof eventCols[c] === 'undefined') { eventCols[c] = []; } eventCols[c].push(this._children[i]); } return eventCols; }; /** * Position the event according to its time and how this widget is laid * out. * * @param {et2_calendar_event} [event] - Event to be updated * If a single event is not provided, all events are repositioned. */ et2_calendar_daycol.prototype.position_event = function (event) { // If hidden, skip it - it takes too long if (!this.div.is(':visible')) return; // Sort events into minimally-overlapping columns var columns = this._spread_events(); for (var c = 0; c < columns.length; c++) { // Calculate horizontal positioning var left = Math.ceil(5 + (1.5 * 100 / (parseFloat(this.options.width) || 100))); var right = 2; if (columns.length !== 1) { right = !c ? 30 : 2; left += c * (100.0 - left) / columns.length; } for (var i = 0; (columns[c].indexOf(event) >= 0 || !event) && i < columns[c].length; i++) { // Calculate vertical positioning var top_1 = 0; var height = 0; // Position the event if (this.display_settings.granularity === 0) { if (this.all_day.has(columns[c][i].div).length) { columns[c][i].div.prependTo(this.event_wrapper); } columns[c][i].div.css('top', ''); columns[c][i].div.css('height', ''); columns[c][i].div.css('left', ''); columns[c][i].div.css('right', ''); // Strip out of view padding columns[c][i].body.css('padding-top', ''); continue; } if (columns[c][i].options.value.whole_day_on_top) { if (!this.all_day.has(columns[c][i].div).length) { columns[c][i].div.css('top', ''); columns[c][i].div.css('height', ''); columns[c][i].div.css('left', ''); columns[c][i].div.css('right', ''); columns[c][i].body.css('padding-top', ''); columns[c][i].div .appendTo(this.all_day); this.getParent().resizeTimes(); } continue; } else { if (this.all_day.has(columns[c][i].div).length) { columns[c][i].div.appendTo(this.event_wrapper); this.getParent().resizeTimes(); } top_1 = this._time_to_position(columns[c][i].options.value.start_m); height = this._time_to_position(columns[c][i].options.value.end_m) - top_1; } // Position the event if (event && columns[c].indexOf(event) >= 0 || !event) { columns[c][i].div.css('top', top_1 + '%'); columns[c][i].div.css('height', height + '%'); // Remove spacing from border, but only if visible or the height will be wrong if (columns[c][i].div.is(':visible')) { var border_diff = columns[c][i].div.outerHeight() - columns[c][i].div.height(); columns[c][i].div.css('height', 'calc(' + height + '% - ' + border_diff + ')'); } // This gives the wrong height //columns[c][i].div.outerHeight(height+'%'); columns[c][i].div.css('left', left.toFixed(1) + '%'); columns[c][i].div.css('right', right.toFixed(1) + '%'); columns[c][i].div.css('z-index', parseInt(20) + c); columns[c][i]._small_size(); } } // Only wanted to position this event, leave the other columns alone if (event && columns[c].indexOf(event) >= 0) { return; } } }; /** * Calculates the vertical position based on the time * * This calculation is a percentage from 00:00 to 23:59 * * @param {int} time in minutes from midnight * @return {float} position in percent */ et2_calendar_daycol.prototype._time_to_position = function (time) { var pos = 0.0; // 24h pos = ((time / 60) / 24) * 100; return pos.toFixed(1); }; et2_calendar_daycol.prototype.attachToDOM = function () { var result = _super.prototype.attachToDOM.call(this); // Remove the binding for the click handler, unless there's something // custom here. if (!this.onclick) { jQuery(this.node).off("click"); } // But we do want to listen to certain clicks, and handle them internally jQuery(this.node).on('click.et2_daycol', '.calendar_calDayColHeader,.calendar_calAddEvent', jQuery.proxy(this.click, this)); return result; }; /** * Click handler calling custom handler set via onclick attribute to this.onclick, * or the default which is to open a new event at that time. * * Normally, you don't bind to this one, but the attribute is supported if you * can get a reference to the widget. * * @param {Event} _ev * @returns {boolean} */ et2_calendar_daycol.prototype.click = function (_ev) { if (this.getParent().options.readonly) return; // Drag to create in progress if (this.getParent().drag_create.start !== null) return; // Click on the title if (jQuery(_ev.target).hasClass('calendar_calAddEvent')) { if (this.header.has(_ev.target).length == 0 && !_ev.target.dataset.whole_day) { // Default handler to open a new event at the selected time var options = { date: _ev.target.dataset.date || this.options.date, hour: _ev.target.dataset.hour || this.getParent().options.day_start, minute: _ev.target.dataset.minute || 0, owner: this.options.owner }; app.calendar.add(options); return false; } // Header, all day non-blocking else if (this.header.has(_ev.target).length && !jQuery('.hiddenEventBefore', this.header).has(_ev.target).length || this.header.is(_ev.target)) { // Click on the header, but not the title. That's an all-day non-blocking var end = this.date.getFullYear() + '-' + (this.date.getUTCMonth() + 1) + '-' + this.date.getUTCDate() + 'T23:59'; var options_1 = { start: this.date.toJSON(), end: end, non_blocking: true, owner: this.options.owner }; app.calendar.add(options_1); return false; } } // Day label else if (this.title.is(_ev.target) || this.title.has(_ev.target).length) { app.calendar.update_state({ view: 'day', date: this.date.toJSON() }); return false; } }; /** * Code for implementing et2_IDetachedDOM * * @param {array} _attrs array to add further attributes to */ et2_calendar_daycol.prototype.getDetachedAttributes = function (_attrs) { }; et2_calendar_daycol.prototype.getDetachedNodes = function () { return [this.getDOMNode(this)]; }; et2_calendar_daycol.prototype.setDetachedAttributes = function (_nodes, _values) { }; // Resizable interface /** * Resize * * Parent takes care of setting proper width & height for the containing div * here we just need to adjust the events to fit the new size. */ et2_calendar_daycol.prototype.resize = function () { if (this.disabled || !this.div.is(':visible') || this.getParent().disabled) { return; } if (this.display_settings.granularity !== this.getParent().options.granularity) { // Layout has changed this._draw(); // Resize & position all events this.position_event(); } else { // Don't need to resize & reposition, just clear some stuff // to reset for _out_of_view() this.iterateOver(function (widget) { widget._small_size(); }, this, et2_widget_event_1.et2_calendar_event); } this._out_of_view(); }; et2_calendar_daycol._attributes = { date: { name: "Date", type: "any", description: "What date is this daycol for. YYYYMMDD or Date", default: et2_no_init }, owner: { name: "Owner", type: "any", default: et2_no_init, description: "Account ID number of the calendar owner, if not the current user" }, display_birthday_as_event: { name: "Birthdays", type: "boolean", default: false, description: "Display birthdays as events" }, display_holiday_as_event: { name: "Holidays", type: "boolean", default: false, description: "Display holidays as events" } }; return et2_calendar_daycol; }(et2_core_valueWidget_1.et2_valueWidget)); exports.et2_calendar_daycol = et2_calendar_daycol; et2_core_widget_1.et2_register_widget(et2_calendar_daycol, ["calendar-daycol"]); //# sourceMappingURL=et2_widget_daycol.js.map