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