@syncfusion/ej2-gantt
Version:
Essential JS 2 Gantt Component
1,090 lines (1,087 loc) • 2.43 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 } 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)) {
record.taskData[parent.taskFields.startDate] = parent.dateValidationModule.remove(record.ganttProperties.startDate, parent.timezone);
if (parent.taskFields.endDate !== null) {
record.taskData[parent.taskFields.endDate] = parent.dateValidationModule.remove(record.ganttProperties.endDate, parent.timezone);
}
if (parent.taskFields.baselineEndDate || parent.taskFields.baselineStartDate) {
record.taskData[parent.taskFields.baselineStartDate] = parent.dateValidationModule.remove(record.ganttProperties.baselineStartDate, parent.timezone);
record.taskData[parent.taskFields.baselineEndDate] = parent.dateValidationModule.remove(record.ganttProperties.baselineEndDate, parent.timezone);
}
}
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++;
}
/**
* 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 .
* @param {boolean} validateAsMilestone .
* @param {boolean} isLoad .
* @returns {Date} .
* @private
*/
checkStartDate(date, ganttProp, validateAsMilestone, isLoad) {
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) {
dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp.endDate ? ganttProp.isAutoSchedule ? ganttProp.endDate : ganttProp.autoEndDate : tStartDate);
}
let startTime = (!validateAsMilestone || isLoad) ? dayStartTime : dayEndTime;
if (!this.parent.includeWeekend) {
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);
}
}
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());
}
}
/**
* To update given date value to valid end date
*
* @param {Date} date .
* @param {ITaskData} ganttProp .
* @param {boolean} validateAsMilestone .
* @returns {Date} .
* @private
*/
checkEndDate(date, ganttProp, validateAsMilestone) {
if (isNullOrUndefined(date)) {
return null;
}
let dayStartTime;
let dayEndTime;
if (this.parent.weekWorkingTime.length > 0) {
let currentDay = date;
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(date);
}
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 && !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)) {
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)) {
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.
* @returns {void} .
* @private
*/
calculateStartDate(ganttData) {
const ganttProp = ganttData.ganttProperties;
let tempStartDate = null;
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');
}
}
/**
*
* @param {IGanttData} ganttData - Defines the gantt data.
* @returns {void} .
* @private
*/
calculateEndDate(ganttData) {
const ganttProp = ganttData.ganttProperties;
let tempEndDate = null;
let dayStartTime;
let dayEndTime;
if (!isNullOrUndefined(ganttProp.startDate)) {
if (!isNullOrUndefined(ganttProp.endDate) && isNullOrUndefined(ganttProp.duration)) {
if (this.compareDates(ganttProp.startDate, ganttProp.endDate) === 1) {
this.parent.setRecordValue('startDate', new Date(ganttProp.endDate.getTime()), ganttProp, true);
dayStartTime = this.parent['getCurrentDayStartTime'](ganttProp.isAutoSchedule ? ganttProp.autoStartDate : ganttProp.startDate);
dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp.isAutoSchedule ? ganttProp.autoEndDate : ganttProp.endDate);
this.setTime(dayStartTime, ganttProp.startDate);
}
this.calculateDuration(ganttData);
}
if (!isNullOrUndefined(ganttProp.duration)) {
const duration = !isNullOrUndefined(ganttProp.segments) && ganttProp.segments.length > 1 ?
this.totalDuration(ganttProp.segments) : ganttProp.duration;
tempEndDate = this.getEndDate(ganttProp.startDate, duration, ganttProp.durationUnit, ganttProp, false);
}
this.parent.setRecordValue('endDate', tempEndDate, ganttProp, true);
}
else {
tempEndDate = ganttData[this.parent.taskFields.endDate];
if (!isNullOrUndefined(tempEndDate)) {
dayEndTime = this.parent['getCurrentDayEndTime'](tempEndDate);
this.setTime(dayEndTime, tempEndDate);
}
this.parent.setRecordValue('endDate', tempEndDate, ganttProp, true);
}
if (this.parent.taskFields.endDate) {
this.parent.dataOperation.updateMappingData(ganttData, 'endDate');
}
}
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.
* @returns {void} .
*/
calculateDuration(ganttData) {
const ganttProperties = ganttData.ganttProperties;
let tDuration;
if (!isNullOrUndefined(ganttProperties.segments) && ganttProperties.segments.length > 0 &&
!isNullOrUndefined(this.parent.editModule.taskbarEditModule)) {
tDuration = this.parent.editModule.taskbarEditModule.sumOfDuration(ganttProperties.segments);
}
else {
if ((!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 {
tDuration = this.getDuration(ganttProperties.startDate, ganttProperties.endDate, ganttProperties.durationUnit, ganttProperties.isAutoSchedule, ganttProperties.isMilestone);
}
}
this.parent.setRecordValue('duration', tDuration, ganttProperties, true);
const col = 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 textBox = dialog.querySelector('#' + this.parent.element.id + 'Duration')
.ej2_instances[0];
if (!isNullOrUndefined(textBox) && textBox.value !== tDuration.toString()) {
textBox.value = tDuration.toString();
textBox.dataBind();
}
}
}
if (this.parent.taskFields.duration) {
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 .
* @returns {number} .
*/
getNonworkingTime(sDate, eDate, isAutoSchedule, isCheckTimeZone) {
isCheckTimeZone = isNullOrUndefined(isCheckTimeZone) ? true : isCheckTimeZone;
const weekendCount = (!this.parent.includeWeekend && this.parent.autoCalculateDateScheduling && !(this.parent.isLoad &&
this.parent.treeGrid.loadChildOnDemand && this.parent.taskFields.hasChildMapping)) && isAutoSchedule ?
this.getWeekendCount(sDate, eDate) : 0;
const totalHours = this.getNumberOfSeconds(sDate, eDate, isCheckTimeZone);
const holidaysCount = (isAutoSchedule && this.parent.autoCalculateDateScheduling &&
!(this.parent.isLoad && this.parent.treeGrid.loadChildOnDemand && this.parent.taskFields.hasChildMapping)) ? this.getHolidaysCount(sDate, eDate) : 0;
const totWorkDays = (totalHours - (weekendCount * 86400) - (holidaysCount * 86400)) / 86400; // working days between two dates
const nonWorkHours = this.getNonWorkingSecondsOnDate(sDate, eDate, isAutoSchedule);
const totalNonWorkTime = (this.parent.weekWorkingTime.length > 0 ?
this.nonWorkingSeconds(sDate, eDate, isAutoSchedule, totWorkDays) : (totWorkDays * (86400 - this.parent.secondsPerDay))) +
(weekendCount * 86400) + (holidaysCount * 86400) + nonWorkHours;
return totalNonWorkTime;
}
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) {
if (isNullOrUndefined(startDate) || isNullOrUndefined(endDate)) {
return null;
}
isCheckTimeZone = isNullOrUndefined(isCheckTimeZone) ? true : isCheckTimeZone;
let durationValue = 0;
const timeDiff = this.getTimeDifference(startDate, endDate, isCheckTimeZone) / 1000;
const nonWorkHours = this.getNonworkingTime(startDate, endDate, isAutoSchedule, isCheckTimeZone);
const durationHours = timeDiff - nonWorkHours;
let totSeconds;
if (this.parent.weekWorkingTime.length > 0) {
totSeconds = this.nonWorkingSeconds(startDate, endDate, isAutoSchedule, undefined, true);
}
else {
totSeconds = this.parent.secondsPerDay;
}
if (isMilestone && this.parent.getFormatedDate(startDate) === this.parent.getFormatedDate(endDate)) {
durationValue = 0;
}
else {
if (!durationUnit || durationUnit === 'day') {
durationValue = durationHours / totSeconds;
}
else if (durationUnit === 'minute') {
durationValue = durationHours / 60;
}
else {
durationValue = durationHours / 3600;
}
}
return parseFloat(durationValue.toString());
}
/**
*
* @param {number} duration .
* @param {string} durationUnit .
* @param {Date} date .
* @returns {number} .
*/
getDurationAsSeconds(duration, durationUnit, date) {
let value = 0;
let totSeconds;
if (this.parent.weekWorkingTime.length > 0) {
totSeconds = this.parent['getSecondsPerDay'](date);
}
else {
totSeconds = this.parent.secondsPerDay;
}
if (!durationUnit || durationUnit.toLocaleLowerCase() === 'day') {
value = totSeconds * duration;
}
else if (durationUnit.toLocaleLowerCase() === 'hour') {
value = duration * 3600;
}
else {
value = duration * 60;
}
return value;
}
/**
* To get date from start date and duration
*
* @param {Date} startDate .
* @param {number} duration .
* @param {string} durationUnit .
* @param {ITaskData} ganttProp .
* @param {boolean} validateAsMilestone .
* @returns {Date} .
* @private
*/
getEndDate(startDate, duration, durationUnit, ganttProp, validateAsMilestone) {
let tempStart = new Date(startDate.getTime());
let endDate = new Date(startDate.getTime());
const sDate = new Date(startDate.getTime());
let secondDuration;
if (this.parent.weekWorkingTime.length > 0 && (!durationUnit || durationUnit.toLocaleLowerCase() === 'day')) {
secondDuration = this.calculateSecondDuration(duration, sDate, secondDuration, startDate, true);
}
else {
secondDuration = this.getDurationAsSeconds(duration, durationUnit, startDate);
}
let nonWork = 0;
let workHours = 0;
while (secondDuration > 0) {
endDate.setSeconds(endDate.getSeconds() + secondDuration);
nonWork = this.getNonworkingTime(tempStart, endDate, ganttProp.isAutoSchedule, true);
workHours = secondDuration - nonWork;
secondDuration = secondDuration - workHours;
if (secondDuration > 0) {
endDate = this.checkStartDate(endDate, ganttProp, validateAsMilestone);
}
tempStart = new Date(endDate.getTime());
}
return endDate;
}
/**
* Calculate start date based on end date and duration.
*
* @param {Date} endDate - To calculate start date value from end date and duration.
* @param {number} duration - The duration value.
* @param {string} durationUnit - The unit of duration.
* @param {ITaskData} ganttProp - The Gantt task properties.
* @param {boolean} fromValidation - A flag indicating if the calculation is from validation.
* @returns {Date} The calculated start date.
* @private
*/
getStartDate(endDate, duration, durationUnit, ganttProp, fromValidation) {
let tempEnd = new Date(endDate.getTime());
let startDate = new Date(endDate.getTime());
let secondDuration;
const eDate = new Date(tempEnd.getTime());
if (this.parent.weekWorkingTime.length > 0) {
secondDuration = this.calculateSecondDuration(duration, eDate, secondDuration, tempEnd, false);
}
else {
secondDuration = this.getDurationAsSeconds(duration, durationUnit, tempEnd);
}
let nonWork = 0;
let workHours = 0;
while (secondDuration > 0) {
startDate.setSeconds(startDate.getSeconds() - secondDuration);
nonWork = this.getNonworkingTime(startDate, tempEnd, ganttProp.isAutoSchedule, true);
workHours = secondDuration - nonWork;
secondDuration = secondDuration - workHours;
if (secondDuration > 0) {
tempEnd = this.checkEndDate(startDate, ganttProp);
}
tempEnd = new Date(startDate.getTime());
}
/* To render the milestone in proper date while loading */
if (fromValidation && ganttProp.isMilestone) {
startDate.setDate(startDate.getDate() - 1);
const dayEndTime = this.parent['getCurrentDayEndTime'](ganttProp.endDate ? ganttProp.isAutoSchedule ? ganttProp.endDate : ganttProp.autoEndDate : startDate);
this.parent.dateValidationModule.setTime(dayEndTime, startDate);
startDate = this.parent.dateValidationModule.checkStartDate(startDate, ganttProp, true);
}
return startDate;
}
calculateSecondDuration(duration, sDate, secondDuration, startDate, fromEndDate) {
if (duration < 1) {
secondDuration = this.parent['getSecondsPerDay'](sDate) * duration;
}
else {
secondDuration = 0;
let durationValue = duration;
const dayStartTime = this.parent['getCurrentDayStartTime'](sDate);
const dayEndTime = this.parent['getCurrentDayEndTime'](sDate);
if (!(sDate.getHours() < dayEndTime / 3600 && sDate.getHours() > dayStartTime / 3600) && this.fromSegments) {
if (fromEndDate) {
sDate.setDate(sDate.getDate() + 1);
}
else {
sDate.setDate(sDate.getDate() - 1);
}
}
while (durationValue > 0) {
if (this.isOnHolidayOrWeekEnd(sDate, true)) {
do {
if (fromEndDate) {
sDate.setDate(sDate.getDate() + 1);
}
else {
sDate.setDate(sDate.getDate() - 1);
}
} while (this.isOnHolidayOrWeekEnd(sDate, true));
}
if (!this.parent.includeWeekend) {
sDate = fromEndDate ? this.getNextWorkingDay(sDate) : this.getPreviousWorkingDay(sDate);
}
let totSeconds = this.parent['getSecondsPerDay'](sDate);
let num = 0;
if (this.getSecondsInDecimal(startDate) !== this.parent['getStartTime'](startDate) && !Number.isInteger(durationValue)) {
const deciNumber = duration.toString().split('.');
num = parseFloat('.' + deciNumber[1]);
totSeconds = totSeconds * num;
durationValue = durationValue - num;
}
if (durationValue < 1) {
totSeconds = totSeconds * durationValue;
}
secondDuration = secondDuration + totSeconds;
if (fromEndDate) {
sDate.setDate(sDate.getDate() + 1);
}
else {
sDate.setDate(sDate.getDate() - 1);
}
if (!num) {
durationValue--;
}
}
}
return secondDuration;
}
/**
* @param {ITaskData} ganttProp .
* @param {boolean} isLoad .
* @returns {Date} .
* @private
*/
getProjectStartDate(ganttProp, isLoad) {
if (!isNullOrUndefined(this.parent.cloneProjectStartDate)) {
if (typeof this.parent.cloneProjectStartDate === 'string') {
this.parent.cloneProjectStartDate = this.getDateFromFormat(this.parent.cloneProjectStartDate);
}
const cloneStartDate = this.checkStartDate(this.parent.cloneProjectStartDate);
this.parent.cloneProjectStartDate = cloneStartDate;
return new Date(cloneStartDate.getTime());
}
else if (!isNullOrUndefined(this.parent.projectStartDate)) {
const cloneStartDate = this.getDateFromFormat(this.parent.projectStartDate);
this.parent.cloneProjectStartDate = this.checkStartDate(cloneStartDate);
}
else if (!isNullOrUndefined(isLoad)) {
const flatData = this.parent.flatData;
let minStartDate;
if (flatData.length > 0) {
minStartDate = flatData[0].ganttProperties.startDate;
}
else {
minStartDate = new Date();
minStartDate.setHours(0, 0, 0, 0);
}
for (let index = 1; index < flatData.length; index++) {
const startDate = flatData[index].ganttProperties.startDate;
if (!isNullOrUndefined(startDate) && this.compareDates(startDate, minStartDate) === -1) {
minStartDate = startDate;
}
}
this.parent.cloneProjectStartDate = this.checkStartDate(minStartDate, ganttProp);
}
else {
return null;
}
return new Date(this.parent.cloneProjectStartDate.getTime());
}
/**
* @param {ITaskData} ganttProp .
* @param {boolean} isAuto .
* @returns {Date} .
* @private
*/
getValidStartDate(ganttProp, isAuto) {
let sDate = null;
const startDate = isAuto ? ganttProp.autoStartDate : ganttProp.startDate;
const endDate = isAuto ? ganttProp.autoEndDate : ganttProp.endDate;
const duration = !ganttProp.isAutoSchedule && ganttProp.autoDuration ? ganttProp.autoDuration : ganttProp.duration;
if (isNullOrUndefined(startDate)) {
if (!isNullOrUndefined(endDate)) {
sDate = new Date(endDate.getTime());
const dayStartTime = this.parent['getCurrentDayStartTime'](sDate);
this.setTime(dayStartTime, sDate);
}
else if (!isNullOrUndefined(duration)) {
const ganttTask = this.parent.getTaskByUniqueID(ganttProp.uniqueID);
if (this.parent.allowUnscheduledTasks && ganttTask &&
ganttTask.parentItem && isNullOrUndefined(startDate) && isNullOrUndefined(endDate)) {
let parentTask = this.parent.getParentTask(ganttTask.parentItem);
while (parentTask && !parentTask.ganttProperties.startDate) {
parentTask = this.parent.getParentTask(parentTask.parentItem);
}
sDate = (!parentTask || !parentTask.ganttProperties.startDate) ? this.parent.cloneProjectStartDate
: parentTask.ganttProperties.startDate;
}
else {
sDate = this.getProjectStartDate(ganttProp);
}
}
}
else {
sDate = new Date(startDate.getTime());
}
return sDate;
}
/**
*
* @param {ITaskData} ganttProp .
* @param {boolean} isAuto .
* @returns {Date} .
* @private
*/
getValidEndDate(ganttProp, isAuto) {
let eDate = null;
const startDate = isAuto ? ganttProp.autoStartDate : ganttProp.startDate;
const endDate = isAuto ? ganttProp.autoEndDate : ganttProp.endDate;
const duration = isAuto ? ganttProp.autoDuration : ganttProp.duration;
if (isNullOrUndefined(endDate)) {
if (!isNullOrUndefined(startDate)) {
if (ganttProp.isMilestone) {
eDate = this.checkStartDate(startDate);
}
else {
eDate = new Date(startDate.getTime());
const dayEndTime = this.parent['getCurrentDayEndTime'](endDate ? endDate : eDate);
this.setTime(dayEndTime, eDate);
}
}
else if (!isNullOrUndefined(duration)) {
const sDate = this.getValidStartDate(ganttProp);
if (sDate) {
eDate = this.getEndDate(sDate, duration, ganttProp.durationUnit, ganttProp, false);
}
}
}
else {
eDate = new Date(endDate.getTime());
}
return eDate;
}
getWorkingTime(day, currentRange, startDate, totalSeconds, count, nonWorkingHours, workingTimeRanges, nonWorkingTimeRanges) {
if (!isNullOrUndefined(currentRange.from) && !isNullOrUndefined(currentRange.to)) {
startDate.setHours(0, 0, 0, 0);
const tempDate = new Date(startDate.getTime());
startDate.setTime(startDate.getTime() + (currentRange.from * 3600000));
const startHour = new Date(startDate.getTime());
if (currentRange.to === 24) {
const currentRangeTo = 24 * 60 * 60 * 1000;
tempDate.setTime(tempDate.getTime() + (currentRangeTo));
}
else {
tempDate.setTime(tempDate.getTime() + (currentRange.to * 3600000));
}
const endHour = new Date(tempDate.getTime());
const timeDiff = endHour.getTime() - startHour.getTime();
const sdSeconds = this.getSecondsInDecimal(startHour);
let edSeconds = this.getSecondsInDecimal(endHour);
if (edSeconds === 0) {
edSeconds = 86400;
}
totalSeconds += timeDiff / 1000;
if (count === 0) {
this.parent.defaultStartTime = sdSeconds;
if (this.parent.weekWorkingTime.length > 0) {
this.assignStartTime(day, sdSeconds);
}
}
if (count === this[day.toLowerCase() + 'TimeRangeLength'] - 1 || day === '') {
this.parent.defaultEndTime = edSeconds;
if (this.parent.weekWorkingTime.length > 0) {
this.assignEndTime(day, edSeconds);
}
}
if (count > 0) {
if (day === '') {
nonWorkingHours.push(nonWorkingHours[nonWorkingHours.length - 1] +
sdSeconds - workingTimeRanges[count - 1].to);
if (workingTimeRanges[count - 1].to < sdSeconds) {
nonWorkingTimeRanges.push({
from: workingTimeRanges[count - 1].to, to: sdSeconds, isWorking: false,
interval: (sdSeconds - workingTimeRanges[count - 1].to)
});