Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / docs / source / TreeNodeUI.html
1 <html>\r
2 <head>\r
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    \r
4   <title>The source code</title>\r
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />\r
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>\r
7 </head>\r
8 <body  onload="prettyPrint();">\r
9     <pre class="prettyprint lang-js"><div id="cls-Ext.tree.TreeNodeUI"></div>/**\r
10  * @class Ext.tree.TreeNodeUI\r
11  * This class provides the default UI implementation for Ext TreeNodes.\r
12  * The TreeNode UI implementation is separate from the\r
13  * tree implementation, and allows customizing of the appearance of\r
14  * tree nodes.<br>\r
15  * <p>\r
16  * If you are customizing the Tree's user interface, you\r
17  * may need to extend this class, but you should never need to instantiate this class.<br>\r
18  * <p>\r
19  * This class provides access to the user interface components of an Ext TreeNode, through\r
20  * {@link Ext.tree.TreeNode#getUI}\r
21  */\r
22 Ext.tree.TreeNodeUI = function(node){\r
23     this.node = node;\r
24     this.rendered = false;\r
25     this.animating = false;\r
26     this.wasLeaf = true;\r
27     this.ecc = 'x-tree-ec-icon x-tree-elbow';\r
28     this.emptyIcon = Ext.BLANK_IMAGE_URL;\r
29 };\r
30 \r
31 Ext.tree.TreeNodeUI.prototype = {\r
32     // private\r
33     removeChild : function(node){\r
34         if(this.rendered){\r
35             this.ctNode.removeChild(node.ui.getEl());\r
36         } \r
37     },\r
38 \r
39     // private\r
40     beforeLoad : function(){\r
41          this.addClass("x-tree-node-loading");\r
42     },\r
43 \r
44     // private\r
45     afterLoad : function(){\r
46          this.removeClass("x-tree-node-loading");\r
47     },\r
48 \r
49     // private\r
50     onTextChange : function(node, text, oldText){\r
51         if(this.rendered){\r
52             this.textNode.innerHTML = text;\r
53         }\r
54     },\r
55 \r
56     // private\r
57     onDisableChange : function(node, state){\r
58         this.disabled = state;\r
59         if (this.checkbox) {\r
60             this.checkbox.disabled = state;\r
61         }        \r
62         if(state){\r
63             this.addClass("x-tree-node-disabled");\r
64         }else{\r
65             this.removeClass("x-tree-node-disabled");\r
66         } \r
67     },\r
68 \r
69     // private\r
70     onSelectedChange : function(state){\r
71         if(state){\r
72             this.focus();\r
73             this.addClass("x-tree-selected");\r
74         }else{\r
75             //this.blur();\r
76             this.removeClass("x-tree-selected");\r
77         }\r
78     },\r
79 \r
80     // private\r
81     onMove : function(tree, node, oldParent, newParent, index, refNode){\r
82         this.childIndent = null;\r
83         if(this.rendered){\r
84             var targetNode = newParent.ui.getContainer();\r
85             if(!targetNode){//target not rendered\r
86                 this.holder = document.createElement("div");\r
87                 this.holder.appendChild(this.wrap);\r
88                 return;\r
89             }\r
90             var insertBefore = refNode ? refNode.ui.getEl() : null;\r
91             if(insertBefore){\r
92                 targetNode.insertBefore(this.wrap, insertBefore);\r
93             }else{\r
94                 targetNode.appendChild(this.wrap);\r
95             }\r
96             this.node.renderIndent(true, oldParent != newParent);\r
97         }\r
98     },\r
99 \r
100 <div id="method-Ext.tree.TreeNodeUI-addClass"></div>/**\r
101  * Adds one or more CSS classes to the node's UI element.\r
102  * Duplicate classes are automatically filtered out.\r
103  * @param {String/Array} className The CSS class to add, or an array of classes\r
104  */\r
105     addClass : function(cls){\r
106         if(this.elNode){\r
107             Ext.fly(this.elNode).addClass(cls);\r
108         }\r
109     },\r
110 \r
111 <div id="method-Ext.tree.TreeNodeUI-removeClass"></div>/**\r
112  * Removes one or more CSS classes from the node's UI element.\r
113  * @param {String/Array} className The CSS class to remove, or an array of classes\r
114  */\r
115     removeClass : function(cls){\r
116         if(this.elNode){\r
117             Ext.fly(this.elNode).removeClass(cls);  \r
118         }\r
119     },\r
120 \r
121     // private\r
122     remove : function(){\r
123         if(this.rendered){\r
124             this.holder = document.createElement("div");\r
125             this.holder.appendChild(this.wrap);\r
126         }  \r
127     },\r
128 \r
129     // private\r
130     fireEvent : function(){\r
131         return this.node.fireEvent.apply(this.node, arguments);  \r
132     },\r
133 \r
134     // private\r
135     initEvents : function(){\r
136         this.node.on("move", this.onMove, this);\r
137 \r
138         if(this.node.disabled){\r
139             this.onDisableChange(this.node, true);            \r
140         }\r
141         if(this.node.hidden){\r
142             this.hide();\r
143         }\r
144         var ot = this.node.getOwnerTree();\r
145         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;\r
146         if(dd && (!this.node.isRoot || ot.rootVisible)){\r
147             Ext.dd.Registry.register(this.elNode, {\r
148                 node: this.node,\r
149                 handles: this.getDDHandles(),\r
150                 isHandle: false\r
151             });\r
152         }\r
153     },\r
154 \r
155     // private\r
156     getDDHandles : function(){\r
157         return [this.iconNode, this.textNode, this.elNode];\r
158     },\r
159 \r
160 <div id="method-Ext.tree.TreeNodeUI-hide"></div>/**\r
161  * Hides this node.\r
162  */\r
163     hide : function(){\r
164         this.node.hidden = true;\r
165         if(this.wrap){\r
166             this.wrap.style.display = "none";\r
167         }\r
168     },\r
169 \r
170 <div id="method-Ext.tree.TreeNodeUI-show"></div>/**\r
171  * Shows this node.\r
172  */\r
173     show : function(){\r
174         this.node.hidden = false;\r
175         if(this.wrap){\r
176             this.wrap.style.display = "";\r
177         } \r
178     },\r
179 \r
180     // private\r
181     onContextMenu : function(e){\r
182         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {\r
183             e.preventDefault();\r
184             this.focus();\r
185             this.fireEvent("contextmenu", this.node, e);\r
186         }\r
187     },\r
188 \r
189     // private\r
190     onClick : function(e){\r
191         if(this.dropping){\r
192             e.stopEvent();\r
193             return;\r
194         }\r
195         if(this.fireEvent("beforeclick", this.node, e) !== false){\r
196             var a = e.getTarget('a');\r
197             if(!this.disabled && this.node.attributes.href && a){\r
198                 this.fireEvent("click", this.node, e);\r
199                 return;\r
200             }else if(a && e.ctrlKey){\r
201                 e.stopEvent();\r
202             }\r
203             e.preventDefault();\r
204             if(this.disabled){\r
205                 return;\r
206             }\r
207 \r
208             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){\r
209                 this.node.toggle();\r
210             }\r
211 \r
212             this.fireEvent("click", this.node, e);\r
213         }else{\r
214             e.stopEvent();\r
215         }\r
216     },\r
217 \r
218     // private\r
219     onDblClick : function(e){\r
220         e.preventDefault();\r
221         if(this.disabled){\r
222             return;\r
223         }\r
224         if(this.fireEvent("beforedblclick", this.node, e) !== false){\r
225             if(this.checkbox){\r
226                 this.toggleCheck();\r
227             }\r
228             if(!this.animating && this.node.isExpandable()){\r
229                 this.node.toggle();\r
230             }\r
231             this.fireEvent("dblclick", this.node, e);\r
232         }\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 <div id="method-Ext.tree.TreeNodeUI-getEl"></div>/**\r
375  * Returns the element which encapsulates this node.\r
376  * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.\r
377  */\r
378     getEl : function(){\r
379         return this.wrap;  \r
380     },\r
381 \r
382     // private\r
383     appendDDGhost : function(ghostNode){\r
384         ghostNode.appendChild(this.elNode.cloneNode(true));\r
385     },\r
386 \r
387     // private\r
388     getDDRepairXY : function(){\r
389         return Ext.lib.Dom.getXY(this.iconNode);\r
390     },\r
391 \r
392     // private\r
393     onRender : function(){\r
394         this.render();    \r
395     },\r
396 \r
397     // private\r
398     render : function(bulkRender){\r
399         var n = this.node, a = n.attributes;\r
400         var targetNode = n.parentNode ? \r
401               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;\r
402         \r
403         if(!this.rendered){\r
404             this.rendered = true;\r
405 \r
406             this.renderElements(n, a, targetNode, bulkRender);\r
407 \r
408             if(a.qtip){\r
409                if(this.textNode.setAttributeNS){\r
410                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);\r
411                    if(a.qtipTitle){\r
412                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);\r
413                    }\r
414                }else{\r
415                    this.textNode.setAttribute("ext:qtip", a.qtip);\r
416                    if(a.qtipTitle){\r
417                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);\r
418                    }\r
419                } \r
420             }else if(a.qtipCfg){\r
421                 a.qtipCfg.target = Ext.id(this.textNode);\r
422                 Ext.QuickTips.register(a.qtipCfg);\r
423             }\r
424             this.initEvents();\r
425             if(!this.node.expanded){\r
426                 this.updateExpandIcon(true);\r
427             }\r
428         }else{\r
429             if(bulkRender === true) {\r
430                 targetNode.appendChild(this.wrap);\r
431             }\r
432         }\r
433     },\r
434 \r
435     // private\r
436     renderElements : function(n, a, targetNode, bulkRender){\r
437         // add some indent caching, this helps performance when rendering a large tree\r
438         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
439 \r
440         var cb = Ext.isBoolean(a.checked),\r
441             nel,\r
442             href = a.href ? a.href : Ext.isGecko ? "" : "#",\r
443             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
444             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
445             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',\r
446             '<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
447             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',\r
448             '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',\r
449              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",\r
450             '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
451             "</li>"].join('');\r
452 \r
453         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){\r
454             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);\r
455         }else{\r
456             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);\r
457         }\r
458         \r
459         this.elNode = this.wrap.childNodes[0];\r
460         this.ctNode = this.wrap.childNodes[1];\r
461         var cs = this.elNode.childNodes;\r
462         this.indentNode = cs[0];\r
463         this.ecNode = cs[1];\r
464         this.iconNode = cs[2];\r
465         var index = 3;\r
466         if(cb){\r
467             this.checkbox = cs[3];\r
468             // fix for IE6\r
469             this.checkbox.defaultChecked = this.checkbox.checked;\r
470             index++;\r
471         }\r
472         this.anchor = cs[index];\r
473         this.textNode = cs[index].firstChild;\r
474     },\r
475 \r
476 <div id="method-Ext.tree.TreeNodeUI-getAnchor"></div>/**\r
477  * Returns the &lt;a> element that provides focus for the node's UI.\r
478  * @return {HtmlElement} The DOM anchor element.\r
479  */\r
480     getAnchor : function(){\r
481         return this.anchor;\r
482     },\r
483     \r
484 <div id="method-Ext.tree.TreeNodeUI-getTextEl"></div>/**\r
485  * Returns the text node.\r
486  * @return {HtmlNode} The DOM text node.\r
487  */\r
488     getTextEl : function(){\r
489         return this.textNode;\r
490     },\r
491     \r
492 <div id="method-Ext.tree.TreeNodeUI-getIconEl"></div>/**\r
493  * Returns the icon &lt;img> element.\r
494  * @return {HtmlElement} The DOM image element.\r
495  */\r
496     getIconEl : function(){\r
497         return this.iconNode;\r
498     },\r
499 \r
500 <div id="method-Ext.tree.TreeNodeUI-isChecked"></div>/**\r
501  * Returns the checked status of the node. If the node was rendered with no\r
502  * checkbox, it returns false.\r
503  * @return {Boolean} The checked flag.\r
504  */\r
505     isChecked : function(){\r
506         return this.checkbox ? this.checkbox.checked : false; \r
507     },\r
508 \r
509     // private\r
510     updateExpandIcon : function(){\r
511         if(this.rendered){\r
512             var n = this.node, \r
513                 c1, \r
514                 c2,\r
515                 cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",\r
516                 hasChild = n.hasChildNodes();\r
517             if(hasChild || n.attributes.expandable){\r
518                 if(n.expanded){\r
519                     cls += "-minus";\r
520                     c1 = "x-tree-node-collapsed";\r
521                     c2 = "x-tree-node-expanded";\r
522                 }else{\r
523                     cls += "-plus";\r
524                     c1 = "x-tree-node-expanded";\r
525                     c2 = "x-tree-node-collapsed";\r
526                 }\r
527                 if(this.wasLeaf){\r
528                     this.removeClass("x-tree-node-leaf");\r
529                     this.wasLeaf = false;\r
530                 }\r
531                 if(this.c1 != c1 || this.c2 != c2){\r
532                     Ext.fly(this.elNode).replaceClass(c1, c2);\r
533                     this.c1 = c1; this.c2 = c2;\r
534                 }\r
535             }else{\r
536                 if(!this.wasLeaf){\r
537                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");\r
538                     delete this.c1;\r
539                     delete this.c2;\r
540                     this.wasLeaf = true;\r
541                 }\r
542             }\r
543             var ecc = "x-tree-ec-icon "+cls;\r
544             if(this.ecc != ecc){\r
545                 this.ecNode.className = ecc;\r
546                 this.ecc = ecc;\r
547             }\r
548         }\r
549     },\r
550     \r
551     // private\r
552     onIdChange: function(id){\r
553         if(this.rendered){\r
554             this.elNode.setAttribute('ext:tree-node-id', id);\r
555         }\r
556     },\r
557 \r
558     // private\r
559     getChildIndent : function(){\r
560         if(!this.childIndent){\r
561             var buf = [],\r
562                 p = this.node;\r
563             while(p){\r
564                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){\r
565                     if(!p.isLast()) {\r
566                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');\r
567                     } else {\r
568                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');\r
569                     }\r
570                 }\r
571                 p = p.parentNode;\r
572             }\r
573             this.childIndent = buf.join("");\r
574         }\r
575         return this.childIndent;\r
576     },\r
577 \r
578     // private\r
579     renderIndent : function(){\r
580         if(this.rendered){\r
581             var indent = "",\r
582                 p = this.node.parentNode;\r
583             if(p){\r
584                 indent = p.ui.getChildIndent();\r
585             }\r
586             if(this.indentMarkup != indent){ // don't rerender if not required\r
587                 this.indentNode.innerHTML = indent;\r
588                 this.indentMarkup = indent;\r
589             }\r
590             this.updateExpandIcon();\r
591         }\r
592     },\r
593 \r
594     destroy : function(){\r
595         if(this.elNode){\r
596             Ext.dd.Registry.unregister(this.elNode.id);\r
597         }\r
598         \r
599         Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){\r
600             if(this[el]){\r
601                 Ext.fly(this[el]).remove();\r
602                 delete this[el];\r
603             }\r
604         }, this);\r
605         delete this.node;\r
606     }\r
607 };\r
608 \r
609 <div id="cls-Ext.tree.RootTreeNodeUI"></div>/**\r
610  * @class Ext.tree.RootTreeNodeUI\r
611  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.\r
612  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>\r
613  * <p>\r
614  * If you are customizing the Tree's user interface, you\r
615  * may need to extend this class, but you should never need to instantiate this class.<br>\r
616  */\r
617 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
618     // private\r
619     render : function(){\r
620         if(!this.rendered){\r
621             var targetNode = this.node.ownerTree.innerCt.dom;\r
622             this.node.expanded = true;\r
623             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';\r
624             this.wrap = this.ctNode = targetNode.firstChild;\r
625         }\r
626     },\r
627     collapse : Ext.emptyFn,\r
628     expand : Ext.emptyFn\r
629 });</pre>    \r
630 </body>\r
631 </html>