diff options
Diffstat (limited to 'js/dojo/dojox/grid/enhanced/plugins/CellMerge.js')
| -rw-r--r-- | js/dojo/dojox/grid/enhanced/plugins/CellMerge.js | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/js/dojo/dojox/grid/enhanced/plugins/CellMerge.js b/js/dojo/dojox/grid/enhanced/plugins/CellMerge.js new file mode 100644 index 0000000..7284ace --- /dev/null +++ b/js/dojo/dojox/grid/enhanced/plugins/CellMerge.js @@ -0,0 +1,276 @@ +//>>built +define("dojox/grid/enhanced/plugins/CellMerge", [ + "dojo/_base/declare", + "dojo/_base/array", + "dojo/_base/lang", + "dojo/_base/html", + "../_Plugin", + "../../EnhancedGrid" +], function(declare, array, lang, html, _Plugin, EnhancedGrid){ + +var CellMerge = declare("dojox.grid.enhanced.plugins.CellMerge", _Plugin, { + // summary: + // This plugin provides functions to merge(un-merge) adjacent cells within one row. + // Acceptable plugin paramters: + // 1. mergedCells: Array + // An array of objects with structure: + // { + // row: function(Integer)|Integer + // If it's a function, it's a predicate to decide which rows are to be merged. + // It takes an integer (the row index), and should return true or false; + // start: Integer + // The column index of the left most cell that shall be merged. + // end: Integer + // The column index of the right most cell that shall be merged. + // major: Integer + // The column index of the cell whose content should be used as the content of the merged cell. + // It must be larger than or equal to the startColumnIndex, and less than or equal to the endColumnIndex. + // If it is omitted, the content of the leading edge (left-most for ltr, right most for rtl) cell will be used. + // } + + // name: String + // Plugin name + name: "cellMerge", + + constructor: function(grid, args){ + this.grid = grid; + this._records = []; + this._merged = {}; + if(args && lang.isObject(args)){ + this._setupConfig(args.mergedCells); + } + this._initEvents(); + this._mixinGrid(); + }, + //----------------Public---------------------------- + mergeCells: function(rowTester, startColumnIndex, endColumnIndex, majorColumnIndex){ + // summary: + // Merge cells from *startColumnIndex* to *endColumnIndex* at rows that make *rowTester* return true, + // using the content of the cell at *majorColumnIndex* + // tags: + // public + // rowTester: function(Integer)|Integer + // If it's a function, it's a predicate to decide which rows are to be merged. + // It takes an integer (the row index), and should return true or false; + // startColumnIndex: Integer + // The column index of the left most cell that shall be merged. + // endColumnIndex: Integer + // The column index of the right most cell that shall be merged. + // majorColumnIndex: Integer? + // The column index of the cell whose content should be used as the content of the merged cell. + // It must be larger than or equal to the startColumnIndex, and less than or equal to the endColumnIndex. + // If it is omitted, the content of the leading edge (left-most for ltr, right most for rtl) cell will be used. + // return: Object | null + // A handler for the merged cells created by a call of this function. + // This handler can be used later to unmerge cells using the function unmergeCells + // If the merge is not valid, returns null; + var item = this._createRecord({ + "row": rowTester, + "start": startColumnIndex, + "end": endColumnIndex, + "major": majorColumnIndex + }); + if(item){ + this._updateRows(item); + } + return item; + }, + unmergeCells: function(mergeHandler){ + // summary: + // Unmerge the cells that are merged by the *mergeHandler*, which represents a call to the function mergeCells. + // tags: + // public + // mergeHandler: object + // A handler for the merged cells created by a call of function mergeCells. + var idx; + if(mergeHandler && (idx = array.indexOf(this._records, mergeHandler)) >= 0){ + this._records.splice(idx, 1); + this._updateRows(mergeHandler); + } + }, + getMergedCells: function(){ + // summary: + // Get all records of currently merged cells. + // tags: + // public + // return: Array + // An array of records for merged-cells. + // The record has the following structure: + // { + // "row": 1, //the row index + // "start": 2, //the start column index + // "end": 4, //the end column index + // "major": 3, //the major column index + // "handle": someHandle, //The handler that covers this merge cell record. + // } + var res = []; + for(var i in this._merged){ + res = res.concat(this._merged[i]); + } + return res; + }, + getMergedCellsByRow: function(rowIndex){ + // summary: + // Get the records of currently merged cells at the given row. + // tags: + // public + // return: Array + // An array of records for merged-cells. See docs of getMergedCells. + return this._merged[rowIndex] || []; + }, + + //----------------Private-------------------------- + _setupConfig: function(config){ + array.forEach(config, this._createRecord, this); + }, + _initEvents: function(){ + array.forEach(this.grid.views.views, function(view){ + this.connect(view, "onAfterRow", lang.hitch(this, "_onAfterRow", view.index)); + }, this); + }, + _mixinGrid: function(){ + var g = this.grid; + g.mergeCells = lang.hitch(this, "mergeCells"); + g.unmergeCells = lang.hitch(this, "unmergeCells"); + g.getMergedCells = lang.hitch(this, "getMergedCells"); + g.getMergedCellsByRow = lang.hitch(this, "getMergedCellsByRow"); + }, + _getWidth: function(colIndex){ + var node = this.grid.layout.cells[colIndex].getHeaderNode(); + return html.position(node).w; + }, + _onAfterRow: function(viewIdx, rowIndex, subrows){ + try{ + if(rowIndex < 0){ + return; + } + var result = [], i, j, len = this._records.length, + cells = this.grid.layout.cells; + //Apply merge-cell requests one by one. + for(i = 0; i < len; ++i){ + var item = this._records[i]; + var storeItem = this.grid._by_idx[rowIndex]; + if(item.view == viewIdx && item.row(rowIndex, storeItem && storeItem.item, this.grid.store)){ + var res = { + record: item, + hiddenCells: [], + totalWidth: 0, + majorNode: cells[item.major].getNode(rowIndex), + majorHeaderNode: cells[item.major].getHeaderNode() + }; + //Calculated the width of merged cell. + for(j = item.start; j <= item.end; ++j){ + var w = this._getWidth(j, rowIndex); + res.totalWidth += w; + if(j != item.major){ + res.hiddenCells.push(cells[j].getNode(rowIndex)); + } + } + //If width is valid, remember it. There may be multiple merges within one row. + if(subrows.length != 1 || res.totalWidth > 0){ + //Remove conflicted merges. + for(j = result.length - 1; j >= 0; --j){ + var r = result[j].record; + if((r.start >= item.start && r.start <= item.end) || + (r.end >= item.start && r.end <= item.end)){ + result.splice(j, 1); + } + } + result.push(res); + } + } + } + this._merged[rowIndex] = []; + array.forEach(result, function(res){ + array.forEach(res.hiddenCells, function(node){ + html.style(node, "display", "none"); + }); + var pbm = html.marginBox(res.majorHeaderNode).w - html.contentBox(res.majorHeaderNode).w; + var tw = res.totalWidth; + + //Tricky for WebKit. + if(!html.isWebKit){ + tw -= pbm; + } + + html.style(res.majorNode, "width", tw + "px"); + //In case we're dealing with multiple subrows. + res.majorNode.setAttribute("colspan", res.hiddenCells.length + 1); + + this._merged[rowIndex].push({ + "row": rowIndex, + "start": res.record.start, + "end": res.record.end, + "major": res.record.major, + "handle": res.record + }); + }, this); + }catch(e){ + console.warn("CellMerge._onAfterRow() error: ", rowIndex, e); + } + }, + _createRecord: function(item){ + if(this._isValid(item)){ + item = { + "row": item.row, + "start": item.start, + "end": item.end, + "major": item.major + }; + var cells = this.grid.layout.cells; + item.view = cells[item.start].view.index; + item.major = typeof item.major == "number" && !isNaN(item.major) ? item.major : item.start; + if(typeof item.row == "number"){ + var r = item.row; + item.row = function(rowIndex){ + return rowIndex === r; + }; + }else if(typeof item.row == "string"){ + var id = item.row; + item.row = function(rowIndex, storeItem, store){ + try{ + if(store && storeItem && store.getFeatures()['dojo.data.api.Identity']){ + return store.getIdentity(storeItem) == id; + } + }catch(e){ + console.error(e); + } + return false; + }; + } + if(lang.isFunction(item.row)){ + this._records.push(item); + return item; + } + } + return null; + }, + _isValid: function(item){ + var cells = this.grid.layout.cells, + colCount = cells.length; + return (lang.isObject(item) && ("row" in item) && ("start" in item) && ("end" in item) && + item.start >= 0 && item.start < colCount && + item.end > item.start && item.end < colCount && + cells[item.start].view.index == cells[item.end].view.index && + cells[item.start].subrow == cells[item.end].subrow && + !(typeof item.major == "number" && (item.major < item.start || item.major > item.end))); + }, + _updateRows: function(item){ + var min = null; + for(var i = 0, count = this.grid.rowCount; i < count; ++i){ + var storeItem = this.grid._by_idx[i]; + if(storeItem && item.row(i, storeItem && storeItem.item, this.grid.store)){ + this.grid.views.updateRow(i); + if(min === null){ min = i; } + } + } + if(min >= 0){ + this.grid.scroller.rowHeightChanged(min); + } + } +}); + +EnhancedGrid.registerPlugin(CellMerge); + +return CellMerge; +}); |
