source: Dev/trunk/src/client/dojox/grid/enhanced/plugins/CellMerge.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 9.0 KB
Line 
1define([
2        "dojo/_base/declare",
3        "dojo/_base/array",
4        "dojo/_base/lang",
5        "dojo/_base/html",
6        "../_Plugin",
7        "../../EnhancedGrid"
8], function(declare, array, lang, html, _Plugin, EnhancedGrid){
9
10var CellMerge = declare("dojox.grid.enhanced.plugins.CellMerge", _Plugin, {
11        // summary:
12        //              This plugin provides functions to merge(un-merge) adjacent cells within one row.
13        //              Acceptable plugin parameters:
14        //
15        //              - mergedCells: Array: An array of objects with structure:
16        //
17        // |            {
18        // |                    row: function(Integer)|Integer
19        // |                            If it's a function, it's a predicate to decide which rows are to be merged.
20        // |                            It takes an integer (the row index), and should return true or false;
21        // |                    start: Integer
22        // |                            The column index of the left most cell that shall be merged.
23        // |                    end: Integer
24        // |                            The column index of the right most cell that shall be merged.
25        // |                    major: Integer
26        // |                            The column index of the cell whose content should be used as the content of the merged cell.
27        // |                            It must be larger than or equal to the startColumnIndex, and less than or equal to the endColumnIndex.
28        // |                            If it is omitted, the content of the leading edge (left-most for ltr, right most for rtl) cell will be used.
29        // |            }
30       
31        // name: String
32        //              Plugin name
33        name: "cellMerge",
34       
35        constructor: function(grid, args){
36                this.grid = grid;
37                this._records = [];
38                this._merged = {};
39                if(args && lang.isObject(args)){
40                        this._setupConfig(args.mergedCells);
41                }
42                this._initEvents();
43                this._mixinGrid();
44        },
45        //----------------Public----------------------------
46        mergeCells: function(rowTester, startColumnIndex, endColumnIndex, majorColumnIndex){
47                // summary:
48                //              Merge cells from *startColumnIndex* to *endColumnIndex* at rows that make *rowTester* return true,
49                //              using the content of the cell at *majorColumnIndex*
50                // tags:
51                //              public
52                // rowTester: function(Integer)|Integer
53                //              If it's a function, it's a predicate to decide which rows are to be merged.
54                //              It takes an integer (the row index), and should return true or false;
55                // startColumnIndex: Integer
56                //              The column index of the left most cell that shall be merged.
57                // endColumnIndex: Integer
58                //              The column index of the right most cell that shall be merged.
59                // majorColumnIndex: Integer?
60                //              The column index of the cell whose content should be used as the content of the merged cell.
61                //              It must be larger than or equal to the startColumnIndex, and less than or equal to the endColumnIndex.
62                //              If it is omitted, the content of the leading edge (left-most for ltr, right most for rtl) cell will be used.
63                // returns: Object|null
64                //              A handler for the merged cells created by a call of this function.
65                //              This handler can be used later to unmerge cells using the function unmergeCells
66                //              If the merge is not valid, returns null;
67                var item = this._createRecord({
68                        "row": rowTester,
69                        "start": startColumnIndex,
70                        "end": endColumnIndex,
71                        "major": majorColumnIndex
72                });
73                if(item){
74                        this._updateRows(item);
75                }
76                return item; // Object|null
77        },
78        unmergeCells: function(mergeHandler){
79                // summary:
80                //              Unmerge the cells that are merged by the *mergeHandler*, which represents a call to the function mergeCells.
81                // tags:
82                //              public
83                // mergeHandler: object
84                //              A handler for the merged cells created by a call of function mergeCells.
85                var idx;
86                if(mergeHandler && (idx = array.indexOf(this._records, mergeHandler)) >= 0){
87                        this._records.splice(idx, 1);
88                        this._updateRows(mergeHandler);
89                }
90        },
91        getMergedCells: function(){
92                // summary:
93                //              Get all records of currently merged cells.
94                // tags:
95                //              public
96                // returns: Array
97                //              An array of records for merged-cells.
98                //              The record has the following structure:
99                // |    {
100                // |            "row": 1, //the row index
101                // |            "start": 2, //the start column index
102                // |            "end": 4, //the end column index
103                // |            "major": 3, //the major column index
104                // |            "handle": someHandle, //The handler that covers this merge cell record.
105                // |    }
106                var res = [];
107                for(var i in this._merged){
108                        res = res.concat(this._merged[i]);
109                }
110                return res;
111        },
112        getMergedCellsByRow: function(rowIndex){
113                // summary:
114                //              Get the records of currently merged cells at the given row.
115                // tags:
116                //              public
117                // returns: Array
118                //              An array of records for merged-cells. See docs of getMergedCells.
119                return this._merged[rowIndex] || [];
120        },
121       
122        //----------------Private--------------------------
123        _setupConfig: function(config){
124                array.forEach(config, this._createRecord, this);
125        },
126        _initEvents: function(){
127                array.forEach(this.grid.views.views, function(view){
128                        this.connect(view, "onAfterRow", lang.hitch(this, "_onAfterRow", view.index));
129                }, this);
130        },
131        _mixinGrid: function(){
132                var g = this.grid;
133                g.mergeCells = lang.hitch(this, "mergeCells");
134                g.unmergeCells = lang.hitch(this, "unmergeCells");
135                g.getMergedCells = lang.hitch(this, "getMergedCells");
136                g.getMergedCellsByRow = lang.hitch(this, "getMergedCellsByRow");
137        },
138        _getWidth: function(colIndex){
139                var node = this.grid.layout.cells[colIndex].getHeaderNode();
140                return html.position(node).w;
141        },
142        _onAfterRow: function(viewIdx, rowIndex, subrows){
143                try{
144                        if(rowIndex < 0){
145                                return;
146                        }
147                        var result = [], i, j, len = this._records.length,
148                                cells = this.grid.layout.cells;
149                        //Apply merge-cell requests one by one.
150                        for(i = 0; i < len; ++i){
151                                var item = this._records[i];
152                                var storeItem = this.grid._by_idx[rowIndex];
153                                if(item.view == viewIdx && item.row(rowIndex, storeItem && storeItem.item, this.grid.store)){
154                                        var res = {
155                                                record: item,
156                                                hiddenCells: [],
157                                                totalWidth: 0,
158                                                majorNode: cells[item.major].getNode(rowIndex),
159                                                majorHeaderNode: cells[item.major].getHeaderNode()
160                                        };
161                                        //Calculated the width of merged cell.
162                                        for(j = item.start; j <= item.end; ++j){
163                                                var w = this._getWidth(j, rowIndex);
164                                                res.totalWidth += w;
165                                                if(j != item.major){
166                                                        res.hiddenCells.push(cells[j].getNode(rowIndex));
167                                                }
168                                        }
169                                        //If width is valid, remember it. There may be multiple merges within one row.
170                                        if(subrows.length != 1 || res.totalWidth > 0){
171                                                //Remove conflicted merges.
172                                                for(j = result.length - 1; j >= 0; --j){
173                                                        var r = result[j].record;
174                                                        if((r.start >= item.start && r.start <= item.end) ||
175                                                                (r.end >= item.start && r.end <= item.end)){
176                                                                result.splice(j, 1);
177                                                        }
178                                                }
179                                                result.push(res);
180                                        }
181                                }
182                        }
183                        this._merged[rowIndex] = [];
184                        array.forEach(result, function(res){
185                                array.forEach(res.hiddenCells, function(node){
186                                        html.style(node, "display", "none");
187                                });
188                                var pbm = html.marginBox(res.majorHeaderNode).w - html.contentBox(res.majorHeaderNode).w;
189                                var tw = res.totalWidth;
190                               
191                                //Tricky for WebKit.
192                                if(!html.isWebKit){
193                                        tw -= pbm;
194                                }
195                               
196                                html.style(res.majorNode, "width", tw + "px");
197                                //In case we're dealing with multiple subrows.
198                                res.majorNode.setAttribute("colspan", res.hiddenCells.length + 1);
199       
200                                this._merged[rowIndex].push({
201                                        "row": rowIndex,
202                                        "start": res.record.start,
203                                        "end": res.record.end,
204                                        "major": res.record.major,
205                                        "handle": res.record
206                                });
207                        }, this);
208                }catch(e){
209                        console.warn("CellMerge._onAfterRow() error: ", rowIndex, e);
210                }
211        },
212        _createRecord: function(item){
213                if(this._isValid(item)){
214                        item = {
215                                "row": item.row,
216                                "start": item.start,
217                                "end": item.end,
218                                "major": item.major
219                        };
220                        var cells = this.grid.layout.cells;
221                        item.view = cells[item.start].view.index;
222                        item.major = typeof item.major == "number" && !isNaN(item.major) ? item.major : item.start;
223                        if(typeof item.row == "number"){
224                                var r = item.row;
225                                item.row = function(rowIndex){
226                                        return rowIndex === r;
227                                };
228                        }else if(typeof item.row == "string"){
229                                var id = item.row;
230                                item.row = function(rowIndex, storeItem, store){
231                                        try{
232                                                if(store && storeItem && store.getFeatures()['dojo.data.api.Identity']){
233                                                        return store.getIdentity(storeItem) == id;
234                                                }
235                                        }catch(e){
236                                                console.error(e);
237                                        }
238                                        return false;
239                                };
240                        }
241                        if(lang.isFunction(item.row)){
242                                this._records.push(item);
243                                return item;
244                        }
245                }
246                return null;
247        },
248        _isValid: function(item){
249                var cells = this.grid.layout.cells,
250                        colCount = cells.length;
251                return (lang.isObject(item) && ("row" in item) && ("start" in item) && ("end" in item) &&
252                        item.start >= 0 && item.start < colCount &&
253                        item.end > item.start && item.end < colCount &&
254                        cells[item.start].view.index == cells[item.end].view.index &&
255                        cells[item.start].subrow == cells[item.end].subrow &&
256                        !(typeof item.major == "number" && (item.major < item.start || item.major > item.end)));
257        },
258        _updateRows: function(item){
259                var min = null;
260                for(var i = 0, count = this.grid.rowCount; i < count; ++i){
261                        var storeItem = this.grid._by_idx[i];
262                        if(storeItem && item.row(i, storeItem && storeItem.item, this.grid.store)){
263                                this.grid.views.updateRow(i);
264                                if(min === null){ min = i; }
265                        }
266                }
267                if(min >= 0){
268                        this.grid.scroller.rowHeightChanged(min);
269                }
270        }
271});
272
273EnhancedGrid.registerPlugin(CellMerge);
274
275return CellMerge;
276});
Note: See TracBrowser for help on using the repository browser.