@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
3,400 lines • 121 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
require('./kendo.dom.js');
require('./kendo.touch.js');
require('./kendo.draganddrop.js');
require('./kendo.icons.js');
require('./kendo.core.js');
require('./kendo.licensing.js');
require('@progress/kendo-licensing');
require('./kendo.userevents.js');
require('./kendo.html.icon.js');
require('./kendo.html.base.js');
require('@progress/kendo-svg-icons');
const __meta__ = {
id: "gantt.timeline",
name: "Gantt Timeline",
category: "web",
description: "The Gantt Timeline",
depends: [ "dom", "touch", "draganddrop", "icons" ],
hidden: true
};
(function($) {
var Widget = kendo.ui.Widget;
var kendoDomElement = kendo.dom.element;
var kendoTextElement = kendo.dom.text;
var kendoHtmlElement = kendo.dom.html;
var isPlainObject = $.isPlainObject;
var outerWidth = kendo._outerWidth;
var outerHeight = kendo._outerHeight;
var extend = $.extend;
var isRtl = false;
var keys = kendo.keys;
var Query = kendo.data.Query;
var STRING = "string";
var NS = ".kendoGanttTimeline";
var CLICK = "click";
var DBLCLICK = "dblclick";
var MOUSEMOVE = "mousemove";
var MOUSEENTER = "mouseenter";
var MOUSELEAVE = "mouseleave";
var KEYDOWN = "keydown";
var DOT = ".";
var TIME_HEADER_TEMPLATE = ({ start }) => kendo.toString(start, 't');
var DAY_HEADER_TEMPLATE = ({ start }) => kendo.toString(start, 'ddd M/dd');
var WEEK_HEADER_TEMPLATE = ({ start, end }) => `${kendo.toString(start, 'ddd M/dd')} - ${kendo.toString(kendo.date.addDays(end, -1), 'ddd M/dd')}`;
var MONTH_HEADER_TEMPLATE = ({ start }) => kendo.toString(start, 'MMM');
var YEAR_HEADER_TEMPLATE = ({ start }) => kendo.toString(start, 'yyyy');
var RESIZE_HINT = ({ styles }) => `<div class="${styles.marquee}">` +
`<div class="${styles.marqueeColor}"></div>` +
'</div>';
var RESIZE_TOOLTIP_TEMPLATE = ({ styles, messages, start, end, format }) => `<div class="${styles.tooltipWrapper} k-gantt-resize-hint">` +
`<div class="${styles.tooltipContent}">` +
`<div>${kendo.htmlEncode(messages.start)}: ${kendo.toString(start, format)}</div>` +
`<div>${kendo.htmlEncode(messages.end)}: ${kendo.toString(end, format)}</div>` +
'</div>' +
'</div>';
var PERCENT_RESIZE_TOOLTIP_TEMPLATE = ({ styles, text }) => `<div ${kendo.attr("style-z-index")}="100002" class="${styles.tooltipWrapper}" >` +
`<div class="${styles.tooltipContent}">${text}%</div>` +
`<div class="${styles.tooltipCallout}" ${kendo.attr("style-left")}="13px"></div>` +
'</div>';
var TASK_TOOLTIP_TEMPLATE = ({ styles, task, messages }) => `<div class="${kendo.htmlEncode(styles.taskDetails)}">` +
`<strong>${kendo.htmlEncode(task.title)}</strong>` +
`<div class="${styles.taskDetailsPercent}">${kendo.toString(task.percentComplete, "p0")}</div>` +
`<ul class="${styles.reset}">` +
`<li>${kendo.htmlEncode(messages.start)}: ${kendo.toString(task.start, "h:mm tt ddd, MMM d")}</li>` +
`<li>${kendo.htmlEncode(messages.end)}: ${kendo.toString(task.end, "h:mm tt ddd, MMM d")}</li>` +
'</ul>' +
'</div>';
var OFFSET_TOOLTIP_TEMPLATE = ({ offsetPrefix, offsetText }) => `<span>${offsetPrefix}: ${offsetText}</span>`;
var PLANNED_TOOLTIP_TEMPLATE = ({ plannedStart, plannedEnd, startDate, endDate }) => '<div class="k-task-content">' +
`<div>${plannedStart}: ${startDate}</div>` +
`<div>${plannedEnd}: ${endDate}</div>` +
'</div>';
var SIZE_CALCULATION_TEMPLATE = `<table ${kendo.attr("style-visibility")}="hidden">` +
"<tbody>" +
`<tr ${kendo.attr("style-height")}="{0}">` +
"<td> </td>" +
"</tr>" +
"</tbody>" +
"</table>";
var defaultViews = {
day: {
type: "kendo.ui.GanttDayView"
},
week: {
type: "kendo.ui.GanttWeekView"
},
month: {
type: "kendo.ui.GanttMonthView"
},
year: {
type: "kendo.ui.GanttYearView"
}
};
function trimOptions(options) {
delete options.name;
delete options.prefix;
delete options.views;
return options;
}
function getWorkDays(options) {
var workDays = [];
var dayIndex = options.workWeekStart;
workDays.push(dayIndex);
while (options.workWeekEnd != dayIndex) {
if (dayIndex > 6) {
dayIndex -= 7;
} else {
dayIndex++;
}
workDays.push(dayIndex);
}
return workDays;
}
function blurActiveElement() {
var activeElement = kendo._activeElement();
if (activeElement && activeElement.nodeName.toLowerCase() !== "body") {
$(activeElement).trigger("blur");
}
}
var viewStyles = {
alt: "k-table-row k-table-alt-row",
reset: "k-reset",
nonWorking: "k-nonwork-hour",
header: "k-header k-table-td",
gridHeader: "k-grid-header",
gridHeaderWrap: "k-grid-header-wrap",
gridContent: "k-grid-content",
tasksWrapper: "k-gantt-tables",
rowsTable: "k-gantt-rows",
columnsTable: "k-gantt-columns",
tasksTable: "k-gantt-tasks",
dependenciesWrapper: "k-gantt-dependencies",
resource: "k-resource",
resourceAlt: "k-resource k-alt",
headerTable: "k-grid-header-table k-table k-table-md",
table: "k-table k-table-md",
tbody: "k-table-tbody",
tableRow: "k-table-row",
tableCell: "k-table-td",
task: "k-task",
taskSingle: "k-task-single",
taskMilestone: "k-task-milestone",
taskSummary: "k-task-summary",
taskWrap: "k-task-wrap",
taskMilestoneWrap: "k-milestone-wrap",
taskSummaryWrap: "k-summary-wrap",
taskPlanned: "k-task-planned",
taskPlannedMoment: "k-task-moment",
taskPlannedDuration: "k-task-duration",
taskPlannedMomentLeft: "k-moment-left",
taskAdvanced: "k-task-advanced",
taskDelayed: "k-task-delayed",
taskOffset: "k-task-offset",
taskOffsetWrap: "k-task-offset-wrap",
taskInnerWrap: "k-task-inner-wrap",
resourcesWrap: "k-resources-wrap",
taskDot: "k-task-dot",
taskDotStart: "k-task-start",
taskDotEnd: "k-task-end",
taskDragHandle: "k-task-draghandle",
taskContent: "k-task-content",
taskTemplate: "k-task-template",
taskActions: "k-task-actions",
taskDelete: "k-task-delete",
taskComplete: "k-task-complete",
taskDetails: "k-task-details",
taskDetailsPercent: "k-task-pct",
link: "k-link",
iconDelete: "x",
taskResizeHandle: "k-resize-handle",
taskResizeHandleWest: "k-resize-w",
taskResizeHandleEast: "k-resize-e",
taskSummaryProgress: "k-task-summary-progress",
taskSummaryComplete: "k-task-summary-complete",
line: "k-gantt-line",
lineHorizontal: "k-gantt-line-h",
lineVertical: "k-gantt-line-v",
arrowWest: "k-arrow-w",
arrowEast: "k-arrow-e",
dragHint: "k-drag-hint",
dependencyHint: "k-gantt-dependency-hint",
tooltipWrapper: "k-tooltip",
tooltipContent: "k-tooltip-content",
tooltipCallout: "k-callout k-callout-s",
callout: "k-callout",
marquee: "k-marquee k-gantt-marquee",
marqueeColor: "k-marquee-color",
offsetTooltipAdvanced: "k-offset-tooltip-advanced",
offsetTooltipDelay: "k-offset-tooltip-delayed",
plannedTooltip: "k-planned-tooltip"
};
var GanttView = kendo.ui.GanttView = Widget.extend({
init: function(element, options) {
Widget.fn.init.call(this, element, options);
this.title = this.options.title || this.options.name;
this.header = this.element.find(DOT + GanttView.styles.gridHeader);
this.content = this.element.find(DOT + GanttView.styles.gridContent);
this.contentWidth = this.content.width();
this._workDays = getWorkDays(this.options);
this._headerTree = options.headerTree;
this._taskTree = options.taskTree;
this._taskTemplate = options.taskTemplate ?
kendo.template(options.taskTemplate, extend({}, kendo.Template, options.templateSettings)) :
null;
this._dependencyTree = options.dependencyTree;
this._taskCoordinates = {};
this._currentTime();
},
destroy: function() {
Widget.fn.destroy.call(this);
clearTimeout(this._tooltipTimeout);
this.headerRow = null;
this.header = null;
this.content = null;
this._dragHint = null;
this._resizeHint = null;
this._resizeTooltip = null;
this._taskTooltip = null;
this._percentCompleteResizeTooltip = null;
this._headerTree = null;
this._taskTree = null;
this._dependencyTree = null;
},
options: {
showWorkHours: false,
showWorkDays: false,
workDayStart: new Date(1980, 1, 1, 8, 0, 0),
workDayEnd: new Date(1980, 1, 1, 17, 0, 0),
workWeekStart: 1,
workWeekEnd: 5,
hourSpan: 1,
slotSize: 100,
currentTimeMarker: {
updateInterval: 10000
}
},
renderLayout: function() {
this._slots = this._createSlots();
this._tableWidth = this._calculateTableWidth();
this.createLayout(this._layout());
this._slotDimensions();
this._adjustHeight();
this.content.find(DOT + GanttView.styles.dependenciesWrapper).width(this._tableWidth);
},
_adjustHeight: function() {
if (this.content) {
this.content.height(this.element.height() - outerHeight(this.header));
}
},
createLayout: function(rows) {
var headers = this._headers(rows);
var colgroup = this._colgroup();
var tree = this._headerTree;
var header = kendoDomElement("tbody", { className: GanttView.styles.tbody }, headers);
var table = kendoDomElement("table", { className: GanttView.styles.headerTable, style: { width: this._tableWidth + "px" }, role: "presentation" }, [colgroup, header]);
tree.render([table]);
this.headerRow = this.header.find("table").first().find("tr").last();
},
_slotDimensions: function() {
var headers = this.headerRow[0].children;
var slots = this._timeSlots();
var slot;
var header;
for (var i = 0, length = headers.length; i < length; i++) {
header = headers[i];
slot = slots[i];
slot.offsetLeft = header.offsetLeft;
slot.offsetWidth = header.offsetWidth;
}
},
render: function(tasks) {
var taskCount = tasks.length;
var styles = GanttView.styles;
var contentTable;
var rowsTable = this._rowsTable(taskCount);
var columnsTable = this._columnsTable(taskCount);
var tasksTable = this._tasksTable(tasks);
var currentTimeMarker = this.options.currentTimeMarker;
var calculatedSize = this.options.calculatedSize;
var totalHeight;
this._taskTree.render([rowsTable, columnsTable, tasksTable]);
contentTable = this.content.find(DOT + styles.rowsTable);
if (calculatedSize) {
totalHeight = calculatedSize.row * tasks.length;
this.content.find(DOT + styles.tasksTable).height(totalHeight);
contentTable.height(totalHeight);
}
this._contentHeight = contentTable.height();
this._rowHeight = calculatedSize ? calculatedSize.row : this._contentHeight / contentTable.find("tr").length;
this.content.find(DOT + styles.columnsTable).height(this._contentHeight);
if (currentTimeMarker !== false && currentTimeMarker.updateInterval !== undefined) {
this._renderCurrentTime();
}
},
_rowsTable: function(rowCount) {
var rows = [];
var row;
var styles = GanttView.styles;
var attributes = [{ className: styles.tableRow }, { className: styles.alt }];
for (var i = 0; i < rowCount; i++) {
row = kendoDomElement("tr", attributes[i % 2], [
kendoDomElement("td", { className: styles.tableCell }, [
kendoTextElement("\u00a0")
])
]);
rows.push(row);
}
return this._createTable(1, rows, { className: styles.rowsTable + " k-grid-table " + styles.table });
},
_columnsTable: function() {
var cells = [];
var row;
var styles = GanttView.styles;
var slots = this._timeSlots();
var slotsCount = slots.length;
var slot;
var slotSpan;
var totalSpan = 0;
var attributes;
for (var i = 0; i < slotsCount; i++) {
slot = slots[i];
attributes = { className: styles.tableCell };
slotSpan = slot.span;
totalSpan += slotSpan;
if (slotSpan !== 1) {
attributes.colspan = slotSpan;
}
if (slot.isNonWorking) {
attributes.className += " " + styles.nonWorking;
}
cells.push(kendoDomElement("td", attributes, [
kendoTextElement("\u00a0")
]));
}
row = kendoDomElement("tr", { className: styles.tableRow }, cells);
return this._createTable(totalSpan, [row], { className: styles.columnsTable + " " + styles.table });
},
_tasksTable: function(tasks) {
var rows = [];
var row;
var cell;
var position;
var plannedPosition;
var task;
var styles = GanttView.styles;
var coordinates = this._taskCoordinates = {};
var size = this._calculateMilestoneWidth();
var milestoneWidth = Math.round(size.width);
var resourcesField = this.options.resourcesField;
var className = [styles.resource, styles.resourceAlt];
var calculatedSize = this.options.calculatedSize;
var resourcesPosition;
var resourcesMargin = this._calculateResourcesMargin();
var taskBorderWidth = this._calculateTaskBorderWidth();
var resourceStyle;
var showPlannedTasks = this.options.showPlannedTasks;
var attributes = [{ className: styles.tableRow }, { className: styles.alt }];
var taskElement;
var addCoordinates = function(rowIndex) {
var taskLeft;
var taskRight;
taskLeft = position.left;
taskRight = taskLeft + position.width;
if (task.isMilestone()) {
taskLeft -= milestoneWidth / 2;
taskRight = taskLeft + milestoneWidth;
}
coordinates[task.id] = {
start: taskLeft,
end: taskRight,
rowIndex: rowIndex
};
};
for (var i = 0, l = tasks.length; i < l; i++) {
task = tasks[i];
position = this._taskPosition(task);
if (showPlannedTasks) {
plannedPosition = this._taskPositionPlanned(task);
plannedPosition.borderWidth = taskBorderWidth;
}
position.borderWidth = taskBorderWidth;
row = kendoDomElement("tr", attributes[i % 2]);
cell = kendoDomElement("td", { className: styles.tableCell });
if (task.start <= this.end && task.end >= this.start) {
taskElement = this._renderTask(tasks[i], position, plannedPosition);
if (this.options.navigatable) {
taskElement.children[0].attr["tabIndex"] = i ? -1 : 0;
}
cell.children.push(taskElement);
if (task[resourcesField] && task[resourcesField].length) {
if (isRtl) {
resourcesPosition = this._tableWidth - position.left;
} else {
resourcesPosition = Math.max((position.width || size.clientWidth), 0) + position.left;
}
resourceStyle = {
width: (this._tableWidth - (resourcesPosition + resourcesMargin)) + "px"
};
resourceStyle[isRtl ? "right" : "left"] = resourcesPosition + "px";
if (calculatedSize) {
resourceStyle.height = calculatedSize.cell + "px";
}
cell.children.push(kendoDomElement("div",
{
className: styles.resourcesWrap,
style: resourceStyle
},
this._renderResources(task[resourcesField], className[i % 2]))
);
}
addCoordinates(i);
}
row.children.push(cell);
rows.push(row);
}
return this._createTable(1, rows, { className: GanttView.styles.tasksTable + " " + styles.table });
},
_createTable: function(colspan, rows, styles) {
var cols = [];
var colgroup;
var tbody;
for (var i = 0; i < colspan; i++) {
cols.push(kendoDomElement("col"));
}
colgroup = kendoDomElement("colgroup", null, cols);
tbody = kendoDomElement("tbody", { className: GanttView.styles.tbody }, rows);
if (!styles.style) {
styles.style = {};
}
styles.style.width = this._tableWidth + "px";
styles.role = "presentation";
return kendoDomElement("table", styles, [colgroup, tbody]);
},
_calculateTableWidth: function() {
var slots = this._timeSlots();
var maxSpan = 0;
var totalSpan = 0;
var currentSpan;
var tableWidth;
for (var i = 0, length = slots.length; i < length; i++) {
currentSpan = slots[i].span;
totalSpan += currentSpan;
if (currentSpan > maxSpan) {
maxSpan = currentSpan;
}
}
tableWidth = Math.round((totalSpan * this.options.slotSize) / maxSpan);
return tableWidth;
},
_calculateMilestoneWidth: function() {
var size;
var className = GanttView.styles.task + " " + GanttView.styles.taskMilestone;
var boundingClientRect;
var milestone = $(`<div class="${className}">`).css({
visibility: "hidden",
position: "absolute"
});
this.content.append(milestone);
boundingClientRect = milestone[0].getBoundingClientRect();
size = {
"width": boundingClientRect.right - boundingClientRect.left,
"clientWidth": milestone[0].clientWidth
};
milestone.remove();
return size;
},
_calculateResourcesMargin: function() {
var margin;
var wrapper = $(`<div class="${GanttView.styles.resourcesWrap}">`).css({
visibility: "hidden",
position: "absolute"
});
this.content.append(wrapper);
margin = parseInt(wrapper.css(isRtl ? "margin-right" : "margin-left"), 10);
wrapper.remove();
return margin;
},
_calculateTaskBorderWidth: function() {
var width;
var className = GanttView.styles.task + " " + GanttView.styles.taskSingle;
var computedStyle;
var task = $(`<div class="${className}">`).css({
visibility: "hidden",
position: "absolute"
});
this.content.append(task);
computedStyle = kendo.getComputedStyles(task[0], ["border-left-width"]);
width = parseFloat(computedStyle["border-left-width"], 10);
task.remove();
return width;
},
_renderTask: function(task, position, plannedPosition) {
var editable = this.options.editable;
var taskLeft = position.left;
var styles = GanttView.styles;
var wrapClassName = styles.taskWrap;
var calculatedSize = this.options.calculatedSize;
var dragHandleStyle = {};
var taskWrapAttr = {
className: wrapClassName,
style: { left: taskLeft + "px" }
};
var children = [];
var endTaskDotRight = 0;
var taskFullWidth = position.width;
var taskWrapper, taskElement, progressHandleOffset, plannedElement;
var endTaskDotLeft, taskOffsetWrap, offsetElement, offsetWidth;
if (calculatedSize) {
taskWrapAttr.style.height = calculatedSize.cell + "px";
}
if (plannedPosition) {
if (task.isMilestone()) {
plannedElement = this._renderPlannedMilestone(position, plannedPosition);
} else {
plannedElement = this._renderPlannedSingleTask(position, plannedPosition, task);
}
children.push(plannedElement);
if (isRtl && plannedPosition.left <= position.left) {
taskWrapAttr.style.left = plannedPosition.left + "px";
}
}
if (task.summary) {
taskElement = this._renderSummary(task, position, plannedPosition);
taskWrapAttr.className += " " + styles.taskSummaryWrap;
} else if (task.isMilestone()) {
taskElement = this._renderMilestone(task, position);
taskWrapAttr.className += " " + styles.taskMilestoneWrap;
} else {
taskElement = this._renderSingleTask(task, position, plannedPosition);
}
if (plannedPosition && !task.isMilestone() && task.plannedStart < task.end && task.plannedEnd > task.start && task.plannedEnd < task.end) {
if (isRtl) {
taskFullWidth = position.left + position.width - plannedPosition.left;
} else {
taskFullWidth = plannedPosition.left + plannedPosition.width - position.left;
}
if (isRtl) {
offsetWidth = plannedPosition.left - position.left;
} else {
offsetWidth = position.left + position.width - (plannedPosition.left + plannedPosition.width);
}
offsetElement = kendoDomElement("div", {
className: styles.taskOffset,
style: { width: offsetWidth - 2 * plannedPosition.borderWidth + "px" }
});
if (editable && editable.resize !== false && editable.update !== false && !task.summary) {
if (editable.destroy !== false) {
offsetElement.children.push(kendoDomElement("span", { className: styles.taskActions, "aria-hidden": "true" }, [
kendoDomElement("a", { className: styles.link + " " + styles.taskDelete, href: "#", "aria-label": "Delete" }, [
kendoDomElement($(kendo.ui.icon(styles.iconDelete))[0])
])
]));
}
if (isRtl) {
offsetElement.children.push(kendoDomElement("span", {
className: styles.taskResizeHandle + " " + styles.taskResizeHandleWest,
style: {
right: position.width - 5 + "px"
}
}));
} else {
offsetElement.children.push(kendoDomElement("span", {
className: styles.taskResizeHandle + " " + styles.taskResizeHandleEast
}));
}
}
taskOffsetWrap = kendoDomElement("div", {
className: styles.taskOffsetWrap + " " + styles.taskInnerWrap
}, [ taskElement, offsetElement ]);
children.push(taskOffsetWrap);
} else if (plannedPosition) {
children.push(kendoDomElement("div", {
className: styles.taskInnerWrap
}, [ taskElement ]));
} else {
children.push(taskElement);
}
taskWrapper = kendoDomElement("div", taskWrapAttr, children);
if (editable && editable.dependencyCreate !== false) {
if (plannedPosition && task.plannedEnd > task.end) {
endTaskDotRight = plannedPosition.left + plannedPosition.width - position.left - position.width - 3 + "px";
}
taskWrapper.children.push(kendoDomElement("div", {
className: styles.taskDot + " " + styles.taskDotStart
}));
if (isRtl) {
endTaskDotRight = "auto";
if (plannedPosition && task.plannedEnd > task.end) {
endTaskDotLeft = position.left - plannedPosition.left + "px";
}
}
taskWrapper.children.push(kendoDomElement("div", {
className: styles.taskDot + " " + styles.taskDotEnd,
style: { right: endTaskDotRight, left: endTaskDotLeft }
}));
}
if (!task.summary && !task.isMilestone() && editable && editable.dragPercentComplete !== false && editable.update !== false && this._taskTemplate === null) {
progressHandleOffset = Math.round(taskFullWidth * task.percentComplete);
dragHandleStyle[isRtl ? "right" : "left"] = progressHandleOffset + "px";
taskWrapper.children.push(kendoDomElement("div", { className: styles.taskDragHandle, style: dragHandleStyle }));
}
return taskWrapper;
},
_renderSingleTask: function(task, position, plannedPosition) {
var styles = GanttView.styles;
var progressWidth;
var taskChildren = [];
var taskContent;
var editable = this.options.editable;
var classes = styles.task + " " + styles.taskSingle;
var widthExceptDelay = position.width;
if (plannedPosition) {
if (task.plannedEnd && task.plannedEnd <= task.start) {
classes += " " + styles.taskDelayed;
} else if (task.plannedEnd && task.plannedEnd > task.end) {
classes += " " + styles.taskAdvanced;
} else if (task.plannedEnd && task.plannedEnd < task.end) {
if (!isRtl) {
widthExceptDelay = widthExceptDelay - (position.left + position.width - plannedPosition.left - plannedPosition.width);
} else {
widthExceptDelay = widthExceptDelay + position.left - plannedPosition.left;
}
}
}
progressWidth = Math.round(widthExceptDelay * task.percentComplete);
if (this._taskTemplate !== null) {
taskContent = kendoHtmlElement(this._taskTemplate(task));
} else {
taskContent = kendoTextElement(task.title);
taskChildren.push(kendoDomElement("div", { className: styles.taskComplete, style: { width: progressWidth + "px" }, "aria-hidden": "true" }));
}
var content = kendoDomElement("div", { className: styles.taskContent }, [
kendoDomElement("div", { className: styles.taskTemplate }, [
taskContent
])
]);
taskChildren.push(content);
if (editable) {
if (editable.destroy !== false && (!plannedPosition || !task.plannedEnd || (task.end <= task.plannedEnd || task.start >= task.plannedEnd))) {
content.children.push(kendoDomElement("span", { className: styles.taskActions, "aria-hidden": "true" }, [
kendoDomElement("a", { className: styles.link + " " + styles.taskDelete, href: "#", "aria-label": "Delete" }, [
kendoDomElement($(kendo.ui.icon(styles.iconDelete))[0])
])
]));
}
if (editable.resize !== false && editable.update !== false) {
content.children.push(kendoDomElement("span", {
className: styles.taskResizeHandle + " " + styles.taskResizeHandleWest
}));
content.children.push(kendoDomElement("span", {
className: styles.taskResizeHandle + " " + styles.taskResizeHandleEast
}));
}
}
var element = kendoDomElement("div", {
className: classes, "data-uid": task.uid, role: "treeitem", style:
{ width: Math.max((widthExceptDelay - position.borderWidth * 2), 0) + "px" }
}, taskChildren);
return element;
},
_renderMilestone: function(task) {
var styles = GanttView.styles;
var classes = styles.task + " " + styles.taskMilestone;
var showPlanned = this.options.showPlannedTasks;
if (showPlanned && task.plannedEnd && task.plannedEnd < task.start) {
classes += " " + styles.taskDelayed;
} else if (task.plannedStart && task.plannedStart > task.end) {
classes += " " + styles.taskAdvanced;
}
return kendoDomElement("div", { className: classes, "data-uid": task.uid, role: "treeitem", "aria-label": task.title });
},
_renderSummary: function(task, position, plannedPosition) {
var styles = GanttView.styles;
var widthExceptDelay = position.width;
var progressWidth;
var classes = styles.task + " " + styles.taskSummary;
if (plannedPosition) {
if (task.plannedEnd && task.plannedEnd <= task.start) {
classes += " " + styles.taskDelayed;
} else if (task.plannedEnd && task.plannedEnd > task.end) {
classes += " " + styles.taskAdvanced;
} else if (task.plannedEnd && task.plannedEnd < task.end) {
if (!isRtl) {
widthExceptDelay = widthExceptDelay - (position.left + position.width - plannedPosition.left - plannedPosition.width);
} else {
widthExceptDelay = widthExceptDelay + position.left - plannedPosition.left;
}
}
}
progressWidth = Math.round(widthExceptDelay * task.percentComplete);
var element = kendoDomElement("div", { className: classes, "data-uid": task.uid, role: "treeitem", "aria-label": task.title, style: { width: widthExceptDelay + "px" } }, [
kendoDomElement("div", { className: styles.taskSummaryProgress, style: { width: progressWidth + "px" } }, [
kendoDomElement("div", { className: styles.taskSummaryComplete, style: { width: position.width + "px" } })
])
]);
return element;
},
_renderPlannedSingleTask: function(position, plannedPosition, task) {
var styles = GanttView.styles;
var children = [];
var style = {};
if (task.plannedStart && task.plannedEnd) {
children.push(kendoDomElement("div", { className: styles.taskPlannedMoment + " " + styles.taskPlannedMomentLeft }));
children.push(kendoDomElement("div", {
className: styles.taskPlannedDuration, style:
{ width: Math.max((plannedPosition.width - plannedPosition.borderWidth * 2 - 16), 0) + "px" }
}));
children.push(kendoDomElement("div", { className: styles.taskPlannedMoment }));
} else if (task.plannedStart) {
children.push(kendoDomElement("div", { className: styles.taskPlannedMoment + " " + styles.taskPlannedMomentLeft }));
} else if (task.plannedEnd) {
children.push(kendoDomElement("div", { className: styles.taskPlannedMoment, style: { "margin-left": Math.max(plannedPosition.width - 5, 0) + "px" } }));
}
if (isRtl) {
style = {
"margin-right": position.left - plannedPosition.left + position.width - plannedPosition.width + "px"
};
} else {
style = {
"margin-left": plannedPosition.left - position.left + "px"
};
}
var element = kendoDomElement("div", {
className: styles.taskPlanned,
style: style
}, children);
return element;
},
_renderPlannedMilestone: function(position, plannedPosition) {
var styles = GanttView.styles;
var style = {};
var element;
if (isRtl) {
style = {
"margin-right": position.left - plannedPosition.left + "px"
};
} else {
style = {
"margin-left": plannedPosition.left - position.left + "px"
};
}
element = kendoDomElement("div", {
className: styles.taskPlanned,
style: style
}, [
kendoDomElement("div", { className: styles.taskPlannedMoment })
]);
return element;
},
_renderResources: function(resources, className) {
var children = [];
var resource;
for (var i = 0, length = resources.length; i < length; i++) {
resource = resources[i];
children.push(kendoDomElement("span", {
className: className,
style: {
"color": resource.get("color")
}
}, [kendoTextElement(resource.get("name"))]));
}
if (isRtl) {
children.reverse();
}
return children;
},
_taskPosition: function(task) {
var round = Math.round;
var startLeft = round(this._offset(isRtl ? task.end : task.start));
var endLeft = round(this._offset(isRtl ? task.start : task.end));
return { left: startLeft, width: endLeft - startLeft };
},
_taskPositionPlanned: function(task) {
var round = Math.round;
var startLeft = round(this._offset(isRtl ? task.plannedEnd : task.plannedStart));
var endLeft = round(this._offset(isRtl ? task.plannedStart : task.plannedEnd));
return { left: startLeft, width: endLeft - startLeft };
},
_offset: function(date) {
var slots = this._timeSlots();
var slot;
var startOffset;
var slotDuration;
var slotOffset = 0;
var startIndex;
if (!slots.length) {
return 0;
}
startIndex = this._slotIndex("start", date);
slot = slots[startIndex];
if (slot.end < date) {
slotOffset = slot.offsetWidth;
} else if (slot.start <= date) {
startOffset = date - slot.start;
slotDuration = slot.end - slot.start;
slotOffset = (startOffset / slotDuration) * slot.offsetWidth;
}
if (isRtl) {
slotOffset = (slot.offsetWidth + 1) - slotOffset; // Add one pixel for border
}
return slot.offsetLeft + slotOffset;
},
_slotIndex: function(field, value, reverse) {
var slots = this._timeSlots();
var startIdx = 0;
var endIdx = slots.length - 1;
var middle;
if (reverse) {
slots = [].slice.call(slots).reverse();
}
do {
middle = Math.ceil((endIdx + startIdx) / 2);
if (slots[middle][field] < value) {
startIdx = middle;
} else {
if (middle === endIdx) {
middle--;
}
endIdx = middle;
}
} while (startIdx !== endIdx);
if (reverse) {
startIdx = (slots.length - 1) - startIdx;
}
return startIdx;
},
_timeByPosition: function(x, snap, snapToEnd) {
var slot = this._slotByPosition(x);
if (snap) {
return snapToEnd ? slot.end : slot.start;
}
var offsetLeft = x - this.element.find(DOT + GanttView.styles.tasksTable).offset().left;
var duration = slot.end - slot.start;
var slotOffset = offsetLeft - slot.offsetLeft;
if (isRtl) {
slotOffset = slot.offsetWidth - slotOffset;
}
return new Date(slot.start.getTime() + (duration * (slotOffset / slot.offsetWidth)));
},
_slotByPosition: function(x) {
var offsetLeft = x - this.element.find(DOT + GanttView.styles.tasksTable).offset().left;
var slotIndex = this._slotIndex("offsetLeft", offsetLeft, isRtl);
return this._timeSlots()[slotIndex];
},
_renderDependencies: function(dependencies) {
var elements = [];
var tree = this._dependencyTree;
for (var i = 0, l = dependencies.length; i < l; i++) {
elements.push.apply(elements, this._renderDependency(dependencies[i]));
}
tree.render(elements);
},
_renderDependency: function(dependency) {
var predecessor = this._taskCoordinates[dependency.predecessorId];
var successor = this._taskCoordinates[dependency.successorId];
var elements;
var method;
if (!predecessor || !successor) {
return [];
}
method = "_render" + ["FF", "FS", "SF", "SS"][isRtl ? 3 - dependency.type : dependency.type];
elements = this[method](predecessor, successor);
for (var i = 0, length = elements.length; i < length; i++) {
elements[i].attr["data-uid"] = dependency.uid;
}
return elements;
},
_renderFF: function(from, to) {
var lines = this._dependencyFF(from, to, false);
lines[lines.length - 1].children[0] = this._arrow(true);
return lines;
},
_renderSS: function(from, to) {
var lines = this._dependencyFF(to, from, true);
lines[0].children[0] = this._arrow(false);
return lines.reverse();
},
_renderFS: function(from, to) {
var lines = this._dependencyFS(from, to, false);
lines[lines.length - 1].children[0] = this._arrow(false);
return lines;
},
_renderSF: function(from, to) {
var lines = this._dependencyFS(to, from, true);
lines[0].children[0] = this._arrow(true);
return lines.reverse();
},
_dependencyFF: function(from, to, reverse) {
var that = this;
var lines = [];
var left = 0;
var top = 0;
var width = 0;
var height = 0;
var dir = reverse ? "start" : "end";
var delta;
var overlap = 2;
var arrowOverlap = 1;
var rowHeight = this._rowHeight;
var minLineWidth = 10;
var fromTop = from.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
var toTop = to.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
var styles = GanttView.styles;
var addHorizontal = function() {
lines.push(that._line(styles.line + " " + styles.lineHorizontal, { left: left + "px", top: top + "px", width: width + "px" }));
};
var addVertical = function() {
lines.push(that._line(styles.line + " " + styles.lineVertical, { left: left + "px", top: top + "px", height: height + "px" }));
};
left = from[dir];
top = fromTop;
width = minLineWidth;
delta = to[dir] - from[dir];
if ((delta) > 0 !== reverse) {
width = Math.abs(delta) + minLineWidth;
}
if (reverse) {
left -= width;
width -= arrowOverlap;
addHorizontal();
} else {
addHorizontal();
left += width - overlap;
}
if (toTop < top) {
height = top - toTop;
height += overlap;
top = toTop;
addVertical();
} else {
height = toTop - top;
height += overlap;
addVertical();
top += (height - overlap);
}
width = Math.abs(left - to[dir]);
if (!reverse) {
width -= arrowOverlap;
left -= width;
}
addHorizontal();
return lines;
},
_dependencyFS: function(from, to, reverse) {
var that = this;
var lines = [];
var left = 0;
var top = 0;
var width = 0;
var height = 0;
var rowHeight = this._rowHeight;
var minLineHeight = Math.floor(rowHeight / 2);
var minLineWidth = 10;
var minDistance = 2 * minLineWidth;
var delta = to.start - from.end;
var overlap = 2;
var arrowOverlap = 1;
var fromTop = from.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
var toTop = to.rowIndex * rowHeight + Math.floor(rowHeight / 2) - 1;
var styles = GanttView.styles;
var addHorizontal = function() {
lines.push(that._line(styles.line + " " + styles.lineHorizontal, { left: left + "px", top: top + "px", width: width + "px" }));
};
var addVertical = function() {
lines.push(that._line(styles.line + " " + styles.lineVertical, { left: left + "px", top: top + "px", height: height + "px" }));
};
left = from.end;
top = fromTop;
width = minLineWidth;
if (reverse) {
left += arrowOverlap;
if (delta > minDistance) {
width = delta - (minLineWidth - overlap);
}
width -= arrowOverlap;
}
addHorizontal();
left += width - overlap;
if ((delta) <= minDistance) {
height = reverse ? Math.abs(toTop - fromTop) - minLineHeight : minLineHeight;
if (toTop < fromTop) {
top -= height;
height += overlap;
addVertical();
} else {
addVertical();
top += height;
}
width = (from.end - to.start) + minDistance;
if (width < minLineWidth) {
width = minLineWidth;
}
left -= width - overlap;
addHorizontal();
}
if (toTop < fromTop) {
height = top - toTop;
top = toTop;
height += overlap;
addVertical();
} else {
height = toTop - top;
addVertical();
top += height;
}
width = to.start - left;
if (!reverse) {
width -= arrowOverlap;
}
addHorizontal();
return lines;
},
_line: function(className, styles) {
return kendoDomElement("div", { className: className, style: styles });
},
_arrow: function(direction) {
return kendoDomElement("span", { className: direction ? GanttView.styles.arrowWest : GanttView.styles.arrowEast });
},
_colgroup: function() {
var slots = this._timeSlots();
var count = slots.length;
var cols = [];
for (var i = 0; i < count; i++) {
for (var j = 0, length = slots[i].span; j < length; j++) {
cols.push(kendoDomElement("col"));
}
}
return kendoDomElement("colgroup", null, cols);
},
_createDragHint: function(element) {
var styles = GanttView.styles;
var plannedElement;
this._dragHint = element
.clone()
.addClass(styles.dragHint)
.css({
"cursor": "move"
});
plannedElement = this._dragHint.find(DOT + styles.taskPlanned);
plannedElement.css({
"visibility": "hidden"
});
if (isRtl && element.find(DOT + styles.taskAdvanced).length > 0) {
plannedElement.css({
"margin-right": "auto",
"width": 0
});
this._dragHint.find(DOT + styles.taskDotEnd).css({
"left": 0
});
}
element
.closest("td")
.append(this._dragHint);
},
_updateDragHint: function(start) {
var left = this._offset(start);
this._dragHint
.css({
"left": left
});
},
_removeDragHint: function() {
if (this._dragHint) {
this._dragHint.remove();
this._dragHint = null;
}
},
_createResizeHint: function(task) {
var styles = GanttView.styles;
var taskTop = this._taskCoordinates[task.id].rowIndex * this._rowHeight;
var tooltipHeight;
var tooltipTop;
var options = this.options;
var messages = options.messages;
this._resizeHint = $(RESIZE_HINT({ styles: styles })).css({
"top": 0,
"height": this._contentHeight
});
this.content.append(this._resizeHint);
this._resizeTooltip = $(RESIZE_TOOLTIP_TEMPLATE({
styles: styles,
start: task.start,
end: task.end,
messages: messages.views,
format: options.resizeTooltipFormat
}))
.css({
"z-index": "100002",
"top": 0,
"left": 0
});
this.content.append(this._resizeTooltip);
this._resizeTooltipWidth = outerWidth(this._resizeTooltip);
tooltipHeight = outerHeight(this._resizeTooltip);
tooltipTop = taskTop - tooltipHeight;
if (tooltipTop < 0) {
tooltipTop = taskTop + this._rowHeight;
}
this._resizeTooltipTop = tooltipTop;
},
_updateResizeHint: function(start, end, resizeStart) {
var left = this._offset(isRtl ? end : start);
var right = this._offset(isRtl ? start : end);
var width = right - left;
var tooltipLeft = (resizeStart !== isRtl) ? left : right;
var tablesWidth = this._tableWidth - kendo.support.scrollbar();
var tooltipWidth = this._resizeTooltipWidth;
var options = this.options;
var messages = options.messages;
var tableOffset = this.element.find(DOT + GanttView.styles.tasksTable).offset().left - this.element.find(DOT + GanttView.styles.tasksWrapper).offset().left;
if (isRtl) {
left += tableOffset;
}
this._resizeHint
.css({
"left": left,
"width": width
});
if (this._resizeTooltip) {
this._resizeTooltip.remove();
}
tooltipLeft -= Math.round(tooltipWidth / 2);
if (tooltipLeft < 0) {
tooltipLeft = 0;
} else if (tooltipLeft + tooltipWidth > tablesWidth) {
tooltipLeft = tablesWidth - tooltipWidth;
}
if (isRtl) {
tooltipLeft += tableOffset;
}
this._resizeTooltip = $(RESIZE_TOOLTIP_TEMPLATE({
styles: GanttView.styles,
start: start,
end: end,
messages: messages.views,
format: options.resizeTooltipFormat
}))
.css({
"z-index": "100002",
"top": this._resizeTooltipTop,
"left": tooltipLeft,
"min-width": tooltipWidth
})
.appendTo(this.content);
},
_removeResizeHint: function() {
this._resizeHint.remove();
this._resizeHint = null;
this._resizeTooltip.remove();
this._resizeTooltip = null;
},
_updatePercentCompleteTooltip: function(top, left, text) {
this._removePercentCompleteTooltip();
var tooltip = this._percentCompleteResizeTooltip = $(PERCENT_RESIZE_TOOLTIP_TEMPLATE({ styles: GanttView.styles, text: text }));
kendo.applyStylesFromKendoAttributes(tooltip, ["z-index", "left"]);
tooltip.appendTo(this.element);
var tooltipMiddle = Math.round(outerWidth(tooltip) / 2);
var arrow = tooltip.find(DOT + GanttView.styles.callout);
var arrowHeight = Math.round(outerWidth(arrow) / 2);
tooltip.css({
"top": top - (outerHeight(tooltip) + arrowHeight),
"left": left - tooltipMiddle
});
arrow.css("left", tooltipMiddle - arrowHeight);
},
_removePercentCompleteTooltip: function() {
if (this._percentCompleteResizeTooltip) {
this._percentCompleteResizeTooltip.remove();
}
this._percentCompleteResizeTooltip = null;
},
_updateDependencyDragHint: function(from, to) {
this._removeDependencyDragHint();
this._creteDependencyDragHint(from, to);
},
_creteDependencyDragHint: function(from, to) {
var styles = GanttView.styles;
var deltaX = to.x - from.x;
var deltaY = to.y - from.y;
var width = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var angle = Math.atan(deltaY / deltaX);
if (deltaX < 0) {
angle += Math.PI;
}
$("<div class='" + styles.line + " " + styles.lineHorizontal + " " + styles.dependencyHint + "'></div>")
.css({
"top": from.y,
"left": from.x,
"width": width,
"transform-origin": "0% 0",
"-ms-transform-origin": "0% 0",
"-webkit-transform-origin": "0% 0",
"transform": "rotate(" + angle + "rad)",
"-ms-transform": "rotate(" + angle + "rad)",
"-webkit-transform": "rotate(" + angle + "rad)"
})
.appendTo(this.content);
},
_removeDependencyDragHint: function() {
this.content.find(DOT + GanttView.styles.dependencyHint).remove();
},
_createTaskTooltip: function(task, element, mouseLeft) {
var styles = GanttView.styles;
var options = this.options;
var content = this.content;
var contentOffset = content.offset();
var contentScrollLeft = kendo.scrollLeft(content);
var row = $(element).parents("tr").first();
var rowOffset = row.offset();
var template = (options.tooltip && options.tooltip.template) ? kendo.template(options.tooltip.template) : TASK_TOOLTIP_TEMPLATE;
var left = isRtl ? mouseLeft - (contentOffset.left + contentScrollLeft + kendo.support.scrollbar())
: mouseLeft - (contentOffset.left - contentScrollLeft);
var top = (rowOffset.top + outerHeight(row) - contentOffset.top) + content.scrollTop();
var tooltip = this._taskTooltip = $('<div class="' + styles.tooltipWrapper + '" >' +
'<div class="' + styles.taskContent + '"></div></div>');
tooltip
.css({
"z-index": "100002",
"left": left,
"top": top
})
.appendTo(content)
.find(DOT + styles.taskContent)
.append(template({
styles: styles,
task: task,
messages: options.messages.views
}));
this._adjustTooltipDimensions(tooltip, rowOffset, contentOffset, left, contentScrollLeft);
},
_removeTaskTooltip: function() {
if (this._taskTooltip) {
this._taskTooltip.remove();
}
this._taskTooltip = null;
},
_createOffsetTooltip: function(task, element, mouseLeft) {
var styles = GanttView.styles;
var content = this.content;
var contentOffset = content.offset();
var contentScrollLeft = kendo.scrollLeft(content);
var row = element.parents("tr").first();
var rowOffset = row.offset();
var left = isRtl ? mouseLeft - (contentOffset.left + contentScrollLeft + kendo.support.scrollbar())
: mouseLeft - (contentOffset.left - contentScrollLeft);
var top = (rowOffset.top + outerHeight(row) - contentOffset.top) + content.scrollTop();
var tooltip = this._offsetTooltip = $('<div class="' + styles.tooltipWrapper + '" ></div>');
var offsetValue = Math.round((task.end.getTime() - task.plannedEnd.getTime()) / 60000);
var plannedTasksMessages = this.options.messages.plannedTasks;
var minutes = offsetValue % 60;
var offsetText = minutes + " " + plannedTasksMessages.minutes;
var hours, days;
if (offsetValue >= 60) {
hours = offsetValue = Math.floor(offsetValue / 60);
offsetText = hours + " " + plannedTasksMessages.hours;
if (minutes !== 0) {
offsetText += (" " + minutes + " " + plannedTasksMessages.minutes);
}
if (offsetValue >= 24) {
hours = offsetValue % 24;
days = offsetValue = Math.floor(offsetValue / 24);
offsetText = days + " " + plannedTasksMessages.days;
if (hours !== 0) {
offsetText += (" " + hours + " " + plannedTasksMessages.hours);
}
}
}
tooltip
.css({
"z-index": "100002",
"left": left,
"top": top
})
.addClass(styles.offsetTooltipDelay)
.appendTo(content)
.append(OFFSET_TOOLTIP_TEMPLATE({
offsetPrefix: plannedTasksMessages.offsetTooltipDelay,
offsetText: offsetText
}));
this._adjustTooltipDimensions(tooltip, rowOffset, contentOffset, left, contentScrollLeft);
},
_removeOffsetTooltip: function() {
if (this._offsetTooltip) {
this._offsetTooltip.remove();
}
this._offsetTooltip = null;
},
_createPlannedTooltip: function(task, element, mouseLeft) {
var styles = GanttView.styles;
var content = this.content;
var contentOffset = content.offset();
var contentScrollLeft = kendo.scrollLeft(content);
var row = element.parents("tr").first();
var rowOffset = row.offset();
var left = isRtl ? mouseLeft - (contentOffset.left + contentScrollLeft + kendo.support.scrollbar())
: mouseLeft - (contentOffset.left - contentScrollLeft);
var top = (rowOffset.top + outerHeight(row) - contentOffset.top) + content.scrollTop();
var tooltip = this._plannedTooltip = $('<div class="' + styles.tooltipWrapper + ' ' + styles.plannedTooltip + '" ></div>');
var editorMessages = this.options.messages.editor;
tooltip
.css({
"z-index": "100002",
"left": left,
"top": top
})
.appendTo(content)
.append(PLANNED_TOOLTIP_TEMPLATE({
plannedStart: editorMessages.plannedStart,
startDate: kendo.toString(task.plannedStart, "H:mm tt ddd, MMM dd"),
plannedEnd: editorMessages.plannedEnd,
endDate: kendo.toString(task.plannedEnd, "H:mm tt ddd, MMM dd")
}));
this._adjustTooltipDimensions(tooltip, rowOffset, contentOffset, left, contentScrollLeft);
},
_removePlannedTooltip: function() {
if (this._plannedTooltip) {
this._plannedTooltip.remove();
}
this._plannedTooltip = null;
},
_adjustTooltipDimensions: function(tooltip, rowOffset, contentOffset, left, contentScrollLeft) {
var content = this.content;
var contentWidth = content.width();
var tooltipWidth;
if (outerHeight(tooltip) < rowOffset.top - contentOffset.top) {
tooltip.css("top", ((rowOffset.top - contentOffset.top) - outerHeight(tooltip)) + content.scrollTop());
}
tooltipWidth = outerWidth(tooltip);
if ((tooltipWidth + left) - contentScrollLeft > contentWidth) {
left -= tooltipWidth;
if (left < contentScrollLeft) {
left = (contentScrollLeft + contentWidth) - (tooltipWidth + 17);
}
tooltip.css("left", left);
}
},
_scrollTo: function(element) {
var elementLeft = element.offset().left;
var elementWidth = element.width();
var elementRight = elementLeft + elementWidth;
var row = element.closest("tr");
var rowTop = row.offset().top;
var rowHeight = row.height();
var rowBottom = rowTop + rowHeight;
var content = this.content;
var contentOffset = content.offset();
var contentTop = contentOffset.top;
var contentHeight = content.height();
var contentBottom = contentTop + contentHeight;
var contentLeft = contentOffset.left;
var contentWidth = content.width();
var contentRight = contentLeft + contentWidth;
var scrollbarWidth = kendo.support.scrollbar();
if (rowTop < contentTop) {
content.scrollTop(content.scrollTop() + (rowTop - contentTop));
} else if (rowBottom > contentBottom) {
content.scrollTop(content.scrollTop() + (rowBottom + scrollbarWidth - contentBottom));
}
if (elementLeft < contentLeft && elementWidth > contentWidth && elementRight < contentRight ||
elementRight > contentRight && elementWidth < contentWidth) {
kendo.scrollLeft(content, kendo.scrollLeft(content) + (elementRight + scrollbarWidth - contentRight));
} else if (elementRight > contentRight && elementWidth > contentWidth && elementLeft > contentLeft ||
elementLeft < contentLeft && elementWidth < contentWidth) {
kendo.scrollLeft(content, kendo.scrollLeft(content) + (elementLeft - contentLeft));
}
},
_scrollToDate: function(date) {
var viewStart = this.start;
var viewEnd = this.end;
var offset;
if (date >= viewStart && date < viewEnd) {
offset = this._offset(date);
if (kendo.support.isRtl(this.element)) {
offset = this._tableWidth - offset;
}
kendo.scrollLeft(this.content, offset);
}
},
_timeSlots: function() {
if (!this._slots || !this._slots.length) {
return [];
}
return this._slots[this._slots.length - 1];
},
_headers: function(columnLevels) {
var rows = [];
var level;
var headers;
var column;
var headerText;
var styles = GanttView.styles;
for (var levelIndex = 0, levelCount = columnLevels.length; levelIndex < levelCount; levelIndex++) {
level = columnLevels[levelIndex];
headers = [];
for (var columnIndex = 0, columnCount = level.length; columnIndex < columnCount; columnIndex++) {
column = level[columnIndex];
headerText = kendoHtmlElement(column.text);
headers.push(kendoDomElement("td", { colspan: column.span, className: styles.header + (column.isNonWorking ? (" " + styles.nonWorking) : "") }, [headerText]));
}
rows.push(kendoDomElement("tr", { className: styles.tableRow }, headers));
}
return rows;
},
_hours: function(start, end) {
var slotEnd;
var slots = [];
var options = this.options;
var workDayStart = options.workDayStart.getHours();
var workDayEnd = options.workDayEnd.getHours();
var isWorkHour;
var hours;
var hourSpan = options.hourSpan;
start = new Date(start);
end = new Date(end);
if (options.showWorkHours) {
start.setHours(workDayStart);
}
while (start < end) {
slotEnd = new Date(start);
hours = slotEnd.getHours();
isWorkHour = hours >= workDayStart && hours < workDayEnd;
slotEnd.setHours(slotEnd.getHours() + hourSpan);
if (hours == slotEnd.getHours()) {
// Chrome DTS Fix
slotEnd.setHours(slotEnd.getHours() + 2 * hourSpan);
}
if (!options.showWorkHours || isWorkHour) {
slots.push({
start: start,
end: slotEnd,
isNonWorking: !isWorkHour,
span: 1
});
}
start = slotEnd;
}
return slots;
},
_days: function(start, end) {
var slotEnd;
var slots = [];
var isWorkDay;
start = new Date(start);
end = new Date(end);
while (start < end) {
slotEnd = end < kendo.date.nextDay(start) ? end : kendo.date.nextDay(start);
isWorkDay = this._isWorkDay(start);
if (!this.options.showWorkDays || isWorkDay) {
slots.push({
start: start,
end: slotEnd,
isNonWorking: !isWorkDay,
span: 1
});
}
start = slotEnd;
}
return slots;
},
_weeks: function(start, end) {
var slotEnd;
var slots = [];
var firstDay = this.calendarInfo().firstDay;
var daySlots;
var span;
start = new Date(start);
end = new Date(end);
while (start < end) {
slotEnd = kendo.date.dayOfWeek(kendo.date.addDays(start, 1), firstDay, 1);
if (slotEnd > end) {
slotEnd = end;
}
daySlots = this._days(start, slotEnd);
span = daySlots.length;
if (span > 0) {
slots.push({
start: daySlots[0].start,
end: daySlots[span - 1].end,
span: span
});
}
start = slotEnd;
}
return slots;
},
_months: function(start, end) {
var slotEnd;
var endMonth;
var slots = [];
var daySlots;
var span;
start = new Date(start);
end = new Date(end);
while (start < end) {
slotEnd = new Date(start);
endMonth = kendo.date.firstDayOfMonth(new Date(slotEnd.setMonth(slotEnd.getMonth() + 1)));
slotEnd = end < endMonth ? end : endMonth;
daySlots = this._days(start, slotEnd);
span = daySlots.length;
if (span > 0) {
slots.push({
start: daySlots[0].start,
end: daySlots[span - 1].end,
span: span
});
}
start = slotEnd;
}
return slots;
},
_years: function(start, end) {
var slotEnd;
var monthSpan;
var endMonth;
var slots = [];
start = new Date(start);
end = new Date(end);
while (start < end) {
slotEnd = new Date(start);
slotEnd = kendo.date.firstDayOfMonth(new Date(slotEnd.setMonth(12)));
if (slotEnd >= end) {
slotEnd = end;
}
endMonth = slotEnd.getMonth() || 12;
monthSpan = endMonth - start.getMonth();
slots.push({
start: start,
end: slotEnd,
span: monthSpan
});
start = slotEnd;
}
return slots;
},
_slotHeaders: function(slots, template) {
var columns = [];
var slot;
for (var i = 0, l = slots.length; i < l; i++) {
slot = slots[i];
columns.push({
text: template(slot),
isNonWorking: !!slot.isNonWorking,
span: slot.span
});
}
return columns;
},
_isWorkDay: function(date) {
var day = date.getDay();
var workDays = this._workDays;
for (var i = 0, l = workDays.length; i < l; i++) {
if (workDays[i] === day) {
return true;
}
}
return false;
},
calendarInfo: function() {
return kendo.getCulture().calendars.standard;
},
_renderCurrentTime: function() {
var currentTime = this._getCurrentTime();
var timeOffset = this._offset(currentTime);
var element = $("<div class='k-current-time'></div>");
var viewStyles = GanttView.styles;
var tablesWrap = this.element.find(DOT + viewStyles.tasksWrapper);
var tasksTable = this.element.find(DOT + viewStyles.tasksTable);
var slot;
if (!this.content || !this._timeSlots().length) {
return;
}
this.content.find(".k-current-time").remove();
slot = this._timeSlots()[this._slotIndex("start", currentTime)];
if (currentTime < slot.start || currentTime > slot.end) {
return;
}
if (tablesWrap.length && tasksTable.length) {
timeOffset += tasksTable.offset().left - tablesWrap.offset().left;
}
element.css({
left: timeOffset + "px",
top: "0px",
width: "1px",
height: this._contentHeight + "px"
})
.appendTo(this.content);
},
_getCurrentTime: function() {
// Introduced for testing purposes
return new Date();
},
_currentTime: function() {
var markerOptions = this.options.currentTimeMarker;
if (markerOptions !== false && markerOptions.updateInterval !== undefined) {
this._renderCurrentTime();
this._currentTimeUpdateTimer = setInterval(this._renderCurrentTime.bind(this), markerOptions.updateInterval);
}
}
});
extend(true, GanttView, { styles: viewStyles });
kendo.ui.GanttDayView = GanttView.extend({
name: "day",
options: {
timeHeaderTemplate: TIME_HEADER_TEMPLATE,
dayHeaderTemplate: DAY_HEADER_TEMPLATE,
resizeTooltipFormat: "h:mm tt ddd, MMM d"
},
range: function(range) {
var optionsRange = this.options.range;
this.start = kendo.date.getDate(range.start);
this.end = kendo.date.getDate(range.end);
if (kendo.date.getMilliseconds(range.end) > 0 || this.end.getTime() === this.start.getTime()) {
this.end = kendo.date.addDays(this.end, 1);
}
if (optionsRange && optionsRange.start) {
this.start = kendo.date.getDate(optionsRange.start);
this.start.setHours(optionsRange.start.getHours());
}
if (optionsRange && optionsRange.end) {
this.end = kendo.date.getDate(optionsRange.end);
this.end.setHours(optionsRange.end.getHours());
}
},
_createSlots: function() {
var daySlots;
var daySlot;
var hourSlots;
var hours;
var slots = [];
daySlots = this._days(this.start, this.end);
hourSlots = [];
for (var i = 0, l = daySlots.length; i < l; i++) {
daySlot = daySlots[i];
hours = this._hours(daySlot.start, daySlot.end);
daySlot.span = hours.length;
hourSlots.push.apply(hourSlots, hours);
}
slots.push(daySlots);
slots.push(hourSlots);
return slots;
},
_layout: function() {
var rows = [];
var options = this.options;
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.dayHeaderTemplate)));
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.timeHeaderTemplate)));
return rows;
}
});
kendo.ui.GanttWeekView = GanttView.extend({
name: "week",
options: {
dayHeaderTemplate: DAY_HEADER_TEMPLATE,
weekHeaderTemplate: WEEK_HEADER_TEMPLATE,
resizeTooltipFormat: "h:mm tt ddd, MMM d"
},
range: function(range) {
var optionsRange = this.options.range;
var calendarInfo = this.calendarInfo();
var firstDay = calendarInfo.firstDay;
var rangeEnd = range.end;
var endDay;
if (firstDay === rangeEnd.getDay()) {
rangeEnd.setDate(rangeEnd.getDate() + 7);
}
this.start = kendo.date.getDate(kendo.date.dayOfWeek(range.start, firstDay, -1));
this.end = kendo.date.getDate(kendo.date.dayOfWeek(rangeEnd, firstDay, 1));
if (optionsRange && optionsRange.start) {
this.start = kendo.date.getDate(optionsRange.start);
}
if (optionsRange && optionsRange.end) {
endDay = new Date(optionsRange.end);
if (kendo.date.getDate(endDay) < optionsRange.end) {
this.end = kendo.date.getDate(new Date(endDay.setDate(endDay.getDate() + 1)));
} else {
this.end = kendo.date.getDate(endDay);
}
}
},
_createSlots: function() {
var slots = [];
slots.push(this._weeks(this.start, this.end));
slots.push(this._days(this.start, this.end));
return slots;
},
_layout: function() {
var rows = [];
var options = this.options;
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.weekHeaderTemplate)));
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.dayHeaderTemplate)));
return rows;
}
});
kendo.ui.GanttMonthView = GanttView.extend({
name: "month",
options: {
weekHeaderTemplate: WEEK_HEADER_TEMPLATE,
monthHeaderTemplate: MONTH_HEADER_TEMPLATE,
resizeTooltipFormat: "dddd, MMM d, yyyy"
},
range: function(range) {
var optionsRange = this.options.range;
var endDay;
this.start = kendo.date.firstDayOfMonth(range.start);
this.end = kendo.date.addDays(kendo.date.getDate(kendo.date.lastDayOfMonth(range.end)), 1);
if (optionsRange && optionsRange.start) {
this.start = kendo.date.getDate(optionsRange.start);
}
if (optionsRange && optionsRange.end) {
endDay = new Date(optionsRange.end);
if (kendo.date.getDate(endDay) < optionsRange.end) {
this.end = kendo.date.getDate(new Date(endDay.setDate(endDay.getDate() + 1)));
} else {
this.end = kendo.date.getDate(endDay);
}
}
},
_createSlots: function() {
var slots = [];
slots.push(this._months(this.start, this.end));
slots.push(this._weeks(this.start, this.end));
return slots;
},
_layout: function() {
var rows = [];
var options = this.options;
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.monthHeaderTemplate)));
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.weekHeaderTemplate)));
return rows;
}
});
kendo.ui.GanttYearView = GanttView.extend({
name: "year",
options: {
yearHeaderTemplate: YEAR_HEADER_TEMPLATE,
monthHeaderTemplate: MONTH_HEADER_TEMPLATE,
resizeTooltipFormat: "dddd, MMM d, yyyy"
},
range: function(range) {
var optionsRange = this.options.range;
var firstDayOfMonth;
this.start = kendo.date.firstDayOfMonth(new Date(range.start.setMonth(0)));
this.end = kendo.date.firstDayOfMonth(new Date(range.end.setMonth(12))); //set month to first month of next year
if (optionsRange && optionsRange.start) {
this.start = kendo.date.firstDayOfMonth(optionsRange.start);
}
if (optionsRange && optionsRange.end) {
firstDayOfMonth = kendo.date.firstDayOfMonth(optionsRange.end);
this.end = kendo.date.getDate(new Date(firstDayOfMonth.setMonth(firstDayOfMonth.getMonth() + 1)));
}
},
_createSlots: function() {
var slots = [];
var monthSlots = this._months(this.start, this.end);
$(monthSlots).each(function(index, slot) {
slot.span = 1;
});
slots.push(this._years(this.start, this.end));
slots.push(monthSlots);
return slots;
},
_layout: function() {
var rows = [];
var options = this.options;
rows.push(this._slotHeaders(this._slots[0], kendo.template(options.yearHeaderTemplate)));
rows.push(this._slotHeaders(this._slots[1], kendo.template(options.monthHeaderTemplate)));
return rows;
}
});
var timelineStyles = {
wrapper: "k-gantt-timeline k-grid",
gridHeader: "k-grid-header",
gridHeaderWrap: "k-grid-header-wrap",
gridContent: "k-grid-content",
gridContentWrap: "k-grid-content",
tasksWrapper: "k-gantt-tables",
dependenciesWrapper: "k-gantt-dependencies",
task: "k-task",
taskOffset: "k-task-offset",
taskOffsetWrap: "k-task-offset-wrap",
taskPlanned: "k-task-planned",
line: "k-gantt-line",
taskResizeHandle: "k-resize-handle",
taskResizeHandleWest: "k-resize-w",
taskDragHandle: "k-task-draghandle",
taskComplete: "k-task-complete",
taskDelete: "k-task-delete",
taskWrapActive: "k-task-wrap-active",
taskWrap: "k-task-wrap",
taskDot: "k-task-dot",
taskDotStart: "k-task-start",
taskDotEnd: "k-task-end",
hovered: "k-hover",
selected: "k-selected",
origin: "k-origin"
};
var GanttTimeline = kendo.ui.GanttTimeline = Widget.extend({
init: function(element, options) {
Widget.fn.init.call(this, element, options);
if (!this.options.views || !this.options.views.length) {
this.options.views = ["day", "week", "month"];
}
isRtl = kendo.support.isRtl(element);
this._wrapper();
this._domTrees();
this._views();
this._selectable();
this._draggable();
this._resizable();
this._percentResizeDraggable();
this._createDependencyDraggable();
this._attachEvents();
this._navigatable();
this._tooltip();
},
options: {
name: "GanttTimeline",
messages: {
views: {
day: "Day",
week: "Week",
month: "Month",
year: "Year",
start: "Start",
end: "End"
}
},
snap: true,
selectable: true,
editable: true
},
destroy: function() {
Widget.fn.destroy.call(this);
clearTimeout(this._tooltipTimeout);
if (this._currentTimeUpdateTimer) {
clearInterval(this._currentTimeUpdateTimer);
}
this._unbindView(this._selectedView);
if (this._moveDraggable) {
this._moveDraggable.destroy();
}
if (this._resizeDraggable) {
this._resizeDraggable.destroy();
}
if (this._percentDraggable) {
this._percentDraggable.destroy();
}
if (this._dependencyDraggable) {
this._dependencyDraggable.destroy();
}
if (this.touch) {
this.touch.destroy();
}
this._headerTree = null;
this._taskTree = null;
this._dependencyTree = null;
this.wrapper.off(NS);
kendo.destroy(this.wrapper);
},
_findNext: function(row) {
return row.next().find(".k-task");
},
_findFirst: function(row) {
return row.closest(".k-table-tbody").find(".k-task").first();
},
_findLast: function(row) {
return row.closest(".k-table-tbody").find(".k-task").last();
},
_findPrev: function(row) {
return row.prev().find(".k-task");
},
_scrollTasks: function(dir) {
var that = this;
var timelineWrapper = that.wrapper;
var timelineScroll = kendo.scrollLeft(timelineWrapper.find(".k-grid-content"));
kendo.scrollLeft(timelineWrapper.find(".k-grid-content"), (timelineScroll + (20 * dir)));
},
_navigatable: function() {
var that = this;
if (!that.options.navigatable) {
return;
}
that.wrapper.on("keydown" + NS, ".k-gantt-tables .k-gantt-tasks .k-task", function(e) {
var target = $(e.target);
var row = target.closest(".k-table-row");
var handled = false;
var newTask;
if (e.keyCode == keys.HOME) {
newTask = that._findFirst(row);
}
if (e.keyCode == keys.END) {
newTask = that._findLast(row);
}
if (e.keyCode == keys.DOWN) {
newTask = that._findNext(row);
}
if (e.keyCode == keys.UP) {
newTask = that._findPrev(row);
}
if (newTask) {
handled = true;
if (newTask[0] !== target[0]) {
target.closest(".k-gantt-tasks").find(".k-task").attr("tabindex", -1);
newTask.attr("tabindex", 0);
newTask.trigger("focus");
}
}
if (e.keyCode == keys.LEFT) {
if (e.altKey) {
if (target.hasClass("k-task-summary")) {
that.trigger("collapse", { uid: target.attr("data-uid") });
setTimeout(function() {
that.select().trigger("focus");
}, 1);
}
} else {
that._scrollTasks(-1);
}
handled = true;
}
if (e.keyCode == keys.RIGHT) {
if (e.altKey) {
if (target.hasClass("k-task-summary")) {
that.trigger("expand", { uid: target.attr("data-uid") });
setTimeout(function() {
that.select().trigger("focus");
}, 1);
}
} else {
that._scrollTasks(1);
}
handled = true;
}
if (e.keyCode == keys.ENTER) {
handled = true;
if (that.options.editable.update !== false) {
that.trigger("editTask", { uid: target.attr("data-uid") });
e.stopPropagation();
}
}
if (e.keyCode == keys.DELETE) {
handled = true;
if (that.options.editable.update !== false) {
that.trigger("removeTask", { uid: target.attr("data-uid") });
e.stopPropagation();
}
}
if (handled) {
e.preventDefault();
}
})
.on(CLICK + NS, DOT + GanttTimeline.styles.task , function(e) {
e.preventDefault();
var task = $(this);
that.wrapper.find(DOT + GanttTimeline.styles.task).attr("tabindex", "-1");
task.attr("tabindex", "0").trigger("focus");
});
},
_wrapper: function() {
var styles = GanttTimeline.styles;
var that = this;
var options = this.options;
var calculateSize = function() {
var rowHeight = typeof options.rowHeight === STRING ? options.rowHeight :
options.rowHeight + "px";
var table = $(kendo.format(SIZE_CALCULATION_TEMPLATE, rowHeight));
var calculatedRowHeight;
var calculatedCellHeight;
var content = that.wrapper.find(DOT + styles.tasksWrapper);
kendo.applyStylesFromKendoAttributes(table, ["height", "visibility"]);
content.append(table);
calculatedRowHeight = outerHeight(table.find("tr"));
calculatedCellHeight = table.find("td").height();
table.remove();
return {
"row": calculatedRowHeight,
"cell": calculatedCellHeight
};
};
this.wrapper = this.element
.addClass(styles.wrapper)
.append("<div class='" + styles.gridHeader + "'><div class='" + styles.gridHeaderWrap + "'></div></div>")
.append("<div role='tree' class='" + styles.gridContentWrap + "'><div class='" + styles.tasksWrapper + "'></div><div class='" + styles.dependenciesWrapper + "'></div></div>");
if (options.rowHeight) {
this._calculatedSize = calculateSize();
}
},
_domTrees: function() {
var styles = GanttTimeline.styles;
var tree = kendo.dom.Tree;
var wrapper = this.wrapper;
this._headerTree = new tree(wrapper.find(DOT + styles.gridHeaderWrap)[0]);
this._taskTree = new tree(wrapper.find(DOT + styles.tasksWrapper)[0]);
this._dependencyTree = new tree(wrapper.find(DOT + styles.dependenciesWrapper)[0]);
},
_views: function() {
var views = this.options.views;
var view;
var isSettings;
var name;
var defaultView;
var selected;
this.views = {};
for (var i = 0, l = views.length; i < l; i++) {
view = views[i];
isSettings = isPlainObject(view);
if (isSettings && view.selectable === false) {
continue;
}
name = isSettings ? ((typeof view.type !== "string") ? view.title : view.type) : view;
defaultView = defaultViews[name];
if (defaultView) {
if (isSettings) {
view.type = defaultView.type;
}
defaultView.title = this.options.messages.views[name];
}
view = extend({ title: name }, defaultView, isSettings ? view : {});
if (name) {
this.views[name] = view;
if (!selected || view.selected) {
selected = name;
}
}
}
if (selected) {
this._selectedViewName = selected;
}
},
view: function(name) {
if (name) {
this._selectView(name);
this.trigger("navigate", { view: name, action: "changeView" });
}
return this._selectedView;
},
_selectView: function(name) {
if (name && this.views[name]) {
if (this._selectedView) {
this._unbindView(this._selectedView);
}
this._selectedView = this._initializeView(name);
this._selectedViewName = name;
}
},
_viewByIndex: function(index) {
var view;
var views = this.views;
for (view in views) {
if (!index) {
return view;
}
index--;
}
},
_initializeView: function(name) {
var view = this.views[name];
if (view) {
var type = view.type;
if (typeof type === "string") {
type = kendo.getter(view.type)(window);
}
if (type) {
var newRange = {};
extend(newRange, this.options.range, view.range);
var newDate = view.date || this.options.date;
view = new type(this.wrapper, trimOptions(extend(true, {
headerTree: this._headerTree,
taskTree: this._taskTree,
dependencyTree: this._dependencyTree,
calculatedSize: this._calculatedSize
}, view, this.options, {
date: newDate,
range: newRange
})));
} else {
throw new Error("There is no such view");
}
}
return view;
},
_unbindView: function(view) {
if (view) {
view.destroy();
}
},
_range: function(tasks) {
var startOrder = {
field: "start",
dir: "asc"
};
var endOrder = {
field: "end",
dir: "desc"
};
if (!tasks || !tasks.length) {
return { start: new Date(), end: new Date() };
}
var start = new Query(tasks).sort(startOrder).toArray()[0].start || new Date();
var end = new Query(tasks).sort(endOrder).toArray()[0].end || new Date();
return {
start: new Date(start),
end: new Date(end)
};
},
_render: function(tasks) {
var view = this.view();
var range = this._range(tasks);
var date = view.options.date;
this._tasks = tasks;
view.range(range);
view.renderLayout();
view.render(tasks);
if (date) {
view._scrollToDate(date);
}
},
_renderDependencies: function(dependencies) {
this.view()._renderDependencies(dependencies);
},
_taskByUid: function(uid) {
var tasks = this._tasks;
var length = tasks.length;
var task;
for (var i = 0; i < length; i++) {
task = tasks[i];
if (task.uid === uid) {
return task;
}
}
},
_draggable: function() {
var that = this;
var element;
var task;
var currentStart;
var startOffset;
var snap = this.options.snap;
var styles = GanttTimeline.styles;
var editable = this.options.editable;
var cleanUp = function() {
that.view()._removeDragHint();
if (element) {
element.css("opacity", 1);
}
element = null;
task = null;
that.dragInProgress = false;
};
if (!editable || editable.move === false || editable.update === false) {
return;
}
this._moveDraggable = new kendo.ui.Draggable(this.wrapper, {
distance: 0,
filter: DOT + styles.task + "," + DOT + styles.taskOffset,
holdToDrag: kendo.support.mobileOS,
ignore: DOT + styles.taskResizeHandle
});
this._moveDraggable
.bind("dragstart", function(e) {
var view = that.view();
element = e.currentTarget.closest(DOT + styles.taskWrap);
task = that._taskByUid(e.currentTarget.parent().find(DOT + styles.task).attr("data-uid"));
if (that.trigger("moveStart", { task: task })) {
e.preventDefault();
return;
}
currentStart = task.start;
startOffset = view._timeByPosition(e.x.location, snap) - currentStart;
view._createDragHint(element);
element.css("opacity", 0.5);
clearTimeout(that._tooltipTimeout);
that.dragInProgress = true;
})
.bind("drag", kendo.throttle(function(e) {
if (!that.dragInProgress) {
return;
}
var view = that.view();
var date = new Date(view._timeByPosition(e.x.location, snap) - startOffset);
var updateHintDate = date;
if (!that.trigger("move", { task: task, start: date })) {
currentStart = date;
if (isRtl) {
updateHintDate = new Date(currentStart.getTime() + task.duration());
}
view._updateDragHint(updateHintDate);
}
}, 15))
.bind("dragend", function() {
that.trigger("moveEnd", { task: task, start: currentStart });
cleanUp();
})
.bind("dragcancel", function() {
cleanUp();
})
.userEvents.bind("select", function() {
blurActiveElement();
});
},
_resizable: function() {
var that = this;
var element;
var task;
var currentStart;
var currentEnd;
var resizeStart;
var snap = this.options.snap;
var styles = GanttTimeline.styles;
var editable = this.options.editable;
var cleanUp = function() {
that.view()._removeResizeHint();
element = null;
task = null;
that.dragInProgress = false;
};
if (!editable || editable.resize === false || editable.update === false) {
return;
}
this._resizeDraggable = new kendo.ui.Draggable(this.wrapper, {
distance: 0,
filter: DOT + styles.taskResizeHandle,
holdToDrag: false
});
this._resizeDraggable
.bind("dragstart", function(e) {
resizeStart = e.currentTarget.hasClass(styles.taskResizeHandleWest);
if (isRtl) {
resizeStart = !resizeStart;
}
element = e.currentTarget.closest(DOT + styles.taskWrap).find(DOT + styles.task);
task = that._taskByUid(element.attr("data-uid"));
if (that.trigger("resizeStart", { task: task })) {
e.preventDefault();
return;
}
currentStart = task.start;
currentEnd = task.end;
that.view()._createResizeHint(task);
clearTimeout(that._tooltipTimeout);
that.dragInProgress = true;
})
.bind("drag", kendo.throttle(function(e) {
if (!that.dragInProgress) {
return;
}
var view = that.view();
var date = view._timeByPosition(e.x.location, snap, !resizeStart);
if (resizeStart) {
if (date < currentEnd) {
currentStart = date;
} else {
currentStart = currentEnd;
}
} else {
if (date > currentStart) {
currentEnd = date;
} else {
currentEnd = currentStart;
}
}
if (!that.trigger("resize", { task: task, start: currentStart, end: currentEnd })) {
view._updateResizeHint(currentStart, currentEnd, resizeStart);
}
}, 15))
.bind("dragend", function() {
that.trigger("resizeEnd", { task: task, resizeStart: resizeStart, start: currentStart, end: currentEnd });
cleanUp();
})
.bind("dragcancel", function() {
cleanUp();
})
.userEvents.bind("select", function() {
blurActiveElement();
});
},
_percentResizeDraggable: function() {
var that = this;
var task;
var taskElement;
var taskElementOffset;
var timelineOffset;
var originalPercentWidth;
var maxPercentWidth;
var currentPercentComplete;
var tooltipTop;
var tooltipLeft;
var styles = GanttTimeline.styles;
var delta;
var editable = this.options.editable;
var cleanUp = function() {
that.view()._removePercentCompleteTooltip();
taskElement = null;
task = null;
that.dragInProgress = false;
};
var updateElement = function(width) {
var taskDragHandle = taskElement.siblings(DOT + styles.taskDragHandle);
if (!taskDragHandle.length) {
taskDragHandle = taskElement.closest(DOT + styles.taskWrap).find(DOT + styles.taskDragHandle);
}
taskDragHandle.css(isRtl ? "right" : "left", width);
taskElement
.find(DOT + styles.taskComplete)
.width(width);
};
if (!editable || editable.dragPercentComplete === false || editable.update === false) {
return;
}
this._percentDraggable = new kendo.ui.Draggable(this.wrapper, {
distance: 0,
filter: DOT + styles.taskDragHandle,
holdToDrag: false
});
this._percentDraggable
.bind("dragstart", function(e) {
if (that.trigger("percentResizeStart")) {
e.preventDefault();
return;
}
taskElement = e.currentTarget.siblings(DOT + styles.task);
if (!taskElement.length) {
taskElement = e.currentTarget.closest(DOT + styles.taskWrap).find(DOT + styles.task);
}
task = that._taskByUid(taskElement.attr("data-uid"));
currentPercentComplete = task.percentComplete;
taskElementOffset = taskElement.offset();
timelineOffset = this.element.offset();
originalPercentWidth = taskElement.find(DOT + styles.taskComplete).width();
maxPercentWidth = outerWidth(taskElement);
clearTimeout(that._tooltipTimeout);
that.dragInProgress = true;
})
.bind("drag", kendo.throttle(function(e) {
if (!that.dragInProgress) {
return;
}
delta = isRtl ? -e.x.initialDelta : e.x.initialDelta;
var currentWidth = Math.max(0, Math.min(maxPercentWidth, originalPercentWidth + delta));
currentPercentComplete = Math.round((currentWidth / maxPercentWidth) * 100);
updateElement(currentWidth);
tooltipTop = taskElementOffset.top - timelineOffset.top;
tooltipLeft = taskElementOffset.left + currentWidth - timelineOffset.left;
if (isRtl) {
tooltipLeft += (maxPercentWidth - 2 * currentWidth);
}
that.view()._updatePercentCompleteTooltip(tooltipTop, tooltipLeft, currentPercentComplete);
}, 15))
.bind("dragend", function() {
that.trigger("percentResizeEnd", { task: task, percentComplete: currentPercentComplete / 100 });
cleanUp();
})
.bind("dragcancel", function() {
updateElement(originalPercentWidth);
cleanUp();
})
.userEvents.bind("select", function() {
blurActiveElement();
});
},
_createDependencyDraggable: function() {
var that = this;
var originalHandle;
var hoveredHandle = $();
var hoveredTask = $();
var startX;
var startY;
var styles = GanttTimeline.styles;
var editable = this.options.editable;
var cleanUp = function() {
originalHandle
.css("display", "")
.removeClass(styles.hovered);
originalHandle.parent().removeClass(styles.origin);
originalHandle = null;
toggleHandles(false);
hoveredTask = $();
hoveredHandle = $();
that.view()._removeDependencyDragHint();
that.dragInProgress = false;
};
var toggleHandles = function(value) {
if (!hoveredTask.hasClass(styles.origin)) {
hoveredTask.find(DOT + styles.taskDot).css("display", value ? "block" : "");
hoveredHandle.toggleClass(styles.hovered, value);
}
};
if (!editable || editable.dependencyCreate === false) {
return;
}
this._dependencyDraggable = new kendo.ui.Draggable(this.wrapper, {
distance: 0,
filter: DOT + styles.taskDot,
holdToDrag: false
});
this._dependencyDraggable
.bind("dragstart", function(e) {
if (that.trigger("dependencyDragStart")) {
e.preventDefault();
return;
}
originalHandle = e.currentTarget
.css("display", "block")
.addClass(styles.hovered);
originalHandle.parent().addClass(styles.origin);
var elementOffset = originalHandle.offset();
var tablesOffset = that.wrapper.find(DOT + styles.tasksWrapper).offset();
startX = Math.round(elementOffset.left - tablesOffset.left + (outerHeight(originalHandle) / 2));
startY = Math.round(elementOffset.top - tablesOffset.top + (outerWidth(originalHandle) / 2));
clearTimeout(that._tooltipTimeout);
that.dragInProgress = true;
})
.bind("drag", kendo.throttle(function(e) {
if (!that.dragInProgress) {
return;
}
that.view()._removeDependencyDragHint();
var target = $(kendo.elementUnderCursor(e));
var tablesOffset = that.wrapper.find(DOT + styles.tasksWrapper).offset();
var currentX = e.x.location - tablesOffset.left;
var currentY = e.y.location - tablesOffset.top;
that.view()._updateDependencyDragHint({ x: startX, y: startY }, { x: currentX, y: currentY });
toggleHandles(false);
hoveredHandle = (target.hasClass(styles.taskDot)) ? target : $();
hoveredTask = target.closest(DOT + styles.taskWrap);
toggleHandles(true);
}, 15))
.bind("dragend", function() {
if (hoveredHandle.length) {
var fromStart = originalHandle.hasClass(styles.taskDotStart);
var toStart = hoveredHandle.hasClass(styles.taskDotStart);
var type = fromStart ? (toStart ? 3 : 2) : (toStart ? 1 : 0);
var predecessorElement = originalHandle.siblings(DOT + styles.task);
if (!predecessorElement.length) {
predecessorElement = originalHandle.closest(DOT + styles.taskWrap).find(DOT + styles.task);
}
var predecessor = that._taskByUid(predecessorElement.attr("data-uid"));
var successorElement = hoveredHandle.siblings(DOT + styles.task);
if (!successorElement.length) {
successorElement = hoveredHandle.closest(DOT + styles.taskWrap).find(DOT + styles.task);
}
var successor = that._taskByUid(successorElement.attr("data-uid"));
if (predecessor !== successor) {
that.trigger("dependencyDragEnd", { type: type, predecessor: predecessor, successor: successor });
}
}
cleanUp();
})
.bind("dragcancel", function() {
cleanUp();
})
.userEvents.bind("select", function() {
blurActiveElement();
});
},
_selectable: function() {
var that = this;
var styles = GanttTimeline.styles;
if (this.options.selectable) {
this.wrapper
.on(CLICK + NS, DOT + styles.task + "," + DOT + styles.taskOffset, function(e) {
e.stopPropagation();
var uid = $(this).attr("data-uid");
if (!uid) {
uid = $(this).closest(DOT + styles.taskWrap).find(DOT + styles.task).data("uid");
}
if (!e.ctrlKey) {
that.trigger("select", { uid: uid });
} else {
that.trigger("clear");
}
})
.on(CLICK + NS, DOT + styles.taskWrap, function(e) {
e.stopPropagation();
// Decrease z-index of wrap so the we can get to the
// dependency line underneath, if there is any
$(this).css("z-index", "0");
var target = $(document.elementFromPoint(e.clientX, e.clientY));
if (target.hasClass(styles.line)) {
target.click();
}
$(this).css("z-index", "");
})
.on(CLICK + NS, DOT + styles.tasksWrapper, function() {
if (that.selectDependency().length > 0) {
that.clearSelection();
} else {
that.trigger("clear");
}
})
.on(CLICK + NS, DOT + styles.line, function(e) {
e.stopPropagation();
that.selectDependency(this);
});
}
},
select: function(value) {
var element = this.wrapper.find(value);
var styles = GanttTimeline.styles;
if (element.length) {
this.clearSelection();
element.addClass(styles.selected);
if (kendo.support.mobileOS) {
element.parent().addClass(styles.taskWrapActive);
}
return;
}
return this.wrapper.find(DOT + styles.task + DOT + styles.selected);
},
selectDependency: function(value) {
var element = this.wrapper.find(value);
var uid;
var styles = GanttTimeline.styles;
if (element.length) {
this.clearSelection();
this.trigger("clear");
uid = $(element).attr("data-uid");
this.wrapper.find(DOT + styles.line + "[data-uid='" + uid + "']").addClass(styles.selected);
return;
}
return this.wrapper.find(DOT + styles.line + DOT + styles.selected);
},
clearSelection: function() {
var styles = GanttTimeline.styles;
this.wrapper
.find(DOT + styles.selected)
.removeClass(styles.selected);
if (kendo.support.mobileOS) {
this.wrapper
.find(DOT + styles.taskWrapActive)
.removeClass(styles.taskWrapActive);
}
},
_attachEvents: function() {
var that = this;
var styles = GanttTimeline.styles;
var editable = this.options.editable;
if (editable) {
this.wrapper
.on(CLICK + NS, DOT + styles.taskDelete, function(e) {
that.trigger("removeTask", { uid: $(this).closest(DOT + styles.taskWrap).find(DOT + styles.task).attr("data-uid") });
e.stopPropagation();
e.preventDefault();
})
.on(KEYDOWN + NS, function(e) {
var selectedDependency;
var editable = that.options.editable;
if (e.keyCode === keys.DELETE && editable && editable.dependencyDestroy !== false) {
selectedDependency = that.selectDependency();
if (selectedDependency.length) {
that.trigger("removeDependency", { uid: selectedDependency.attr("data-uid") });
that.clearSelection();
}
}
});
if (!kendo.support.mobileOS) {
this.wrapper
.on(DBLCLICK + NS, DOT + styles.task + "," + DOT + styles.taskOffset, function(e) {
if (that.options.editable.update !== false) {
that.trigger("editTask", { uid: $(this).closest(DOT + styles.taskWrap).find(DOT + styles.task).attr("data-uid") });
e.stopPropagation();
e.preventDefault();
}
});
} else {
this.touch = this.wrapper
.kendoTouch({
filter: DOT + styles.task + "," + DOT + styles.taskOffset,
doubletap: function(e) {
if (that.options.editable.update !== false) {
that.trigger("editTask", { uid: $(e.touch.currentTarget).closest(DOT + styles.taskWrap).find(DOT + styles.task).attr("data-uid") });
}
}
}).data("kendoTouch");
}
}
},
_tooltip: function() {
var that = this;
var tooltipOptions = this.options.tooltip;
var styles = GanttTimeline.styles;
var currentMousePosition;
var mouseMoveHandler = function(e) {
currentMousePosition = e.clientX;
};
if (tooltipOptions && tooltipOptions.visible === false) {
return;
}
if (!kendo.support.mobileOS) {
this.wrapper
.on(MOUSEENTER + NS, DOT + styles.task, function() {
var element = this;
var task = that._taskByUid($(this).attr("data-uid"));
if (that.dragInProgress) {
return;
}
that._tooltipTimeout = setTimeout(function() {
that.view()._createTaskTooltip(task, element, currentMousePosition);
}, 800);
$(this).on(MOUSEMOVE, mouseMoveHandler);
})
.on(MOUSELEAVE + NS, DOT + styles.task, function() {
clearTimeout(that._tooltipTimeout);
that.view()._removeTaskTooltip();
$(this).off(MOUSEMOVE, mouseMoveHandler);
})
.on(MOUSEENTER + NS, DOT + styles.taskOffset, function() {
var taskElement = $(this).closest(DOT + styles.taskWrap).find(DOT + styles.task);
var task = that._taskByUid(taskElement.attr("data-uid"));
if (that.dragInProgress) {
return;
}
that._offsetTooltipTimeout = setTimeout(function() {
that.view()._createOffsetTooltip(task, taskElement, currentMousePosition);
}, 800);
$(this).on(MOUSEMOVE, mouseMoveHandler);
})
.on(MOUSELEAVE + NS, DOT + styles.taskOffset, function() {
clearTimeout(that._offsetTooltipTimeout);
that.view()._removeOffsetTooltip();
$(this).off(MOUSEMOVE, mouseMoveHandler);
})
.on(MOUSEENTER + NS, DOT + styles.taskPlanned, function() {
var taskElement = $(this).closest(DOT + styles.taskWrap).find(DOT + styles.task);
var task = that._taskByUid(taskElement.attr("data-uid"));
that._plannedTooltipTimeout = setTimeout(function() {
that.view()._createPlannedTooltip(task, taskElement, currentMousePosition);
}, 800);
$(this).on(MOUSEMOVE, mouseMoveHandler);
})
.on(MOUSELEAVE + NS, DOT + styles.taskPlanned, function() {
clearTimeout(that._plannedTooltipTimeout);
that.view()._removePlannedTooltip();
$(this).off(MOUSEMOVE, mouseMoveHandler);
});
} else {
this.wrapper
.on(CLICK + NS, DOT + styles.taskDelete, function(e) {
e.stopPropagation();
that.view()._removeTaskTooltip();
})
.on(MOUSELEAVE + NS, DOT + styles.task, function(e) {
var parents = $(e.relatedTarget).parents(DOT + styles.taskWrap, DOT + styles.task);
if (parents.length === 0) {
that.view()._removeTaskTooltip();
}
})
.on(MOUSELEAVE + NS, DOT + styles.taskOffset, function(e) {
var parents = $(e.relatedTarget).parents(DOT + styles.taskWrap, DOT + styles.task);
if (parents.length === 0) {
that.view()._removeOffsetTooltip();
}
})
.on(MOUSELEAVE + NS, DOT + styles.taskPlanned, function(e) {
var parents = $(e.relatedTarget).parents(DOT + styles.taskWrap, DOT + styles.task);
if (parents.length === 0) {
that.view()._removePlannedTooltip();
}
});
if (this.touch) {
this.touch
.bind("tap", function(e) {
var element = $(e.touch.target);
var currentPosition = e.touch.x.client;
var task;
if (!element.hasClass(styles.task)) {
task = that._taskByUid(element.closest(DOT + styles.taskWrap).find(DOT + styles.task).attr("data-uid"));
if (that.view()._offsetTooltip) {
that.view()._removeOffsetTooltip();
}
that.view()._createOffsetTooltip(task, element, currentPosition);
} else {
task = that._taskByUid(element.attr("data-uid"));
if (that.view()._taskTooltip) {
that.view()._removeTaskTooltip();
}
that.view()._createTaskTooltip(task, element, currentPosition);
}
})
.bind("doubletap", function() {
that.view()._removeTaskTooltip();
that.view()._removeOffsetTooltip();
});
}
}
},
_setPlanned: function(value) {
this.options.showPlannedTasks = value;
this.view().options.showPlannedTasks = value;
}
});
extend(true, GanttTimeline, { styles: timelineStyles });
})(window.kendo.jQuery);
var kendo$1 = kendo;
exports.__meta__ = __meta__;
exports.default = kendo$1;