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