@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,109 lines (950 loc) • 39.3 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.m.SinglePlanningCalendarMonthGrid.
sap.ui.define([
'sap/ui/core/Control',
'sap/ui/core/format/DateFormat',
'sap/ui/unified/calendar/CalendarDate',
'sap/ui/unified/calendar/CalendarUtils',
'sap/ui/unified/DateTypeRange',
'sap/ui/unified/library',
'sap/ui/core/LocaleData',
'sap/ui/core/Locale',
'sap/ui/core/delegate/ItemNavigation',
'sap/ui/core/dnd/DragDropInfo',
'sap/ui/core/CustomData',
'sap/ui/events/KeyCodes',
'sap/base/Log',
'sap/ui/core/Core',
'./Link',
'./library',
'./PlanningCalendarLegend',
'./SinglePlanningCalendarMonthGridRenderer',
'sap/ui/thirdparty/jquery',
'sap/ui/core/InvisibleMessage',
'sap/ui/core/library',
"sap/ui/core/date/CalendarWeekNumbering",
"sap/ui/core/date/CalendarUtils",
"sap/ui/core/Configuration",
"sap/ui/core/date/UI5Date",
"sap/ui/unified/DateRange",
"sap/m/LinkAccessibleRole"
],
function (
Control,
DateFormat,
CalendarDate,
CalendarUtils,
DateTypeRange,
unifiedLibrary,
LocaleData,
Locale,
ItemNavigation,
DragDropInfo,
CustomData,
KeyCodes,
Log,
Core,
Link,
library,
PlanningCalendarLegend,
SinglePlanningCalendarMonthGridRenderer,
jQuery,
InvisibleMessage,
coreLibrary,
CalendarWeekNumbering,
CalendarDateUtils,
Configuration,
UI5Date,
DateRange,
LinkAccessibleRole
) {
"use strict";
var APP_HEIGHT_COMPACT = 1.5625; //rem
var CELL_HEADER_HEIGHT_COMPACT = 1.5; //rem
var APP_HEIGHT_COZY = 2.125; //rem
var CELL_HEADER_HEIGHT_COZY = 1.75; //rem
var InvisibleMessageMode = coreLibrary.InvisibleMessageMode;
var SinglePlanningCalendarSelectionMode = library.SinglePlanningCalendarSelectionMode;
/**
* Constructor for a new <code>SinglePlanningCalendarMonthGrid</code>.
*
* @param {string} [sId] ID for the new control, generated automatically if no ID is given
* @param {object} [mSettings] Initial settings for the new control
*
* @class
*
* Displays a grid for the <code>SinglePlanningCalendarMonthView</code> in which appointments are rendered.
*
* <h3>Overview</h3>
*
* The <code>SinglePlanningCalendarMonthGrid</code> has the following structure:
*
* <ul>
* <li>The grid shows one whole month.</li>
* <li>Each cell in the grid represents a single day.</li>
* <li>Each row represents a week.</li>
* <li>Appointments are displayed as a list inside each day.
* They are sorted by their start dates.
* Their widths do not show their durations.</li>
* </ul>
*
* @extends sap.ui.core.Control
*
* @author SAP SE
* @version 1.117.4
*
* @constructor
* @private
* @alias sap.m.SinglePlanningCalendarMonthGrid
*/
var SinglePlanningCalendarMonthGrid = Control.extend("sap.m.SinglePlanningCalendarMonthGrid", /** @lends sap.m.SinglePlanningCalendarMonthGrid.prototype */ {
metadata: {
library: "sap.m",
properties: {
/**
* Determines the start date of the grid, as a UI5Date or JavaScript Date object. It is considered as a local date.
* The time part will be ignored. The current date is used as default.
*/
startDate: { type: "object", group: "Data" },
/**
* Determines whether the appointments in the grid are draggable.
*
* The drag and drop interaction is visualized by a placeholder highlighting the area where the
* appointment can be dropped by the user.
*/
enableAppointmentsDragAndDrop: { type: "boolean", group: "Misc", defaultValue: false },
/**
* If set, the first day of the displayed week is this day. Valid values are 0 to 6 starting on Sunday.
* If there is no valid value set, the default of the used locale is used.
*
* @since 1.98
*/
firstDayOfWeek : {type : "int", group : "Appearance", defaultValue : -1},
/**
* If set, the calendar week numbering is used for display.
* If not set, the calendar week numbering of the global configuration is used.
* Note: This property should not be used with firstDayOfWeek property.
* @since 1.110.0
*/
calendarWeekNumbering : { type : "sap.ui.core.date.CalendarWeekNumbering", group : "Appearance", defaultValue: null},
/**
* Determines whether more than one day will be selectable.
* <b>Note:</b> selecting more than one day is possible with a combination of <code>Ctrl + mouse click</code>
*/
dateSelectionMode: { type: "sap.m.SinglePlanningCalendarSelectionMode", group: "Behavior", defaultValue: SinglePlanningCalendarSelectionMode.SingleSelect }
},
aggregations: {
/**
* The appointments to be displayed in the grid. Appointments outside the visible time frame are not rendered.
* Appointments longer than a day are displayed in all of the affected days.
*/
appointments: { type: "sap.ui.unified.CalendarAppointment", multiple: true, singularName: "appointment", dnd: { draggable: true }},
/**
* The special days in the header visualized as a date range with type.
*
* <b>Note:</b> If one day is assigned to more than one type, only the first type is used.
*/
specialDates: { type: "sap.ui.unified.DateTypeRange", multiple: true, singularName: "specialDate" },
_appsPlaceholders: { type: "sap.m.SinglePlanningCalendarMonthGrid._internal.IntervalPlaceholder", multiple: true, visibility: "hidden", dnd: { droppable: true } },
/**
* Dates or date ranges for selected dates.
*
* To set a single date (instead of a range), set only the <code>startDate</code> property
* of the {@link sap.ui.unified.DateRange} class.
*/
selectedDates : {type : "sap.ui.unified.DateRange", multiple : true, singularName : "selectedDate"}
},
dnd: true,
associations: {
/**
* Association to the <code>PlanningCalendarLegend</code> explaining the colors of the <code>Appointments</code>.
*
* <b>Note:</b> The legend does not have to be rendered but must exist and all required types must be assigned.
*/
legend: { type: "sap.m.PlanningCalendarLegend", multiple: false }
},
events: {
/**
* Fired when a grid cell is pressed.
*/
cellPress: {
parameters: {
/**
* The start date as a UI5Date or JavaScript Date object of the focused grid cell.
*/
startDate: { type: "object" },
/**
* The end date as a UI5Date or JavaScript Date object of the focused grid cell.
*/
endDate: { type: "object" }
}
},
/**
* Fired when a 'more' button is pressed.
* <b>Note:</b> The 'more' button appears when multiple appointments
* exist and the available space is not sufficient to display all of them.
*/
moreLinkPress: {
parameters: {
/**
* The date as a UI5Date or JavaScript Date object of the cell with the
* pressed more link.
*/
date: { type: "object" }
}
},
/**
* Fired if an appointment is dropped.
*/
appointmentDrop: {
parameters: {
/**
* The dropped appointment.
*/
appointment: { type: "sap.ui.unified.CalendarAppointment" },
/**
* Start date of the dropped appointment as a UI5Date or JavaScript Date object.
*/
startDate: { type: "object" },
/**
* End date of the dropped appointment as a UI5Date or JavaScript Date object.
*/
endDate: { type: "object" },
/**
* Determines whether the drop type is to copy the appointment (when <code>true</code>) or to move it.
*/
copy: { type: "boolean" }
}
},
/**
* Fired when the selected state of an appointment is changed.
* @since 1.72
*/
appointmentSelect: {
parameters: {
/**
* The appointment on which the event was triggered.
*/
appointment: {type: "sap.ui.unified.CalendarAppointment"},
/**
* All appointments with changed selected state.
*/
appointments : {type : "sap.ui.unified.CalendarAppointment[]"}
}
}
}
},
renderer: SinglePlanningCalendarMonthGridRenderer
});
SinglePlanningCalendarMonthGrid.prototype.init = function() {
this._aLinks = [];
this._handleMorePress = this._handleMorePress.bind(this);
this._oDateFormat = DateFormat.getDateTimeInstance({ pattern: "YYYYMMdd" });
this._oFormatAriaApp = DateFormat.getDateTimeInstance({
pattern: "EEEE dd/MM/YYYY 'at' " + this._getCoreLocaleData().getTimePattern("medium")
});
this._oFormatAriaFullDayCell = DateFormat.getDateTimeInstance({
pattern: "EEEE dd/MM/YYYY"
});
this.setStartDate(UI5Date.getInstance());
this._configureAppointmentsDragAndDrop();
this._oUnifiedRB = sap.ui.getCore().getLibraryResourceBundle("sap.ui.unified");
};
SinglePlanningCalendarMonthGrid.prototype.exit = function() {
if (this._oItemNavigation) {
this.removeDelegate(this._oItemNavigation);
this._oItemNavigation.destroy();
delete this._oItemNavigation;
}
for (var i = 0; i < this._aLinks.length; i++) {
if (this._aLinks[i]) {
this._aLinks[i].destroy();
}
}
delete this._aLinks;
};
SinglePlanningCalendarMonthGrid.prototype.onBeforeRendering = function() {
var oStartDate = this.getStartDate();
this._oAppointmentsToRender = this._calculateAppointmentsNodes(oStartDate);
this._createAppointmentsDndPlaceholders(oStartDate);
this._oInvisibleMessage = InvisibleMessage.getInstance();
if (this.getFirstDayOfWeek() !== -1 && this.getCalendarWeekNumbering()) {
Log.warning("Both properties firstDayOfWeek and calendarWeekNumbering should not be used at the same time!");
}
};
SinglePlanningCalendarMonthGrid.prototype._checkDateSelected = function(oDay) {
var aSelectedDate = this.getAggregation("selectedDates");
var oRange;
var oStartDate;
var oEndDate;
if (!aSelectedDate) {
return false;
}
for (var i = 0; i < aSelectedDate.length; i++) {
oRange = aSelectedDate[i];
oStartDate = oRange.getStartDate() && CalendarDate.fromLocalJSDate(oRange.getStartDate());
oEndDate = oRange.getEndDate() && CalendarDate.fromLocalJSDate(oRange.getEndDate());
if (oStartDate && oDay.isSame(oStartDate) || (oStartDate && oEndDate && oDay.isSameOrAfter(oStartDate) && oDay.isSameOrBefore(oEndDate))) {
return true;
}
}
return false;
};
SinglePlanningCalendarMonthGrid.prototype.onAfterRendering = function() {
this._initItemNavigation();
};
SinglePlanningCalendarMonthGrid.prototype._getColumns = function() {
return 7;
};
SinglePlanningCalendarMonthGrid.prototype._getRows = function() {
return 6;
};
SinglePlanningCalendarMonthGrid.prototype._getDateFormatter = function() {
return this._oDateFormat;
};
SinglePlanningCalendarMonthGrid.prototype._getAppointmetsForADay = function(oDate) {
return this._oAppointmentsToRender.filter(function(app) {
return app.start.valueOf() === oDate.valueOf();
});
};
SinglePlanningCalendarMonthGrid.prototype._getPreviousAppointmetsForADay = function(oDate) {
return this._oAppointmentsToRender.filter(function(app) {
return app.start.valueOf() < oDate.valueOf()
&& app.end.valueOf() >= oDate.valueOf();
}).map(function(app) {
// adjust their widths because we could render
// them in several weeks separately
var newApp = {
data: app.data,
start: app.start,
end: app.end,
len: app.len,
level: app.level,
width: app.width
};
newApp.width -= CalendarUtils._daysBetween(oDate, app.start);
newApp.hasPrevious = true;
return newApp;
}, this);
};
/**
* Handles the <code>tap</code> event on the grid.
*
* @param {jQuery.Event} oEvent The event object
*/
SinglePlanningCalendarMonthGrid.prototype.ontap = function(oEvent) {
var bMultiDateSelection = SinglePlanningCalendarSelectionMode.MultiSelect === this.getDateSelectionMode();
if (!bMultiDateSelection && !(oEvent.metaKey || oEvent.ctrlKey)) {
this.removeAllSelectedDates();
}
this._bMultiDateSelect = true;
this._fireSelectionEvent(oEvent);
};
SinglePlanningCalendarMonthGrid.prototype._rangeSelection = function(oStartDate) {
var oCurrentDate = UI5Date.getInstance(CalendarDate.fromLocalJSDate(oStartDate));
var oTarget;
var i;
var _bSelectWeek = false;
for (i = 0; i < 7; i++) {
if (!this._checkDateSelected(CalendarDate.fromLocalJSDate(oCurrentDate))) {
_bSelectWeek = true;
break;
}
oCurrentDate.setDate(oCurrentDate.getDate() + 1);
}
oCurrentDate = UI5Date.getInstance(CalendarDate.fromLocalJSDate(oStartDate));
for (i = 0; i < 7; i++) {
oTarget = document.querySelector('[sap-ui-date="' + oCurrentDate.getTime() + '"]');
if (!(_bSelectWeek && oTarget && oTarget.classList.contains("sapMSPCMonthDaySelected"))){
this._toggleMarkCell(oTarget, oCurrentDate);
}
oCurrentDate.setDate(oCurrentDate.getDate() + 1);
}
};
SinglePlanningCalendarMonthGrid.prototype.removeAllSelectedDates = function() {
this.removeAllAggregation("selectedDates");
};
/**
* Handles the <code>keydown</code> event when any key is pressed.
*
* @param {jQuery.Event} oEvent The event object.
*/
SinglePlanningCalendarMonthGrid.prototype.onkeydown = function(oEvent) {
var bMultiDateSelection = SinglePlanningCalendarSelectionMode.MultiSelect === this.getDateSelectionMode();
if (oEvent.which === KeyCodes.SPACE || oEvent.which === KeyCodes.ENTER || oEvent.which === KeyCodes.ARROW_UP ||
oEvent.which === KeyCodes.ARROW_DOWN || oEvent.which === KeyCodes.ARROW_LEFT || oEvent.which === KeyCodes.ARROW_RIGHT) {
if (oEvent.which === KeyCodes.SPACE && !oEvent.shiftKey && bMultiDateSelection) {
this._bMultiDateSelect = true;
} else if (oEvent.which === KeyCodes.SPACE && oEvent.shiftKey && bMultiDateSelection) {
this._bCurrentWeekSelection = true;
} else if (
(oEvent.which === KeyCodes.ARROW_UP || oEvent.which === KeyCodes.ARROW_DOWN ||
oEvent.which === KeyCodes.ARROW_LEFT || oEvent.which === KeyCodes.ARROW_RIGHT) &&
oEvent.shiftKey && bMultiDateSelection) {
this._bMultiDateSelectWithArrow = true;
}
this._fireSelectionEvent(oEvent);
var oControl = this._findSrcControl(oEvent);
if (oControl && oControl.isA("sap.ui.unified.CalendarAppointment")) {
var sBundleKey = oControl.getSelected() ? "APPOINTMENT_SELECTED" : "APPOINTMENT_UNSELECTED";
this._oInvisibleMessage.announce(this._oUnifiedRB.getText(sBundleKey), InvisibleMessageMode.Polite);
}
// Prevent scrolling
oEvent.preventDefault();
}
};
SinglePlanningCalendarMonthGrid.prototype._findSrcControl = function (oEvent) {
// data-sap-ui-related - This is a relation to appointment object.
// This is the connection between the DOM Element and the Object representing an appointment.
var oTargetElement = oEvent.target,
oTargetsParentElement = oTargetElement.parentElement,
sAppointmentId;
if (!oTargetsParentElement || oTargetsParentElement.classList.contains("sapMSPCMonthDays")) {
return oEvent.srcControl;
} else if (oTargetsParentElement.classList.contains("sapUiCalendarRowApps")) {
sAppointmentId = oTargetsParentElement.getAttribute("data-sap-ui-related") || oTargetsParentElement.id;
} else {
sAppointmentId = oTargetElement.getAttribute("data-sap-ui-related") || oTargetElement.id;
}
// finding the appointment
return this.getAppointments().find(function (oAppointment) {
return oAppointment.sId === sAppointmentId;
});
};
SinglePlanningCalendarMonthGrid.prototype._handelMultiDateSelection = function (oTarget, oStartDate, oEndDate, oEvent) {
if (this._bMultiDateSelect) {
this._bMultiDateSelect = false;
this._toggleMarkCell(oTarget, oStartDate);
} else if (this._bMultiDateSelectWithArrow){
this._bMultiDateSelectWithArrow = false;
var oDate = UI5Date.getInstance(CalendarDate.fromLocalJSDate(oStartDate));
switch (oEvent.which) {
case KeyCodes.ARROW_UP : oDate.setDate(oDate.getDate() - 7); break;
case KeyCodes.ARROW_DOWN : oDate.setDate(oDate.getDate() + 7); break;
case KeyCodes.ARROW_LEFT: oDate.setDate(oDate.getDate() - 1); break;
case KeyCodes.ARROW_RIGHT: oDate.setDate(oDate.getDate() + 1); break;
default: break;
}
oTarget = document.querySelector('[sap-ui-date="' + oDate.getTime() + '"]');
oStartDate = UI5Date.getInstance(oDate.getTime());
oStartDate = UI5Date.getInstance(oDate.getUTCFullYear(), oStartDate.getUTCMonth(), oStartDate.getUTCDate());
this._toggleMarkCell(oTarget, oStartDate);
} else if (this._bCurrentWeekSelection && SinglePlanningCalendarSelectionMode.MultiSelect === this.getDateSelectionMode()){
this._bCurrentWeekSelection = false;
var iFirstDayOfWeek;
var oWeekConfigurationValues = CalendarDateUtils.getWeekConfigurationValues(this.getCalendarWeekNumbering(), new Locale(Configuration.getFormatSettings().getFormatLocale().toString()));
var iAPIFirstDayOfWeek = this.getFirstDayOfWeek();
if (iAPIFirstDayOfWeek < 0 || iAPIFirstDayOfWeek > 6) {
if (oWeekConfigurationValues) {
iFirstDayOfWeek = oWeekConfigurationValues.firstDayOfWeek;
} else {
var oLocaleData = this._getCoreLocaleData();
iFirstDayOfWeek = oLocaleData.getFirstDayOfWeek();
}
} else {
iFirstDayOfWeek = iAPIFirstDayOfWeek;
}
oStartDate.setDate(oStartDate.getDate() - oStartDate.getDay() + iFirstDayOfWeek);
oEndDate.setDate(oStartDate.getDate() + 6);
this._rangeSelection(oStartDate, oEndDate);
}
};
/**
* Helper function handling <code>keydown</code> or <code>tap</code> event on the grid.
*
* @param {jQuery.Event} oEvent The event object.
*/
SinglePlanningCalendarMonthGrid.prototype._fireSelectionEvent = function (oEvent) {
var oSrcControl = this._findSrcControl(oEvent),
oTarget = oEvent.target,
bIsCell = oTarget && oTarget.classList.contains("sapMSPCMonthDay"),
bIsLink = oTarget && oTarget.classList.contains("sapMLnk"),
bWeekNumberSelect = oTarget && oTarget.classList.contains("sapMSPCMonthWeekNumber"),
oFirstSiblingElement = bWeekNumberSelect && oEvent.originalEvent.target.nextSibling.children[0],
iTimestamp,
oStartDate,
oEndDate;
if (oSrcControl && oSrcControl.isA("sap.m.SinglePlanningCalendarMonthGrid") && bIsCell && !bIsLink) {
iTimestamp = parseInt(oTarget.getAttribute("sap-ui-date"));
oStartDate = UI5Date.getInstance(iTimestamp);
oStartDate = UI5Date.getInstance(oStartDate.getUTCFullYear(), oStartDate.getUTCMonth(), oStartDate.getUTCDate());
oEndDate = UI5Date.getInstance(oStartDate);
oEndDate.setDate(oEndDate.getDate() + 1);
if (this._bMultiDateSelect || this._bCurrentWeekSelection || this._bMultiDateSelectWithArrow) {
this._handelMultiDateSelection(oTarget, oStartDate, oEndDate, oEvent);
this.fireEvent("selectDate", {startDate: oStartDate, endDate: oEndDate});
}
this.fireEvent("cellPress", {startDate: oStartDate, endDate: oEndDate});
this.fireAppointmentSelect({
appointment: undefined,
appointments: this._toggleAppointmentSelection(undefined, true)
});
} else if (bWeekNumberSelect) {
iTimestamp = parseInt(oFirstSiblingElement.getAttribute("sap-ui-date"));
oStartDate = UI5Date.getInstance(iTimestamp);
oStartDate = UI5Date.getInstance(oStartDate.getUTCFullYear(), oStartDate.getUTCMonth(), oStartDate.getUTCDate());
oEndDate = UI5Date.getInstance(oStartDate);
oEndDate.setDate(oEndDate.getDate() + 6);
this._bCurrentWeekSelection = true;
this._bMultiDateSelect = false;
this._handelMultiDateSelection(oTarget, oStartDate, oEndDate, oEvent);
this.fireEvent("selectDate", {startDate: oStartDate, endDate: oEndDate});
} else if (oSrcControl && oSrcControl.isA("sap.ui.unified.CalendarAppointment")) {
// add suffix in appointment
if (oTarget.parentElement && oTarget.parentElement.getAttribute("id")) {
var sTargetId = oTarget.parentElement.getAttribute("id");
// data-sap-ui-related - This is a relation to appointment object.
// This is the connection between the DOM Element and the Object representing an appointment.
var sBaseIDPart = oTarget.parentElement.getAttribute("data-sap-ui-related");
var sSuffix = sTargetId.replace(sBaseIDPart + "-", "");
oSrcControl._setAppointmentPartSuffix(sSuffix);
}
this.fireAppointmentSelect({
appointment: oSrcControl,
appointments: this._toggleAppointmentSelection(oSrcControl, !(oEvent.ctrlKey || oEvent.metaKey))
});
}
};
SinglePlanningCalendarMonthGrid.prototype._toggleMarkCell = function (oTarget, oDay) {
if (oTarget && !oTarget.classList.contains("sapMSPCMonthDaySelected")) {
this.addAggregation("selectedDates", new DateRange({startDate: UI5Date.getInstance(oDay)}));
} else {
var aSelectedDates = this.getAggregation("selectedDates");
if (!aSelectedDates) {
return;
}
for (var i = 0; i < aSelectedDates.length; i++){
var oSelectStartDate = aSelectedDates[i].getStartDate();
if (CalendarDate.fromLocalJSDate(oSelectStartDate).isSame(CalendarDate.fromLocalJSDate(oDay))) {
this.removeAggregation("selectedDates", i);
break;
}
}
}
};
/**
* Selects or deselects an appointment that is passed as a parameter. If it is selected, it is going to be
* deselected and vice versa. If modifier keys are pressed - the previously selected appointments will be
* preserved.
*
* @param {sap.m.CalendarAppointment} oAppointment The appointment to be selected/deselected.
* @param {boolean} [bRemoveOldSelection=false] If true, previously selected appointments will be deselected.
* @returns {array} Array of the appointments with changed selected state
* @private
*/
SinglePlanningCalendarMonthGrid.prototype._toggleAppointmentSelection = function (oAppointment, bRemoveOldSelection) {
var aChangedApps = [],
oAppointmentDomRef = oAppointment && oAppointment.getDomRef(),
aAppointments,
iAppointmentsLength,
i;
if (bRemoveOldSelection) {
aAppointments = this.getAppointments();
for (i = 0, iAppointmentsLength = aAppointments.length; i < iAppointmentsLength; i++) {
// Deselecting all selected appointments if a grid cell is focused or
// all selected appointments different than the currently focused appointment
if ( (!oAppointment || aAppointments[i].getId() !== oAppointment.getId()) && aAppointments[i].getSelected()) {
aAppointments[i].setProperty("selected", false);
aChangedApps.push(aAppointments[i]);
}
}
}
if (oAppointment) {
oAppointment.setProperty("selected", !oAppointment.getSelected());
aChangedApps.push(oAppointment);
this._sSelectedAppointment = oAppointment.getSelected() && oAppointmentDomRef ? oAppointment : undefined;
} else {
this._sSelectedAppointment = undefined;
}
return aChangedApps;
};
SinglePlanningCalendarMonthGrid.prototype._getMoreLink = function(iAppointmentsCount, oCalendarDate, iCellIndex) {
var sMore = Core
.getLibraryResourceBundle("sap.m")
.getText("SPC_MORE_LINK", [iAppointmentsCount.toString()]),
oLink = new Link({
accessibleRole: LinkAccessibleRole.Button,
text: sMore,
press: this._handleMorePress
}).addCustomData(new CustomData({
key: "date",
value: oCalendarDate.valueOf().toString(),
writeToDom: true
}));
if (this._aLinks[iCellIndex]) {
this._aLinks[iCellIndex].destroy();
}
this._aLinks[iCellIndex] = oLink;
return oLink;
};
SinglePlanningCalendarMonthGrid.prototype._handleMorePress = function(oEvent) {
var iTimestamp = parseInt(oEvent.getSource().getCustomData()[0].getValue()),
oDate = UI5Date.getInstance(iTimestamp);
oDate = UI5Date.getInstance(oDate.getUTCFullYear(), oDate.getUTCMonth(), oDate.getUTCDate());
this.fireEvent("moreLinkPress", { date: oDate });
};
SinglePlanningCalendarMonthGrid.prototype._getCoreLocaleData = function() {
var sLocale = Core.getConfiguration().getFormatSettings().getFormatLocale().toString(),
oLocale = new Locale(sLocale);
return LocaleData.getInstance(oLocale);
};
SinglePlanningCalendarMonthGrid.prototype._getCells = function() {
return this._getVisibleDays(this.getStartDate());
};
SinglePlanningCalendarMonthGrid.prototype._getVerticalLabels = function() {
var aDays = this._getVisibleDays(this.getStartDate()),
iColumns = this._getColumns(),
aResult = [],
sLocale = Core.getConfiguration().getFormatLocale().toString();
for (var i = 0; i < this._getRows(); i++) {
var oDateFormat = DateFormat.getInstance({pattern: "w", calendarType: "Gregorian", calendarWeekNumbering: this.getCalendarWeekNumbering()}, new Locale(sLocale));
var iWeekNumber = Number(oDateFormat.format(aDays[i * iColumns].toUTCJSDate(), true));
aResult.push(iWeekNumber);
}
return aResult;
};
SinglePlanningCalendarMonthGrid.prototype._getVisibleDays = function(oStartDate) {
var oCalStartDate,
iAPIFirstDayOfWeek,
oDay,
oCalDate,
iDaysOldMonth,
oFirstDay,
iFirstDayOfWeek,
aVisibleDays = [];
// If date passed generate days for new start date else return the current one
if (!oStartDate) {
return aVisibleDays;
}
oCalStartDate = CalendarDate.fromLocalJSDate(oStartDate);
iAPIFirstDayOfWeek = this.getFirstDayOfWeek();
if (iAPIFirstDayOfWeek < 0 || iAPIFirstDayOfWeek > 6) {
var oWeekConfigurationValues = CalendarDateUtils.getWeekConfigurationValues(this.getCalendarWeekNumbering(), new Locale(Configuration.getFormatSettings().getFormatLocale().toString()));
if (oWeekConfigurationValues) {
iFirstDayOfWeek = oWeekConfigurationValues.firstDayOfWeek;
} else {
var oLocaleData = this._getCoreLocaleData();
iFirstDayOfWeek = oLocaleData.getFirstDayOfWeek();
}
}
// determine weekday of first day in month
oFirstDay = new CalendarDate(oCalStartDate);
oFirstDay.setDate(1);
iDaysOldMonth = oFirstDay.getDay() - iFirstDayOfWeek;
if (iDaysOldMonth < 0) {
iDaysOldMonth = 7 + iDaysOldMonth;
}
if (iDaysOldMonth > 0) {
// determine first day for display
oFirstDay.setDate(1 - iDaysOldMonth);
}
oDay = new CalendarDate(oFirstDay);
for (var i = 0; i < this._getColumns() * this._getRows(); i++) {
oCalDate = new CalendarDate(oDay);
aVisibleDays.push(oCalDate);
oDay.setDate(oDay.getDate() + 1);
}
return aVisibleDays;
};
SinglePlanningCalendarMonthGrid.prototype._getAppointmentsToRender = function() {
return this._oAppointmentsToRender;
};
SinglePlanningCalendarMonthGrid.prototype._calculateAppointmentsNodes = function(oStartDate) {
var aVisibleDays = this._getVisibleDays(oStartDate),
oFirstVisibleDay = aVisibleDays[0],
oLastVisibleDay = aVisibleDays[aVisibleDays.length - 1],
// We do not need appointments without start and end dates
aApps = this.getAppointments().filter(function(app) {
var bValid = app.getStartDate() && app.getEndDate();
if (!bValid) {
Log.warning("Appointment " + app.getId() + " has no start or no end date. It is ignored.");
}
return bValid;
// Map to a structure ready for calculations
}).map(function(app) {
var oStart = CalendarDate.fromLocalJSDate(app.getStartDate()),
oEnd = CalendarDate.fromLocalJSDate(app.getEndDate());
return {
data: app,
start: oStart,
end: oEnd,
len: CalendarUtils._daysBetween(oEnd, oStart)
};
// Get only the visible appointments
}).filter(function(app) {
return CalendarUtils._isBetween(app.start, oFirstVisibleDay, oLastVisibleDay, true) // app starts in the current view port
|| CalendarUtils._isBetween(app.end, oFirstVisibleDay, oLastVisibleDay, true) // app ends in the current view port
|| (CalendarUtils._isBetween(oFirstVisibleDay, app.start, oLastVisibleDay, true) // app starts before the view port...
&& CalendarUtils._isBetween(oLastVisibleDay, oFirstVisibleDay, app.end,true)); // ...and ends after the view port
// Sort by start date
}).sort(function compare(a, b) {
return a.start.valueOf() - b.start.valueOf();
}),
// Array of taken levels per visible day
aVisibleDaysLevels = [],
oApp,
iAppointmentStartIndex,
iAppointmentEndIndex,
iFirstFreeIndex,
i,
j,
k;
for (i = 0; i < aVisibleDays.length; i++) {
aVisibleDaysLevels.push([]);
}
// Each appointment gets its width and level
for (i = 0; i < aApps.length; i++) {
oApp = aApps[i];
iAppointmentStartIndex = CalendarUtils._daysBetween(oApp.start, aVisibleDays[0]);
iAppointmentEndIndex = iAppointmentStartIndex + oApp.len;
// If appointment is out of bounds, set it in bounds
iAppointmentStartIndex = iAppointmentStartIndex > 0 ? iAppointmentStartIndex : 0;
iAppointmentEndIndex = iAppointmentEndIndex < aVisibleDays.length ? iAppointmentEndIndex : aVisibleDays.length - 1;
oApp.width = oApp.len + 1;
// Find the first level that is not taken for the start date of the appointment
iFirstFreeIndex = aVisibleDaysLevels[iAppointmentStartIndex].indexOf(true);
if (iFirstFreeIndex === -1) {
iFirstFreeIndex = aVisibleDaysLevels[iAppointmentStartIndex].length;
}
// Rendered position of appointment in day
oApp.level = iFirstFreeIndex;
// Adjust the taken levels for all days of the current appointment
for (j = iAppointmentStartIndex; j <= iAppointmentEndIndex; j++) {
aVisibleDaysLevels[j][iFirstFreeIndex] = false;
for (k = 0; k < iFirstFreeIndex; k++) {
if (aVisibleDaysLevels[j][k] === undefined) {
aVisibleDaysLevels[j][k] = true;
}
}
}
}
this._aAppsLevelsPerDay = aVisibleDaysLevels;
return aApps;
};
SinglePlanningCalendarMonthGrid.prototype._getMoreCountPerCell = function(iCellIndex) {
var aLevelsForADay = this._aAppsLevelsPerDay[iCellIndex];
var iMaxAppointmentsRendered = this._getMaxAppointments();
var iMoreCount = 0;
if (aLevelsForADay.length < iMaxAppointmentsRendered) {
return 0;
}
// Count the number of hidden appointments
for (var i = iMaxAppointmentsRendered - 1; i < aLevelsForADay.length; i++){
if (!aLevelsForADay[i]) {
iMoreCount++;
}
}
return iMoreCount;
};
SinglePlanningCalendarMonthGrid.prototype._configureAppointmentsDragAndDrop = function() {
this.addDragDropConfig(new DragDropInfo({
sourceAggregation: "appointments",
targetAggregation: "_appsPlaceholders",
dragStart: function(oEvent) {
if (!this.getEnableAppointmentsDragAndDrop()) {
oEvent.preventDefault();
return false;
}
var fnHandleAppsOverlay = function() {
var $Overlay = jQuery(".sapMSinglePCOverlay");
setTimeout(function() {
$Overlay.addClass("sapMSinglePCOverlayDragging");
});
jQuery(document).one("dragend", function() {
$Overlay.removeClass("sapMSinglePCOverlayDragging");
});
};
fnHandleAppsOverlay();
}.bind(this),
dragEnter: function(oEvent) {
var oDragSession = oEvent.getParameter("dragSession"),
fnAlignIndicator = function() {
var $Indicator = jQuery(oDragSession.getIndicator());
$Indicator.css("min-height", oDragSession.getDropControl().$().outerHeight());
$Indicator.css("min-width", oDragSession.getDropControl().$().outerWidth());
};
if (!oDragSession.getIndicator()) {
setTimeout(fnAlignIndicator, 0);
} else {
fnAlignIndicator();
}
},
drop: function(oEvent) {
var oDragSession = oEvent.getParameter("dragSession"),
oAppointment = oDragSession.getDragControl(),
oPlaceholder = oDragSession.getDropControl(),
oCellCalStartDate = oPlaceholder.getDate(),
oAppCalStartDate = CalendarDate.fromLocalJSDate(oAppointment.getStartDate()),
oAppCalEndDate = CalendarDate.fromLocalJSDate(oAppointment.getEndDate()),
iOffset = CalendarUtils._daysBetween(oCellCalStartDate, oAppCalStartDate),
oStartDate = new CalendarDate(oAppCalStartDate),
oEndDate = new CalendarDate(oAppCalEndDate),
oBrowserEvent = oEvent.getParameter("browserEvent"),
bCopy = (oBrowserEvent.metaKey || oBrowserEvent.ctrlKey);
oStartDate.setDate(oStartDate.getDate() + iOffset);
oEndDate.setDate(oEndDate.getDate() + iOffset);
this.$().find(".sapMSinglePCOverlay").removeClass("sapMSinglePCOverlayDragging");
if (oAppCalStartDate.valueOf() === oCellCalStartDate.valueOf()) {
return;
}
this.fireAppointmentDrop({
appointment: oAppointment,
startDate: oStartDate.toLocalJSDate(),
endDate: oEndDate.toLocalJSDate(),
copy: bCopy
});
}.bind(this)
}));
};
SinglePlanningCalendarMonthGrid.prototype._initItemNavigation = function() {
// Collect the dom references of the items
var oRootRef = this.getDomRef();
this._aGridCells = this.$().find(".sapMSPCMonthDay").toArray();
// Initialize the delegate and apply it to the control (only once)
if (!this._oItemNavigation) {
this._oItemNavigation = new ItemNavigation();
this.addDelegate(this._oItemNavigation);
this._oItemNavigation.attachEvent(ItemNavigation.Events.BorderReached, this._itemNavigationBorderReached, this);
}
// After each rendering the delegate needs to be initialized as well
// Set the root dom node that surrounds the items
this._oItemNavigation.setRootDomRef(oRootRef);
// Set the array of dom nodes representing the items
this._oItemNavigation.setItemDomRefs(this._aGridCells);
// Turn off the cycling
this._oItemNavigation.setCycling(false);
//this way we do not hijack the browser back/forward navigation
this._oItemNavigation.setDisabledModifiers({
sapnext: ["alt", "meta"],
sapprevious: ["alt", "meta"],
saphome: ["alt", "meta"],
sapend: ["meta"]
});
// explicitly setting table mode
this._oItemNavigation.setTableMode(false).setColumns(this._getColumns(), true);
// Set the page size
this._oItemNavigation.setPageSize(this._aGridCells.length);
};
SinglePlanningCalendarMonthGrid.prototype._itemNavigationBorderReached = function(oEvent) {
var oGridCell,
iFocusedDateTimestamp,
oItemNavigationEvent = oEvent.getParameter("event"),
iOffset;
if (oItemNavigationEvent.target.classList.contains("sapMSPCMonthDay")) {
oGridCell = oItemNavigationEvent.target;
iFocusedDateTimestamp = parseInt(oGridCell.getAttribute("sap-ui-date"));
switch (oItemNavigationEvent.keyCode) {
case KeyCodes.ARROW_LEFT:
iOffset = -1;
break;
case KeyCodes.ARROW_UP:
iOffset = -this._getColumns();
break;
case KeyCodes.ARROW_RIGHT:
iOffset = 1;
break;
case KeyCodes.ARROW_DOWN:
iOffset = this._getColumns();
break;
default:
break;
}
this.fireEvent("borderReached", {
startDate: iFocusedDateTimestamp,
offset: iOffset
});
}
};
SinglePlanningCalendarMonthGrid.prototype._createAppointmentsDndPlaceholders = function(oStartDate) {
var aDays = this._getVisibleDays(oStartDate);
this.destroyAggregation("_appsPlaceholders");
for (var i = 0; i < aDays.length; i++) {
var oPlaceholder = new IntervalPlaceholder({
date: aDays[i]
});
this.addAggregation("_appsPlaceholders", oPlaceholder, true);
}
};
var IntervalPlaceholder = Control.extend("sap.m.SinglePlanningCalendarMonthGrid._internal.IntervalPlaceholder", {
metadata: {
properties: {
date: { type: "object", group: "Data" }
}
},
renderer: {
apiVersion: 2,
render: function(oRm, oControl) {
oRm.openStart("div", oControl)
.class("sapMSinglePCPlaceholder")
.openEnd()
.close("div");
}
}
});
SinglePlanningCalendarMonthGrid.prototype._getCellStartInfo = function(oStartDate) {
var sStartTime = Core
.getLibraryResourceBundle("sap.ui.unified")
.getText("CALENDAR_START_TIME");
return sStartTime + ": " + this._oFormatAriaFullDayCell.format(oStartDate) + "; ";
};
SinglePlanningCalendarMonthGrid.prototype._getAppointmentAnnouncementInfo = function(oAppointment) {
var oUnifiedRB = Core.getLibraryResourceBundle("sap.ui.unified"),
sStartTime = oUnifiedRB.getText("CALENDAR_START_TIME"),
sEndTime = oUnifiedRB.getText("CALENDAR_END_TIME"),
sFormattedStartDate = this._oFormatAriaApp.format(oAppointment.getStartDate()),
sFormattedEndDate = this._oFormatAriaApp.format(oAppointment.getEndDate()),
sAppInfo = sStartTime + ": " + sFormattedStartDate + "; " + sEndTime + ": " + sFormattedEndDate;
return sAppInfo + "; " + PlanningCalendarLegend.findLegendItemForItem(Core.byId(this._sLegendId), oAppointment);
};
SinglePlanningCalendarMonthGrid.prototype._getMaxAppointments = function() {
return this._isCompact() ? 4 : 3;
};
SinglePlanningCalendarMonthGrid.prototype._getDensitySizes = function() {
return this._isCompact() ? {
appHeight: APP_HEIGHT_COMPACT,
cellHeaderHeight: CELL_HEADER_HEIGHT_COMPACT
} : {
appHeight: APP_HEIGHT_COZY,
cellHeaderHeight: CELL_HEADER_HEIGHT_COZY
};
};
SinglePlanningCalendarMonthGrid.prototype._isCompact = function() {
var oDomRef = this.getDomRef()
|| (this.getParent() && this.getParent().getDomRef && this.getParent().getDomRef()
|| (this.getParent() && this.getParent().getRootNode && this.getParent().getRootNode()));
while (oDomRef && oDomRef.classList) {
if (oDomRef.classList.contains("sapUiSizeCompact")) {
return true;
}
oDomRef = oDomRef.parentNode;
}
return false;
};
SinglePlanningCalendarMonthGrid.prototype._getSpecialDates = function(){
var specialDates = this.getSpecialDates();
for (var i = 0; i < specialDates.length; i++) {
var bNeedsSecondTypeAdding = specialDates[i].getSecondaryType() === unifiedLibrary.CalendarDayType.NonWorking
&& specialDates[i].getType() !== unifiedLibrary.CalendarDayType.NonWorking;
if (bNeedsSecondTypeAdding) {
var newSpecialDate = new DateTypeRange();
newSpecialDate.setType(unifiedLibrary.CalendarDayType.NonWorking);
newSpecialDate.setStartDate(specialDates[i].getStartDate());
if (specialDates[i].getEndDate()) {
newSpecialDate.setEndDate(specialDates[i].getEndDate());
}
specialDates.push(newSpecialDate);
}
}
return specialDates;
};
SinglePlanningCalendarMonthGrid.prototype.applyFocusInfo = function() {
// directly focus appointment part, if there is any selected
this._sSelectedAppointment && this._sSelectedAppointment.focus();
return this;
};
return SinglePlanningCalendarMonthGrid;
});