4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
13 * @class Roo.tree.TreeNodeUI
15 * @param {Object} node The node to render
16 * The TreeNode UI implementation is separate from the
17 * tree implementation. Unless you are customizing the tree UI,
18 * you should never have to use this directly.
20 Roo.tree.TreeNodeUI = function(node){
22 this.rendered = false;
23 this.animating = false;
24 this.emptyIcon = Roo.BLANK_IMAGE_URL;
27 Roo.tree.TreeNodeUI.prototype = {
28 removeChild : function(node){
30 this.ctNode.removeChild(node.ui.getEl());
34 beforeLoad : function(){
35 this.addClass("x-tree-node-loading");
38 afterLoad : function(){
39 this.removeClass("x-tree-node-loading");
42 onTextChange : function(node, text, oldText){
44 this.textNode.innerHTML = text;
48 onDisableChange : function(node, state){
49 this.disabled = state;
51 this.addClass("x-tree-node-disabled");
53 this.removeClass("x-tree-node-disabled");
57 onSelectedChange : function(state){
60 this.addClass("x-tree-selected");
63 this.removeClass("x-tree-selected");
67 onMove : function(tree, node, oldParent, newParent, index, refNode){
68 this.childIndent = null;
70 var targetNode = newParent.ui.getContainer();
71 if(!targetNode){//target not rendered
72 this.holder = document.createElement("div");
73 this.holder.appendChild(this.wrap);
76 var insertBefore = refNode ? refNode.ui.getEl() : null;
78 targetNode.insertBefore(this.wrap, insertBefore);
80 targetNode.appendChild(this.wrap);
82 this.node.renderIndent(true);
86 addClass : function(cls){
88 Roo.fly(this.elNode).addClass(cls);
92 removeClass : function(cls){
94 Roo.fly(this.elNode).removeClass(cls);
100 this.holder = document.createElement("div");
101 this.holder.appendChild(this.wrap);
105 fireEvent : function(){
106 return this.node.fireEvent.apply(this.node, arguments);
109 initEvents : function(){
110 this.node.on("move", this.onMove, this);
111 var E = Roo.EventManager;
114 var el = Roo.fly(a, '_treeui');
116 if(Roo.isOpera){ // opera render bug ignores the CSS
117 el.setStyle("text-decoration", "none");
120 el.on("click", this.onClick, this);
121 el.on("dblclick", this.onDblClick, this);
124 Roo.EventManager.on(this.checkbox,
125 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
128 el.on("contextmenu", this.onContextMenu, this);
130 var icon = Roo.fly(this.iconNode);
131 icon.on("click", this.onClick, this);
132 icon.on("dblclick", this.onDblClick, this);
133 icon.on("contextmenu", this.onContextMenu, this);
134 E.on(this.ecNode, "click", this.ecClick, this, true);
136 if(this.node.disabled){
137 this.addClass("x-tree-node-disabled");
139 if(this.node.hidden){
140 this.addClass("x-tree-node-disabled");
142 var ot = this.node.getOwnerTree();
143 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
144 if(dd && (!this.node.isRoot || ot.rootVisible)){
145 Roo.dd.Registry.register(this.elNode, {
147 handles: this.getDDHandles(),
153 getDDHandles : function(){
154 return [this.iconNode, this.textNode];
159 this.wrap.style.display = "none";
165 this.wrap.style.display = "";
169 onContextMenu : function(e){
170 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
173 this.fireEvent("contextmenu", this.node, e);
177 onClick : function(e){
182 if(this.fireEvent("beforeclick", this.node, e) !== false){
183 if(!this.disabled && this.node.attributes.href){
184 this.fireEvent("click", this.node, e);
192 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
196 this.fireEvent("click", this.node, e);
202 onDblClick : function(e){
210 if(!this.animating && this.node.hasChildNodes()){
213 this.fireEvent("dblclick", this.node, e);
216 onCheckChange : function(){
217 var checked = this.checkbox.checked;
218 this.node.attributes.checked = checked;
219 this.fireEvent('checkchange', this.node, checked);
222 ecClick : function(e){
223 if(!this.animating && this.node.hasChildNodes()){
228 startDrop : function(){
229 this.dropping = true;
232 // delayed drop so the click event doesn't get fired on a drop
233 endDrop : function(){
234 setTimeout(function(){
235 this.dropping = false;
236 }.createDelegate(this), 50);
240 this.updateExpandIcon();
241 this.ctNode.style.display = "";
245 if(!this.node.preventHScroll){
246 try{this.anchor.focus();
250 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
251 var l = noscroll.scrollLeft;
253 noscroll.scrollLeft = l;
258 toggleCheck : function(value){
259 var cb = this.checkbox;
261 cb.checked = (value === undefined ? !cb.checked : value);
271 animExpand : function(callback){
272 var ct = Roo.get(this.ctNode);
274 if(!this.node.hasChildNodes()){
275 this.updateExpandIcon();
276 this.ctNode.style.display = "";
277 Roo.callback(callback);
280 this.animating = true;
281 this.updateExpandIcon();
284 callback : function(){
285 this.animating = false;
286 Roo.callback(callback);
289 duration: this.node.ownerTree.duration || .25
293 highlight : function(){
294 var tree = this.node.getOwnerTree();
295 Roo.fly(this.wrap).highlight(
296 tree.hlColor || "C3DAF9",
297 {endColor: tree.hlBaseColor}
301 collapse : function(){
302 this.updateExpandIcon();
303 this.ctNode.style.display = "none";
306 animCollapse : function(callback){
307 var ct = Roo.get(this.ctNode);
308 ct.enableDisplayMode('block');
311 this.animating = true;
312 this.updateExpandIcon();
315 callback : function(){
316 this.animating = false;
317 Roo.callback(callback);
320 duration: this.node.ownerTree.duration || .25
324 getContainer : function(){
332 appendDDGhost : function(ghostNode){
333 ghostNode.appendChild(this.elNode.cloneNode(true));
336 getDDRepairXY : function(){
337 return Roo.lib.Dom.getXY(this.iconNode);
340 onRender : function(){
344 render : function(bulkRender){
345 var n = this.node, a = n.attributes;
346 var targetNode = n.parentNode ?
347 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
350 this.rendered = true;
352 this.renderElements(n, a, targetNode, bulkRender);
355 if(this.textNode.setAttributeNS){
356 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
358 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
361 this.textNode.setAttribute("ext:qtip", a.qtip);
363 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
367 a.qtipCfg.target = Roo.id(this.textNode);
368 Roo.QuickTips.register(a.qtipCfg);
371 if(!this.node.expanded){
372 this.updateExpandIcon();
375 if(bulkRender === true) {
376 targetNode.appendChild(this.wrap);
381 renderElements : function(n, a, targetNode, bulkRender){
382 // add some indent caching, this helps performance when rendering a large tree
383 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
384 var t = n.getOwnerTree();
385 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
386 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
387 var cb = typeof a.checked == 'boolean';
388 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
389 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
390 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
391 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
392 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
393 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
394 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
395 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
396 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
397 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
400 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
401 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
402 n.nextSibling.ui.getEl(), buf.join(""));
404 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
407 this.elNode = this.wrap.childNodes[0];
408 this.ctNode = this.wrap.childNodes[1];
409 var cs = this.elNode.childNodes;
410 this.indentNode = cs[0];
412 this.iconNode = cs[2];
415 this.checkbox = cs[3];
418 this.anchor = cs[index];
419 this.textNode = cs[index].firstChild;
422 getAnchor : function(){
426 getTextEl : function(){
427 return this.textNode;
430 getIconEl : function(){
431 return this.iconNode;
434 isChecked : function(){
435 return this.checkbox ? this.checkbox.checked : false;
438 updateExpandIcon : function(){
440 var n = this.node, c1, c2;
441 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
442 var hasChild = n.hasChildNodes();
446 c1 = "x-tree-node-collapsed";
447 c2 = "x-tree-node-expanded";
450 c1 = "x-tree-node-expanded";
451 c2 = "x-tree-node-collapsed";
454 this.removeClass("x-tree-node-leaf");
455 this.wasLeaf = false;
457 if(this.c1 != c1 || this.c2 != c2){
458 Roo.fly(this.elNode).replaceClass(c1, c2);
459 this.c1 = c1; this.c2 = c2;
463 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
469 var ecc = "x-tree-ec-icon "+cls;
471 this.ecNode.className = ecc;
477 getChildIndent : function(){
478 if(!this.childIndent){
482 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
484 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
486 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
491 this.childIndent = buf.join("");
493 return this.childIndent;
496 renderIndent : function(){
499 var p = this.node.parentNode;
501 indent = p.ui.getChildIndent();
503 if(this.indentMarkup != indent){ // don't rerender if not required
504 this.indentNode.innerHTML = indent;
505 this.indentMarkup = indent;
507 this.updateExpandIcon();
512 Roo.tree.RootTreeNodeUI = function(){
513 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
515 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
518 var targetNode = this.node.ownerTree.innerCt.dom;
519 this.node.expanded = true;
520 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
521 this.wrap = this.ctNode = targetNode.firstChild;
524 collapse : function(){