source: Dev/trunk/src/client/dojox/grid/_TreeView.js @ 536

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

Added Dojo 1.9.3 release.

File size: 14.6 KB
Line 
1define([
2        "dijit/registry",
3        "../main",
4        "dojo/_base/declare",
5        "dojo/_base/array",
6        "dojo/_base/lang",
7        "dojo/_base/event",
8        "dojo/dom-attr",
9        "dojo/dom-class",
10        "dojo/dom-style",
11        "dojo/dom-construct",
12        "dojo/query",
13        "dojo/parser",
14        "dojo/text!./resources/Expando.html",
15        "dijit/_Widget",
16        "dijit/_TemplatedMixin",
17        "./_View",
18        "./_Builder",
19        "./util"
20], function(dijit, dojox, declare, array, lang, event, domAttr, domClass,
21        domStyle, domCtr, query, parser, template, _Widget, _TemplatedMixin, _View, _Builder, util){
22
23declare("dojox.grid._Expando", [ _Widget, _TemplatedMixin ], {
24        open: false,
25        toggleClass: "",
26        itemId: "",
27        cellIdx: -1,
28        view: null,
29        rowNode: null,
30        rowIdx: -1,
31        expandoCell: null,
32        level: 0,
33        templateString: template,
34        _toggleRows: function(toggleClass, open){
35                if(!toggleClass || !this.rowNode){ return; }
36                if(query("table.dojoxGridRowTableNeedsRowUpdate").length){
37                        if(this._initialized){
38                                this.view.grid.updateRow(this.rowIdx);
39                        }
40                        return;
41                }
42                var self = this;
43                var g = this.view.grid;
44                if(g.treeModel){
45                        var p = this._tableRow ? domAttr.get(this._tableRow, "dojoxTreeGridPath") : "";
46                        if(p){
47                                query("tr[dojoxTreeGridPath^=\"" + p + "/\"]", this.rowNode).forEach(function(n){
48                                        var en = query(".dojoxGridExpando", n)[0];
49                                        if(en && en.parentNode && en.parentNode.parentNode &&
50                                                                !domClass.contains(en.parentNode.parentNode, "dojoxGridNoChildren")){
51                                                var ew = dijit.byNode(en);
52                                                if(ew){
53                                                        ew._toggleRows(toggleClass, ew.open&&open);
54                                                }
55                                        }
56                                        n.style.display = open ? "" : "none";
57                                });
58                        }
59                }else{
60                        query("tr." + toggleClass, this.rowNode).forEach(function(n){
61                                if(domClass.contains(n, "dojoxGridExpandoRow")){
62                                        var en = query(".dojoxGridExpando", n)[0];
63                                        if(en){
64                                                var ew = dijit.byNode(en);
65                                                var toggleClass = ew ? ew.toggleClass : en.getAttribute("toggleClass");
66                                                var wOpen = ew ? ew.open : self.expandoCell.getOpenState(en.getAttribute("itemId"));
67                                                self._toggleRows(toggleClass, wOpen&&open);
68                                        }
69                                }
70                                n.style.display = open ? "" : "none";
71                        });
72                }
73        },
74        setOpen: function(open){
75                if(open && domClass.contains(this.domNode, "dojoxGridExpandoLoading")){
76                        open = false;
77                }
78                var view = this.view;
79                var grid = view.grid;
80                var store = grid.store;
81                var treeModel = grid.treeModel;
82                var d = this;
83                var idx = this.rowIdx;
84                var me = grid._by_idx[idx];
85                if(!me){ return; }
86                if(treeModel && !this._loadedChildren){
87                        if(open){
88                                // Do this to make sure our children are fully-loaded
89                                var itm = grid.getItem(domAttr.get(this._tableRow, "dojoxTreeGridPath"));
90                                if(itm){
91                                        this.expandoInner.innerHTML = "o";
92                                        domClass.add(this.domNode, "dojoxGridExpandoLoading");
93                                        treeModel.getChildren(itm, function(items){
94                                                d._loadedChildren = true;
95                                                d._setOpen(open);
96                                        });
97                                }else{
98                                        this._setOpen(open);
99                                }
100                        }else{
101                                this._setOpen(open);
102                        }
103                }else if(!treeModel && store){
104                        if(open){
105                                var data = grid._by_idx[this.rowIdx];
106                                if(data&&!store.isItemLoaded(data.item)){
107                                        this.expandoInner.innerHTML = "o";
108                                        domClass.add(this.domNode, "dojoxGridExpandoLoading");
109                                        store.loadItem({
110                                                item: data.item,
111                                                onItem: lang.hitch(this, function(i){
112                                                        var idty = store.getIdentity(i);
113                                                        grid._by_idty[idty] = grid._by_idx[this.rowIdx] = { idty: idty, item: i };
114                                                        this._setOpen(open);
115                                                })
116                                        });
117                                }else{
118                                        this._setOpen(open);
119                                }
120                        }else{
121                                this._setOpen(open);
122                        }
123                }else{
124                        this._setOpen(open);
125                }
126        },
127        _setOpen: function(open){
128                if(open && this._tableRow && domClass.contains(this._tableRow, "dojoxGridNoChildren")){
129                        this._setOpen(false);
130                        return;
131                }
132                this.expandoInner.innerHTML = open ? "-" : "+";
133                domClass.remove(this.domNode, "dojoxGridExpandoLoading");
134                domClass.toggle(this.domNode, "dojoxGridExpandoOpened", open);
135                if(this._tableRow){
136                        domClass.toggle(this._tableRow, "dojoxGridRowCollapsed", !open);
137                        var base = domAttr.get(this._tableRow, "dojoxTreeGridBaseClasses");
138                        var new_base = "";
139                        if(open){
140                                new_base = lang.trim((" " + base + " ").replace(" dojoxGridRowCollapsed ", " "));
141                        }else{
142                                if((" " + base + " ").indexOf(' dojoxGridRowCollapsed ') < 0){
143                                        new_base = base + (base ? ' ' : '' ) + 'dojoxGridRowCollapsed';
144                                }else{
145                                        new_base = base;
146                                }
147                        }
148                        domAttr.set(this._tableRow, 'dojoxTreeGridBaseClasses', new_base);
149                }
150                var changed = (this.open !== open);
151                this.open = open;
152                if(this.expandoCell && this.itemId){
153                        this.expandoCell.openStates[this.itemId] = open;
154                }
155                var v = this.view;
156                var g = v.grid;
157                if(this.toggleClass && changed){
158                        if(!this._tableRow || !this._tableRow.style.display){
159                                this._toggleRows(this.toggleClass, open);
160                        }
161                }
162                if(v && this._initialized && this.rowIdx >= 0){
163                        g.rowHeightChanged(this.rowIdx);
164                        g.postresize();
165                        v.hasVScrollbar(true);
166                }
167                this._initialized = true;
168        },
169        onToggle: function(e){
170                this.setOpen(!this.open);
171                event.stop(e);
172        },
173        setRowNode: function(rowIdx, rowNode, view){
174                if(this.cellIdx < 0 || !this.itemId){ return false; }
175                this._initialized = false;
176                this.view = view;
177                this.rowNode = rowNode;
178                this.rowIdx = rowIdx;
179                this.expandoCell = view.structure.cells[0][this.cellIdx];
180                var d = this.domNode;
181                if(d && d.parentNode && d.parentNode.parentNode){
182                        this._tableRow = d.parentNode.parentNode;
183                }
184                this.open = this.expandoCell.getOpenState(this.itemId);
185                if(view.grid.treeModel){
186                        // TODO: Rather than hard-code the 18px and 3px, we should probably
187                        // calculate them based off css or something...  However, all the
188                        // themes that we support use these values.
189                        domStyle.set(this.domNode , "marginLeft" , (this.level * 18) + "px");
190                        if(this.domNode.parentNode){
191                                domStyle.set(this.domNode.parentNode, "backgroundPosition", ((this.level * 18) + (3)) + "px");
192                        }
193                }
194                this.setOpen(this.open);
195                return true;
196        }
197});
198
199var _TreeContentBuilder = declare("dojox.grid._TreeContentBuilder", _Builder._ContentBuilder, {
200        generateHtml: function(inDataIndex, inRowIndex){
201                var
202                        html = this.getTableArray(),
203                        v = this.view,
204                        row = v.structure.cells[0],
205                        item = this.grid.getItem(inRowIndex),
206                        grid = this.grid,
207                        store = this.grid.store;
208
209                util.fire(this.view, "onBeforeRow", [inRowIndex, [row]]);
210               
211                var createRow = function(level, rowItem, summaryRow, toggleClasses, rowStack, shown){
212                        if(!shown){
213                                if(html[0].indexOf('dojoxGridRowTableNeedsRowUpdate') == -1){
214                                        html[0] = html[0].replace("dojoxGridRowTable", "dojoxGridRowTable dojoxGridRowTableNeedsRowUpdate");
215                                }
216                                return;
217                        }
218                        var rowNodeIdx = html.length;
219                        toggleClasses = toggleClasses || [];
220                        var tcJoin = toggleClasses.join('|');
221                        var tcString = toggleClasses[toggleClasses.length - 1];
222                        var clString = tcString + (summaryRow ? " dojoxGridSummaryRow" : "");
223                        var sString = "";
224                        if(grid.treeModel && rowItem && !grid.treeModel.mayHaveChildren(rowItem)){
225                                clString += " dojoxGridNoChildren";
226                        }
227                        html.push('<tr style="' + sString + '" class="' + clString + '" dojoxTreeGridPath="' + rowStack.join('/') +  '" dojoxTreeGridBaseClasses="' + clString + '">');
228                        var nextLevel = level + 1;
229                        var parentCell = null;
230                        for(var i=0, cell; (cell=row[i]); i++){
231                                var m = cell.markup, cc = cell.customClasses = [], cs = cell.customStyles = [];
232                                // content (format can fill in cc and cs as side-effects)
233                                m[5] = cell.formatAtLevel(rowStack, rowItem, level, summaryRow, tcString, cc);
234                                // classes
235                                m[1] = cc.join(' ');
236                                // styles
237                                m[3] = cs.join(';');
238                                // in-place concat
239                                html.push.apply(html, m);
240                                if(!parentCell && cell.level === nextLevel && cell.parentCell){
241                                        parentCell = cell.parentCell;
242                                }
243                        }
244                        html.push('</tr>');
245                        if(rowItem && store && store.isItem(rowItem)){
246                                var idty = store.getIdentity(rowItem);
247                                if(typeof grid._by_idty_paths[idty] == "undefined"){
248                                        grid._by_idty_paths[idty] = rowStack.join('/');
249                                }
250                        }
251                        var expandoCell;
252                        var parentOpen;
253                        var path;
254                        var values;
255                        var iStack = rowStack.concat([]);
256                        if(grid.treeModel && rowItem){
257                                if(grid.treeModel.mayHaveChildren(rowItem)){
258                                        expandoCell = v.structure.cells[0][grid.expandoCell||0];
259                                        parentOpen = expandoCell.getOpenState(rowItem) && shown;
260                                        path = new dojox.grid.TreePath(rowStack.join('/'), grid);
261                                        values = path.children(true)||[];
262                                        array.forEach(values, function(cItm, idx){
263                                                var nToggle = tcJoin.split('|');
264                                                nToggle.push(nToggle[nToggle.length - 1] + "-" + idx);
265                                                iStack.push(idx);
266                                                createRow(nextLevel, cItm, false, nToggle, iStack, parentOpen);
267                                                iStack.pop();
268                                        });
269                                }
270                        }else if(rowItem && parentCell && !summaryRow){
271                                expandoCell = v.structure.cells[0][parentCell.level];
272                                parentOpen = expandoCell.getOpenState(rowItem) && shown;
273                                if(store.hasAttribute(rowItem, parentCell.field)){
274                                        var tToggle = tcJoin.split('|');
275                                        tToggle.pop();
276                                        path = new dojox.grid.TreePath(rowStack.join('/'), grid);
277                                        values = path.children(true)||[];
278                                        if(values.length){
279                                                html[rowNodeIdx] = '<tr class="' + tToggle.join(' ') +' dojoxGridExpandoRow" dojoxTreeGridPath="' + rowStack.join('/') + '">';
280                                                array.forEach(values, function(cItm, idx){
281                                                        var nToggle = tcJoin.split('|');
282                                                        nToggle.push(nToggle[nToggle.length - 1] + "-" + idx);
283                                                        iStack.push(idx);
284                                                        createRow(nextLevel, cItm, false, nToggle, iStack, parentOpen);
285                                                        iStack.pop();
286                                                });
287                                                iStack.push(values.length);
288                                                createRow(level, rowItem, true, toggleClasses, iStack, parentOpen);
289                                        }else{
290                                                html[rowNodeIdx] = '<tr class="' + tcString + ' dojoxGridNoChildren" dojoxTreeGridPath="' + rowStack.join('/') + '">';
291                                        }
292                                }else{
293                                        if(!store.isItemLoaded(rowItem)){
294                                                html[0] = html[0].replace("dojoxGridRowTable", "dojoxGridRowTable dojoxGridRowTableNeedsRowUpdate");
295                                        }else{
296                                                html[rowNodeIdx] = '<tr class="' + tcString + ' dojoxGridNoChildren" dojoxTreeGridPath="' + rowStack.join('/') + '">';
297                                        }
298                                }
299                        }else if(rowItem && !summaryRow && toggleClasses.length > 1){
300                                html[rowNodeIdx] = '<tr class="' + toggleClasses[toggleClasses.length - 2] + '" dojoxTreeGridPath="' + rowStack.join('/') + '">';
301                        }
302                };
303                createRow(0, item, false, ["dojoxGridRowToggle-" + inRowIndex], [inRowIndex], true);
304                html.push('</table>');
305                return html.join(''); // String
306        },
307        findTarget: function(inSource, inTag){
308                var n = inSource;
309                while(n && (n!=this.domNode)){
310                        if(n.tagName && n.tagName.toLowerCase() == 'tr'){
311                                break;
312                        }
313                        n = n.parentNode;
314                }
315                return (n != this.domNode) ? n : null;
316        },
317        getCellNode: function(inRowNode, inCellIndex){
318                var node = query("td[idx='" + inCellIndex + "']", inRowNode)[0];
319                if(node&&node.parentNode&&!domClass.contains(node.parentNode, "dojoxGridSummaryRow")){
320                        return node;
321                }
322        },
323        decorateEvent: function(e){
324                e.rowNode = this.findRowTarget(e.target);
325                if(!e.rowNode){return false;}
326                e.rowIndex = domAttr.get(e.rowNode, 'dojoxTreeGridPath');
327                this.baseDecorateEvent(e);
328                e.cell = this.grid.getCell(e.cellIndex);
329                return true; // Boolean
330        }
331});
332
333return declare("dojox.grid._TreeView", _View, {
334        _contentBuilderClass: _TreeContentBuilder,
335        _onDndDrop: function(source, nodes, copy){
336                if(this.grid && this.grid.aggregator){
337                        this.grid.aggregator.clearSubtotalCache();
338                }
339                this.inherited(arguments);
340        },
341        postCreate: function(){
342                this.inherited(arguments);
343                this.connect(this.grid, '_cleanupExpandoCache', '_cleanupExpandoCache');
344        },
345        _cleanupExpandoCache: function(index, identity, item){
346                if(index == -1){
347                        return;
348                }
349                array.forEach(this.grid.layout.cells, function(cell){
350                        if(typeof cell['openStates'] != 'undefined'){
351                                if(identity in cell.openStates){
352                                        delete cell.openStates[identity];
353                                }
354                        }
355                });
356                if(typeof index == "string" && index.indexOf('/') > -1){
357                        var path = new dojox.grid.TreePath(index, this.grid);
358                        var ppath = path.parent();
359                        while(ppath){
360                                path = ppath;
361                                ppath = path.parent();
362                        }
363                        var pitem = path.item();
364                        if(!pitem){
365                                return;
366                        }
367                        var idty = this.grid.store.getIdentity(pitem);
368                        if(typeof this._expandos[idty] != 'undefined'){
369                                for(var i in this._expandos[idty]){
370                                        var exp = this._expandos[idty][i];
371                                        if(exp){
372                                                exp.destroy();
373                                        }
374                                        delete this._expandos[idty][i];
375                                }
376                                delete this._expandos[idty];
377                        }
378                }else{
379                        for(var i in this._expandos){
380                                if(typeof this._expandos[i] != 'undefined'){
381                                        for(var j in this._expandos[i]){
382                                                var exp = this._expandos[i][j];
383                                                if(exp){
384                                                        exp.destroy();
385                                                }
386                                        }
387                                }
388                        }
389                        this._expandos = {};
390                }
391        },
392        postMixInProperties: function(){
393                this.inherited(arguments);
394                this._expandos = {};
395        },
396        onBeforeRow: function(inRowIndex, cells){
397                // Save off our expando if we have one so we don't have to create it
398                // again
399                var g = this.grid;
400                if(g._by_idx && g._by_idx[inRowIndex] && g._by_idx[inRowIndex].idty){
401                        var idty = g._by_idx[inRowIndex].idty;
402                        this._expandos[idty] = this._expandos[idty] || {};
403                }
404                this.inherited(arguments);
405        },
406        onAfterRow: function(inRowIndex, cells, inRowNode){
407                array.forEach(query("span.dojoxGridExpando", inRowNode), function(n){
408                        if(n && n.parentNode){
409                                // Either create our expando or put the existing expando back
410                                // into place
411                                var tc = n.getAttribute("toggleClass");
412                                var idty;
413                                var expando;
414                                var g = this.grid;
415                                if(g._by_idx && g._by_idx[inRowIndex] && g._by_idx[inRowIndex].idty){
416                                        idty = g._by_idx[inRowIndex].idty;
417                                        expando = this._expandos[idty][tc];
418                                }
419                                if(expando){
420                                        domCtr.place(expando.domNode, n, "replace");
421                                        expando.itemId = n.getAttribute("itemId");
422                                        expando.cellIdx = parseInt(n.getAttribute("cellIdx"), 10);
423                                        if(isNaN(expando.cellIdx)){
424                                                expando.cellIdx = -1;
425                                        }
426                                }else{
427                                        if(idty){
428                                                expando = parser.parse(n.parentNode)[0];
429                                                this._expandos[idty][tc] = expando;
430                                        }
431                                }
432                                if(expando && !expando.setRowNode(inRowIndex, inRowNode, this)){
433                                        expando.domNode.parentNode.removeChild(expando.domNode);
434                                }
435                        }
436                }, this);
437                var alt = false;
438                var self = this;
439                query("tr[dojoxTreeGridPath]", inRowNode).forEach(function(n){
440                        domClass.toggle(n, "dojoxGridSubRowAlt", alt);
441                        domAttr.set(n, "dojoxTreeGridBaseClasses", n.className);
442                        alt = !alt;
443                        self.grid.rows.styleRowNode(domAttr.get(n, 'dojoxTreeGridPath'), n);
444                });
445                this.inherited(arguments);
446        },
447        updateRowStyles: function(inRowIndex){
448                var rowNodes = query("tr[dojoxTreeGridPath='" + inRowIndex + "']", this.domNode);
449                if(rowNodes.length){
450                        this.styleRowNode(inRowIndex, rowNodes[0]);
451                }
452        },
453        getCellNode: function(inRowIndex, inCellIndex){
454                var row = query("tr[dojoxTreeGridPath='" + inRowIndex + "']", this.domNode)[0];
455                if(row){
456                        return this.content.getCellNode(row, inCellIndex);
457                }
458        },
459        destroy: function(){
460                this._cleanupExpandoCache();
461                this.inherited(arguments);
462        }
463});
464});
Note: See TracBrowser for help on using the repository browser.