source: Dev/branches/rest-dojo-ui/client/dojox/grid/_TreeView.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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.