/**
Column manager for grids. Grids will listen to events fired by this component. A column manager is usually created by
sending a "columnManager" config object to the constructor of a grid.Grid view.
@namespace grid
@class ColumnManager
@extends Core
@constructor
@param {Object} config
@example
columnManager:{
columns:{
'country':{
heading:'Country',
removable:false,
sortable:true,
movable:true,
width:200,
renderer:function (val) {
return '<span style="color:blue">' + val + '</span>';
}
},
'capital':{
heading:'Capital',
sortable:true,
removable:true,
movable:true,
width:150
},
population:{
heading:'Population',
movable:true,
removable:true
}
}
}
Is example of a ColumnManager config object sent to a grid. It defines three columns, "country", "capital" and "population". These names
corresponds to keys in the data sets. How to configure columns is specified in {{#crossLink "grid.Column"}}{{/crossLink}}
*/
ludo.grid.ColumnManager = new Class({
Extends:ludo.Core,
type:'grid.ColumnManager',
/**
* Always fill view, i.e. dynamically increase with of last visible column when
* total width of visible columns is less than width of the Grid.
* @config fill
* @type {Boolean}
* @default true
*/
fill:true,
/**
* Configuration of columns
* @config {Object} columns
* @default {}
*/
columns:{},
columnKeys:[],
statefulProperties:['columns', 'columnKeys'],
/**
* Internal column lookup. Flat version of this.columns
* @property {Object} columnLookup
* @private
*/
columnLookup:{},
ludoConfig:function (config) {
this.parent(config);
this.setConfigParams(config, ['fill','columns']);
this.createColumnLookup();
if (config.columnKeys !== undefined && this.hasValidColumnKeys(config.columnKeys)) {
this.columnKeys = config.columnKeys;
} else {
this.columnKeys = this.getLeafKeysFromColumns();
}
},
getLeafKeysFromColumns:function (parent) {
var ret = [];
parent = parent || this.columns;
for (var key in parent) {
if (parent.hasOwnProperty(key)) {
ret.push(key);
if (parent[key].columns !== undefined) {
var keys = this.getLeafKeysFromColumns(parent[key].columns);
for (var i = 0; i < keys.length; i++) {
ret.push(keys[i]);
}
}
}
}
return ret;
},
createColumnLookup:function (parent, groupName) {
parent = parent || this.columns;
for (var key in parent) {
if (parent.hasOwnProperty(key)) {
this.columnLookup[key] = parent[key];
this.columnLookup[key].group = groupName;
if (parent[key].columns !== undefined) {
this.createColumnLookup(parent[key].columns, key);
}
}
}
},
hasValidColumnKeys:function (keys) {
for (var i = 0; i < keys.length; i++) {
if (this.columnLookup[keys[i]] === undefined)return false;
}
return true;
},
hasLastColumnDynamicWidth:function () {
return this.fill;
},
getColumns:function () {
return this.columns;
},
getColumn:function (key) {
return this.columnLookup[key];
},
getLeafKeys:function () {
var ret = [];
for (var i = 0; i < this.columnKeys.length; i++) {
if (this.columnLookup[this.columnKeys[i]].columns === undefined) {
ret.push(this.columnKeys[i]);
}
}
return ret;
},
/**
Returns object of visible columns, example:
@method getVisibleColumns
@return {Object} visible columns
@example
{
country : {
heading : 'Country'
},
population: {
heading : 'Population'
}
}
*/
getVisibleColumns:function () {
var ret = {};
var keys = this.getLeafKeys();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!this.isHidden(key)) {
ret[key] = this.columnLookup[key];
}
}
return ret;
},
getHeadingFor:function (column) {
return this.getColumnKey(column, 'heading') || '';
},
getMinWidthOf:function (column) {
if (this.isGroup(column)) {
var children = this.getIdOfChildren(column);
var ret = 0;
for (var i = 0; i < children.length; i++) {
ret += this.getMinWidthOf(children[i]);
}
return ret;
}
return this.getColumnKey(column, 'minWidth') || 50;
},
getMaxWidthOf:function (column) {
return this.getColumnKey(column, 'maxWidth') || 1000;
},
getWidthOf:function (column) {
var stretchedWidth = this.getStrechedWithOf(column);
if (stretchedWidth) return stretchedWidth;
if (this.isGroup(column)) {
var columns = this.getColumnsInGroup(column);
var width = 0;
Object.each(columns, function (value, column) {
if(!this.isHidden(column))width += this.getWidthOf(column);
}.bind(this));
return width;
} else {
return this.getColumnKey(column, 'width') || 100;
}
},
isGroup:function (column) {
return this.columnLookup[column] !== undefined && this.columnLookup[column].columns !== undefined;
},
getColumnsInGroup:function (group) {
return this.columnLookup[group].columns;
},
getStrechedWithOf:function (column) {
return this.getColumnKey(column, 'stretchWidth');
},
isRemovable:function (column) {
return this.getColumnKey(column, 'removable') ? true : false;
},
/**
* Returns true if column with given id is in a group.
* @method isInAGroup
* @param {String} column
* @return {Boolean} is in a group
*/
isInAGroup:function (column) {
return this.getColumnKey(column, 'group') !== undefined;
},
/**
* Returns id of parent group
* @method getGroupIdOf
* @param {String} column
* @return {String} group id
*/
getGroupIdOf:function (column) {
return this.getColumnKey(column, 'group');
},
/**
* Returns parent group object for a column
* @method getGroupFor
* @param {String} column
* @return {grid.Column|undefined} parent
*/
getGroupFor:function (column) {
var id = this.getGroupIdOf(column);
return id ? this.columnLookup[id] : undefined;
},
getChildCount:function (groupId) {
var group = this.getColumn(groupId);
if (group.columns !== undefined) {
return Object.getLength(group.columns);
}
return 0;
},
getIdOfChildren:function (groupId) {
var group = this.getColumn(groupId);
if (group) {
return Object.keys(group.columns);
}
return 0;
},
isInSameGroup:function (columnA, columnB) {
return this.isInAGroup(columnA) && this.getGroupIdOf(columnA) == this.getGroupIdOf(columnB);
},
isSortable:function (column) {
return this.getColumnKey(column, 'sortable') ? true : false;
},
isHidden:function (column) {
var hidden = this.getColumnKey(column, 'hidden');
if (hidden)return true;
var parentGroup;
if (parentGroup = this.getGroupIdOf(column)) {
return this.isHidden(parentGroup);
}
return hidden;
},
isVisible:function (column) {
return !this.isHidden(column);
},
/**
* Returns true if column with given id is resizable
* @method isResizable
* @param {String} column
* @return {Boolean}
*/
isResizable:function (column) {
var resizable = this.getColumnKey(column, 'resizable') !== false;
if (resizable && this.hasLastColumnDynamicWidth() && this.isLastVisibleColumn(column)) {
resizable = false;
}
return resizable;
},
isMovable:function (column) {
var parent = this.getGroupIdOf(column);
if (parent && this.getChildCount(parent) == 1) {
return false;
}
return this.getColumnKey(column, 'movable') || false;
},
hasMovableColumns:function () {
for (var i = 0; i < this.columnKeys.length; i++) {
if (this.isMovable(this.columnKeys[i]))return true;
}
return false;
},
getAlignmentOf:function (column) {
return this.getColumnKey(column, 'align') || 'left';
},
getHeaderAlignmentOf:function(column){
return this.getColumnKey(column, 'headerAlign') || 'left';
},
setLeft:function (column, left) {
this.columnLookup[column].left = left;
},
getLeftPosOf:function (column) {
return this.getColumnKey(column, 'left') || 0;
},
getRendererFor:function (column) {
return this.getColumnKey(column, 'renderer');
},
setWidth:function (column, width) {
this.columnLookup[column].width = width;
},
setStretchedWidth:function (width) {
this.columnLookup[this.getLastVisible()].stretchWidth = width;
this.fireEvent('stretch');
},
clearStretchedWidths:function () {
for (var i = 0; i < this.columnKeys.length; i++) {
this.columnLookup[this.columnKeys[i]].stretchWidth = undefined;
}
},
increaseWithFor:function (column, increaseBy) {
var width = this.getWidthOf(column);
this.columnLookup[column].width = width + increaseBy;
this.fireEvent('resize');
this.fireEvent('state');
},
getColumnKey:function (column, key) {
if (this.columnLookup[column] !== undefined) {
return this.columnLookup[column][key];
}
return null;
},
getTotalWidth:function () {
var cols = this.getVisibleColumns();
var ret = 0;
for (var col in cols) {
if (cols.hasOwnProperty(col)) {
ret += this.getWidthOf(col);
}
}
return ret;
},
getMinPosOf:function (column) {
return this.getTotalWidthOfPreviousOf(column) + this.getMinWidthOf(column);
},
getMaxPosOf:function (column) {
return this.getTotalWidthOfPreviousOf(column) + this.getMaxWidthOf(column);
},
getTotalWidthOfPreviousOf:function (column) {
var keys = this.getLeafKeys();
var ret = 0;
for (var i = 0; i < keys.length; i++) {
if (keys[i] == column) {
return ret;
}
if (!this.isHidden(keys[i])) {
ret += this.getWidthOf(keys[i]);
}
}
return 0;
},
/**
* Insert a column before given column
* @method insertColumnBefore
* @param {String} column id
* @param {String} before column id
*/
insertColumnBefore:function (column, before) {
this.moveColumn(column, before, 'before');
},
/**
* Insert a column after given column
* @method insertColumnAfter
* @param {String} column id
* @param {String} after column id
*/
insertColumnAfter:function (column, after) {
this.moveColumn(column, after, 'after');
},
moveColumn:function (column, insertAt, beforeOrAfter) {
var indexAt = this.getInsertionPoint(insertAt, beforeOrAfter);
var indexThis = this.columnKeys.indexOf(column);
if (this.isInAGroup(column) && !this.isInSameGroup(column, insertAt)) {
this.removeFromGroup(column);
}
var i,j;
var indexes = [indexThis];
if (this.isGroup(column)) {
var children = this.getIdOfChildren(column);
for (i = 0; i < children.length; i++) {
indexes.push(this.columnKeys.indexOf(children[i]));
}
}
if(this.isInAGroup(insertAt)){
this.insertIntoSameGroupAs(column,insertAt);
}
var ret = [];
for (i = 0; i < this.columnKeys.length; i++) {
if (i == indexAt && beforeOrAfter == 'before') {
for (j = 0; j < indexes.length; j++) {
ret.push(this.columnKeys[indexes[j]]);
}
}
if (indexes.indexOf(i) == -1) {
ret.push(this.columnKeys[i]);
}
if (i == indexAt && beforeOrAfter == 'after') {
for (j = 0; j < indexes.length; j++) {
ret.push(this.columnKeys[indexes[j]]);
}
}
}
this.columnKeys = ret;
/**
* Fired when a column has been moved
* @event movecolumn
*/
this.fireEvent('movecolumn', [column, this.columnKeys[indexAt], beforeOrAfter]);
this.fireEvent('state');
},
getInsertionPoint:function(insertAtColumn, pos){
var ret = this.columnKeys.indexOf(insertAtColumn);
if (pos === 'after' && this.isGroup(insertAtColumn)){
var columns = Object.keys(this.getColumnsInGroup(insertAtColumn));
for(var i=0;i<columns.length;i++){
ret = Math.max(ret, this.columnKeys.indexOf(columns[i]));
}
}
return ret;
},
/**
* @method insertIntoSameGroupAs
* @param {String} column
* @param {String} as
* @private
*/
insertIntoSameGroupAs:function(column, as){
var group = this.columnLookup[as].group;
this.columnLookup[column].group = group;
this.columnLookup[group].columns[column] = this.columnLookup[column];
this.clearCache();
},
isLastVisibleColumn:function (column) {
var keys = this.getLeafKeys();
for (var i = keys.length - 1; i >= 0; i--) {
var key = keys[i];
if (!this.isHidden([key])) {
return key === column;
}
}
return false;
},
/**
* Remove column from a group
* @method removeFromGroup
* @param {String} column
* @return {Boolean} success
*/
removeFromGroup:function (column) {
var group = this.getGroupFor(column);
if (group) {
delete group.columns[column];
this.getColumn(column).group = undefined;
this.clearCache();
return true;
}
return false;
},
hideColumn:function (column) {
if (this.columnExists(column) && !this.isHidden(column)) {
this.columnLookup[column].hidden = true;
/**
* Fired when a column is hidden
* @event hidecolumn
*/
this.fireEvent('hidecolumn', column);
this.fireEvent('state');
}
},
columnExists:function (column) {
return this.columnLookup[column] !== undefined;
},
hideAllColumns:function () {
var keys = this.getLeafKeys();
for (var i = 0; i < keys.length; i++) {
this.columnLookup[keys[i]].hidden = true;
}
},
showColumn:function (column) {
if (this.columnExists(column) && this.isHidden([column])) {
this.columnLookup[column].hidden = false;
/**
* Fired when a column is shown
* @event showcolumn
*/
this.fireEvent('showcolumn', column);
this.fireEvent('state');
}
},
getIndexOfLastVisible:function () {
var keys = this.getLeafKeys();
for (var i = keys.length - 1; i >= 0; i--) {
if (!this.isHidden(keys[i])) {
return i;
}
}
return null;
},
getLastVisible:function () {
return this.getLeafKeys()[this.getIndexOfLastVisible()];
},
countHeaderRows:undefined,
getCountRows:function () {
if (this.countHeaderRows === undefined) {
var ret = 0;
var keys = this.getLeafKeys();
for (var i = 0; i < keys.length; i++) {
ret = Math.max(ret, this.getStartRowOf(keys[i]));
}
this.countHeaderRows = ret + 1;
}
return this.countHeaderRows;
},
countParentCache:{},
getStartRowOf:function (column) {
if (this.countParentCache[column] === undefined) {
var ret = 0;
if (this.columnLookup[column].group !== undefined) {
var col = this.columnLookup[column].group;
while (col) {
ret++;
col = this.columnLookup[col].group;
}
}
this.countParentCache[column] = ret;
}
return this.countParentCache[column];
},
clearCache:function(){
this.countParentCache = {};
this.columnDepthCache = {};
},
/**
* Return array of column keys for a header row, 0 is first row
* @method getColumnsInRow
* @param {Number} rowNumber
* @return {Array} columns
*/
getColumnsInRow:function (rowNumber) {
var ret = [];
for(var i=0;i<this.columnKeys.length;i++){
if(!this.isHidden(this.columnKeys[i])){
var col = this.columnKeys[i];
var startRow = this.getStartRowOf(col);
if(startRow <= rowNumber && !this.isGroup(col)){
ret.push(col);
}else{
if(startRow == rowNumber){
ret.push(col);
}
}
}
}
return ret;
},
getRowSpanOf:function(column){
var countRows = this.getCountRows();
return countRows - this.getStartRowOf(column) - (this.isGroup(column) ? this.getChildDepthOf(column) : 0);
},
columnDepthCache:{},
getChildDepthOf:function(column){
if(this.columnDepthCache[column] === undefined){
if(this.isGroup(column)){
var ret = 0;
var children = this.getIdOfChildren(column);
for(var i=0;i<children.length;i++){
ret = Math.max(ret, this.getChildDepthOf(children[i]));
}
ret++;
this.columnDepthCache[column] = ret;
}else{
this.columnDepthCache[column] = 0;
}
}
return this.columnDepthCache[column];
},
getHiddenColumns:function(){
var ret = [];
for(var i=0;i<this.columnKeys.length;i++){
if(this.isHidden(this.columnKeys[i])){
ret.push(this.columnKeys[i]);
}
}
return ret;
},
canBeMovedTo:function(column, to){
return column !== to;
}
});