/** Column manager for grids. Grids will listen to events fired by this component. A column manager is usually created by sending a "columns" array to the constructor of a grid.Grid view. @namespace ludo.grid @class ludo.grid.ColumnManager @augments Core @param {Object} config @fires ludo.grid.ColumnManager#showcolumn Fired when a column is shown. Argument: {String} column name @fires ludo.grid.ColumnManager#hideColumn Fired when a column is hidden. Argument: {String} column name @fires ludo.grid.ColumnManager#moveColumn Fired when a column has been moved. Argument: 1) {String} column moved, 2) {String} new sibling column 3) {String} before or after new sibling @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', fill:true, columns:{}, columnKeys:[], statefulProperties:['columns', 'columnKeys'], columnLookup:{}, __construct: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: @function getVisibleColumns @memberof ludo.grid.ColumnManager.prototype @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. * @function isInAGroup * @param {String} column * @return {Boolean} is in a group * @memberof ludo.grid.ColumnManager.prototype */ isInAGroup:function (column) { return this.getColumnKey(column, 'group') !== undefined; }, /** * Returns id of parent group * @function getGroupIdOf * @param {String} column * @return {String} group id * @memberof ludo.grid.ColumnManager.prototype */ getGroupIdOf:function (column) { return this.getColumnKey(column, 'group'); }, /** * Returns parent group object for a column * @function getGroupFor * @param {String} column * @return {grid.Column|undefined} parent * @memberof ludo.grid.ColumnManager.prototype */ 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 ludo.util.lengthOfObject(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 * @function isResizable * @param {String} column * @return {Boolean} * @memberof ludo.grid.ColumnManager.prototype */ 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 * @function insertColumnBefore * @param {String} column id * @param {String} before column id * @memberof ludo.grid.ColumnManager.prototype */ insertColumnBefore:function (column, before) { this.moveColumn(column, before, 'before'); }, /** * Insert a column after given column * @function insertColumnAfter * @param {String} column id * @param {String} after column id * @memberof ludo.grid.ColumnManager.prototype */ 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; 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; }, /** * @function insertIntoSameGroupAs * @param {String} column * @param {String} as * memberof ludo.grid.ColumnManager.prototype * @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 * @function removeFromGroup * @param {String} column * @return {Boolean} success * @memberof ludo.grid.ColumnManager.prototype */ 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; 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; 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 * @function getColumnsInRow * @param {Number} rowNumber * @return {Array} columns * @memberof ludo.grid.ColumnManager.prototype */ 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; } });