@syncfusion/ej2-gantt
Version:
Essential JS 2 Gantt Component
1,004 lines (1,000 loc) • 2.66 MB
JavaScript
import { isNullOrUndefined, extend, getValue, setValue, EventHandler, formatUnit, createElement, removeClass, addClass, closest, Browser, append, merge, remove, deleteObject, initializeCSPTemplate, ChildProperty, Property, Collection, Complex, SanitizeHtmlHelper, compile, isObject, Component, Internationalization, L10n, KeyboardEvents, isUndefined, isObjectArray, Event, NotifyPropertyChanges, classList } from '@syncfusion/ej2-base';
import { Tooltip as Tooltip$1, createSpinner, showSpinner, hideSpinner, Dialog } from '@syncfusion/ej2-popups';
import { DataManager, ODataAdaptor, ODataV4Adaptor, WebApiAdaptor, WebMethodAdaptor, CacheAdaptor, RemoteSaveAdaptor, UrlAdaptor, Query, DataUtil, Deferred } from '@syncfusion/ej2-data';
import { click, Predicate, getActualProperties, getObject, ExcelExport as ExcelExport$1, PdfExport as PdfExport$1, Print, Search, Toolbar as Toolbar$1, RowDD as RowDD$1, DetailRow, Reorder as Reorder$1, Resize as Resize$1, ContextMenu as ContextMenu$1, ColumnMenu as ColumnMenu$1, ColumnChooser, Aggregate, Edit as Edit$2, Group, Page, Sort as Sort$1, Filter as Filter$1, Grid, ForeignKey, filterAfterOpen, getFilterMenuPostion, getCustomDateFormat, setCssInGridPopUp, parentsUntil as parentsUntil$1, ValueFormatter, getForeignData } from '@syncfusion/ej2-grids';
import { TreeGrid, Edit as Edit$1, VirtualScroll as VirtualScroll$1, Toolbar as Toolbar$2, Selection as Selection$2, RowDD as RowDD$2, Reorder as Reorder$2, Resize as Resize$2, Aggregate as Aggregate$1, Page as Page$1, Sort as Sort$2, Filter as Filter$2, ContextMenu as ContextMenu$2, ExcelExport as ExcelExport$2, ColumnMenu as ColumnMenu$2 } from '@syncfusion/ej2-treegrid';
import { AutoComplete, DropDownList, ComboBox, MultiSelect, CheckBoxSelection } from '@syncfusion/ej2-dropdowns';
import { SvgRenderer } from '@syncfusion/ej2-svg-base';
import { Splitter as Splitter$1 } from '@syncfusion/ej2-layouts';
import { Tab, Toolbar as Toolbar$4, ContextMenu as ContextMenu$3 } from '@syncfusion/ej2-navigations';
import { RichTextEditor, MarkdownEditor, FormatPainter, FileManager, EmojiPicker, Table, Image, Toolbar as Toolbar$3, Link, HtmlEditor, QuickToolbar, Count } from '@syncfusion/ej2-richtexteditor';
import { MaskedTextBox, NumericTextBox, TextBox, FormValidator } from '@syncfusion/ej2-inputs';
import { CheckBox, RadioButton } from '@syncfusion/ej2-buttons';
import { DatePicker, DateTimePicker } from '@syncfusion/ej2-calendars';
import { PdfDashStyle, PdfColor, PdfFontFamily, PdfPen, PdfStringFormat, PdfBorderOverlapStyle, ElementLayouter, RectangleF, PdfLayoutBreakType, PointF, SizeF, PdfLayoutType, RowLayoutResult, PdfLayoutResult, PdfLayoutFormat, PdfLayoutElement, PdfStringLayouter, PdfStandardFont, PdfFontStyle, PdfSolidBrush, PdfImage, PdfBitmap, PdfTextWebLink, PdfTextAlignment, PdfLineCap, PdfVerticalAlignment, PdfPageTemplateElement, PdfPageNumberField, PdfPageCountField, PdfCompositeField, PdfLinearGradientBrush, PdfPath, PdfBrushes, PdfWordWrapType, PdfPage, PdfDocument, PdfPageSettings, PdfPageOrientation } from '@syncfusion/ej2-pdf-export';
/**
* Common methods used in Gantt
*/
/**
* @param {Element} elem .
* @param {string} selector .
* @param {boolean} isID .
* @returns {Element} .
* @hidden
*/
function parentsUntil(elem, selector, isID) {
let parent = elem;
while (parent) {
if (isID ? parent.id === selector : parent.classList.contains(selector)) {
break;
}
parent = parent.parentElement;
}
return parent;
}
/**
* @param {ITaskData} ganttProp .
* @returns {boolean} .
* @hidden
*/
function isScheduledTask(ganttProp) {
if (isNullOrUndefined(ganttProp.startDate) && isNullOrUndefined(ganttProp.endDate) &&
isNullOrUndefined(ganttProp.duration)) {
return null;
}
else if (isNullOrUndefined(ganttProp.startDate) || isNullOrUndefined(ganttProp.endDate) ||
isNullOrUndefined(ganttProp.duration)) {
return false;
}
else {
return true;
}
}
/**
* @param {Gantt} parent .
* @returns {boolean} .
* @hidden
*/
function isCountRequired(parent) {
if (parent.dataSource && !(parent.dataSource instanceof DataManager) &&
'result' in parent.dataSource) {
return true;
}
return false;
}
/**
* @param {object} obj .
* @returns {object} .
* @hidden
*/
function getSwapKey(obj) {
const temp = {};
for (const key of Object.keys(obj)) {
temp[obj[key]] = key;
}
return temp;
}
/**
* @param {object} obj .
* @returns {boolean} .
* @hidden
*/
function isEmptyObject(obj) {
if (isNullOrUndefined(obj) || typeof obj !== 'object') {
return false;
}
return Object.keys(obj).length === 0;
}
/**
* @param {Date} date .
* @returns {number} .
* @hidden
*/
function getUniversalTime(date) {
const year = date.getFullYear();
const month = date.getMonth();
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const milliseconds = date.getMilliseconds();
return Date.UTC(year, month, day, hours, minutes, seconds, milliseconds);
}
/**
* @param {object} dataSource .
* @returns {boolean} .
* @hidden
*/
function isRemoteData(dataSource) {
if (dataSource instanceof DataManager) {
const adaptor = dataSource.adaptor;
return (adaptor instanceof ODataAdaptor || (adaptor instanceof ODataV4Adaptor) ||
(adaptor instanceof WebApiAdaptor) || (adaptor instanceof WebMethodAdaptor) ||
(adaptor instanceof CacheAdaptor) || (adaptor instanceof RemoteSaveAdaptor) ||
adaptor instanceof UrlAdaptor);
}
return false;
}
/**
* @param {IGanttData[]} records .
* @param {boolean} isNotExtend .
* @param {ITaskAddedEventArgs} eventArgs .
* @param {Gantt} parent .
* @returns {object[]} .
* @hidden
*/
function getTaskData(records, isNotExtend, eventArgs, parent) {
if (eventArgs) {
let result;
for (let i = 0; i < records.length; i++) {
const data = isNotExtend ?
(records[parseInt(i.toString(), 10)].taskData) : extend({}, records[parseInt(i.toString(), 10)].taskData, {}, true);
result = (data);
}
return result;
}
else {
const result = [];
for (let i = 0; i < records.length; i++) {
if (!isNullOrUndefined(parent) && parent.timezone) {
updateDates(records[i], parent);
}
const data = isNotExtend ? (records[parseInt(i.toString(), 10)].taskData) :
extend({}, records[parseInt(i.toString(), 10)].taskData, {}, true);
result.push(data);
}
return result;
}
}
/**
* @param {IGanttData} record .
* @param {Gantt} parent .
* @returns {null} .
* @hidden
*/
function updateDates(record, parent) {
// let startDate: Date = (record as IGanttData).taskData[parent.taskFields.startDate];
if (record && !isNullOrUndefined(record.ganttProperties)) {
const taskData = record.taskData;
const ganttProps = record.ganttProperties;
// Update start date
taskData[parent.taskFields.startDate] = parent.dateValidationModule.remove(ganttProps.startDate, parent.timezone);
// Update end date if defined
if (parent.taskFields.endDate !== null) {
taskData[parent.taskFields.endDate] = parent.dateValidationModule.remove(ganttProps.endDate, parent.timezone);
}
// Update baseline dates if defined
if (parent.taskFields.baselineStartDate || parent.taskFields.baselineEndDate) {
taskData[parent.taskFields.baselineStartDate] = parent.dateValidationModule.remove(ganttProps.baselineStartDate, parent.timezone);
taskData[parent.taskFields.baselineEndDate] = parent.dateValidationModule.remove(ganttProps.baselineEndDate, parent.timezone);
}
// Update custom date columns
parent.editModule['processCustomDateColumns'](record, taskData, parent, 'remove');
}
return null;
}
/**
* @param {string} str .
* @param {string[]} args .
* @returns {string} .
* @hidden
*/
function formatString(str, args) {
let regx;
for (let i = 0; i < args.length; i++) {
// eslint-disable-next-line security/detect-non-literal-regexp
regx = new RegExp('\\{' + (i) + '\\}', 'gm');
str = str.replace(regx, args[i].toString());
}
return str;
}
/**
* @param {any} value .
* @param {string} key1 .
* @param {any} collection .
* @param {string} key2
* @returns {number} .
* @hidden
*/
/* eslint-disable-next-line */
function getIndex(value, key1, collection, key2) {
let index = -1;
for (let i = 0; i < collection.length; i++) {
if (getValue(key1, collection[i]) === getValue(key1, value) && isNullOrUndefined(key2)
|| (!isNullOrUndefined(key2) && getValue(key1, collection[i]) === getValue(key1, value)
&& getValue(key2, collection[i]) === getValue(key2, value))) {
index = i;
break;
}
}
return index;
}
/**
* @param {number} value .
* @returns {number} .
* @hidden
*/
function pixelToPoint(value) {
return (value * 76) / 92;
}
/**
* @param {number} value .
* @returns {number} .
* @hidden
*/
function pointToPixel(value) {
return (value * 92) / 76;
}
let uid = 0;
/**
* @returns {number} .
* @hidden
*/
function getUid() {
return uid++;
}
/**
* Defines the constraint types used for task scheduling.
*
* Available values:
* - `0`: AsSoonAsPossible (ASAP) – Task starts as early as possible. Default for auto-scheduled tasks.
* - `1`: AsLateAsPossible (ALAP) – Task finishes as late as possible without delaying dependents.
* - `2`: MustStartOn (MSO) – Task must start on the specified date.
* - `3`: MustFinishOn (MFO) – Task must finish on the specified date.
* - `4`: StartNoEarlierThan (SNET) – Task cannot start before the specified date.
* - `5`: StartNoLaterThan (SNLT) – Task must start on or before the specified date.
* - `6`: FinishNoEarlierThan (FNET) – Task cannot finish before the specified date.
* - `7`: FinishNoLaterThan (FNLT) – Task must finish on or before the specified date.
*/
var ConstraintType;
(function (ConstraintType) {
ConstraintType[ConstraintType["AsSoonAsPossible"] = 0] = "AsSoonAsPossible";
ConstraintType[ConstraintType["AsLateAsPossible"] = 1] = "AsLateAsPossible";
ConstraintType[ConstraintType["MustStartOn"] = 2] = "MustStartOn";
ConstraintType[ConstraintType["MustFinishOn"] = 3] = "MustFinishOn";
ConstraintType[ConstraintType["StartNoEarlierThan"] = 4] = "StartNoEarlierThan";
ConstraintType[ConstraintType["StartNoLaterThan"] = 5] = "StartNoLaterThan";
ConstraintType[ConstraintType["FinishNoEarlierThan"] = 6] = "FinishNoEarlierThan";
ConstraintType[ConstraintType["FinishNoLaterThan"] = 7] = "FinishNoLaterThan";
})(ConstraintType || (ConstraintType = {}));
/**
* Date processor is used to handle date of task data.
*/
class DateProcessor {
constructor(parent) {
this.fromSegments = false;
this.mondayTimeRangeLength = 0;
this.tuesdayTimeRangeLength = 0;
this.wednesdayTimeRangeLength = 0;
this.thursdayTimeRangeLength = 0;
this.fridayTimeRangeLength = 0;
this.saturdayTimeRangeLength = 0;
this.sundayTimeRangeLength = 0;
this.parent = parent;
}
/**
* @param {ITaskData} ganttProp .
* @returns {boolean} .
*/
isValidateNonWorkDays(ganttProp) {
return (!isNullOrUndefined(ganttProp) && ganttProp.isAutoSchedule &&
(!this.parent.includeWeekend || this.parent.totalHolidayDates.length > 0)) ||
(isNullOrUndefined(ganttProp) && (!this.parent.includeWeekend || this.parent.totalHolidayDates.length > 0));
}
/**
* Method to convert given date value as valid start date
*
* @param {Date} date .
* @param {ITaskData} ganttProp - The Gantt properties related to the task.
* @param {boolean} validateAsMilestone - Indicates whether the date should be validated as a milestone.
* @param {boolean} isLoad - A flag indicating if the method is called during the loading process.
* @param {boolean} isBaseline - Indicates whether the calculation is specific to baseline dates.
* @returns {Date} .
* @private
*/
checkStartDate(date, ganttProp, validateAsMilestone, isLoad, isBaseline) {
if (isNullOrUndefined(date)) {
return null;
}
const currentDay = new Date(date.getTime());
let dayStartTime = this.parent['getCurrentDayStartTime'](currentDay);
let dayEndTime = this.parent['getCurrentDayEndTime'](currentDay);
let cloneStartDate = new Date(date.getTime());
const hour = this.getSecondsInDecimal(cloneStartDate);
validateAsMilestone = isNullOrUndefined(validateAsMilestone) ? !isNullOrUndefined(ganttProp) ?
ganttProp.isMilestone : false : validateAsMilestone;
if (hour < dayStartTime && (!validateAsMilestone || isLoad)) {
this.setTime(dayStartTime, cloneStartDate);
}
else if (hour < dayStartTime && validateAsMilestone) {
this.setTime(dayStartTime, cloneStartDate);
}
else if ((hour === dayEndTime && (!ganttProp || !validateAsMilestone)) || hour > dayEndTime) {
cloneStartDate.setDate(cloneStartDate.getDate() + 1);
dayStartTime = this.parent['getCurrentDayStartTime'](cloneStartDate);
this.setTime(dayStartTime, cloneStartDate);
}
else if (hour > dayStartTime && hour < dayEndTime) {
let workingRange = this.parent.workingTimeRanges;
if (this.parent.weekWorkingTime.length > 0) {
workingRange = this.parent['getWorkingRange'](cloneStartDate);
}
for (let index = 0; index < workingRange.length; index++) {
const value = workingRange[index];
if (hour >= value.to && (workingRange[index + 1] &&
hour < workingRange[index + 1].from)) {
// milestone can fall at end any interval time
if ((hour === value.to && (!ganttProp || !validateAsMilestone)) || hour !== value.to) {
this.setTime(workingRange[index + 1].from, cloneStartDate);
}
break;
}
}
}
let tStartDate;
if (this.parent.autoCalculateDateScheduling && !(this.parent.isLoad && this.parent.treeGrid.loadChildOnDemand &&
this.parent.taskFields.hasChildMapping)) {
do {
tStartDate = new Date(cloneStartDate.getTime());
const holidayLength = this.parent.totalHolidayDates.length;
// check holidays and weekends
if (this.isValidateNonWorkDays(ganttProp)) {
dayStartTime = this.parent['getCurrentDayStartTime'](tStartDate);
if (ganttProp) {
if (!isBaseline) {
dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp.endDate ? ganttProp.isAutoSchedule ? ganttProp.endDate : ganttProp.autoEndDate : tStartDate);
}
else {
dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp.baselineEndDate ? ganttProp.baselineEndDate : tStartDate);
}
}
let startTime = (!validateAsMilestone || isLoad) ? dayStartTime : dayEndTime;
if (!this.parent.includeWeekend && !isBaseline) {
const tempDate = new Date(cloneStartDate.getTime());
cloneStartDate = this.getNextWorkingDay(cloneStartDate);
startTime = this.parent['getCurrentDayStartTime'](cloneStartDate);
if (tempDate.getTime() !== cloneStartDate.getTime() && !validateAsMilestone) {
this.setTime(startTime, cloneStartDate);
}
}
if (!isBaseline) {
for (let count = 0; count < holidayLength; count++) {
const holidayFrom = this.getDateFromFormat(new Date(this.parent.totalHolidayDates[count]));
const holidayTo = new Date(holidayFrom.getTime());
holidayFrom.setHours(0, 0, 0, 0);
holidayTo.setHours(23, 59, 59, 59);
if (cloneStartDate.getTime() >= holidayFrom.getTime() && cloneStartDate.getTime() < holidayTo.getTime()) {
cloneStartDate.setDate(cloneStartDate.getDate() + 1);
startTime = this.parent['getCurrentDayStartTime'](cloneStartDate);
this.setTime(startTime, cloneStartDate);
}
}
}
}
} while (tStartDate.getTime() !== cloneStartDate.getTime());
return new Date(cloneStartDate.getTime());
}
else {
return new Date(cloneStartDate.getTime());
}
}
getAdjustedStartDate(constraintDate, ganttProp) {
const adjustedDate = new Date(constraintDate);
adjustedDate.setDate(adjustedDate.getDate() + 1);
const checkedEnd = this.parent.dateValidationModule.checkEndDate(adjustedDate);
return this.parent.dateValidationModule.getStartDate(checkedEnd, ganttProp.duration, ganttProp.durationUnit, ganttProp);
}
getDateByConstraint(ganttData, date, validPredecessor) {
const ganttProp = ganttData['ganttProperties'] ? ganttData['ganttProperties'] : ganttData;
let constraintDate = new Date(ganttProp.constraintDate);
const isLoad = this.parent.isLoad;
if (isNullOrUndefined(validPredecessor)) {
validPredecessor = true;
}
if ((!constraintDate || !date) && validPredecessor) {
return null;
}
if (this.parent.editModule && this.parent.editModule.dialogModule && this.parent.editModule.dialogModule['dialogConstraintDate']) {
constraintDate = new Date(this.parent.editModule.dialogModule['dialogConstraintDate']);
}
else {
constraintDate = this.parent['assignTimeToDate'](constraintDate, this.parent['getCurrentDayStartTime'](constraintDate));
}
let parentRecord = null;
const record = this.parent.getRecordByID(ganttProp.taskId);
if (record && record.parentItem) {
parentRecord = record.parentItem;
}
switch (ganttProp.constraintType) {
case ConstraintType.AsSoonAsPossible:
if (isLoad) {
return date;
}
else {
ganttProp.constraintDate = null;
if (ganttProp.predecessor && ganttProp.predecessor.length > 0) {
return this.parent['assignTimeToDate'](date, this.parent['getCurrentDayStartTime'](date));
}
else if (parentRecord) {
return this.parent.getRecordByID(parentRecord.taskId).ganttProperties.startDate;
}
else {
return this.parent.dateValidationModule.checkStartDate(new Date(this.parent.projectStartDate));
}
}
case ConstraintType.AsLateAsPossible:
if (isLoad) {
return date;
}
else {
if (parentRecord) {
const checkedEnd = this.parent.dateValidationModule.checkEndDate(this.parent.getRecordByID(parentRecord.taskId).ganttProperties.endDate);
const start = this.parent.dateValidationModule.getStartDate(checkedEnd, ganttProp.duration, ganttProp.durationUnit, ganttProp);
return start;
}
else {
const checkedEnd = this.parent.dateValidationModule.checkEndDate(new Date(this.parent.projectEndDate));
const start = this.parent.dateValidationModule.getStartDate(checkedEnd, ganttProp.duration, ganttProp.durationUnit, ganttProp);
return start;
}
}
case ConstraintType.MustStartOn: {
const isViolation = constraintDate.getTime() !== ganttProp.startDate.getTime();
let constraintValue;
if (this.parent.editModule && this.parent.editModule.dialogModule) {
constraintValue = this.parent.editModule.dialogModule['dialogConstraintValue'];
}
const isConstraintTypeRestricted = isNullOrUndefined(constraintValue) ||
constraintValue === 2 ||
constraintValue === 3;
if (isViolation && !isLoad && isConstraintTypeRestricted) {
this.parent.constraintViolationType = 'MustStartOn';
}
constraintValue = null;
if (isLoad) {
return this.parent.dateValidationModule.checkStartDate(new Date(constraintDate));
}
else {
return this.parent['assignTimeToDate'](date, this.parent['getCurrentDayStartTime'](date));
}
}
case ConstraintType.MustFinishOn: {
const isViolation = constraintDate.getTime() !== ganttProp.endDate.getTime();
if (isViolation && !isLoad) {
this.parent.constraintViolationType = 'MustFinishOn';
}
return this.getAdjustedStartDate(constraintDate, ganttProp);
}
case ConstraintType.StartNoEarlierThan: {
if (constraintDate.getTime() < date.getTime() || !isLoad) {
return this.parent['assignTimeToDate'](date, this.parent['getCurrentDayStartTime'](date));
}
return this.parent.dateValidationModule.checkStartDate(new Date(constraintDate));
}
case ConstraintType.StartNoLaterThan: {
const isViolation = constraintDate.getTime() < date.getTime();
if (isViolation && !isLoad) {
this.parent.constraintViolationType = 'StartNoLaterThan';
}
if (!isLoad) {
const cellEditModule = this.parent.editModule && this.parent.editModule.cellEditModule;
const constraintField = this.parent.taskFields && this.parent.taskFields.constraintDate;
if (cellEditModule &&
cellEditModule.isCellEdit &&
cellEditModule.editedColumn &&
isViolation &&
cellEditModule.editedColumn.field === constraintField) {
return constraintDate;
}
if (this.parent.editModule && this.parent.editModule.dialogModule && isViolation && this.parent.editModule.dialogModule['dialogConstraintDate']) {
return constraintDate;
}
return this.parent['assignTimeToDate'](date, this.parent['getCurrentDayStartTime'](date));
}
if (isViolation && isLoad) {
return this.parent.dateValidationModule.checkStartDate(new Date(constraintDate));
}
else {
return date;
}
}
case ConstraintType.FinishNoEarlierThan: {
const endDate = this.getEndDate(date, ganttProp.duration, ganttProp.durationUnit, ganttProp, false, false);
if (endDate.getTime() < constraintDate.getTime()) {
return this.getAdjustedStartDate(constraintDate, ganttProp);
}
else {
return date;
}
}
case ConstraintType.FinishNoLaterThan: {
const endDate = this.getEndDate(date, ganttProp.duration, ganttProp.durationUnit, ganttProp, false, false);
const isViolation = constraintDate.getTime() < endDate.getTime();
if (isViolation && !isLoad) {
this.parent.constraintViolationType = 'FinishNoLaterThan';
}
if (!isLoad) {
if (isViolation) {
return this.getAdjustedStartDate(constraintDate, ganttProp);
}
return this.parent['assignTimeToDate'](date, this.parent['getCurrentDayEndTime'](date));
}
if (isViolation) {
return this.getAdjustedStartDate(constraintDate, ganttProp);
}
else {
return date;
}
}
default:
return date;
}
}
/**
* To update given date value to valid end date
*
* @param {Date} date .
* @param {ITaskData} ganttProp .
* @param {boolean} validateAsMilestone .
* @param {boolean} isBaseline - Indicates whether the calculation is specific to baseline dates.
* @returns {Date} .
* @private
*/
checkEndDate(date, ganttProp, validateAsMilestone, isBaseline) {
if (isNullOrUndefined(date)) {
return null;
}
let dayStartTime;
let dayEndTime;
if (this.parent.weekWorkingTime.length > 0) {
let currentDay = new Date(date.getTime());
if (!this.parent.includeWeekend && ganttProp && ganttProp.isAutoSchedule || (this.parent.editModule
&& this.parent.editModule.taskbarEditModule && this.parent.editModule.taskbarEditModule.taskBarEditRecord
&& !this.parent.editModule.taskbarEditModule.taskBarEditRecord.ganttProperties.isAutoSchedule)) {
currentDay = this.getNextWorkingDay(currentDay);
}
dayStartTime = this.parent['getStartTime'](currentDay);
dayEndTime = this.parent['getEndTime'](currentDay);
}
else {
dayStartTime = this.parent.defaultStartTime;
dayEndTime = this.parent.defaultEndTime;
}
let cloneEndDate = new Date(date.getTime());
const hour = this.getSecondsInDecimal(cloneEndDate);
if (hour > dayEndTime) {
this.setTime(dayEndTime, cloneEndDate);
}
else if (hour <= dayStartTime && dayEndTime !== 86400 && !validateAsMilestone) {
const taskfields = this.parent.taskFields;
if (this.parent.editModule && this.parent.editModule['editedRecord'] && (!this.parent.editModule['editedRecord'][taskfields.startDate] && this.parent.editModule['editedRecord'][taskfields.endDate])) {
cloneEndDate.setDate(cloneEndDate.getDate());
}
else {
cloneEndDate.setDate(cloneEndDate.getDate() - 1);
}
dayEndTime = this.parent['getCurrentDayEndTime'](cloneEndDate);
this.setTime(dayEndTime, cloneEndDate);
}
else if (hour > dayStartTime && hour < dayEndTime) {
for (let index = 0; index < this.parent.workingTimeRanges.length; index++) {
const value = this.parent.workingTimeRanges[index];
if (hour > value.to && (this.parent.workingTimeRanges[index + 1] &&
hour <= this.parent.workingTimeRanges[index + 1].from)) {
this.setTime(this.parent.workingTimeRanges[index].to, cloneEndDate);
break;
}
}
}
let tempCheckDate;
if (this.parent.autoCalculateDateScheduling && !(this.parent.isLoad && this.parent.treeGrid.loadChildOnDemand &&
this.parent.taskFields.hasChildMapping)) {
do {
tempCheckDate = new Date(cloneEndDate.getTime());
const holidayLength = this.parent.totalHolidayDates.length;
if (this.isValidateNonWorkDays(ganttProp) && !isBaseline) {
if (!this.parent.includeWeekend) {
const tempDate = new Date(cloneEndDate.getTime());
cloneEndDate = this.getPreviousWorkingDay(cloneEndDate);
dayEndTime = this.parent['getCurrentDayEndTime'](cloneEndDate);
if (tempDate.getTime() !== cloneEndDate.getTime()) {
this.setTime(dayEndTime, cloneEndDate);
}
}
for (let count = 0; count < holidayLength; count++) {
const holidayFrom = this.getDateFromFormat(new Date(this.parent.totalHolidayDates[count]));
const holidayTo = new Date(holidayFrom.getTime());
const tempHoliday = new Date(cloneEndDate.getTime());
tempHoliday.setMinutes(cloneEndDate.getMilliseconds() - 2);
holidayFrom.setHours(0, 0, 0, 0);
holidayTo.setHours(23, 59, 59, 59);
if (cloneEndDate.getTime() >= holidayFrom.getTime() && cloneEndDate.getTime() < holidayTo.getTime() ||
tempHoliday.getTime() >= holidayFrom.getTime() && tempHoliday.getTime() < holidayTo.getTime()) {
cloneEndDate.setDate(cloneEndDate.getDate() - 1);
dayEndTime = this.parent['getCurrentDayEndTime'](cloneEndDate);
if (!(cloneEndDate.getTime() === holidayFrom.getTime() && dayEndTime === 86400 &&
this.getSecondsInDecimal(cloneEndDate) === 0)) {
this.setTime(dayEndTime, cloneEndDate);
}
}
}
}
} while (tempCheckDate.getTime() !== cloneEndDate.getTime());
return new Date(cloneEndDate.getTime());
}
else {
if (!isNullOrUndefined(cloneEndDate) && this.parent.defaultEndTime !== 86400) {
dayEndTime = this.parent['getCurrentDayEndTime'](date);
this.setTime(dayEndTime, cloneEndDate);
}
return new Date(cloneEndDate.getTime());
}
}
/**
* To validate the baseline start date
*
* @param {Date} date .
* @param {ITaskData} ganttProp .
* @returns {Date} .
* @private
*/
checkBaselineStartDate(date, ganttProp) {
if (isNullOrUndefined(date)) {
return null;
}
else {
let dayStartTime = this.parent['getCurrentDayStartTime'](date);
const dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp ? ganttProp.endDate ? ganttProp.isAutoSchedule ? ganttProp.endDate : ganttProp.autoEndDate : date : date);
const cloneDate = new Date(date.getTime());
const hour = this.getSecondsInDecimal(cloneDate);
if (hour < dayStartTime) {
this.setTime(dayStartTime, cloneDate);
}
else if (hour > dayEndTime) {
cloneDate.setDate(cloneDate.getDate() + 1);
if (this.parent.weekWorkingTime.length > 0) {
dayStartTime = this.parent['getStartTime'](cloneDate);
}
else {
dayStartTime = this.parent.defaultStartTime;
}
this.setTime(dayStartTime, cloneDate);
}
else if (hour > dayStartTime && hour < dayEndTime) {
for (let i = 0; i < this.parent.workingTimeRanges.length; i++) {
const value = this.parent.workingTimeRanges[i];
if (hour > value.to && (this.parent.workingTimeRanges[i + 1] &&
hour < this.parent.workingTimeRanges[i + 1].from)) {
this.setTime(this.parent.workingTimeRanges[i + 1].from, cloneDate);
break;
}
}
}
return cloneDate;
}
}
/**
* To validate baseline end date
*
* @param {Date} date .
* @param {ITaskData} ganttProp .
* @returns {Date} .
* @private
*/
checkBaselineEndDate(date, ganttProp) {
if (isNullOrUndefined(date)) {
return null;
}
else {
let dayEndTime = this.parent['getCurrentDayEndTime'](date);
const dayStartTime = this.parent['getCurrentDayStartTime'](ganttProp ? ganttProp.startDate ? ganttProp.isAutoSchedule ? ganttProp.startDate : ganttProp.autoStartDate : date : date);
const cloneDate = new Date(date.getTime());
const hour = this.getSecondsInDecimal(cloneDate);
if (hour > dayEndTime) {
this.setTime(dayEndTime, cloneDate);
}
else if (hour < dayStartTime && !isNullOrUndefined(ganttProp) && !ganttProp.isMilestone) {
cloneDate.setDate(cloneDate.getDate() - 1);
dayEndTime = this.parent['getCurrentDayEndTime'](cloneDate);
this.setTime(dayEndTime, cloneDate);
}
else if (hour > dayStartTime && hour < dayEndTime) {
for (let i = 0; i < this.parent.workingTimeRanges.length; i++) {
const value = this.parent.workingTimeRanges[i];
if (hour > value.to && (this.parent.workingTimeRanges[i + 1] && hour <= this.parent.workingTimeRanges[i + 1].from)) {
this.setTime(this.parent.workingTimeRanges[i].to, cloneDate);
break;
}
}
}
if (ganttProp && ganttProp.baselineStartDate && cloneDate &&
ganttProp.baselineStartDate.getTime() > cloneDate.getTime()) {
cloneDate.setDate(cloneDate.getDate() + 1);
}
return cloneDate;
}
}
/**
* To calculate start date value from duration and end date
*
* @param {IGanttData} ganttData - Defines the gantt data.
* @param {boolean} isBaseline - Indicates whether the calculation is specific to baseline dates.
* @returns {void} .
* @private
*/
calculateStartDate(ganttData, isBaseline) {
const ganttProp = ganttData.ganttProperties;
let tempStartDate = null;
if (isBaseline) {
if (!isNullOrUndefined(ganttProp.baselineEndDate) && !isNullOrUndefined(ganttProp.baselineDuration)) {
tempStartDate = this.getStartDate(ganttProp.baselineEndDate, ganttProp.baselineDuration, ganttProp.durationUnit, ganttProp, undefined, true);
}
this.parent.setRecordValue('baselineStartDate', tempStartDate, ganttProp, true);
if (this.parent.taskFields.baselineStartDate) {
this.parent.dataOperation.updateMappingData(ganttData, 'baselineStartDate');
}
}
else {
if (!isNullOrUndefined(ganttProp.endDate) && !isNullOrUndefined(ganttProp.duration)) {
tempStartDate = this.getStartDate(ganttProp.endDate, ganttProp.duration, ganttProp.durationUnit, ganttProp);
}
this.parent.setRecordValue('startDate', tempStartDate, ganttProp, true);
if (this.parent.taskFields.startDate) {
this.parent.dataOperation.updateMappingData(ganttData, 'startDate');
}
}
}
/**
* Gets task field mappings based on baseline context.
*
* @param {boolean} isBaseline - Flag indicating if baseline fields should be used.
* @returns {{ startdateField: string, enddateField: string, durationField: string }} - Object containing field names for start date, end date, and duration.
*/
getFieldMappings(isBaseline) {
return {
startdateField: isBaseline ? 'baselineStartDate' : 'startDate',
enddateField: isBaseline ? 'baselineEndDate' : 'endDate',
durationField: isBaseline ? 'baselineDuration' : 'duration'
};
}
/**
*
* @param {IGanttData} ganttData - Defines the gantt data.
* @param {boolean} isBaseline - Indicates whether the calculation is specific to baseline dates.
* @returns {void} .
* @public
*/
/* eslint-disable */
calculateEndDate(ganttData, isBaseline) {
const ganttProp = ganttData.ganttProperties;
let tempEndDate = null;
let dayStartTime;
let dayEndTime;
const { startdateField, enddateField, durationField } = this.getFieldMappings(isBaseline);
if (!isNullOrUndefined(ganttProp[startdateField])) {
if (!isNullOrUndefined(ganttProp[enddateField]) && isNullOrUndefined(ganttProp[durationField])) {
if (this.compareDates(ganttProp[startdateField], ganttProp[enddateField]) === 1) {
this.parent.setRecordValue(startdateField, new Date(ganttProp[enddateField].getTime()), ganttProp, true);
dayStartTime = this.parent['getCurrentDayStartTime'](ganttProp.isAutoSchedule && !isBaseline ? ganttProp.autoStartDate : ganttProp[startdateField]);
dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp.isAutoSchedule && !isBaseline ? ganttProp.autoEndDate : ganttProp[enddateField]);
this.setTime(dayStartTime, ganttProp[startdateField]);
}
this.calculateDuration(ganttData, isBaseline);
}
if (!isNullOrUndefined(ganttProp[durationField])) {
const duration = !isBaseline && !isNullOrUndefined(ganttProp.segments) && ganttProp.segments.length > 1 ?
this.totalDuration(ganttProp.segments) : ganttProp[durationField];
tempEndDate = this.getEndDate(ganttProp[startdateField], duration, ganttProp.durationUnit, ganttProp, false, isBaseline);
}
this.parent.setRecordValue(enddateField, tempEndDate, ganttProp, true);
}
else {
tempEndDate = ganttData[this.parent.taskFields[enddateField]];
if (!isNullOrUndefined(tempEndDate)) {
dayEndTime = this.parent['getCurrentDayEndTime'](tempEndDate);
this.setTime(dayEndTime, tempEndDate);
}
this.parent.setRecordValue(enddateField, tempEndDate, ganttProp, true);
}
if (this.parent.taskFields[enddateField]) {
this.parent.dataOperation.updateMappingData(ganttData, enddateField);
}
}
/* eslint-enable */
totalDuration(segments) {
let duration = 0;
for (let i = 0; i < segments.length; i++) {
duration += segments[i].duration + segments[i].offsetDuration;
}
return duration;
}
/**
* To calculate duration from start date and end date
*
* @param {IGanttData} ganttData - Defines the gantt data.
* @param {boolean} isBaseline - Indicates whether the calculation is specific to baseline dates.
* @returns {void} .
*/
calculateDuration(ganttData, isBaseline) {
const ganttProperties = ganttData.ganttProperties;
let tDuration;
if (!isBaseline && !isNullOrUndefined(ganttProperties.segments) && ganttProperties.segments.length > 0 &&
!isNullOrUndefined(this.parent.editModule.taskbarEditModule)) {
tDuration = this.parent.editModule.taskbarEditModule.sumOfDuration(ganttProperties.segments);
}
else {
if (!isBaseline && (!isNullOrUndefined(this.parent.taskFields.milestone)) && (!isNullOrUndefined(ganttProperties.startDate))
&& !isNullOrUndefined(ganttProperties.endDate) &&
(ganttProperties.startDate).getTime() === (ganttProperties.endDate).getTime()
&& (ganttData.taskData[this.parent.taskFields.milestone] === false)) {
tDuration = 1;
}
else {
const startDate = isBaseline ? ganttProperties.baselineStartDate : ganttProperties.startDate;
const endDate = isBaseline ? ganttProperties.baselineEndDate : ganttProperties.endDate;
const durationUnit = ganttProperties.durationUnit;
const isAutoSchedule = isBaseline ? false : ganttProperties.isAutoSchedule;
const isMilestone = isBaseline ? false : ganttProperties.isMilestone;
tDuration = this.getDuration(startDate, endDate, durationUnit, isAutoSchedule, isMilestone);
}
}
const duration = isBaseline ? 'baselineDuration' : 'duration';
this.parent.setRecordValue(duration, tDuration, ganttProperties, true);
const col = isBaseline ? this.parent.columnByField[this.parent.columnMapping.baselineDuration] :
this.parent.columnByField[this.parent.columnMapping.duration];
if (!isNullOrUndefined(this.parent.editModule) && !isNullOrUndefined(this.parent.editModule.cellEditModule) &&
!this.parent.editModule.cellEditModule.isCellEdit && !isNullOrUndefined(col)) {
if (!isNullOrUndefined(col.edit) && !isNullOrUndefined(col.edit.read)) {
const dialog = this.parent.editModule.dialogModule.dialog;
if (!isNullOrUndefined(dialog)) {
const textBoxElement = isBaseline ? dialog.querySelector('#' + this.parent.element.id + 'BaselineDuration') :
dialog.querySelector('#' + this.parent.element.id + 'Duration');
if (!isNullOrUndefined(textBoxElement)) {
const textBox = textBoxElement.ej2_instances[0];
if (!isNullOrUndefined(textBox) && textBox.value !== tDuration.toString()) {
textBox.value = tDuration.toString();
textBox.dataBind();
}
}
}
}
if (this.parent.taskFields.duration || this.parent.taskFields.baselineDuration) {
this.parent.dataOperation.updateMappingData(ganttData, duration);
if (this.parent.taskFields.durationUnit) {
this.parent.dataOperation.updateMappingData(ganttData, 'durationUnit');
}
}
}
}
/**
*
* @param {Date} sDate Method to get total nonworking time between two date values
* @param {Date} eDate .
* @param {boolean} isAutoSchedule .
* @param {boolean} isCheckTimeZone .
* @param {boolean} isBaseline - Indicates whether the calculation is specific to baseline dates.
* @returns {number} .
*/
getNonworkingTime(sDate, eDate, isAutoSchedule, isCheckTimeZone, isBaseline) {
const parent = this.parent;
isCheckTimeZone = isNullOrUndefined(isCheckTimeZone) ? true : isCheckTimeZone;
const shouldCheckWeekend = (!isBaseline && !parent.includeWeekend && parent.autoCalculateDateScheduling &&
!(parent.isLoad && parent.treeGrid.loadChildOnDemand && parent.taskFields.hasChildMapping)) && isAutoSchedule;
const weekendCount = shouldCheckWeekend ? this.getWeekendCount(sDate, eDate) : 0;
const totalSeconds = this.getNumberOfSeconds(sDate, eDate, isCheckTimeZone);
const shouldCheckHolidays = (isAutoSchedule && parent.autoCalculateDateScheduling &&
!(parent.isLoad && parent.treeGrid.loadChildOnDemand && parent.taskFields.hasChildMapping)) && !isBaseline;
const holidaysCount = shouldCheckHolidays ? this.getHolidaysCount(sDate, eDate) : 0;
const totalWorkDays = (totalSeconds - (weekendCount * 86400) - (holidaysCount * 86400)) / 86400;
const nonWorkingSecondsOnDate = this.getNonWorkingSecondsOnDate(sDate, eDate, isAutoSchedule, isBaseline);
const nonWorkingTime = parent.weekWorkingTime.length > 0 ?
this.nonWorkingSeconds(sDate, eDate, isAutoSchedule, totalWorkDays) :
(totalWorkDays * (86400 - parent.secondsPerDay));
return nonWorkingTime + (weekendCount * 86400) + (holidaysCount * 86400) + nonWorkingSecondsOnDate;
}
nonWorkingSeconds(sDate, eDate, isAutoSchedule, workDays, fromDuration) {
const newStartDate = sDate.getTime() > eDate.getTime() ? new Date(eDate.getTime()) : new Date(sDate.getTime());
const newEndDate = sDate.getTime() > eDate.getTime() ? new Date(sDate.getTime()) : new Date(eDate.getTime());
let timeDiff = 0;
let count = 0;
if (fromDuration) {
const dayStartTime = this.parent['getCurrentDayStartTime'](newStartDate);
const dayEndTime = this.parent['getCurrentDayEndTime'](newStartDate);
if (!(newStartDate.getHours() < dayEndTime / 3600 && newStartDate.getHours() >= dayStartTime / 3600)) {
newStartDate.setDate(newStartDate.getDate() + 1);
}
}
else {
newStartDate.setDate(newStartDate.getDate() + 1);
newStartDate.setHours(0, 0, 0, 0);
newEndDate.setHours(0, 0, 0, 0);
}
if (workDays > 0 || isNullOrUndefined(workDays)) {
while ((fromDuration && newStartDate.getTime() <= newEndDate.getTime())
|| (!fromDuration && newStartDate.getTime() < newEndDate.getTime())) {
if (isAutoSchedule) {
if (this.isOnHolidayOrWeekEnd(newStartDate, true)) {
do {
newStartDate.setDate(newStartDate.getDate() + 1);
} while (this.isOnHolidayOrWeekEnd(newStartDate, true));
}
if (!this.parent.includeWeekend) {
this.getNextWorkingDay(newStartDate);
}
}
if (newStartDate.getTime() <= newEndDate.getTime()) {
count++;
const currentDaySeconds = this.parent['getSecondsPerDay'](newStartDate);
if (fromDuration) {
timeDiff += currentDaySeconds;
}
else {
timeDiff += 86400 - currentDaySeconds;
}
newStartDate.setDate(newStartDate.getDate() + 1);
if (isAutoSchedule) {
if (this.isOnHolidayOrWeekEnd(newStartDate, true)) {
do {
newStartDate.setDate(newStartDate.getDate() + 1);
} while (this.isOnHolidayOrWeekEnd(newStartDate, true));
}
if (!this.parent.includeWeekend) {
this.getNextWorkingDay(newStartDate);
}
}
}
}
}
else {
return 0;
}
if (fromDuration) {
if (timeDiff > 0) {
timeDiff = timeDiff / count;
}
else {
timeDiff = this.parent.secondsPerDay;
}
}
return timeDiff;
}
/**
*
* @param {Date} startDate .
* @param {Date} endDate .
* @param {string} durationUnit .
* @param {boolean} isAutoSchedule .
* @param {boolean} isMilestone .
* @param {boolean} isCheckTimeZone .
* @returns {number} .
* @private
*/
getDuration(startDate, endDate, durationUnit, isAutoSchedule, isMilestone, isCheckTimeZone = true) {
if (!startDate || !endDate) {
return null;
}
let durationValue = 0;
const isSameDay = this.parent.getFormatedDate(startDate) === this.parent.getFormatedDate(endDate);
let totSeconds;
if (this.parent.weekWorkingTime.length > 0) {
const tempStartDate = new Date(startDate);
totSeconds = this.nonWorkingSeconds(startDate, endDate, isAutoSchedule, undefined, true);
let totalDurationInDays = 0;
while (tempStartDate <= endDate) {
const currentDateString = this.parent.getFormatedDate(tempStartDate);
const dayWorkingSeconds = this.parent['getSecondsPerDay'](tempStartDate);
const currentStartDate = new Date(tempStartDate);
const currentEndDate = new Date(tempStartDate);
currentStartDate.setHours(0, 0, 0, 0);
currentEndDate.setHours(0, 0, 0, 0);
currentStartDate.setSeconds(this.parent['getCurrentDayStartTime'](tempStartDate));
currentEndDate.setSeconds(this.parent['getCurrentDayEndTime'](tempStartDate));
if (currentDateString === this.parent.getFormatedDate(startDate)) {
currentStartDate.setTime(startDate.getTime());
}
if (currentDateString === this.parent.getFormatedDate(endDate)) {
currentEndDate.setTime(endDate.getTime());
}
const timeDiffSeconds = this.getTimeDifference(currentStartDate, currentEndDate, isCheckTimeZone) / 1000;
const nonWorkSeconds = this.getNonworkingTime(currentStartDate, currentEndDate, isAutoSchedule, isCheckTimeZone);
totalDurationInDays += Math.max(0, timeDiffSeconds - nonWorkSeconds) / dayWorkingSeconds;