6e33f85ac3c5b9bbdcb9d846c3d3db98ddaf1928
[roojs1] / Roo / tree / TreePanel.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13 /**
14  * @class Roo.tree.TreePanel
15  * @extends Roo.data.Tree
16
17  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
18  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
19  * @cfg {Boolean} enableDD true to enable drag and drop
20  * @cfg {Boolean} enableDrag true to enable just drag
21  * @cfg {Boolean} enableDrop true to enable just drop
22  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
23  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
24  * @cfg {String} ddGroup The DD group this TreePanel belongs to
25  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
26  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
27  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
28  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
29  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
35  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
36  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
37  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
38  * 
39  * @constructor
40  * @param {String/HTMLElement/Element} el The container element
41  * @param {Object} config
42  */
43 Roo.tree.TreePanel = function(el, config){
44     var root = false;
45     var loader = false;
46     if (config.root) {
47         root = config.root;
48         delete config.root;
49     }
50     if (config.loader) {
51         loader = config.loader;
52         delete config.loader;
53     }
54     
55     Roo.apply(this, config);
56     Roo.tree.TreePanel.superclass.constructor.call(this);
57     this.el = Roo.get(el);
58     this.el.addClass('x-tree');
59     //console.log(root);
60     if (root) {
61         this.setRootNode( Roo.factory(root, Roo.tree));
62     }
63     if (loader) {
64         this.loader = Roo.factory(loader, Roo.tree);
65     }
66    /**
67     * Read-only. The id of the container element becomes this TreePanel's id.
68     */
69     this.id = this.el.id;
70     this.addEvents({
71         /**
72         * @event beforeload
73         * Fires before a node is loaded, return false to cancel
74         * @param {Node} node The node being loaded
75         */
76         "beforeload" : true,
77         /**
78         * @event load
79         * Fires when a node is loaded
80         * @param {Node} node The node that was loaded
81         */
82         "load" : true,
83         /**
84         * @event textchange
85         * Fires when the text for a node is changed
86         * @param {Node} node The node
87         * @param {String} text The new text
88         * @param {String} oldText The old text
89         */
90         "textchange" : true,
91         /**
92         * @event beforeexpand
93         * Fires before a node is expanded, return false to cancel.
94         * @param {Node} node The node
95         * @param {Boolean} deep
96         * @param {Boolean} anim
97         */
98         "beforeexpand" : true,
99         /**
100         * @event beforecollapse
101         * Fires before a node is collapsed, return false to cancel.
102         * @param {Node} node The node
103         * @param {Boolean} deep
104         * @param {Boolean} anim
105         */
106         "beforecollapse" : true,
107         /**
108         * @event expand
109         * Fires when a node is expanded
110         * @param {Node} node The node
111         */
112         "expand" : true,
113         /**
114         * @event disabledchange
115         * Fires when the disabled status of a node changes
116         * @param {Node} node The node
117         * @param {Boolean} disabled
118         */
119         "disabledchange" : true,
120         /**
121         * @event collapse
122         * Fires when a node is collapsed
123         * @param {Node} node The node
124         */
125         "collapse" : true,
126         /**
127         * @event beforeclick
128         * Fires before click processing on a node. Return false to cancel the default action.
129         * @param {Node} node The node
130         * @param {Roo.EventObject} e The event object
131         */
132         "beforeclick":true,
133         /**
134         * @event checkchange
135         * Fires when a node with a checkbox's checked property changes
136         * @param {Node} this This node
137         * @param {Boolean} checked
138         */
139         "checkchange":true,
140         /**
141         * @event click
142         * Fires when a node is clicked
143         * @param {Node} node The node
144         * @param {Roo.EventObject} e The event object
145         */
146         "click":true,
147         /**
148         * @event dblclick
149         * Fires when a node is double clicked
150         * @param {Node} node The node
151         * @param {Roo.EventObject} e The event object
152         */
153         "dblclick":true,
154         /**
155         * @event contextmenu
156         * Fires when a node is right clicked
157         * @param {Node} node The node
158         * @param {Roo.EventObject} e The event object
159         */
160         "contextmenu":true,
161         /**
162         * @event beforechildrenrendered
163         * Fires right before the child nodes for a node are rendered
164         * @param {Node} node The node
165         */
166         "beforechildrenrendered":true,
167         /**
168         * @event startdrag
169         * Fires when a node starts being dragged
170         * @param {Roo.tree.TreePanel} this
171         * @param {Roo.tree.TreeNode} node
172         * @param {event} e The raw browser event
173         */ 
174        "startdrag" : true,
175        /**
176         * @event enddrag
177         * Fires when a drag operation is complete
178         * @param {Roo.tree.TreePanel} this
179         * @param {Roo.tree.TreeNode} node
180         * @param {event} e The raw browser event
181         */
182        "enddrag" : true,
183        /**
184         * @event dragdrop
185         * Fires when a dragged node is dropped on a valid DD target
186         * @param {Roo.tree.TreePanel} this
187         * @param {Roo.tree.TreeNode} node
188         * @param {DD} dd The dd it was dropped on
189         * @param {event} e The raw browser event
190         */
191        "dragdrop" : true,
192        /**
193         * @event beforenodedrop
194         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
195         * passed to handlers has the following properties:<br />
196         * <ul style="padding:5px;padding-left:16px;">
197         * <li>tree - The TreePanel</li>
198         * <li>target - The node being targeted for the drop</li>
199         * <li>data - The drag data from the drag source</li>
200         * <li>point - The point of the drop - append, above or below</li>
201         * <li>source - The drag source</li>
202         * <li>rawEvent - Raw mouse event</li>
203         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
204         * to be inserted by setting them on this object.</li>
205         * <li>cancel - Set this to true to cancel the drop.</li>
206         * </ul>
207         * @param {Object} dropEvent
208         */
209        "beforenodedrop" : true,
210        /**
211         * @event nodedrop
212         * Fires after a DD object is dropped on a node in this tree. The dropEvent
213         * passed to handlers has the following properties:<br />
214         * <ul style="padding:5px;padding-left:16px;">
215         * <li>tree - The TreePanel</li>
216         * <li>target - The node being targeted for the drop</li>
217         * <li>data - The drag data from the drag source</li>
218         * <li>point - The point of the drop - append, above or below</li>
219         * <li>source - The drag source</li>
220         * <li>rawEvent - Raw mouse event</li>
221         * <li>dropNode - Dropped node(s).</li>
222         * </ul>
223         * @param {Object} dropEvent
224         */
225        "nodedrop" : true,
226         /**
227         * @event nodedragover
228         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
229         * passed to handlers has the following properties:<br />
230         * <ul style="padding:5px;padding-left:16px;">
231         * <li>tree - The TreePanel</li>
232         * <li>target - The node being targeted for the drop</li>
233         * <li>data - The drag data from the drag source</li>
234         * <li>point - The point of the drop - append, above or below</li>
235         * <li>source - The drag source</li>
236         * <li>rawEvent - Raw mouse event</li>
237         * <li>dropNode - Drop node(s) provided by the source.</li>
238         * <li>cancel - Set this to true to signal drop not allowed.</li>
239         * </ul>
240         * @param {Object} dragOverEvent
241         */
242        "nodedragover" : true
243         
244     });
245     if(this.singleExpand){
246        this.on("beforeexpand", this.restrictExpand, this);
247     }
248     if (this.editor) {
249         this.editor.tree = this;
250         this.editor = Roo.factory(this.editor, Roo.tree);
251     }
252     
253     if (this.selModel) {
254         this.selModel = Roo.factory(this.selModel, Roo.tree);
255     }
256    
257 };
258 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
259     rootVisible : true,
260     animate: Roo.enableFx,
261     lines : true,
262     enableDD : false,
263     hlDrop : Roo.enableFx,
264   
265     renderer: false,
266     
267     rendererTip: false,
268     // private
269     restrictExpand : function(node){
270         var p = node.parentNode;
271         if(p){
272             if(p.expandedChild && p.expandedChild.parentNode == p){
273                 p.expandedChild.collapse();
274             }
275             p.expandedChild = node;
276         }
277     },
278
279     // private override
280     setRootNode : function(node){
281         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
282         if(!this.rootVisible){
283             node.ui = new Roo.tree.RootTreeNodeUI(node);
284         }
285         return node;
286     },
287
288     /**
289      * Returns the container element for this TreePanel
290      */
291     getEl : function(){
292         return this.el;
293     },
294
295     /**
296      * Returns the default TreeLoader for this TreePanel
297      */
298     getLoader : function(){
299         return this.loader;
300     },
301
302     /**
303      * Expand all nodes
304      */
305     expandAll : function(){
306         this.root.expand(true);
307     },
308
309     /**
310      * Collapse all nodes
311      */
312     collapseAll : function(){
313         this.root.collapse(true);
314     },
315
316     /**
317      * Returns the selection model used by this TreePanel
318      */
319     getSelectionModel : function(){
320         if(!this.selModel){
321             this.selModel = new Roo.tree.DefaultSelectionModel();
322         }
323         return this.selModel;
324     },
325
326     /**
327      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
328      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
329      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
330      * @return {Array}
331      */
332     getChecked : function(a, startNode){
333         startNode = startNode || this.root;
334         var r = [];
335         var f = function(){
336             if(this.attributes.checked){
337                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
338             }
339         }
340         startNode.cascade(f);
341         return r;
342     },
343
344     /**
345      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
346      * @param {String} path
347      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
348      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
349      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
350      */
351     expandPath : function(path, attr, callback){
352         attr = attr || "id";
353         var keys = path.split(this.pathSeparator);
354         var curNode = this.root;
355         if(curNode.attributes[attr] != keys[1]){ // invalid root
356             if(callback){
357                 callback(false, null);
358             }
359             return;
360         }
361         var index = 1;
362         var f = function(){
363             if(++index == keys.length){
364                 if(callback){
365                     callback(true, curNode);
366                 }
367                 return;
368             }
369             var c = curNode.findChild(attr, keys[index]);
370             if(!c){
371                 if(callback){
372                     callback(false, curNode);
373                 }
374                 return;
375             }
376             curNode = c;
377             c.expand(false, false, f);
378         };
379         curNode.expand(false, false, f);
380     },
381
382     /**
383      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
384      * @param {String} path
385      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
386      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
387      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
388      */
389     selectPath : function(path, attr, callback){
390         attr = attr || "id";
391         var keys = path.split(this.pathSeparator);
392         var v = keys.pop();
393         if(keys.length > 0){
394             var f = function(success, node){
395                 if(success && node){
396                     var n = node.findChild(attr, v);
397                     if(n){
398                         n.select();
399                         if(callback){
400                             callback(true, n);
401                         }
402                     }else if(callback){
403                         callback(false, n);
404                     }
405                 }else{
406                     if(callback){
407                         callback(false, n);
408                     }
409                 }
410             };
411             this.expandPath(keys.join(this.pathSeparator), attr, f);
412         }else{
413             this.root.select();
414             if(callback){
415                 callback(true, this.root);
416             }
417         }
418     },
419
420     getTreeEl : function(){
421         return this.el;
422     },
423
424     /**
425      * Trigger rendering of this TreePanel
426      */
427     render : function(){
428         if (this.innerCt) {
429             return this; // stop it rendering more than once!!
430         }
431         
432         this.innerCt = this.el.createChild({tag:"ul",
433                cls:"x-tree-root-ct " +
434                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
435
436         if(this.containerScroll){
437             Roo.dd.ScrollManager.register(this.el);
438         }
439         if((this.enableDD || this.enableDrop) && !this.dropZone){
440            /**
441             * The dropZone used by this tree if drop is enabled
442             * @type Roo.tree.TreeDropZone
443             */
444              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
445                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
446            });
447         }
448         if((this.enableDD || this.enableDrag) && !this.dragZone){
449            /**
450             * The dragZone used by this tree if drag is enabled
451             * @type Roo.tree.TreeDragZone
452             */
453             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
454                ddGroup: this.ddGroup || "TreeDD",
455                scroll: this.ddScroll
456            });
457         }
458         this.getSelectionModel().init(this);
459         if (!this.root) {
460             Roo.log("ROOT not set in tree");
461             return this;
462         }
463         this.root.render();
464         if(!this.rootVisible){
465             this.root.renderChildren();
466         }
467         return this;
468     }
469 });