UNPKG

angular-gantt

Version:

Gantt chart component for AngularJS

441 lines (361 loc) 17 kB
(function(){ 'use strict'; angular.module('gantt').factory('GanttRowsManager', ['GanttRow', 'ganttArrays', '$filter', '$timeout', 'moment', function(Row, arrays, $filter, $timeout, moment) { var RowsManager = function(gantt) { var self = this; this.gantt = gantt; this.rowsMap = {}; this.rows = []; this.sortedRows = []; this.filteredRows = []; this.customFilteredRows = []; this.visibleRows = []; this.rowsTaskWatchers = []; this._defaultFilterImpl = function(sortedRows, filterRow, filterRowComparator) { return $filter('filter')(sortedRows, filterRow, filterRowComparator); }; this.filterImpl = this._defaultFilterImpl; this.customRowSorters = []; this.customRowFilters = []; this.gantt.$scope.$watchGroup(['filterTask', 'filterTaskComparator'], function(newValues, oldValues) { if (newValues !== oldValues) { self.updateVisibleTasks(); } }); this.gantt.$scope.$watchGroup(['filterRow', 'filterRowComparator'], function(newValues, oldValues) { if (newValues !== oldValues) { self.updateVisibleRows(); } }); this.gantt.$scope.$watch('sortMode', function(newValue, oldValue) { if (newValue !== oldValue) { self.sortRows(); } }); // Listen to vertical scrollbar visibility changes to update columns width var _oldVScrollbarVisible = this.gantt.scroll.isVScrollbarVisible(); this.gantt.$scope.$watchGroup(['maxHeight', 'gantt.rowsManager.visibleRows.length'], function(newValue, oldValue) { if (newValue !== oldValue) { $timeout(function() { var newVScrollbarVisible = self.gantt.scroll.isVScrollbarVisible(); if (newVScrollbarVisible !== _oldVScrollbarVisible) { _oldVScrollbarVisible = newVScrollbarVisible; self.gantt.columnsManager.updateColumnsMeta(); } }); } }); this.gantt.api.registerMethod('rows', 'sort', RowsManager.prototype.sortRows, this); this.gantt.api.registerMethod('rows', 'applySort', RowsManager.prototype.applySort, this); this.gantt.api.registerMethod('rows', 'refresh', RowsManager.prototype.updateVisibleObjects, this); this.gantt.api.registerMethod('rows', 'removeRowSorter', RowsManager.prototype.removeCustomRowSorter, this); this.gantt.api.registerMethod('rows', 'addRowSorter', RowsManager.prototype.addCustomRowSorter, this); this.gantt.api.registerMethod('rows', 'removeRowFilter', RowsManager.prototype.removeCustomRowFilter, this); this.gantt.api.registerMethod('rows', 'addRowFilter', RowsManager.prototype.addCustomRowFilter, this); this.gantt.api.registerMethod('rows', 'setFilterImpl', RowsManager.prototype.setFilterImpl, this); this.gantt.api.registerEvent('tasks', 'add'); this.gantt.api.registerEvent('tasks', 'change'); this.gantt.api.registerEvent('tasks', 'viewChange'); this.gantt.api.registerEvent('tasks', 'rowChange'); this.gantt.api.registerEvent('tasks', 'remove'); this.gantt.api.registerEvent('tasks', 'filter'); this.gantt.api.registerEvent('rows', 'add'); this.gantt.api.registerEvent('rows', 'change'); this.gantt.api.registerEvent('rows', 'remove'); this.gantt.api.registerEvent('rows', 'move'); this.gantt.api.registerEvent('rows', 'filter'); this.updateVisibleObjects(); }; RowsManager.prototype.resetNonModelLists = function() { this.rows = []; this.sortedRows = []; this.filteredRows = []; this.customFilteredRows = []; this.visibleRows = []; }; RowsManager.prototype.addRow = function(rowModel, modelOrderChanged) { // Copy to new row (add) or merge with existing (update) var row, i, l, isUpdate = false; this.gantt.objectModel.cleanRow(rowModel); if (rowModel.id in this.rowsMap) { row = this.rowsMap[rowModel.id]; if (modelOrderChanged) { this.rows.push(row); this.sortedRows.push(row); this.filteredRows.push(row); this.customFilteredRows.push(row); this.visibleRows.push(row); } if (row.model === rowModel) { return; } var toRemoveIds = arrays.getRemovedIds(rowModel.tasks, row.model.tasks); for (i= 0, l=toRemoveIds.length; i<l; i++) { var toRemoveId = toRemoveIds[i]; row.removeTask(toRemoveId); } row.model = rowModel; isUpdate = true; } else { row = new Row(this, rowModel); this.rowsMap[rowModel.id] = row; this.rows.push(row); this.sortedRows.push(row); this.filteredRows.push(row); this.customFilteredRows.push(row); this.visibleRows.push(row); } if (rowModel.tasks !== undefined && rowModel.tasks.length > 0) { for (i = 0, l = rowModel.tasks.length; i < l; i++) { var taskModel = rowModel.tasks[i]; row.addTask(taskModel); } row.updateVisibleTasks(); } if (isUpdate) { this.gantt.api.rows.raise.change(row); } else { this.gantt.api.rows.raise.add(row); } if (!isUpdate) { var watcher = this.gantt.$scope.$watchCollection(function() {return rowModel.tasks;}, function(newTasks, oldTasks) { if (newTasks !== oldTasks) { var i, l; var toRemoveIds = arrays.getRemovedIds(newTasks, oldTasks); for (i= 0, l = toRemoveIds.length; i<l; i++) { var toRemove = toRemoveIds[i]; row.removeTask(toRemove); } if (newTasks !== undefined) { for (i= 0, l = newTasks.length; i<l; i++) { var toAdd = newTasks[i]; row.addTask(toAdd); } row.updateVisibleTasks(); } } }); this.rowsTaskWatchers.push(watcher); } return isUpdate; }; RowsManager.prototype.removeRow = function(rowId) { if (rowId in this.rowsMap) { delete this.rowsMap[rowId]; // Remove from map var removedRow; var row; var indexOf = arrays.indexOfId(this.rows, rowId, ['model', 'id']); if (indexOf > -1) { removedRow = this.rows.splice(indexOf, 1)[0]; // Remove from array var deregisterFunction = this.rowsTaskWatchers.splice(indexOf, 1)[0]; // Remove watcher deregisterFunction(); } arrays.removeId(this.sortedRows, rowId, ['model', 'id']); arrays.removeId(this.filteredRows, rowId, ['model', 'id']); arrays.removeId(this.customFilteredRows, rowId, ['model', 'id']); arrays.removeId(this.visibleRows, rowId, ['model', 'id']); this.gantt.api.rows.raise.remove(removedRow); return row; } return undefined; }; RowsManager.prototype.removeAll = function() { this.rowsMap = {}; this.rows = []; this.sortedRows = []; this.filteredRows = []; this.customFilteredRows = []; this.visibleRows = []; for (var i= 0, l=this.rowsTaskWatchers.length; i<l; i++) { var deregisterFunction = this.rowsTaskWatchers[i]; deregisterFunction(); } this.rowsTaskWatchers = []; }; RowsManager.prototype.sortRows = function() { var expression = this.gantt.options.value('sortMode'); if (expression !== undefined) { var reverse = false; if (angular.isString(expression) && expression.charAt(0) === '-') { reverse = true; expression = expression.substr(1); } var angularOrderBy = $filter('orderBy'); this.sortedRows = angularOrderBy(this.rows, expression, reverse); } else { this.sortedRows = this.rows.slice(); } this.sortedRows = this.applyCustomRowSorters(this.sortedRows); this.updateVisibleRows(); }; RowsManager.prototype.removeCustomRowSorter = function(sorterFunction) { var i = this.customRowSorters.indexOf(sorterFunction); if (i > -1) { this.customRowSorters.splice(i, 1); } }; RowsManager.prototype.addCustomRowSorter = function(sorterFunction) { this.customRowSorters.push(sorterFunction); }; RowsManager.prototype.applyCustomRowSorters = function(sortedRows) { angular.forEach(this.customRowSorters, function(sorterFunction) { sortedRows = sorterFunction(sortedRows); }); return sortedRows; }; /** * Applies current view sort to data model. */ RowsManager.prototype.applySort = function() { var data = this.gantt.$scope.data; data.splice(0, data.length); // empty data. var rows = []; for (var i = 0, l = this.sortedRows.length; i < l; i++) { data.push(this.sortedRows[i].model); rows.push(this.sortedRows[i]); } this.rows = rows; }; RowsManager.prototype.moveRow = function(row, targetRow) { var sortMode = this.gantt.options.value('sortMode'); if (sortMode !== undefined) { // Apply current sort to model this.applySort(); this.gantt.options.set('sortMode', undefined); } var targetRowIndex = this.rows.indexOf(targetRow); var rowIndex = this.rows.indexOf(row); if (targetRowIndex > -1 && rowIndex > -1 && targetRowIndex !== rowIndex) { arrays.moveToIndex(this.rows, rowIndex, targetRowIndex); arrays.moveToIndex(this.rowsTaskWatchers, rowIndex, targetRowIndex); arrays.moveToIndex(this.gantt.$scope.data, rowIndex, targetRowIndex); this.gantt.api.rows.raise.change(row); this.gantt.api.rows.raise.move(row, rowIndex, targetRowIndex); this.updateVisibleObjects(); this.sortRows(); } }; RowsManager.prototype.updateVisibleObjects = function() { this.updateVisibleRows(); this.updateVisibleTasks(); }; RowsManager.prototype.updateVisibleRows = function() { var oldFilteredRows = this.filteredRows; var filterRow = this.gantt.options.value('filterRow'); if (filterRow) { if (typeof(filterRow) === 'object') { filterRow = {model: filterRow}; } var filterRowComparator = this.gantt.options.value('filterRowComparator'); if (typeof(filterRowComparator) === 'function') { //fix issue this.gantt is undefined // var gantt = this.gantt; filterRowComparator = function(actual, expected) { //fix actual.model is undefined return gantt.options.value('filterRowComparator')(actual, expected); }; } this.filteredRows = this.filterImpl(this.sortedRows, filterRow, filterRowComparator); } else { this.filteredRows = this.sortedRows.slice(0); } var raiseEvent = !angular.equals(oldFilteredRows, this.filteredRows); this.customFilteredRows = this.applyCustomRowFilters(this.filteredRows); // TODO: Implement rowLimit like columnLimit to enhance performance for gantt with many rows this.visibleRows = this.customFilteredRows; if (raiseEvent) { this.gantt.api.rows.raise.filter(this.sortedRows, this.filteredRows); } }; RowsManager.prototype.removeCustomRowFilter = function(filterFunction) { var i = this.customRowFilters.indexOf(filterFunction); if (i > -1) { this.customRowFilters.splice(i, 1); } }; RowsManager.prototype.addCustomRowFilter = function(filterFunction) { this.customRowFilters.push(filterFunction); }; RowsManager.prototype.applyCustomRowFilters = function(filteredRows) { angular.forEach(this.customRowFilters, function(filterFunction) { filteredRows = filterFunction(filteredRows); }); return filteredRows; }; RowsManager.prototype.setFilterImpl = function(filterImpl) { if (!filterImpl) { this.filterImpl = this._defaultFilterImpl; } else { this.filterImpl = filterImpl; } }; RowsManager.prototype.updateVisibleTasks = function() { var oldFilteredTasks = []; var filteredTasks = []; var tasks = []; angular.forEach(this.rows, function(row) { oldFilteredTasks = oldFilteredTasks.concat(row.filteredTasks); row.updateVisibleTasks(); filteredTasks = filteredTasks.concat(row.filteredTasks); tasks = tasks.concat(row.tasks); }); var filterEvent = !angular.equals(oldFilteredTasks, filteredTasks); if (filterEvent) { this.gantt.api.tasks.raise.filter(tasks, filteredTasks); } }; // Update the position/size of all tasks in the Gantt RowsManager.prototype.updateTasksPosAndSize = function() { for (var i = 0, l = this.rows.length; i < l; i++) { this.rows[i].updateTasksPosAndSize(); } }; RowsManager.prototype.getExpandedFrom = function(from) { from = from ? moment(from) : from; var minRowFrom = from; angular.forEach(this.rows, function(row) { if (minRowFrom === undefined || minRowFrom > row.from) { minRowFrom = row.from; } }); if (minRowFrom && (!from || minRowFrom < from)) { return minRowFrom; } return from; }; RowsManager.prototype.getExpandedTo = function(to) { to = to ? moment(to) : to; var maxRowTo = to; angular.forEach(this.rows, function(row) { if (maxRowTo === undefined || maxRowTo < row.to) { maxRowTo = row.to; } }); var toDate = this.gantt.options.value('toDate'); if (maxRowTo && (!toDate || maxRowTo > toDate)) { return maxRowTo; } return to; }; RowsManager.prototype.getDefaultFrom = function() { var defaultFrom; angular.forEach(this.rows, function(row) { if (defaultFrom === undefined || row.from < defaultFrom) { defaultFrom = row.from; } }); return defaultFrom; }; RowsManager.prototype.getDefaultTo = function() { var defaultTo; angular.forEach(this.rows, function(row) { if (defaultTo === undefined || row.to > defaultTo) { defaultTo = row.to; } }); return defaultTo; }; return RowsManager; }]); }());