Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / src / widgets / tree / TreeNodeUI.js
index 1be1da6..6c1aa08 100644 (file)
 /*!
- * Ext JS Library 3.0.3
- * Copyright(c) 2006-2009 Ext JS, LLC
- * licensing@extjs.com
- * http://www.extjs.com/license
+ * Ext JS Library 3.3.1
+ * Copyright(c) 2006-2010 Sencha Inc.
+ * licensing@sencha.com
+ * http://www.sencha.com/license
  */
-/**\r
- * @class Ext.tree.TreeNodeUI\r
- * This class provides the default UI implementation for Ext TreeNodes.\r
- * The TreeNode UI implementation is separate from the\r
- * tree implementation, and allows customizing of the appearance of\r
- * tree nodes.<br>\r
- * <p>\r
- * If you are customizing the Tree's user interface, you\r
- * may need to extend this class, but you should never need to instantiate this class.<br>\r
- * <p>\r
- * This class provides access to the user interface components of an Ext TreeNode, through\r
- * {@link Ext.tree.TreeNode#getUI}\r
- */\r
-Ext.tree.TreeNodeUI = function(node){\r
-    this.node = node;\r
-    this.rendered = false;\r
-    this.animating = false;\r
-    this.wasLeaf = true;\r
-    this.ecc = 'x-tree-ec-icon x-tree-elbow';\r
-    this.emptyIcon = Ext.BLANK_IMAGE_URL;\r
-};\r
-\r
-Ext.tree.TreeNodeUI.prototype = {\r
-    // private\r
-    removeChild : function(node){\r
-        if(this.rendered){\r
-            this.ctNode.removeChild(node.ui.getEl());\r
-        } \r
-    },\r
-\r
-    // private\r
-    beforeLoad : function(){\r
-         this.addClass("x-tree-node-loading");\r
-    },\r
-\r
-    // private\r
-    afterLoad : function(){\r
-         this.removeClass("x-tree-node-loading");\r
-    },\r
-\r
-    // private\r
-    onTextChange : function(node, text, oldText){\r
-        if(this.rendered){\r
-            this.textNode.innerHTML = text;\r
-        }\r
-    },\r
-\r
-    // private\r
-    onDisableChange : function(node, state){\r
-        this.disabled = state;\r
-               if (this.checkbox) {\r
-                       this.checkbox.disabled = state;\r
-               }        \r
-        if(state){\r
-            this.addClass("x-tree-node-disabled");\r
-        }else{\r
-            this.removeClass("x-tree-node-disabled");\r
-        } \r
-    },\r
-\r
-    // private\r
-    onSelectedChange : function(state){\r
-        if(state){\r
-            this.focus();\r
-            this.addClass("x-tree-selected");\r
-        }else{\r
-            //this.blur();\r
-            this.removeClass("x-tree-selected");\r
-        }\r
-    },\r
-\r
-    // private\r
-    onMove : function(tree, node, oldParent, newParent, index, refNode){\r
-        this.childIndent = null;\r
-        if(this.rendered){\r
-            var targetNode = newParent.ui.getContainer();\r
-            if(!targetNode){//target not rendered\r
-                this.holder = document.createElement("div");\r
-                this.holder.appendChild(this.wrap);\r
-                return;\r
-            }\r
-            var insertBefore = refNode ? refNode.ui.getEl() : null;\r
-            if(insertBefore){\r
-                targetNode.insertBefore(this.wrap, insertBefore);\r
-            }else{\r
-                targetNode.appendChild(this.wrap);\r
-            }\r
-            this.node.renderIndent(true, oldParent != newParent);\r
-        }\r
-    },\r
-\r
-/**\r
- * Adds one or more CSS classes to the node's UI element.\r
- * Duplicate classes are automatically filtered out.\r
- * @param {String/Array} className The CSS class to add, or an array of classes\r
- */\r
-    addClass : function(cls){\r
-        if(this.elNode){\r
-            Ext.fly(this.elNode).addClass(cls);\r
-        }\r
-    },\r
-\r
-/**\r
- * Removes one or more CSS classes from the node's UI element.\r
- * @param {String/Array} className The CSS class to remove, or an array of classes\r
- */\r
-    removeClass : function(cls){\r
-        if(this.elNode){\r
-            Ext.fly(this.elNode).removeClass(cls);  \r
-        }\r
-    },\r
-\r
-    // private\r
-    remove : function(){\r
-        if(this.rendered){\r
-            this.holder = document.createElement("div");\r
-            this.holder.appendChild(this.wrap);\r
-        }  \r
-    },\r
-\r
-    // private\r
-    fireEvent : function(){\r
-        return this.node.fireEvent.apply(this.node, arguments);  \r
-    },\r
-\r
-    // private\r
-    initEvents : function(){\r
-        this.node.on("move", this.onMove, this);\r
-\r
-        if(this.node.disabled){\r
-            this.onDisableChange(this.node, true);            \r
-        }\r
-        if(this.node.hidden){\r
-            this.hide();\r
-        }\r
-        var ot = this.node.getOwnerTree();\r
-        var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;\r
-        if(dd && (!this.node.isRoot || ot.rootVisible)){\r
-            Ext.dd.Registry.register(this.elNode, {\r
-                node: this.node,\r
-                handles: this.getDDHandles(),\r
-                isHandle: false\r
-            });\r
-        }\r
-    },\r
-\r
-    // private\r
-    getDDHandles : function(){\r
-        return [this.iconNode, this.textNode, this.elNode];\r
-    },\r
-\r
-/**\r
- * Hides this node.\r
- */\r
-    hide : function(){\r
-        this.node.hidden = true;\r
-        if(this.wrap){\r
-            this.wrap.style.display = "none";\r
-        }\r
-    },\r
-\r
-/**\r
- * Shows this node.\r
- */\r
-    show : function(){\r
-        this.node.hidden = false;\r
-        if(this.wrap){\r
-            this.wrap.style.display = "";\r
-        } \r
-    },\r
-\r
-    // private\r
-    onContextMenu : function(e){\r
-        if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {\r
-            e.preventDefault();\r
-            this.focus();\r
-            this.fireEvent("contextmenu", this.node, e);\r
-        }\r
-    },\r
-\r
-    // private\r
-    onClick : function(e){\r
-        if(this.dropping){\r
-            e.stopEvent();\r
-            return;\r
-        }\r
-        if(this.fireEvent("beforeclick", this.node, e) !== false){\r
-            var a = e.getTarget('a');\r
-            if(!this.disabled && this.node.attributes.href && a){\r
-                this.fireEvent("click", this.node, e);\r
-                return;\r
-            }else if(a && e.ctrlKey){\r
-                e.stopEvent();\r
-            }\r
-            e.preventDefault();\r
-            if(this.disabled){\r
-                return;\r
-            }\r
-\r
-            if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){\r
-                this.node.toggle();\r
-            }\r
-\r
-            this.fireEvent("click", this.node, e);\r
-        }else{\r
-            e.stopEvent();\r
-        }\r
-    },\r
-\r
-    // private\r
-    onDblClick : function(e){\r
-        e.preventDefault();\r
-        if(this.disabled){\r
-            return;\r
-        }\r
-        if(this.fireEvent("beforedblclick", this.node, e) !== false){\r
-            if(this.checkbox){\r
-                this.toggleCheck();\r
-            }\r
-            if(!this.animating && this.node.isExpandable()){\r
-                this.node.toggle();\r
-            }\r
-            this.fireEvent("dblclick", this.node, e);\r
-        }\r
-    },\r
-\r
-    onOver : function(e){\r
-        this.addClass('x-tree-node-over');\r
-    },\r
-\r
-    onOut : function(e){\r
-        this.removeClass('x-tree-node-over');\r
-    },\r
-\r
-    // private\r
-    onCheckChange : function(){\r
-        var checked = this.checkbox.checked;\r
-               // fix for IE6\r
-               this.checkbox.defaultChecked = checked;         \r
-        this.node.attributes.checked = checked;\r
-        this.fireEvent('checkchange', this.node, checked);\r
-    },\r
-\r
-    // private\r
-    ecClick : function(e){\r
-        if(!this.animating && this.node.isExpandable()){\r
-            this.node.toggle();\r
-        }\r
-    },\r
-\r
-    // private\r
-    startDrop : function(){\r
-        this.dropping = true;\r
-    },\r
-    \r
-    // delayed drop so the click event doesn't get fired on a drop\r
-    endDrop : function(){ \r
-       setTimeout(function(){\r
-           this.dropping = false;\r
-       }.createDelegate(this), 50); \r
-    },\r
-\r
-    // private\r
-    expand : function(){\r
-        this.updateExpandIcon();\r
-        this.ctNode.style.display = "";\r
-    },\r
-\r
-    // private\r
-    focus : function(){\r
-        if(!this.node.preventHScroll){\r
-            try{this.anchor.focus();\r
-            }catch(e){}\r
-        }else{\r
-            try{\r
-                var noscroll = this.node.getOwnerTree().getTreeEl().dom;\r
-                var l = noscroll.scrollLeft;\r
-                this.anchor.focus();\r
-                noscroll.scrollLeft = l;\r
-            }catch(e){}\r
-        }\r
-    },\r
-\r
-/**\r
- * Sets the checked status of the tree node to the passed value, or, if no value was passed,\r
- * toggles the checked status. If the node was rendered with no checkbox, this has no effect.\r
- * @param {Boolean} (optional) The new checked status.\r
- */\r
-    toggleCheck : function(value){\r
-        var cb = this.checkbox;\r
-        if(cb){\r
-            cb.checked = (value === undefined ? !cb.checked : value);\r
-            this.onCheckChange();\r
-        }\r
-    },\r
-\r
-    // private\r
-    blur : function(){\r
-        try{\r
-            this.anchor.blur();\r
-        }catch(e){} \r
-    },\r
-\r
-    // private\r
-    animExpand : function(callback){\r
-        var ct = Ext.get(this.ctNode);\r
-        ct.stopFx();\r
-        if(!this.node.isExpandable()){\r
-            this.updateExpandIcon();\r
-            this.ctNode.style.display = "";\r
-            Ext.callback(callback);\r
-            return;\r
-        }\r
-        this.animating = true;\r
-        this.updateExpandIcon();\r
-        \r
-        ct.slideIn('t', {\r
-           callback : function(){\r
-               this.animating = false;\r
-               Ext.callback(callback);\r
-            },\r
-            scope: this,\r
-            duration: this.node.ownerTree.duration || .25\r
-        });\r
-    },\r
-\r
-    // private\r
-    highlight : function(){\r
-        var tree = this.node.getOwnerTree();\r
-        Ext.fly(this.wrap).highlight(\r
-            tree.hlColor || "C3DAF9",\r
-            {endColor: tree.hlBaseColor}\r
-        );\r
-    },\r
-\r
-    // private\r
-    collapse : function(){\r
-        this.updateExpandIcon();\r
-        this.ctNode.style.display = "none";\r
-    },\r
-\r
-    // private\r
-    animCollapse : function(callback){\r
-        var ct = Ext.get(this.ctNode);\r
-        ct.enableDisplayMode('block');\r
-        ct.stopFx();\r
-\r
-        this.animating = true;\r
-        this.updateExpandIcon();\r
-\r
-        ct.slideOut('t', {\r
-            callback : function(){\r
-               this.animating = false;\r
-               Ext.callback(callback);\r
-            },\r
-            scope: this,\r
-            duration: this.node.ownerTree.duration || .25\r
-        });\r
-    },\r
-\r
-    // private\r
-    getContainer : function(){\r
-        return this.ctNode;  \r
-    },\r
-\r
-    // private\r
-    getEl : function(){\r
-        return this.wrap;  \r
-    },\r
-\r
-    // private\r
-    appendDDGhost : function(ghostNode){\r
-        ghostNode.appendChild(this.elNode.cloneNode(true));\r
-    },\r
-\r
-    // private\r
-    getDDRepairXY : function(){\r
-        return Ext.lib.Dom.getXY(this.iconNode);\r
-    },\r
-\r
-    // private\r
-    onRender : function(){\r
-        this.render();    \r
-    },\r
-\r
-    // private\r
-    render : function(bulkRender){\r
-        var n = this.node, a = n.attributes;\r
-        var targetNode = n.parentNode ? \r
-              n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;\r
-        \r
-        if(!this.rendered){\r
-            this.rendered = true;\r
-\r
-            this.renderElements(n, a, targetNode, bulkRender);\r
-\r
-            if(a.qtip){\r
-               if(this.textNode.setAttributeNS){\r
-                   this.textNode.setAttributeNS("ext", "qtip", a.qtip);\r
-                   if(a.qtipTitle){\r
-                       this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);\r
-                   }\r
-               }else{\r
-                   this.textNode.setAttribute("ext:qtip", a.qtip);\r
-                   if(a.qtipTitle){\r
-                       this.textNode.setAttribute("ext:qtitle", a.qtipTitle);\r
-                   }\r
-               } \r
-            }else if(a.qtipCfg){\r
-                a.qtipCfg.target = Ext.id(this.textNode);\r
-                Ext.QuickTips.register(a.qtipCfg);\r
-            }\r
-            this.initEvents();\r
-            if(!this.node.expanded){\r
-                this.updateExpandIcon(true);\r
-            }\r
-        }else{\r
-            if(bulkRender === true) {\r
-                targetNode.appendChild(this.wrap);\r
-            }\r
-        }\r
-    },\r
-\r
-    // private\r
-    renderElements : function(n, a, targetNode, bulkRender){\r
-        // add some indent caching, this helps performance when rendering a large tree\r
-        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
-\r
-        var cb = typeof a.checked == 'boolean';\r
-\r
-        var href = a.href ? a.href : Ext.isGecko ? "" : "#";\r
-        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
-            '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
-            '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',\r
-            '<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
-            cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',\r
-            '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',\r
-             a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",\r
-            '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
-            "</li>"].join('');\r
-\r
-        var nel;\r
-        if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){\r
-            this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);\r
-        }else{\r
-            this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);\r
-        }\r
-        \r
-        this.elNode = this.wrap.childNodes[0];\r
-        this.ctNode = this.wrap.childNodes[1];\r
-        var cs = this.elNode.childNodes;\r
-        this.indentNode = cs[0];\r
-        this.ecNode = cs[1];\r
-        this.iconNode = cs[2];\r
-        var index = 3;\r
-        if(cb){\r
-            this.checkbox = cs[3];\r
-                       // fix for IE6\r
-                       this.checkbox.defaultChecked = this.checkbox.checked;                                           \r
-            index++;\r
-        }\r
-        this.anchor = cs[index];\r
-        this.textNode = cs[index].firstChild;\r
-    },\r
-\r
-/**\r
- * Returns the &lt;a> element that provides focus for the node's UI.\r
- * @return {HtmlElement} The DOM anchor element.\r
- */\r
-    getAnchor : function(){\r
-        return this.anchor;\r
-    },\r
-    \r
-/**\r
- * Returns the text node.\r
- * @return {HtmlNode} The DOM text node.\r
- */\r
-    getTextEl : function(){\r
-        return this.textNode;\r
-    },\r
-    \r
-/**\r
- * Returns the icon &lt;img> element.\r
- * @return {HtmlElement} The DOM image element.\r
- */\r
-    getIconEl : function(){\r
-        return this.iconNode;\r
-    },\r
-\r
-/**\r
- * Returns the checked status of the node. If the node was rendered with no\r
- * checkbox, it returns false.\r
- * @return {Boolean} The checked flag.\r
- */\r
-    isChecked : function(){\r
-        return this.checkbox ? this.checkbox.checked : false; \r
-    },\r
-\r
-    // private\r
-    updateExpandIcon : function(){\r
-        if(this.rendered){\r
-            var n = this.node, c1, c2;\r
-            var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";\r
-            var hasChild = n.hasChildNodes();\r
-            if(hasChild || n.attributes.expandable){\r
-                if(n.expanded){\r
-                    cls += "-minus";\r
-                    c1 = "x-tree-node-collapsed";\r
-                    c2 = "x-tree-node-expanded";\r
-                }else{\r
-                    cls += "-plus";\r
-                    c1 = "x-tree-node-expanded";\r
-                    c2 = "x-tree-node-collapsed";\r
-                }\r
-                if(this.wasLeaf){\r
-                    this.removeClass("x-tree-node-leaf");\r
-                    this.wasLeaf = false;\r
-                }\r
-                if(this.c1 != c1 || this.c2 != c2){\r
-                    Ext.fly(this.elNode).replaceClass(c1, c2);\r
-                    this.c1 = c1; this.c2 = c2;\r
-                }\r
-            }else{\r
-                if(!this.wasLeaf){\r
-                    Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");\r
-                    delete this.c1;\r
-                    delete this.c2;\r
-                    this.wasLeaf = true;\r
-                }\r
-            }\r
-            var ecc = "x-tree-ec-icon "+cls;\r
-            if(this.ecc != ecc){\r
-                this.ecNode.className = ecc;\r
-                this.ecc = ecc;\r
-            }\r
-        }\r
-    },\r
-    \r
-    // private\r
-    onIdChange: function(id){\r
-        if(this.rendered){\r
-            this.elNode.setAttribute('ext:tree-node-id', id);\r
-        }\r
-    },\r
-\r
-    // private\r
-    getChildIndent : function(){\r
-        if(!this.childIndent){\r
-            var buf = [];\r
-            var p = this.node;\r
-            while(p){\r
-                if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){\r
-                    if(!p.isLast()) {\r
-                        buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');\r
-                    } else {\r
-                        buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');\r
-                    }\r
-                }\r
-                p = p.parentNode;\r
-            }\r
-            this.childIndent = buf.join("");\r
-        }\r
-        return this.childIndent;\r
-    },\r
-\r
-    // private\r
-    renderIndent : function(){\r
-        if(this.rendered){\r
-            var indent = "";\r
-            var p = this.node.parentNode;\r
-            if(p){\r
-                indent = p.ui.getChildIndent();\r
-            }\r
-            if(this.indentMarkup != indent){ // don't rerender if not required\r
-                this.indentNode.innerHTML = indent;\r
-                this.indentMarkup = indent;\r
-            }\r
-            this.updateExpandIcon();\r
-        }\r
-    },\r
-\r
-    destroy : function(){\r
-        if(this.elNode){\r
-            Ext.dd.Registry.unregister(this.elNode.id);\r
-        }\r
-        delete this.elNode;\r
-        delete this.ctNode;\r
-        delete this.indentNode;\r
-        delete this.ecNode;\r
-        delete this.iconNode;\r
-        delete this.checkbox;\r
-        delete this.anchor;\r
-        delete this.textNode;\r
-        \r
-        if (this.holder){\r
-             delete this.wrap;\r
-             Ext.removeNode(this.holder);\r
-             delete this.holder;\r
-        }else{\r
-            Ext.removeNode(this.wrap);\r
-            delete this.wrap;\r
-        }\r
-    }\r
-};\r
-\r
-/**\r
- * @class Ext.tree.RootTreeNodeUI\r
- * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.\r
- * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>\r
- * <p>\r
- * If you are customizing the Tree's user interface, you\r
- * may need to extend this class, but you should never need to instantiate this class.<br>\r
- */\r
-Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
-    // private\r
-    render : function(){\r
-        if(!this.rendered){\r
-            var targetNode = this.node.ownerTree.innerCt.dom;\r
-            this.node.expanded = true;\r
-            targetNode.innerHTML = '<div class="x-tree-root-node"></div>';\r
-            this.wrap = this.ctNode = targetNode.firstChild;\r
-        }\r
-    },\r
-    collapse : Ext.emptyFn,\r
-    expand : Ext.emptyFn\r
+/**
+ * @class Ext.tree.TreeNodeUI
+ * This class provides the default UI implementation for Ext TreeNodes.
+ * The TreeNode UI implementation is separate from the
+ * tree implementation, and allows customizing of the appearance of
+ * tree nodes.<br>
+ * <p>
+ * If you are customizing the Tree's user interface, you
+ * may need to extend this class, but you should never need to instantiate this class.<br>
+ * <p>
+ * This class provides access to the user interface components of an Ext TreeNode, through
+ * {@link Ext.tree.TreeNode#getUI}
+ */
+Ext.tree.TreeNodeUI = Ext.extend(Object, {
+    
+    constructor : function(node){
+        Ext.apply(this, {
+            node: node,
+            rendered: false,
+            animating: false,
+            wasLeaf: true,
+            ecc: 'x-tree-ec-icon x-tree-elbow',
+            emptyIcon: Ext.BLANK_IMAGE_URL    
+        });
+    },
+    
+    // private
+    removeChild : function(node){
+        if(this.rendered){
+            this.ctNode.removeChild(node.ui.getEl());
+        }
+    },
+
+    // private
+    beforeLoad : function(){
+         this.addClass("x-tree-node-loading");
+    },
+
+    // private
+    afterLoad : function(){
+         this.removeClass("x-tree-node-loading");
+    },
+
+    // private
+    onTextChange : function(node, text, oldText){
+        if(this.rendered){
+            this.textNode.innerHTML = text;
+        }
+    },
+    
+    // private
+    onIconClsChange : function(node, cls, oldCls){
+        if(this.rendered){
+            Ext.fly(this.iconNode).replaceClass(oldCls, cls);
+        }
+    },
+    
+    // private
+    onIconChange : function(node, icon){
+        if(this.rendered){
+            //'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
+            var empty = Ext.isEmpty(icon);
+            this.iconNode.src = empty ? this.emptyIcon : icon;
+            Ext.fly(this.iconNode)[empty ? 'removeClass' : 'addClass']('x-tree-node-inline-icon');
+        }
+    },
+    
+    // private
+    onTipChange : function(node, tip, title){
+        if(this.rendered){
+            var hasTitle = Ext.isDefined(title);
+            if(this.textNode.setAttributeNS){
+                this.textNode.setAttributeNS("ext", "qtip", tip);
+                if(hasTitle){
+                    this.textNode.setAttributeNS("ext", "qtitle", title);
+                }
+            }else{
+                this.textNode.setAttribute("ext:qtip", tip);
+                if(hasTitle){
+                    this.textNode.setAttribute("ext:qtitle", title);
+                }
+            }
+        }
+    },
+    
+    // private
+    onHrefChange : function(node, href, target){
+        if(this.rendered){
+            this.anchor.href = this.getHref(href);
+            if(Ext.isDefined(target)){
+                this.anchor.target = target;
+            }
+        }
+    },
+    
+    // private
+    onClsChange : function(node, cls, oldCls){
+        if(this.rendered){
+            Ext.fly(this.elNode).replaceClass(oldCls, cls);
+        }    
+    },
+
+    // private
+    onDisableChange : function(node, state){
+        this.disabled = state;
+        if (this.checkbox) {
+            this.checkbox.disabled = state;
+        }
+        this[state ? 'addClass' : 'removeClass']('x-tree-node-disabled');
+    },
+
+    // private
+    onSelectedChange : function(state){
+        if(state){
+            this.focus();
+            this.addClass("x-tree-selected");
+        }else{
+            //this.blur();
+            this.removeClass("x-tree-selected");
+        }
+    },
+
+    // private
+    onMove : function(tree, node, oldParent, newParent, index, refNode){
+        this.childIndent = null;
+        if(this.rendered){
+            var targetNode = newParent.ui.getContainer();
+            if(!targetNode){//target not rendered
+                this.holder = document.createElement("div");
+                this.holder.appendChild(this.wrap);
+                return;
+            }
+            var insertBefore = refNode ? refNode.ui.getEl() : null;
+            if(insertBefore){
+                targetNode.insertBefore(this.wrap, insertBefore);
+            }else{
+                targetNode.appendChild(this.wrap);
+            }
+            this.node.renderIndent(true, oldParent != newParent);
+        }
+    },
+
+/**
+ * Adds one or more CSS classes to the node's UI element.
+ * Duplicate classes are automatically filtered out.
+ * @param {String/Array} className The CSS class to add, or an array of classes
+ */
+    addClass : function(cls){
+        if(this.elNode){
+            Ext.fly(this.elNode).addClass(cls);
+        }
+    },
+
+/**
+ * Removes one or more CSS classes from the node's UI element.
+ * @param {String/Array} className The CSS class to remove, or an array of classes
+ */
+    removeClass : function(cls){
+        if(this.elNode){
+            Ext.fly(this.elNode).removeClass(cls);
+        }
+    },
+
+    // private
+    remove : function(){
+        if(this.rendered){
+            this.holder = document.createElement("div");
+            this.holder.appendChild(this.wrap);
+        }
+    },
+
+    // private
+    fireEvent : function(){
+        return this.node.fireEvent.apply(this.node, arguments);
+    },
+
+    // private
+    initEvents : function(){
+        this.node.on("move", this.onMove, this);
+
+        if(this.node.disabled){
+            this.onDisableChange(this.node, true);
+        }
+        if(this.node.hidden){
+            this.hide();
+        }
+        var ot = this.node.getOwnerTree();
+        var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
+        if(dd && (!this.node.isRoot || ot.rootVisible)){
+            Ext.dd.Registry.register(this.elNode, {
+                node: this.node,
+                handles: this.getDDHandles(),
+                isHandle: false
+            });
+        }
+    },
+
+    // private
+    getDDHandles : function(){
+        return [this.iconNode, this.textNode, this.elNode];
+    },
+
+/**
+ * Hides this node.
+ */
+    hide : function(){
+        this.node.hidden = true;
+        if(this.wrap){
+            this.wrap.style.display = "none";
+        }
+    },
+
+/**
+ * Shows this node.
+ */
+    show : function(){
+        this.node.hidden = false;
+        if(this.wrap){
+            this.wrap.style.display = "";
+        }
+    },
+
+    // private
+    onContextMenu : function(e){
+        if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
+            e.preventDefault();
+            this.focus();
+            this.fireEvent("contextmenu", this.node, e);
+        }
+    },
+
+    // private
+    onClick : function(e){
+        if(this.dropping){
+            e.stopEvent();
+            return;
+        }
+        if(this.fireEvent("beforeclick", this.node, e) !== false){
+            var a = e.getTarget('a');
+            if(!this.disabled && this.node.attributes.href && a){
+                this.fireEvent("click", this.node, e);
+                return;
+            }else if(a && e.ctrlKey){
+                e.stopEvent();
+            }
+            e.preventDefault();
+            if(this.disabled){
+                return;
+            }
+
+            if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
+                this.node.toggle();
+            }
+
+            this.fireEvent("click", this.node, e);
+        }else{
+            e.stopEvent();
+        }
+    },
+
+    // private
+    onDblClick : function(e){
+        e.preventDefault();
+        if(this.disabled){
+            return;
+        }
+        if(this.fireEvent("beforedblclick", this.node, e) !== false){
+            if(this.checkbox){
+                this.toggleCheck();
+            }
+            if(!this.animating && this.node.isExpandable()){
+                this.node.toggle();
+            }
+            this.fireEvent("dblclick", this.node, e);
+        }
+    },
+
+    onOver : function(e){
+        this.addClass('x-tree-node-over');
+    },
+
+    onOut : function(e){
+        this.removeClass('x-tree-node-over');
+    },
+
+    // private
+    onCheckChange : function(){
+        var checked = this.checkbox.checked;
+        // fix for IE6
+        this.checkbox.defaultChecked = checked;
+        this.node.attributes.checked = checked;
+        this.fireEvent('checkchange', this.node, checked);
+    },
+
+    // private
+    ecClick : function(e){
+        if(!this.animating && this.node.isExpandable()){
+            this.node.toggle();
+        }
+    },
+
+    // private
+    startDrop : function(){
+        this.dropping = true;
+    },
+
+    // delayed drop so the click event doesn't get fired on a drop
+    endDrop : function(){
+       setTimeout(function(){
+           this.dropping = false;
+       }.createDelegate(this), 50);
+    },
+
+    // private
+    expand : function(){
+        this.updateExpandIcon();
+        this.ctNode.style.display = "";
+    },
+
+    // private
+    focus : function(){
+        if(!this.node.preventHScroll){
+            try{this.anchor.focus();
+            }catch(e){}
+        }else{
+            try{
+                var noscroll = this.node.getOwnerTree().getTreeEl().dom;
+                var l = noscroll.scrollLeft;
+                this.anchor.focus();
+                noscroll.scrollLeft = l;
+            }catch(e){}
+        }
+    },
+
+/**
+ * Sets the checked status of the tree node to the passed value, or, if no value was passed,
+ * toggles the checked status. If the node was rendered with no checkbox, this has no effect.
+ * @param {Boolean} value (optional) The new checked status.
+ */
+    toggleCheck : function(value){
+        var cb = this.checkbox;
+        if(cb){
+            cb.checked = (value === undefined ? !cb.checked : value);
+            this.onCheckChange();
+        }
+    },
+
+    // private
+    blur : function(){
+        try{
+            this.anchor.blur();
+        }catch(e){}
+    },
+
+    // private
+    animExpand : function(callback){
+        var ct = Ext.get(this.ctNode);
+        ct.stopFx();
+        if(!this.node.isExpandable()){
+            this.updateExpandIcon();
+            this.ctNode.style.display = "";
+            Ext.callback(callback);
+            return;
+        }
+        this.animating = true;
+        this.updateExpandIcon();
+
+        ct.slideIn('t', {
+           callback : function(){
+               this.animating = false;
+               Ext.callback(callback);
+            },
+            scope: this,
+            duration: this.node.ownerTree.duration || .25
+        });
+    },
+
+    // private
+    highlight : function(){
+        var tree = this.node.getOwnerTree();
+        Ext.fly(this.wrap).highlight(
+            tree.hlColor || "C3DAF9",
+            {endColor: tree.hlBaseColor}
+        );
+    },
+
+    // private
+    collapse : function(){
+        this.updateExpandIcon();
+        this.ctNode.style.display = "none";
+    },
+
+    // private
+    animCollapse : function(callback){
+        var ct = Ext.get(this.ctNode);
+        ct.enableDisplayMode('block');
+        ct.stopFx();
+
+        this.animating = true;
+        this.updateExpandIcon();
+
+        ct.slideOut('t', {
+            callback : function(){
+               this.animating = false;
+               Ext.callback(callback);
+            },
+            scope: this,
+            duration: this.node.ownerTree.duration || .25
+        });
+    },
+
+    // private
+    getContainer : function(){
+        return this.ctNode;
+    },
+
+/**
+ * Returns the element which encapsulates this node.
+ * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.
+ */
+    getEl : function(){
+        return this.wrap;
+    },
+
+    // private
+    appendDDGhost : function(ghostNode){
+        ghostNode.appendChild(this.elNode.cloneNode(true));
+    },
+
+    // private
+    getDDRepairXY : function(){
+        return Ext.lib.Dom.getXY(this.iconNode);
+    },
+
+    // private
+    onRender : function(){
+        this.render();
+    },
+
+    // private
+    render : function(bulkRender){
+        var n = this.node, a = n.attributes;
+        var targetNode = n.parentNode ?
+              n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
+
+        if(!this.rendered){
+            this.rendered = true;
+
+            this.renderElements(n, a, targetNode, bulkRender);
+
+            if(a.qtip){
+                this.onTipChange(n, a.qtip, a.qtipTitle);
+            }else if(a.qtipCfg){
+                a.qtipCfg.target = Ext.id(this.textNode);
+                Ext.QuickTips.register(a.qtipCfg);
+            }
+            this.initEvents();
+            if(!this.node.expanded){
+                this.updateExpandIcon(true);
+            }
+        }else{
+            if(bulkRender === true) {
+                targetNode.appendChild(this.wrap);
+            }
+        }
+    },
+
+    // private
+    renderElements : function(n, a, targetNode, bulkRender){
+        // add some indent caching, this helps performance when rendering a large tree
+        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
+
+        var cb = Ext.isBoolean(a.checked),
+            nel,
+            href = this.getHref(a.href),
+            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">',
+            '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
+            '<img alt="" src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
+            '<img alt="" src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
+            cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
+            '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
+             a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
+            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
+            "</li>"].join('');
+
+        if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
+            this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
+        }else{
+            this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
+        }
+
+        this.elNode = this.wrap.childNodes[0];
+        this.ctNode = this.wrap.childNodes[1];
+        var cs = this.elNode.childNodes;
+        this.indentNode = cs[0];
+        this.ecNode = cs[1];
+        this.iconNode = cs[2];
+        var index = 3;
+        if(cb){
+            this.checkbox = cs[3];
+            // fix for IE6
+            this.checkbox.defaultChecked = this.checkbox.checked;
+            index++;
+        }
+        this.anchor = cs[index];
+        this.textNode = cs[index].firstChild;
+    },
+    
+    /**
+     * @private Gets a normalized href for the node.
+     * @param {String} href
+     */
+    getHref : function(href){
+        return Ext.isEmpty(href) ? (Ext.isGecko ? '' : '#') : href;
+    },
+
+/**
+ * Returns the &lt;a> element that provides focus for the node's UI.
+ * @return {HtmlElement} The DOM anchor element.
+ */
+    getAnchor : function(){
+        return this.anchor;
+    },
+
+/**
+ * Returns the text node.
+ * @return {HtmlNode} The DOM text node.
+ */
+    getTextEl : function(){
+        return this.textNode;
+    },
+
+/**
+ * Returns the icon &lt;img> element.
+ * @return {HtmlElement} The DOM image element.
+ */
+    getIconEl : function(){
+        return this.iconNode;
+    },
+
+/**
+ * Returns the checked status of the node. If the node was rendered with no
+ * checkbox, it returns false.
+ * @return {Boolean} The checked flag.
+ */
+    isChecked : function(){
+        return this.checkbox ? this.checkbox.checked : false;
+    },
+
+    // private
+    updateExpandIcon : function(){
+        if(this.rendered){
+            var n = this.node,
+                c1,
+                c2,
+                cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",
+                hasChild = n.hasChildNodes();
+            if(hasChild || n.attributes.expandable){
+                if(n.expanded){
+                    cls += "-minus";
+                    c1 = "x-tree-node-collapsed";
+                    c2 = "x-tree-node-expanded";
+                }else{
+                    cls += "-plus";
+                    c1 = "x-tree-node-expanded";
+                    c2 = "x-tree-node-collapsed";
+                }
+                if(this.wasLeaf){
+                    this.removeClass("x-tree-node-leaf");
+                    this.wasLeaf = false;
+                }
+                if(this.c1 != c1 || this.c2 != c2){
+                    Ext.fly(this.elNode).replaceClass(c1, c2);
+                    this.c1 = c1; this.c2 = c2;
+                }
+            }else{
+                if(!this.wasLeaf){
+                    Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-collapsed");
+                    delete this.c1;
+                    delete this.c2;
+                    this.wasLeaf = true;
+                }
+            }
+            var ecc = "x-tree-ec-icon "+cls;
+            if(this.ecc != ecc){
+                this.ecNode.className = ecc;
+                this.ecc = ecc;
+            }
+        }
+    },
+
+    // private
+    onIdChange: function(id){
+        if(this.rendered){
+            this.elNode.setAttribute('ext:tree-node-id', id);
+        }
+    },
+
+    // private
+    getChildIndent : function(){
+        if(!this.childIndent){
+            var buf = [],
+                p = this.node;
+            while(p){
+                if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
+                    if(!p.isLast()) {
+                        buf.unshift('<img alt="" src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
+                    } else {
+                        buf.unshift('<img alt="" src="'+this.emptyIcon+'" class="x-tree-icon" />');
+                    }
+                }
+                p = p.parentNode;
+            }
+            this.childIndent = buf.join("");
+        }
+        return this.childIndent;
+    },
+
+    // private
+    renderIndent : function(){
+        if(this.rendered){
+            var indent = "",
+                p = this.node.parentNode;
+            if(p){
+                indent = p.ui.getChildIndent();
+            }
+            if(this.indentMarkup != indent){ // don't rerender if not required
+                this.indentNode.innerHTML = indent;
+                this.indentMarkup = indent;
+            }
+            this.updateExpandIcon();
+        }
+    },
+
+    destroy : function(){
+        if(this.elNode){
+            Ext.dd.Registry.unregister(this.elNode.id);
+        }
+
+        Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){
+            if(this[el]){
+                Ext.fly(this[el]).remove();
+                delete this[el];
+            }
+        }, this);
+        delete this.node;
+    }
+});
+
+/**
+ * @class Ext.tree.RootTreeNodeUI
+ * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
+ * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
+ * <p>
+ * If you are customizing the Tree's user interface, you
+ * may need to extend this class, but you should never need to instantiate this class.<br>
+ */
+Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
+    // private
+    render : function(){
+        if(!this.rendered){
+            var targetNode = this.node.ownerTree.innerCt.dom;
+            this.node.expanded = true;
+            targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
+            this.wrap = this.ctNode = targetNode.firstChild;
+        }
+    },
+    collapse : Ext.emptyFn,
+    expand : Ext.emptyFn
 });
\ No newline at end of file