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