angular-gantt
Version:
Gantt chart component for AngularJS
1,027 lines (869 loc) • 109 kB
JavaScript
/*
Project: angular-gantt v1.2.9 - Gantt chart component for AngularJS
Authors: Marco Schweighauser, Rémi Alvergnat
License: MIT
Homepage: https://www.angular-gantt.com
Github: https://github.com/angular-gantt/angular-gantt.git
*/
(function(){
'use strict';
angular.module('gantt.bounds', ['gantt', 'gantt.bounds.templates']).directive('ganttBounds', ['moment', '$compile', '$document', function(moment, $compile, $document) {
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
// Load options from global options attribute.
if (scope.options && typeof(scope.options.bounds) === 'object') {
for (var option in scope.options.bounds) {
scope[option] = scope.options[option];
}
}
if (scope.enabled === undefined) {
scope.enabled = true;
}
api.directives.on.new(scope, function(directiveName, taskScope, taskElement) {
if (directiveName === 'ganttTask') {
var boundsScope = taskScope.$new();
boundsScope.pluginScope = scope;
var ifElement = $document[0].createElement('div');
angular.element(ifElement).attr('data-ng-if', 'task.model.est && task.model.lct && pluginScope.enabled');
var boundsElement = $document[0].createElement('gantt-task-bounds');
if (attrs.templateUrl !== undefined) {
angular.element(boundsElement).attr('data-template-url', attrs.templateUrl);
}
if (attrs.template !== undefined) {
angular.element(boundsElement).attr('data-template', attrs.template);
}
angular.element(ifElement).append(boundsElement);
taskElement.append($compile(ifElement)(boundsScope));
}
});
api.tasks.on.clean(scope, function(model) {
if (model.est !== undefined && !moment.isMoment(model.est)) {
model.est = moment(model.est); //Earliest Start Time
}
if (model.lct !== undefined && !moment.isMoment(model.lct)) {
model.lct = moment(model.lct); //Latest Completion Time
}
});
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.drawtask', ['gantt']).directive('ganttDrawTask', ['$document', 'ganttMouseOffset', 'moment', function(document, mouseOffset, moment) {
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?',
moveThreshold: '=?',
taskModelFactory: '=taskFactory'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
if (scope.enabled === undefined) {
scope.enabled = true;
}
if (scope.moveThreshold === undefined) {
scope.moveThreshold = 0;
}
api.directives.on.new(scope, function(directiveName, directiveScope, element) {
if (directiveName === 'ganttRow') {
var addNewTask = function(x) {
var startDate = api.core.getDateByPosition(x, true);
var endDate = moment(startDate);
var taskModel = scope.taskModelFactory();
taskModel.from = startDate;
taskModel.to = endDate;
var task = directiveScope.row.addTask(taskModel);
task.isResizing = true;
task.updatePosAndSize();
directiveScope.row.updateVisibleTasks();
directiveScope.row.$scope.$digest();
};
var deferDrawing = function(startX) {
var moveTrigger = function(evt) {
var currentX = mouseOffset.getOffset(evt).x;
if (Math.abs(startX - currentX) >= scope.moveThreshold) {
element.off('mousemove', moveTrigger);
addNewTask(startX);
}
};
element.on('mousemove', moveTrigger);
document.one('mouseup', function() {
element.off('mousemove', moveTrigger);
});
};
var drawHandler = function(evt) {
var evtTarget = (evt.target ? evt.target : evt.srcElement);
var enabled = angular.isFunction(scope.enabled) ? scope.enabled(evt): scope.enabled;
if (enabled && evtTarget.className.indexOf('gantt-row') > -1) {
var x = mouseOffset.getOffset(evt).x;
if (scope.moveThreshold === 0) {
addNewTask(x, x);
} else {
deferDrawing(x);
}
}
};
element.on('mousedown', drawHandler);
directiveScope.drawTaskHandler = drawHandler;
}
});
api.directives.on.destroy(scope, function(directiveName, directiveScope, element) {
if (directiveName === 'ganttRow') {
element.off('mousedown', directiveScope.drawTaskHandler);
delete directiveScope.drawTaskHandler;
}
});
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.groups', ['gantt', 'gantt.groups.templates']).directive('ganttGroups', ['ganttUtils', 'GanttHierarchy', '$compile', '$document', function(utils, Hierarchy, $compile, $document) {
// Provides the row sort functionality to any Gantt row
// Uses the sortableState to share the current row
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?',
display: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
// Load options from global options attribute.
if (scope.options && typeof(scope.options.sortable) === 'object') {
for (var option in scope.options.sortable) {
scope[option] = scope.options[option];
}
}
if (scope.enabled === undefined) {
scope.enabled = true;
}
if (scope.display === undefined) {
scope.display = 'group';
}
scope.hierarchy = new Hierarchy();
function refresh() {
scope.hierarchy.refresh(ganttCtrl.gantt.rowsManager.filteredRows);
}
ganttCtrl.gantt.api.registerMethod('groups', 'refresh', refresh, this);
ganttCtrl.gantt.$scope.$watchCollection('gantt.rowsManager.filteredRows', function() {
refresh();
});
api.directives.on.new(scope, function(directiveName, rowScope, rowElement) {
if (directiveName === 'ganttRow') {
var taskGroupScope = rowScope.$new();
taskGroupScope.pluginScope = scope;
var ifElement = $document[0].createElement('div');
angular.element(ifElement).attr('data-ng-if', 'pluginScope.enabled');
var taskGroupElement = $document[0].createElement('gantt-task-group');
if (attrs.templateUrl !== undefined) {
angular.element(taskGroupElement).attr('data-template-url', attrs.templateUrl);
}
if (attrs.template !== undefined) {
angular.element(taskGroupElement).attr('data-template', attrs.template);
}
angular.element(ifElement).append(taskGroupElement);
rowElement.append($compile(ifElement)(taskGroupScope));
}
});
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.labels', ['gantt', 'gantt.labels.templates']).directive('ganttLabels', ['ganttUtils', '$compile', '$document', '$log', function(utils, $compile, $document, $log) {
// Provides the row sort functionality to any Gantt row
// Uses the sortableState to share the current row
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?',
header: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
$log.warn('Angular Gantt Labels plugin is deprecated. Please use Table plugin instead.');
// Load options from global options attribute.
if (scope.options && typeof(scope.options.sortable) === 'object') {
for (var option in scope.options.sortable) {
scope[option] = scope.options[option];
}
}
if (scope.enabled === undefined) {
scope.enabled = true;
}
if (scope.header === undefined) {
scope.header = 'Name';
}
api.directives.on.new(scope, function(directiveName, sideContentScope, sideContentElement) {
if (directiveName === 'ganttSideContent') {
var labelsScope = sideContentScope.$new();
labelsScope.pluginScope = scope;
var ifElement = $document[0].createElement('div');
angular.element(ifElement).attr('data-ng-if', 'pluginScope.enabled');
angular.element(ifElement).addClass('side-element');
var labelsElement = $document[0].createElement('gantt-side-content-labels');
angular.element(ifElement).append(labelsElement);
sideContentElement.append($compile(ifElement)(labelsScope));
}
});
function fitSideWidthToLabels() {
var labels = ganttCtrl.gantt.side.$element[0].getElementsByClassName('gantt-row-label');
var newSideWidth = 0;
angular.forEach(labels, function (label) {
var width = label.children[0].offsetWidth;
newSideWidth = Math.max(newSideWidth, width);
});
if (newSideWidth >= 0) {
api.side.setWidth(newSideWidth);
}
}
api.registerMethod('labels', 'fitSideWidth', fitSideWidthToLabels, this);
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.movable', ['gantt']).directive('ganttMovable', ['ganttMouseButton', 'ganttMouseOffset', 'ganttSmartEvent', 'ganttMovableOptions', 'ganttUtils', 'ganttDom', '$window', '$document', '$timeout',
function(mouseButton, mouseOffset, smartEvent, movableOptions, utils, dom, $window, $document, $timeout) {
// Provides moving and resizing of tasks
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?',
allowMoving: '=?',
allowResizing: '=?',
allowRowSwitching: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
// Load options from global options attribute.
if (scope.options && typeof(scope.options.movable) === 'object') {
for (var option in scope.options.movable) {
scope[option] = scope.options[option];
}
}
movableOptions.initialize(scope);
api.registerEvent('tasks', 'move');
api.registerEvent('tasks', 'moveBegin');
api.registerEvent('tasks', 'moveEnd');
api.registerEvent('tasks', 'resize');
api.registerEvent('tasks', 'resizeBegin');
api.registerEvent('tasks', 'resizeEnd');
api.registerEvent('tasks', 'change');
var _hasTouch = ('ontouchstart' in $window) || $window.DocumentTouch && $document[0] instanceof $window.DocumentTouch;
var _pressEvents = 'touchstart mousedown';
var _moveEvents = 'touchmove mousemove';
var _releaseEvents = 'touchend mouseup';
var taskWithSmallWidth = 15;
var resizeAreaWidthBig = 5;
var resizeAreaWidthSmall = 3;
var scrollSpeed = 15;
var scrollTriggerDistance = 5;
var mouseStartOffsetX;
var moveStartX;
api.directives.on.new(scope, function(directiveName, taskScope, taskElement) {
if (directiveName === 'ganttTask') {
var windowElement = angular.element($window);
var ganttBodyElement = taskScope.row.rowsManager.gantt.body.$element;
var ganttScrollElement = taskScope.row.rowsManager.gantt.scroll.$element;
var taskHasBeenChanged = false;
var taskHasBeenMovedFromAnotherRow = false;
var scrollInterval;
var foregroundElement = taskScope.task.getForegroundElement();
// IE<11 doesn't support `pointer-events: none`
// So task content element must be added to support moving properly.
var contentElement = taskScope.task.getContentElement();
var onPressEvents = function(evt) {
evt.preventDefault();
if (_hasTouch) {
evt = mouseOffset.getTouch(evt);
}
var taskMovable = taskScope.task.model.movable;
var rowMovable = taskScope.task.row.model.movable;
if (typeof(taskMovable) === 'boolean' || angular.isFunction(taskMovable)) {
taskMovable = {enabled: taskMovable};
}
if (typeof(rowMovable) === 'boolean' || angular.isFunction(rowMovable)) {
rowMovable = {enabled: rowMovable};
}
var enabledValue = utils.firstProperty([taskMovable, rowMovable], 'enabled', scope.enabled);
var enabled = angular.isFunction(enabledValue) ? enabledValue(evt, taskScope.task): enabledValue;
if (enabled) {
var taskOffsetX = mouseOffset.getOffsetForElement(foregroundElement[0], evt).x;
var mode = getMoveMode(taskOffsetX);
if (mode !== '' && mouseButton.getButton(evt) === 1) {
var bodyOffsetX = mouseOffset.getOffsetForElement(ganttBodyElement[0], evt).x;
enableMoveMode(mode, bodyOffsetX);
}
taskScope.$digest();
}
};
foregroundElement.on(_pressEvents, onPressEvents);
contentElement.on(_pressEvents, onPressEvents);
var onMousemove = function (evt) {
var taskMovable = taskScope.task.model.movable;
var rowMovable = taskScope.task.row.model.movable;
if (typeof(taskMovable) === 'boolean' || angular.isFunction(taskMovable)) {
taskMovable = {enabled: taskMovable};
}
if (typeof(rowMovable) === 'boolean' || angular.isFunction(rowMovable)) {
rowMovable = {enabled: rowMovable};
}
var enabledValue = utils.firstProperty([taskMovable, rowMovable], 'enabled', scope.enabled);
var enabled = angular.isFunction(enabledValue) ? enabledValue(evt, taskScope.task): enabledValue;
if (enabled && !taskScope.task.isMoving) {
var taskOffsetX = mouseOffset.getOffsetForElement(foregroundElement[0], evt).x;
var mode = getMoveMode(taskOffsetX);
if (mode !== '' && mode !== 'M') {
foregroundElement.css('cursor', getCursor(mode));
contentElement.css('cursor', getCursor(mode));
} else {
foregroundElement.css('cursor', '');
contentElement.css('cursor', '');
}
}
};
foregroundElement.on('mousemove', onMousemove);
contentElement.on('mousemove', onMousemove);
var handleMove = function(evt) {
if (taskScope.task.isMoving && !taskScope.destroyed) {
clearScrollInterval();
moveTask(evt);
scrollScreen(evt);
}
};
var moveTask = function(evt) {
var oldTaskHasBeenChanged = taskHasBeenChanged;
var mousePos = mouseOffset.getOffsetForElement(ganttBodyElement[0], evt);
var x = mousePos.x;
taskScope.task.mouseOffsetX = x;
var taskOutOfRange = taskScope.task.row.rowsManager.gantt.options.value('taskOutOfRange');
var taskMovable = taskScope.task.model.movable;
var rowMovable = taskScope.task.row.model.movable;
if (typeof(taskMovable) === 'boolean' || angular.isFunction(taskMovable)) {
taskMovable = {enabled: taskMovable};
}
if (typeof(rowMovable) === 'boolean' || angular.isFunction(rowMovable)) {
rowMovable = {enabled: rowMovable};
}
if (taskScope.task.moveMode === 'M') {
var allowRowSwitching = utils.firstProperty([taskMovable, rowMovable], 'allowRowSwitching', scope.allowRowSwitching);
if (allowRowSwitching) {
var scrollRect = ganttScrollElement[0].getBoundingClientRect();
var rowCenterLeft = scrollRect.left + scrollRect.width / 2;
var ganttBody = angular.element($document[0].querySelectorAll('.gantt-body'));
ganttBody.css('pointer-events', 'auto'); // pointer-events must be enabled for following to work.
var targetRowElement = dom.findElementFromPoint(rowCenterLeft, evt.clientY, function(element) {
return angular.element(element).hasClass('gantt-row');
});
ganttBody.css('pointer-events', '');
var rows = ganttCtrl.gantt.rowsManager.rows;
var targetRow;
for (var i= 0, l=rows.length; i<l; i++) {
if (targetRowElement === rows[i].$element[0]) {
targetRow = rows[i];
break;
}
}
var sourceRow = taskScope.task.row;
if (targetRow !== undefined && sourceRow !== targetRow) {
targetRow.moveTaskToRow(taskScope.task, true);
sourceRow.$scope.$digest();
targetRow.$scope.$digest();
taskHasBeenChanged = true;
}
}
var allowMoving = utils.firstProperty([taskMovable, rowMovable], 'allowMoving', scope.allowMoving);
if (allowMoving) {
x = x - mouseStartOffsetX;
if (taskOutOfRange !== 'truncate') {
if (x < 0) {
x = 0;
} else if (x + taskScope.task.width >= taskScope.gantt.width) {
x = taskScope.gantt.width - taskScope.task.width;
}
}
taskScope.task.moveTo(x, true);
taskScope.$digest();
if (taskHasBeenChanged) {
taskScope.row.rowsManager.gantt.api.tasks.raise.move(taskScope.task);
}
taskHasBeenChanged = true;
}
} else if (taskScope.task.moveMode === 'E') {
if (x <= taskScope.task.left) {
x = taskScope.task.left;
taskScope.task.moveMode = 'W';
setGlobalCursor(getCursor(taskScope.task.moveMode ));
}
if (taskOutOfRange !== 'truncate' && x >= taskScope.gantt.width) {
x = taskScope.gantt.width;
}
taskScope.task.setTo(x, true);
taskScope.$digest();
if (taskHasBeenChanged) {
taskScope.row.rowsManager.gantt.api.tasks.raise.resize(taskScope.task);
}
taskHasBeenChanged = true;
} else {
if (x > taskScope.task.left + taskScope.task.width) {
x = taskScope.task.left + taskScope.task.width;
taskScope.task.moveMode = 'E';
setGlobalCursor(getCursor(taskScope.task.moveMode ));
}
if (taskOutOfRange !== 'truncate' && x < 0) {
x = 0;
}
taskScope.task.setFrom(x, true);
taskScope.$digest();
if (taskHasBeenChanged) {
taskScope.row.rowsManager.gantt.api.tasks.raise.resize(taskScope.task);
}
taskHasBeenChanged = true;
}
if (!oldTaskHasBeenChanged && taskHasBeenChanged && !taskHasBeenMovedFromAnotherRow) {
var backgroundElement = taskScope.task.getBackgroundElement();
if (taskScope.task.moveMode === 'M') {
backgroundElement.addClass('gantt-task-moving');
taskScope.row.rowsManager.gantt.api.tasks.raise.moveBegin(taskScope.task);
} else {
backgroundElement.addClass('gantt-task-resizing');
taskScope.row.rowsManager.gantt.api.tasks.raise.resizeBegin(taskScope.task);
}
}
};
var scrollScreen = function(evt) {
var mousePos = mouseOffset.getOffsetForElement(ganttBodyElement[0], evt);
var leftScreenBorder = ganttScrollElement[0].scrollLeft;
var screenWidth = ganttScrollElement[0].offsetWidth;
var scrollWidth = ganttScrollElement[0].scrollWidth;
var rightScreenBorder = leftScreenBorder + screenWidth;
var keepOnScrolling = false;
if (mousePos.x < moveStartX) {
// Scroll to the left
if (leftScreenBorder > 0 && mousePos.x <= leftScreenBorder + scrollTriggerDistance) {
mousePos.x -= scrollSpeed;
keepOnScrolling = true;
taskScope.row.rowsManager.gantt.api.scroll.left(scrollSpeed);
}
} else {
// Scroll to the right
if (rightScreenBorder < scrollWidth && mousePos.x >= rightScreenBorder - scrollTriggerDistance) {
mousePos.x += scrollSpeed;
keepOnScrolling = true;
taskScope.row.rowsManager.gantt.api.scroll.right(scrollSpeed);
}
}
if (keepOnScrolling) {
scrollInterval = $timeout(function() {
handleMove(evt);
}, 100, true);
}
};
var clearScrollInterval = function() {
if (scrollInterval !== undefined) {
$timeout.cancel(scrollInterval);
scrollInterval = undefined;
}
};
var getMoveMode = function(x) {
var distance = 0;
var taskMovable = taskScope.task.model.movable;
var rowMovable = taskScope.task.row.model.movable;
if (typeof(taskMovable) === 'boolean') {
taskMovable = {enabled: taskMovable};
}
if (typeof(rowMovable) === 'boolean') {
rowMovable = {enabled: rowMovable};
}
var allowResizing = utils.firstProperty([taskMovable, rowMovable], 'allowResizing', scope.allowResizing);
var allowRowSwitching = utils.firstProperty([taskMovable, rowMovable], 'allowRowSwitching', scope.allowRowSwitching);
var allowMoving = utils.firstProperty([taskMovable, rowMovable], 'allowMoving', scope.allowMoving);
// Define resize&move area. Make sure the move area does not get too small.
if (allowResizing) {
distance = foregroundElement[0].offsetWidth < taskWithSmallWidth ? resizeAreaWidthSmall : resizeAreaWidthBig;
}
if (allowResizing && x > foregroundElement[0].offsetWidth - distance) {
return 'E';
} else if (allowResizing && x < distance) {
return 'W';
} else if ((allowMoving || allowRowSwitching) && x >= distance && x <= foregroundElement[0].offsetWidth - distance) {
return 'M';
} else {
return '';
}
};
var getCursor = function(mode) {
switch (mode) {
case 'E':
return 'e-resize';
case 'W':
return 'w-resize';
case 'M':
return 'move';
}
};
var setGlobalCursor = function(cursor) {
taskElement.css('cursor', cursor);
angular.element($document[0].body).css({
'-moz-user-select': cursor === '' ? '': '-moz-none',
'-webkit-user-select': cursor === '' ? '': 'none',
'-ms-user-select': cursor === '' ? '': 'none',
'user-select': cursor === '' ? '': 'none',
'cursor': cursor
});
};
var enableMoveMode = function(mode, x) {
// Clone taskModel
if (taskScope.task.originalModel === undefined) {
taskScope.task.originalRow = taskScope.task.row;
taskScope.task.originalModel = taskScope.task.model;
taskScope.task.model = angular.copy(taskScope.task.originalModel);
}
// Init mouse start variables
if (!taskHasBeenMovedFromAnotherRow) {
moveStartX = x;
mouseStartOffsetX = x - taskScope.task.modelLeft;
}
// Init task move
taskHasBeenChanged = false;
taskScope.task.moveMode = mode;
taskScope.task.isMoving = true;
taskScope.task.active = true;
// Add move event handler
var taskMoveHandler = function(evt) {
evt.stopImmediatePropagation();
if (_hasTouch) {
evt = mouseOffset.getTouch(evt);
}
handleMove(evt);
};
var moveSmartEvent = smartEvent(taskScope, windowElement, _moveEvents, taskMoveHandler);
moveSmartEvent.bind();
// Remove move event handler on mouse up / touch end
smartEvent(taskScope, windowElement, _releaseEvents, function(evt) {
if (_hasTouch) {
evt = mouseOffset.getTouch(evt);
}
moveSmartEvent.unbind();
disableMoveMode(evt);
taskScope.$digest();
}).bindOnce();
setGlobalCursor(getCursor(mode));
};
var disableMoveMode = function() {
var getBackgroundElement = taskScope.task.getBackgroundElement();
getBackgroundElement.removeClass('gantt-task-moving');
getBackgroundElement.removeClass('gantt-task-resizing');
if (taskScope.task.originalModel !== undefined) {
angular.extend(taskScope.task.originalModel, taskScope.task.model);
taskScope.task.model = taskScope.task.originalModel;
if (taskScope.task.row.model.id !== taskScope.task.originalRow.model.id) {
var targetRow = taskScope.task.row;
targetRow.removeTask(taskScope.task.model.id, false, true);
taskScope.task.row = taskScope.task.originalRow;
targetRow.moveTaskToRow(taskScope.task, false);
}
delete taskScope.task.originalModel;
delete taskScope.task.originalRow;
taskScope.$apply();
}
taskHasBeenMovedFromAnotherRow = false;
taskScope.task.isMoving = false;
taskScope.task.active = false;
// Stop any active auto scroll
clearScrollInterval();
// Set mouse cursor back to default
setGlobalCursor('');
// Raise task changed event
if (taskHasBeenChanged === true) {
// Raise move end event
if (taskScope.task.moveMode === 'M') {
taskScope.row.rowsManager.gantt.api.tasks.raise.moveEnd(taskScope.task);
} else {
taskScope.row.rowsManager.gantt.api.tasks.raise.resizeEnd(taskScope.task);
}
taskHasBeenChanged = false;
taskScope.task.row.sortTasks(); // Sort tasks so they have the right z-order
taskScope.row.rowsManager.gantt.api.tasks.raise.change(taskScope.task);
}
taskScope.task.moveMode = undefined;
};
// Stop scroll cycle (if running) when scope is destroyed.
// This is needed when the task is moved to a new row during scroll because
// the old scope will continue to scroll otherwise
taskScope.$on('$destroy', function() {
taskScope.destroyed = true;
clearScrollInterval();
});
if (taskScope.task.isResizing) {
taskHasBeenMovedFromAnotherRow = true;
enableMoveMode('E', taskScope.task.mouseOffsetX);
delete taskScope.task.isResizing;
} else if (taskScope.task.isMoving) {
// In case the task has been moved to another row a new controller is is created by angular.
// Enable the move mode again if this was the case.
taskHasBeenMovedFromAnotherRow = true;
enableMoveMode('M', taskScope.task.mouseOffsetX);
}
}
});
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.overlap', ['gantt', 'gantt.overlap.templates']).directive('ganttOverlap', ['moment',function(moment) {
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?'
// Add other option attributes for this plugin
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
if (scope.enabled === undefined) {
scope.enabled = true;
}
if (scope.enabled){
api.tasks.on.change(scope, function (task) {
// on every task change check for overlaps
scope.handleOverlaps(task);
});
}
var overlapsTasks = {};
scope.handleOverlaps = function (changedTask) {
// Get all the tasks in the row.
var allTasks = changedTask.row.tasks;
var newOverlapsTasks = {};
var removedOverlapsTasks = {};
angular.forEach(allTasks, function(task) {
removedOverlapsTasks[task.model.id] = task;
});
// set overlaps flag to each task that overlaps other task.
angular.forEach(allTasks,function(currentTask){
var currentStart,currentEnd;
if (currentTask.model.from.isBefore(currentTask.model.to)){
currentStart = currentTask.model.from;
currentEnd = currentTask.model.to;
} else {
currentStart = currentTask.model.to;
currentEnd = currentTask.model.from;
}
var currentRange = moment().range(currentStart, currentEnd);
angular.forEach(allTasks,function(task){
if (currentTask.model.id !== task.model.id){
var start,end;
if (task.model.from.isBefore(task.model.to)){
start = task.model.from;
end = task.model.to;
} else {
start = task.model.to;
end = task.model.from;
}
var range = moment().range(start, end);
if (range.overlaps(currentRange)){
if (!overlapsTasks.hasOwnProperty(task.model.id)) {
newOverlapsTasks[task.model.id] = task;
}
delete removedOverlapsTasks[task.model.id];
if (!overlapsTasks.hasOwnProperty(currentTask.model.id)) {
newOverlapsTasks[currentTask.model.id] = currentTask;
}
delete removedOverlapsTasks[currentTask.model.id];
}
}
});
});
angular.forEach(removedOverlapsTasks, function(task) {
if (task.$element){
task.$element.removeClass('gantt-task-overlaps');
}
delete overlapsTasks[task.model.id];
});
angular.forEach(newOverlapsTasks, function(task) {
if (task.$element){
task.$element.addClass('gantt-task-overlaps');
}
overlapsTasks[task.model.id] = task;
});
overlapsTasks = newOverlapsTasks;
};
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.progress', ['gantt', 'gantt.progress.templates']).directive('ganttProgress', ['moment', '$compile', '$document', function(moment, $compile, $document) {
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
// Load options from global options attribute.
if (scope.options && typeof(scope.options.progress) === 'object') {
for (var option in scope.options.progress) {
scope[option] = scope.options[option];
}
}
if (scope.enabled === undefined) {
scope.enabled = true;
}
api.directives.on.new(scope, function(directiveName, taskScope, taskElement) {
if (directiveName === 'ganttTaskBackground') {
var progressScope = taskScope.$new();
progressScope.pluginScope = scope;
var ifElement = $document[0].createElement('div');
angular.element(ifElement).attr('data-ng-if', 'task.model.progress !== undefined && pluginScope.enabled');
var progressElement = $document[0].createElement('gantt-task-progress');
if (attrs.templateUrl !== undefined) {
angular.element(progressElement).attr('data-template-url', attrs.templateUrl);
}
if (attrs.template !== undefined) {
angular.element(progressElement).attr('data-template', attrs.template);
}
angular.element(ifElement).append(progressElement);
taskElement.append($compile(ifElement)(progressScope));
}
});
api.tasks.on.clean(scope, function(model) {
if (model.est !== undefined && !moment.isMoment(model.est)) {
model.est = moment(model.est); //Earliest Start Time
}
if (model.lct !== undefined && !moment.isMoment(model.lct)) {
model.lct = moment(model.lct); //Latest Completion Time
}
});
}
};
}]);
}());
(function(){
/* global ResizeSensor: false */
/* global ElementQueries: false */
'use strict';
angular.module('gantt.resizeSensor', ['gantt']).directive('ganttResizeSensor', [function() {
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
// Load options from global options attribute.
if (scope.options && typeof(scope.options.progress) === 'object') {
for (var option in scope.options.progress) {
scope[option] = scope.options[option];
}
}
if (scope.enabled === undefined) {
scope.enabled = true;
}
function buildSensor() {
var ganttElement = element.parent().parent().parent()[0].querySelectorAll('div.gantt')[0];
return new ResizeSensor(ganttElement, function() {
ganttCtrl.gantt.$scope.ganttElementWidth = ganttElement.clientWidth;
ganttCtrl.gantt.$scope.$apply();
});
}
var rendered = false;
api.core.on.rendered(scope, function() {
rendered = true;
if (sensor !== undefined) {
sensor.detach();
}
if (scope.enabled) {
ElementQueries.update();
sensor = buildSensor();
}
});
var sensor;
scope.$watch('enabled', function(newValue) {
if (rendered) {
if (newValue && sensor === undefined) {
ElementQueries.update();
sensor = buildSensor();
} else if (!newValue && sensor !== undefined) {
sensor.detach();
sensor = undefined;
}
}
});
}
};
}]);
}());
(function(){
'use strict';
var moduleName = 'gantt.sortable';
var directiveName = 'ganttSortable';
var pluginDependencies = [
'gantt',
{module:'ang-drag-drop', url:'https://github.com/ganarajpr/angular-dragdrop.git#master'}
];
var failedDependencies = [];
var loadedDependencies = [];
var failedDependency;
for (var i = 0, l = pluginDependencies.length; i < l; i++) {
var currentDependency = pluginDependencies[i];
try {
if (angular.isString(currentDependency)) {
currentDependency = {module: currentDependency};
pluginDependencies[i] = currentDependency;
}
angular.module(currentDependency.module);
loadedDependencies.push(currentDependency.module);
} catch (e) {
currentDependency.exception = e;
failedDependencies.push(currentDependency);
}
}
if (failedDependencies.length > 0) {
angular.module(moduleName, []).directive(directiveName, ['$log', function($log) {
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?'
},
link: function() {
$log.warn(moduleName + ' module can\'t require some dependencies:');
for (var i= 0,l =failedDependencies.length; i<l; i++) {
failedDependency = failedDependencies[i];
var errorMessage = failedDependency.module;
if (failedDependency.url) {
errorMessage += ' (' + failedDependency.url + ')';
}
if (failedDependency.exception && failedDependency.exception.message) {
errorMessage += ': ' + failedDependency.exception.message;
}
$log.warn(errorMessage);
}
$log.warn(directiveName + ' plugin directive won\'t be available');
}
};
}]);
} else {