Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / docs / source / TreeNodeUI.html
1 <html>
2 <head>
3   <title>The source code</title>
4     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
5     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
6 </head>
7 <body  onload="prettyPrint();">
8     <pre class="prettyprint lang-js">/*!
9  * Ext JS Library 3.0.3
10  * Copyright(c) 2006-2009 Ext JS, LLC
11  * licensing@extjs.com
12  * http://www.extjs.com/license
13  */
14 <div id="cls-Ext.tree.TreeNodeUI"></div>/**\r
15  * @class Ext.tree.TreeNodeUI\r
16  * This class provides the default UI implementation for Ext TreeNodes.\r
17  * The TreeNode UI implementation is separate from the\r
18  * tree implementation, and allows customizing of the appearance of\r
19  * tree nodes.<br>\r
20  * <p>\r
21  * If you are customizing the Tree's user interface, you\r
22  * may need to extend this class, but you should never need to instantiate this class.<br>\r
23  * <p>\r
24  * This class provides access to the user interface components of an Ext TreeNode, through\r
25  * {@link Ext.tree.TreeNode#getUI}\r
26  */\r
27 Ext.tree.TreeNodeUI = function(node){\r
28     this.node = node;\r
29     this.rendered = false;\r
30     this.animating = false;\r
31     this.wasLeaf = true;\r
32     this.ecc = 'x-tree-ec-icon x-tree-elbow';\r
33     this.emptyIcon = Ext.BLANK_IMAGE_URL;\r
34 };\r
35 \r
36 Ext.tree.TreeNodeUI.prototype = {\r
37     // private\r
38     removeChild : function(node){\r
39         if(this.rendered){\r
40             this.ctNode.removeChild(node.ui.getEl());\r
41         } \r
42     },\r
43 \r
44     // private\r
45     beforeLoad : function(){\r
46          this.addClass("x-tree-node-loading");\r
47     },\r
48 \r
49     // private\r
50     afterLoad : function(){\r
51          this.removeClass("x-tree-node-loading");\r
52     },\r
53 \r
54     // private\r
55     onTextChange : function(node, text, oldText){\r
56         if(this.rendered){\r
57             this.textNode.innerHTML = text;\r
58         }\r
59     },\r
60 \r
61     // private\r
62     onDisableChange : function(node, state){\r
63         this.disabled = state;\r
64                 if (this.checkbox) {\r
65                         this.checkbox.disabled = state;\r
66                 }        \r
67         if(state){\r
68             this.addClass("x-tree-node-disabled");\r
69         }else{\r
70             this.removeClass("x-tree-node-disabled");\r
71         } \r
72     },\r
73 \r
74     // private\r
75     onSelectedChange : function(state){\r
76         if(state){\r
77             this.focus();\r
78             this.addClass("x-tree-selected");\r
79         }else{\r
80             //this.blur();\r
81             this.removeClass("x-tree-selected");\r
82         }\r
83     },\r
84 \r
85     // private\r
86     onMove : function(tree, node, oldParent, newParent, index, refNode){\r
87         this.childIndent = null;\r
88         if(this.rendered){\r
89             var targetNode = newParent.ui.getContainer();\r
90             if(!targetNode){//target not rendered\r
91                 this.holder = document.createElement("div");\r
92                 this.holder.appendChild(this.wrap);\r
93                 return;\r
94             }\r
95             var insertBefore = refNode ? refNode.ui.getEl() : null;\r
96             if(insertBefore){\r
97                 targetNode.insertBefore(this.wrap, insertBefore);\r
98             }else{\r
99                 targetNode.appendChild(this.wrap);\r
100             }\r
101             this.node.renderIndent(true, oldParent != newParent);\r
102         }\r
103     },\r
104 \r
105 <div id="method-Ext.tree.TreeNodeUI-addClass"></div>/**\r
106  * Adds one or more CSS classes to the node's UI element.\r
107  * Duplicate classes are automatically filtered out.\r
108  * @param {String/Array} className The CSS class to add, or an array of classes\r
109  */\r
110     addClass : function(cls){\r
111         if(this.elNode){\r
112             Ext.fly(this.elNode).addClass(cls);\r
113         }\r
114     },\r
115 \r
116 <div id="method-Ext.tree.TreeNodeUI-removeClass"></div>/**\r
117  * Removes one or more CSS classes from the node's UI element.\r
118  * @param {String/Array} className The CSS class to remove, or an array of classes\r
119  */\r
120     removeClass : function(cls){\r
121         if(this.elNode){\r
122             Ext.fly(this.elNode).removeClass(cls);  \r
123         }\r
124     },\r
125 \r
126     // private\r
127     remove : function(){\r
128         if(this.rendered){\r
129             this.holder = document.createElement("div");\r
130             this.holder.appendChild(this.wrap);\r
131         }  \r
132     },\r
133 \r
134     // private\r
135     fireEvent : function(){\r
136         return this.node.fireEvent.apply(this.node, arguments);  \r
137     },\r
138 \r
139     // private\r
140     initEvents : function(){\r
141         this.node.on("move", this.onMove, this);\r
142 \r
143         if(this.node.disabled){\r
144             this.onDisableChange(this.node, true);            \r
145         }\r
146         if(this.node.hidden){\r
147             this.hide();\r
148         }\r
149         var ot = this.node.getOwnerTree();\r
150         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;\r
151         if(dd && (!this.node.isRoot || ot.rootVisible)){\r
152             Ext.dd.Registry.register(this.elNode, {\r
153                 node: this.node,\r
154                 handles: this.getDDHandles(),\r
155                 isHandle: false\r
156             });\r
157         }\r
158     },\r
159 \r
160     // private\r
161     getDDHandles : function(){\r
162         return [this.iconNode, this.textNode, this.elNode];\r
163     },\r
164 \r
165 <div id="method-Ext.tree.TreeNodeUI-hide"></div>/**\r
166  * Hides this node.\r
167  */\r
168     hide : function(){\r
169         this.node.hidden = true;\r
170         if(this.wrap){\r
171             this.wrap.style.display = "none";\r
172         }\r
173     },\r
174 \r
175 <div id="method-Ext.tree.TreeNodeUI-show"></div>/**\r
176  * Shows this node.\r
177  */\r
178     show : function(){\r
179         this.node.hidden = false;\r
180         if(this.wrap){\r
181             this.wrap.style.display = "";\r
182         } \r
183     },\r
184 \r
185     // private\r
186     onContextMenu : function(e){\r
187         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {\r
188             e.preventDefault();\r
189             this.focus();\r
190             this.fireEvent("contextmenu", this.node, e);\r
191         }\r
192     },\r
193 \r
194     // private\r
195     onClick : function(e){\r
196         if(this.dropping){\r
197             e.stopEvent();\r
198             return;\r
199         }\r
200         if(this.fireEvent("beforeclick", this.node, e) !== false){\r
201             var a = e.getTarget('a');\r
202             if(!this.disabled && this.node.attributes.href && a){\r
203                 this.fireEvent("click", this.node, e);\r
204                 return;\r
205             }else if(a && e.ctrlKey){\r
206                 e.stopEvent();\r
207             }\r
208             e.preventDefault();\r
209             if(this.disabled){\r
210                 return;\r
211             }\r
212 \r
213             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){\r
214                 this.node.toggle();\r
215             }\r
216 \r
217             this.fireEvent("click", this.node, e);\r
218         }else{\r
219             e.stopEvent();\r
220         }\r
221     },\r
222 \r
223     // private\r
224     onDblClick : function(e){\r
225         e.preventDefault();\r
226         if(this.disabled){\r
227             return;\r
228         }\r
229         if(this.fireEvent("beforedblclick", this.node, e) !== false){\r
230             if(this.checkbox){\r
231                 this.toggleCheck();\r
232             }\r
233             if(!this.animating && this.node.isExpandable()){\r
234                 this.node.toggle();\r
235             }\r
236             this.fireEvent("dblclick", this.node, e);\r
237         }\r
238     },\r
239 \r
240     onOver : function(e){\r
241         this.addClass('x-tree-node-over');\r
242     },\r
243 \r
244     onOut : function(e){\r
245         this.removeClass('x-tree-node-over');\r
246     },\r
247 \r
248     // private\r
249     onCheckChange : function(){\r
250         var checked = this.checkbox.checked;\r
251                 // fix for IE6\r
252                 this.checkbox.defaultChecked = checked;         \r
253         this.node.attributes.checked = checked;\r
254         this.fireEvent('checkchange', this.node, checked);\r
255     },\r
256 \r
257     // private\r
258     ecClick : function(e){\r
259         if(!this.animating && this.node.isExpandable()){\r
260             this.node.toggle();\r
261         }\r
262     },\r
263 \r
264     // private\r
265     startDrop : function(){\r
266         this.dropping = true;\r
267     },\r
268     \r
269     // delayed drop so the click event doesn't get fired on a drop\r
270     endDrop : function(){ \r
271        setTimeout(function(){\r
272            this.dropping = false;\r
273        }.createDelegate(this), 50); \r
274     },\r
275 \r
276     // private\r
277     expand : function(){\r
278         this.updateExpandIcon();\r
279         this.ctNode.style.display = "";\r
280     },\r
281 \r
282     // private\r
283     focus : function(){\r
284         if(!this.node.preventHScroll){\r
285             try{this.anchor.focus();\r
286             }catch(e){}\r
287         }else{\r
288             try{\r
289                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;\r
290                 var l = noscroll.scrollLeft;\r
291                 this.anchor.focus();\r
292                 noscroll.scrollLeft = l;\r
293             }catch(e){}\r
294         }\r
295     },\r
296 \r
297 <div id="method-Ext.tree.TreeNodeUI-toggleCheck"></div>/**\r
298  * Sets the checked status of the tree node to the passed value, or, if no value was passed,\r
299  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.\r
300  * @param {Boolean} (optional) The new checked status.\r
301  */\r
302     toggleCheck : function(value){\r
303         var cb = this.checkbox;\r
304         if(cb){\r
305             cb.checked = (value === undefined ? !cb.checked : value);\r
306             this.onCheckChange();\r
307         }\r
308     },\r
309 \r
310     // private\r
311     blur : function(){\r
312         try{\r
313             this.anchor.blur();\r
314         }catch(e){} \r
315     },\r
316 \r
317     // private\r
318     animExpand : function(callback){\r
319         var ct = Ext.get(this.ctNode);\r
320         ct.stopFx();\r
321         if(!this.node.isExpandable()){\r
322             this.updateExpandIcon();\r
323             this.ctNode.style.display = "";\r
324             Ext.callback(callback);\r
325             return;\r
326         }\r
327         this.animating = true;\r
328         this.updateExpandIcon();\r
329         \r
330         ct.slideIn('t', {\r
331            callback : function(){\r
332                this.animating = false;\r
333                Ext.callback(callback);\r
334             },\r
335             scope: this,\r
336             duration: this.node.ownerTree.duration || .25\r
337         });\r
338     },\r
339 \r
340     // private\r
341     highlight : function(){\r
342         var tree = this.node.getOwnerTree();\r
343         Ext.fly(this.wrap).highlight(\r
344             tree.hlColor || "C3DAF9",\r
345             {endColor: tree.hlBaseColor}\r
346         );\r
347     },\r
348 \r
349     // private\r
350     collapse : function(){\r
351         this.updateExpandIcon();\r
352         this.ctNode.style.display = "none";\r
353     },\r
354 \r
355     // private\r
356     animCollapse : function(callback){\r
357         var ct = Ext.get(this.ctNode);\r
358         ct.enableDisplayMode('block');\r
359         ct.stopFx();\r
360 \r
361         this.animating = true;\r
362         this.updateExpandIcon();\r
363 \r
364         ct.slideOut('t', {\r
365             callback : function(){\r
366                this.animating = false;\r
367                Ext.callback(callback);\r
368             },\r
369             scope: this,\r
370             duration: this.node.ownerTree.duration || .25\r
371         });\r
372     },\r
373 \r
374     // private\r
375     getContainer : function(){\r
376         return this.ctNode;  \r
377     },\r
378 \r
379     // private\r
380     getEl : function(){\r
381         return this.wrap;  \r
382     },\r
383 \r
384     // private\r
385     appendDDGhost : function(ghostNode){\r
386         ghostNode.appendChild(this.elNode.cloneNode(true));\r
387     },\r
388 \r
389     // private\r
390     getDDRepairXY : function(){\r
391         return Ext.lib.Dom.getXY(this.iconNode);\r
392     },\r
393 \r
394     // private\r
395     onRender : function(){\r
396         this.render();    \r
397     },\r
398 \r
399     // private\r
400     render : function(bulkRender){\r
401         var n = this.node, a = n.attributes;\r
402         var targetNode = n.parentNode ? \r
403               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;\r
404         \r
405         if(!this.rendered){\r
406             this.rendered = true;\r
407 \r
408             this.renderElements(n, a, targetNode, bulkRender);\r
409 \r
410             if(a.qtip){\r
411                if(this.textNode.setAttributeNS){\r
412                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);\r
413                    if(a.qtipTitle){\r
414                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);\r
415                    }\r
416                }else{\r
417                    this.textNode.setAttribute("ext:qtip", a.qtip);\r
418                    if(a.qtipTitle){\r
419                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);\r
420                    }\r
421                } \r
422             }else if(a.qtipCfg){\r
423                 a.qtipCfg.target = Ext.id(this.textNode);\r
424                 Ext.QuickTips.register(a.qtipCfg);\r
425             }\r
426             this.initEvents();\r
427             if(!this.node.expanded){\r
428                 this.updateExpandIcon(true);\r
429             }\r
430         }else{\r
431             if(bulkRender === true) {\r
432                 targetNode.appendChild(this.wrap);\r
433             }\r
434         }\r
435     },\r
436 \r
437     // private\r
438     renderElements : function(n, a, targetNode, bulkRender){\r
439         // add some indent caching, this helps performance when rendering a large tree\r
440         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
441 \r
442         var cb = typeof a.checked == 'boolean';\r
443 \r
444         var href = a.href ? a.href : Ext.isGecko ? "" : "#";\r
445         var buf = ['<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls,'" unselectable="on">',\r
446             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
447             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',\r
448             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',\r
449             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',\r
450             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',\r
451              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",\r
452             '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
453             "</li>"].join('');\r
454 \r
455         var nel;\r
456         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){\r
457             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);\r
458         }else{\r
459             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);\r
460         }\r
461         \r
462         this.elNode = this.wrap.childNodes[0];\r
463         this.ctNode = this.wrap.childNodes[1];\r
464         var cs = this.elNode.childNodes;\r
465         this.indentNode = cs[0];\r
466         this.ecNode = cs[1];\r
467         this.iconNode = cs[2];\r
468         var index = 3;\r
469         if(cb){\r
470             this.checkbox = cs[3];\r
471                         // fix for IE6\r
472                         this.checkbox.defaultChecked = this.checkbox.checked;                                           \r
473             index++;\r
474         }\r
475         this.anchor = cs[index];\r
476         this.textNode = cs[index].firstChild;\r
477     },\r
478 \r
479 <div id="method-Ext.tree.TreeNodeUI-getAnchor"></div>/**\r
480  * Returns the &lt;a> element that provides focus for the node's UI.\r
481  * @return {HtmlElement} The DOM anchor element.\r
482  */\r
483     getAnchor : function(){\r
484         return this.anchor;\r
485     },\r
486     \r
487 <div id="method-Ext.tree.TreeNodeUI-getTextEl"></div>/**\r
488  * Returns the text node.\r
489  * @return {HtmlNode} The DOM text node.\r
490  */\r
491     getTextEl : function(){\r
492         return this.textNode;\r
493     },\r
494     \r
495 <div id="method-Ext.tree.TreeNodeUI-getIconEl"></div>/**\r
496  * Returns the icon &lt;img> element.\r
497  * @return {HtmlElement} The DOM image element.\r
498  */\r
499     getIconEl : function(){\r
500         return this.iconNode;\r
501     },\r
502 \r
503 <div id="method-Ext.tree.TreeNodeUI-isChecked"></div>/**\r
504  * Returns the checked status of the node. If the node was rendered with no\r
505  * checkbox, it returns false.\r
506  * @return {Boolean} The checked flag.\r
507  */\r
508     isChecked : function(){\r
509         return this.checkbox ? this.checkbox.checked : false; \r
510     },\r
511 \r
512     // private\r
513     updateExpandIcon : function(){\r
514         if(this.rendered){\r
515             var n = this.node, c1, c2;\r
516             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";\r
517             var hasChild = n.hasChildNodes();\r
518             if(hasChild || n.attributes.expandable){\r
519                 if(n.expanded){\r
520                     cls += "-minus";\r
521                     c1 = "x-tree-node-collapsed";\r
522                     c2 = "x-tree-node-expanded";\r
523                 }else{\r
524                     cls += "-plus";\r
525                     c1 = "x-tree-node-expanded";\r
526                     c2 = "x-tree-node-collapsed";\r
527                 }\r
528                 if(this.wasLeaf){\r
529                     this.removeClass("x-tree-node-leaf");\r
530                     this.wasLeaf = false;\r
531                 }\r
532                 if(this.c1 != c1 || this.c2 != c2){\r
533                     Ext.fly(this.elNode).replaceClass(c1, c2);\r
534                     this.c1 = c1; this.c2 = c2;\r
535                 }\r
536             }else{\r
537                 if(!this.wasLeaf){\r
538                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");\r
539                     delete this.c1;\r
540                     delete this.c2;\r
541                     this.wasLeaf = true;\r
542                 }\r
543             }\r
544             var ecc = "x-tree-ec-icon "+cls;\r
545             if(this.ecc != ecc){\r
546                 this.ecNode.className = ecc;\r
547                 this.ecc = ecc;\r
548             }\r
549         }\r
550     },\r
551     \r
552     // private\r
553     onIdChange: function(id){\r
554         if(this.rendered){\r
555             this.elNode.setAttribute('ext:tree-node-id', id);\r
556         }\r
557     },\r
558 \r
559     // private\r
560     getChildIndent : function(){\r
561         if(!this.childIndent){\r
562             var buf = [];\r
563             var p = this.node;\r
564             while(p){\r
565                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){\r
566                     if(!p.isLast()) {\r
567                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');\r
568                     } else {\r
569                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');\r
570                     }\r
571                 }\r
572                 p = p.parentNode;\r
573             }\r
574             this.childIndent = buf.join("");\r
575         }\r
576         return this.childIndent;\r
577     },\r
578 \r
579     // private\r
580     renderIndent : function(){\r
581         if(this.rendered){\r
582             var indent = "";\r
583             var p = this.node.parentNode;\r
584             if(p){\r
585                 indent = p.ui.getChildIndent();\r
586             }\r
587             if(this.indentMarkup != indent){ // don't rerender if not required\r
588                 this.indentNode.innerHTML = indent;\r
589                 this.indentMarkup = indent;\r
590             }\r
591             this.updateExpandIcon();\r
592         }\r
593     },\r
594 \r
595     destroy : function(){\r
596         if(this.elNode){\r
597             Ext.dd.Registry.unregister(this.elNode.id);\r
598         }\r
599         delete this.elNode;\r
600         delete this.ctNode;\r
601         delete this.indentNode;\r
602         delete this.ecNode;\r
603         delete this.iconNode;\r
604         delete this.checkbox;\r
605         delete this.anchor;\r
606         delete this.textNode;\r
607         \r
608         if (this.holder){\r
609              delete this.wrap;\r
610              Ext.removeNode(this.holder);\r
611              delete this.holder;\r
612         }else{\r
613             Ext.removeNode(this.wrap);\r
614             delete this.wrap;\r
615         }\r
616     }\r
617 };\r
618 \r
619 <div id="cls-Ext.tree.RootTreeNodeUI"></div>/**\r
620  * @class Ext.tree.RootTreeNodeUI\r
621  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.\r
622  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>\r
623  * <p>\r
624  * If you are customizing the Tree's user interface, you\r
625  * may need to extend this class, but you should never need to instantiate this class.<br>\r
626  */\r
627 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
628     // private\r
629     render : function(){\r
630         if(!this.rendered){\r
631             var targetNode = this.node.ownerTree.innerCt.dom;\r
632             this.node.expanded = true;\r
633             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';\r
634             this.wrap = this.ctNode = targetNode.firstChild;\r
635         }\r
636     },\r
637     collapse : Ext.emptyFn,\r
638     expand : Ext.emptyFn\r
639 });</pre>
640 </body>
641 </html>