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 ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
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)
383 // add some indent caching, this helps performance when rendering a large tree
384 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
385 var t = n.getOwnerTree();
386 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
387 if (typeof(n.attributes.html) != 'undefined') {
388 txt = n.attributes.html;
390 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
391 var cb = typeof a.checked == 'boolean';
392 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
393 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
394 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
395 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
396 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
397 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
398 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
399 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
400 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
401 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
404 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
405 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
406 n.nextSibling.ui.getEl(), buf.join(""));
408 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
411 this.elNode = this.wrap.childNodes[0];
412 this.ctNode = this.wrap.childNodes[1];
413 var cs = this.elNode.childNodes;
414 this.indentNode = cs[0];
416 this.iconNode = cs[2];
419 this.checkbox = cs[3];
422 this.anchor = cs[index];
423 this.textNode = cs[index].firstChild;
426 getAnchor : function(){
430 getTextEl : function(){
431 return this.textNode;
434 getIconEl : function(){
435 return this.iconNode;
438 isChecked : function(){
439 return this.checkbox ? this.checkbox.checked : false;
442 updateExpandIcon : function(){
444 var n = this.node, c1, c2;
445 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
446 var hasChild = n.hasChildNodes();
450 c1 = "x-tree-node-collapsed";
451 c2 = "x-tree-node-expanded";
454 c1 = "x-tree-node-expanded";
455 c2 = "x-tree-node-collapsed";
458 this.removeClass("x-tree-node-leaf");
459 this.wasLeaf = false;
461 if(this.c1 != c1 || this.c2 != c2){
462 Roo.fly(this.elNode).replaceClass(c1, c2);
463 this.c1 = c1; this.c2 = c2;
466 // this changes non-leafs into leafs if they have no children.
467 // it's not very rational behaviour..
469 if(!this.wasLeaf && this.node.leaf){
470 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
476 var ecc = "x-tree-ec-icon "+cls;
478 this.ecNode.className = ecc;
484 getChildIndent : function(){
485 if(!this.childIndent){
489 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
491 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
493 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
498 this.childIndent = buf.join("");
500 return this.childIndent;
503 renderIndent : function(){
506 var p = this.node.parentNode;
508 indent = p.ui.getChildIndent();
510 if(this.indentMarkup != indent){ // don't rerender if not required
511 this.indentNode.innerHTML = indent;
512 this.indentMarkup = indent;
514 this.updateExpandIcon();
519 Roo.tree.RootTreeNodeUI = function(){
520 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
522 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
525 var targetNode = this.node.ownerTree.innerCt.dom;
526 this.node.expanded = true;
527 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
528 this.wrap = this.ctNode = targetNode.firstChild;
531 collapse : function(){