3
* Copyright(c) 2006-2008, Ext JS, LLC.
6
* http://extjs.com/license
10
* @class Ext.tree.TreeNodeUI
11
* This class provides the default UI implementation for Ext TreeNodes.
12
* The TreeNode UI implementation is separate from the
13
* tree implementation, and allows customizing of the appearance of
16
* If you are customizing the Tree's user interface, you
17
* may need to extend this class, but you should never need to instantiate this class.<br>
19
* This class provides access to the user interface components of an Ext TreeNode, through
20
* {@link Ext.tree.TreeNode#getUI}
22
Ext.tree.TreeNodeUI = function(node){
24
this.rendered = false;
25
this.animating = false;
27
this.ecc = 'x-tree-ec-icon x-tree-elbow';
28
this.emptyIcon = Ext.BLANK_IMAGE_URL;
31
Ext.tree.TreeNodeUI.prototype = {
33
removeChild : function(node){
35
this.ctNode.removeChild(node.ui.getEl());
40
beforeLoad : function(){
41
this.addClass("x-tree-node-loading");
45
afterLoad : function(){
46
this.removeClass("x-tree-node-loading");
50
onTextChange : function(node, text, oldText){
52
this.textNode.innerHTML = text;
57
onDisableChange : function(node, state){
58
this.disabled = state;
60
this.checkbox.disabled = state;
63
this.addClass("x-tree-node-disabled");
65
this.removeClass("x-tree-node-disabled");
70
onSelectedChange : function(state){
73
this.addClass("x-tree-selected");
76
this.removeClass("x-tree-selected");
81
onMove : function(tree, node, oldParent, newParent, index, refNode){
82
this.childIndent = null;
84
var targetNode = newParent.ui.getContainer();
85
if(!targetNode){//target not rendered
86
this.holder = document.createElement("div");
87
this.holder.appendChild(this.wrap);
90
var insertBefore = refNode ? refNode.ui.getEl() : null;
92
targetNode.insertBefore(this.wrap, insertBefore);
94
targetNode.appendChild(this.wrap);
96
this.node.renderIndent(true);
101
* Adds one or more CSS classes to the node's UI element.
102
* Duplicate classes are automatically filtered out.
103
* @param {String/Array} className The CSS class to add, or an array of classes
105
addClass : function(cls){
107
Ext.fly(this.elNode).addClass(cls);
112
* Removes one or more CSS classes from the node's UI element.
113
* @param {String/Array} className The CSS class to remove, or an array of classes
115
removeClass : function(cls){
117
Ext.fly(this.elNode).removeClass(cls);
124
this.holder = document.createElement("div");
125
this.holder.appendChild(this.wrap);
130
fireEvent : function(){
131
return this.node.fireEvent.apply(this.node, arguments);
135
initEvents : function(){
136
this.node.on("move", this.onMove, this);
138
if(this.node.disabled){
139
this.addClass("x-tree-node-disabled");
141
this.checkbox.disabled = true;
144
if(this.node.hidden){
147
var ot = this.node.getOwnerTree();
148
var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
149
if(dd && (!this.node.isRoot || ot.rootVisible)){
150
Ext.dd.Registry.register(this.elNode, {
152
handles: this.getDDHandles(),
159
getDDHandles : function(){
160
return [this.iconNode, this.textNode, this.elNode];
167
this.node.hidden = true;
169
this.wrap.style.display = "none";
177
this.node.hidden = false;
179
this.wrap.style.display = "";
184
onContextMenu : function(e){
185
if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
188
this.fireEvent("contextmenu", this.node, e);
193
onClick : function(e){
198
if(this.fireEvent("beforeclick", this.node, e) !== false){
199
var a = e.getTarget('a');
200
if(!this.disabled && this.node.attributes.href && a){
201
this.fireEvent("click", this.node, e);
203
}else if(a && e.ctrlKey){
211
if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
215
this.fireEvent("click", this.node, e);
222
onDblClick : function(e){
230
if(!this.animating && this.node.isExpandable()){
233
this.fireEvent("dblclick", this.node, e);
236
onOver : function(e){
237
this.addClass('x-tree-node-over');
241
this.removeClass('x-tree-node-over');
245
onCheckChange : function(){
246
var checked = this.checkbox.checked;
248
this.checkbox.defaultChecked = checked;
249
this.node.attributes.checked = checked;
250
this.fireEvent('checkchange', this.node, checked);
254
ecClick : function(e){
255
if(!this.animating && this.node.isExpandable()){
261
startDrop : function(){
262
this.dropping = true;
265
// delayed drop so the click event doesn't get fired on a drop
266
endDrop : function(){
267
setTimeout(function(){
268
this.dropping = false;
269
}.createDelegate(this), 50);
274
this.updateExpandIcon();
275
this.ctNode.style.display = "";
280
if(!this.node.preventHScroll){
281
try{this.anchor.focus();
285
var noscroll = this.node.getOwnerTree().getTreeEl().dom;
286
var l = noscroll.scrollLeft;
288
noscroll.scrollLeft = l;
294
* Sets the checked status of the tree node to the passed value, or, if no value was passed,
295
* toggles the checked status. If the node was rendered with no checkbox, this has no effect.
296
* @param {Boolean} (optional) The new checked status.
298
toggleCheck : function(value){
299
var cb = this.checkbox;
301
cb.checked = (value === undefined ? !cb.checked : value);
302
this.onCheckChange();
314
animExpand : function(callback){
315
var ct = Ext.get(this.ctNode);
317
if(!this.node.isExpandable()){
318
this.updateExpandIcon();
319
this.ctNode.style.display = "";
320
Ext.callback(callback);
323
this.animating = true;
324
this.updateExpandIcon();
327
callback : function(){
328
this.animating = false;
329
Ext.callback(callback);
332
duration: this.node.ownerTree.duration || .25
337
highlight : function(){
338
var tree = this.node.getOwnerTree();
339
Ext.fly(this.wrap).highlight(
340
tree.hlColor || "C3DAF9",
341
{endColor: tree.hlBaseColor}
346
collapse : function(){
347
this.updateExpandIcon();
348
this.ctNode.style.display = "none";
352
animCollapse : function(callback){
353
var ct = Ext.get(this.ctNode);
354
ct.enableDisplayMode('block');
357
this.animating = true;
358
this.updateExpandIcon();
361
callback : function(){
362
this.animating = false;
363
Ext.callback(callback);
366
duration: this.node.ownerTree.duration || .25
371
getContainer : function(){
381
appendDDGhost : function(ghostNode){
382
ghostNode.appendChild(this.elNode.cloneNode(true));
386
getDDRepairXY : function(){
387
return Ext.lib.Dom.getXY(this.iconNode);
391
onRender : function(){
396
render : function(bulkRender){
397
var n = this.node, a = n.attributes;
398
var targetNode = n.parentNode ?
399
n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
402
this.rendered = true;
404
this.renderElements(n, a, targetNode, bulkRender);
407
if(this.textNode.setAttributeNS){
408
this.textNode.setAttributeNS("ext", "qtip", a.qtip);
410
this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
413
this.textNode.setAttribute("ext:qtip", a.qtip);
415
this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
419
a.qtipCfg.target = Ext.id(this.textNode);
420
Ext.QuickTips.register(a.qtipCfg);
423
if(!this.node.expanded){
424
this.updateExpandIcon(true);
427
if(bulkRender === true) {
428
targetNode.appendChild(this.wrap);
434
renderElements : function(n, a, targetNode, bulkRender){
435
// add some indent caching, this helps performance when rendering a large tree
436
this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
438
var cb = typeof a.checked == 'boolean';
440
var href = a.href ? a.href : Ext.isGecko ? "" : "#";
441
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">',
442
'<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
443
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
444
'<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
445
cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
446
'<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
447
a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
448
'<ul class="x-tree-node-ct" style="display:none;"></ul>',
452
if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
453
this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
455
this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
458
this.elNode = this.wrap.childNodes[0];
459
this.ctNode = this.wrap.childNodes[1];
460
var cs = this.elNode.childNodes;
461
this.indentNode = cs[0];
463
this.iconNode = cs[2];
466
this.checkbox = cs[3];
468
this.checkbox.defaultChecked = this.checkbox.checked;
471
this.anchor = cs[index];
472
this.textNode = cs[index].firstChild;
476
* Returns the <a> element that provides focus for the node's UI.
477
* @return {HtmlElement} The DOM anchor element.
479
getAnchor : function(){
484
* Returns the text node.
485
* @return {HtmlNode} The DOM text node.
487
getTextEl : function(){
488
return this.textNode;
492
* Returns the icon <img> element.
493
* @return {HtmlElement} The DOM image element.
495
getIconEl : function(){
496
return this.iconNode;
500
* Returns the checked status of the node. If the node was rendered with no
501
* checkbox, it returns false.
502
* @return {Boolean} The checked flag.
504
isChecked : function(){
505
return this.checkbox ? this.checkbox.checked : false;
509
updateExpandIcon : function(){
511
var n = this.node, c1, c2;
512
var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
513
if(n.isExpandable()){
516
c1 = "x-tree-node-collapsed";
517
c2 = "x-tree-node-expanded";
520
c1 = "x-tree-node-expanded";
521
c2 = "x-tree-node-collapsed";
524
this.removeClass("x-tree-node-leaf");
525
this.wasLeaf = false;
527
if(this.c1 != c1 || this.c2 != c2){
528
Ext.fly(this.elNode).replaceClass(c1, c2);
529
this.c1 = c1; this.c2 = c2;
533
Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
539
var ecc = "x-tree-ec-icon "+cls;
541
this.ecNode.className = ecc;
548
getChildIndent : function(){
549
if(!this.childIndent){
553
if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
555
buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
557
buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
562
this.childIndent = buf.join("");
564
return this.childIndent;
568
renderIndent : function(){
571
var p = this.node.parentNode;
573
indent = p.ui.getChildIndent();
575
if(this.indentMarkup != indent){ // don't rerender if not required
576
this.indentNode.innerHTML = indent;
577
this.indentMarkup = indent;
579
this.updateExpandIcon();
583
destroy : function(){
585
Ext.dd.Registry.unregister(this.elNode.id);
589
delete this.indentNode;
591
delete this.iconNode;
592
delete this.checkbox;
594
delete this.textNode;
595
Ext.removeNode(this.ctNode);
600
* @class Ext.tree.RootTreeNodeUI
601
* This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
602
* The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
604
* If you are customizing the Tree's user interface, you
605
* may need to extend this class, but you should never need to instantiate this class.<br>
607
Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
611
var targetNode = this.node.ownerTree.innerCt.dom;
612
this.node.expanded = true;
613
targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
614
this.wrap = this.ctNode = targetNode.firstChild;
617
collapse : Ext.emptyFn,
b'\\ No newline at end of file'