3 <title>The source code</title>
4 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
5 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 <body onload="prettyPrint();">
8 <pre class="prettyprint lang-js">/*!
10 * Copyright(c) 2006-2009 Ext JS, LLC
12 * http://www.extjs.com/license
14 <div id="cls-Ext.tree.TreeNodeUI"></div>/**
\r
15 * @class Ext.tree.TreeNodeUI
\r
16 * This class provides the default UI implementation for Ext TreeNodes.
\r
17 * The TreeNode UI implementation is separate from the
\r
18 * tree implementation, and allows customizing of the appearance of
\r
21 * If you are customizing the Tree's user interface, you
\r
22 * may need to extend this class, but you should never need to instantiate this class.<br>
\r
24 * This class provides access to the user interface components of an Ext TreeNode, through
\r
25 * {@link Ext.tree.TreeNode#getUI}
\r
27 Ext.tree.TreeNodeUI = function(node){
\r
29 this.rendered = false;
\r
30 this.animating = false;
\r
31 this.wasLeaf = true;
\r
32 this.ecc = 'x-tree-ec-icon x-tree-elbow';
\r
33 this.emptyIcon = Ext.BLANK_IMAGE_URL;
\r
36 Ext.tree.TreeNodeUI.prototype = {
\r
38 removeChild : function(node){
\r
40 this.ctNode.removeChild(node.ui.getEl());
\r
45 beforeLoad : function(){
\r
46 this.addClass("x-tree-node-loading");
\r
50 afterLoad : function(){
\r
51 this.removeClass("x-tree-node-loading");
\r
55 onTextChange : function(node, text, oldText){
\r
57 this.textNode.innerHTML = text;
\r
62 onDisableChange : function(node, state){
\r
63 this.disabled = state;
\r
64 if (this.checkbox) {
\r
65 this.checkbox.disabled = state;
\r
68 this.addClass("x-tree-node-disabled");
\r
70 this.removeClass("x-tree-node-disabled");
\r
75 onSelectedChange : function(state){
\r
78 this.addClass("x-tree-selected");
\r
81 this.removeClass("x-tree-selected");
\r
86 onMove : function(tree, node, oldParent, newParent, index, refNode){
\r
87 this.childIndent = null;
\r
89 var targetNode = newParent.ui.getContainer();
\r
90 if(!targetNode){//target not rendered
\r
91 this.holder = document.createElement("div");
\r
92 this.holder.appendChild(this.wrap);
\r
95 var insertBefore = refNode ? refNode.ui.getEl() : null;
\r
97 targetNode.insertBefore(this.wrap, insertBefore);
\r
99 targetNode.appendChild(this.wrap);
\r
101 this.node.renderIndent(true, oldParent != newParent);
\r
105 <div id="method-Ext.tree.TreeNodeUI-addClass"></div>/**
\r
106 * Adds one or more CSS classes to the node's UI element.
\r
107 * Duplicate classes are automatically filtered out.
\r
108 * @param {String/Array} className The CSS class to add, or an array of classes
\r
110 addClass : function(cls){
\r
112 Ext.fly(this.elNode).addClass(cls);
\r
116 <div id="method-Ext.tree.TreeNodeUI-removeClass"></div>/**
\r
117 * Removes one or more CSS classes from the node's UI element.
\r
118 * @param {String/Array} className The CSS class to remove, or an array of classes
\r
120 removeClass : function(cls){
\r
122 Ext.fly(this.elNode).removeClass(cls);
\r
127 remove : function(){
\r
129 this.holder = document.createElement("div");
\r
130 this.holder.appendChild(this.wrap);
\r
135 fireEvent : function(){
\r
136 return this.node.fireEvent.apply(this.node, arguments);
\r
140 initEvents : function(){
\r
141 this.node.on("move", this.onMove, this);
\r
143 if(this.node.disabled){
\r
144 this.onDisableChange(this.node, true);
\r
146 if(this.node.hidden){
\r
149 var ot = this.node.getOwnerTree();
\r
150 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
\r
151 if(dd && (!this.node.isRoot || ot.rootVisible)){
\r
152 Ext.dd.Registry.register(this.elNode, {
\r
154 handles: this.getDDHandles(),
\r
161 getDDHandles : function(){
\r
162 return [this.iconNode, this.textNode, this.elNode];
\r
165 <div id="method-Ext.tree.TreeNodeUI-hide"></div>/**
\r
169 this.node.hidden = true;
\r
171 this.wrap.style.display = "none";
\r
175 <div id="method-Ext.tree.TreeNodeUI-show"></div>/**
\r
179 this.node.hidden = false;
\r
181 this.wrap.style.display = "";
\r
186 onContextMenu : function(e){
\r
187 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
\r
188 e.preventDefault();
\r
190 this.fireEvent("contextmenu", this.node, e);
\r
195 onClick : function(e){
\r
200 if(this.fireEvent("beforeclick", this.node, e) !== false){
\r
201 var a = e.getTarget('a');
\r
202 if(!this.disabled && this.node.attributes.href && a){
\r
203 this.fireEvent("click", this.node, e);
\r
205 }else if(a && e.ctrlKey){
\r
208 e.preventDefault();
\r
213 if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
\r
214 this.node.toggle();
\r
217 this.fireEvent("click", this.node, e);
\r
224 onDblClick : function(e){
\r
225 e.preventDefault();
\r
229 if(this.fireEvent("beforedblclick", this.node, e) !== false){
\r
231 this.toggleCheck();
\r
233 if(!this.animating && this.node.isExpandable()){
\r
234 this.node.toggle();
\r
236 this.fireEvent("dblclick", this.node, e);
\r
240 onOver : function(e){
\r
241 this.addClass('x-tree-node-over');
\r
244 onOut : function(e){
\r
245 this.removeClass('x-tree-node-over');
\r
249 onCheckChange : function(){
\r
250 var checked = this.checkbox.checked;
\r
252 this.checkbox.defaultChecked = checked;
\r
253 this.node.attributes.checked = checked;
\r
254 this.fireEvent('checkchange', this.node, checked);
\r
258 ecClick : function(e){
\r
259 if(!this.animating && this.node.isExpandable()){
\r
260 this.node.toggle();
\r
265 startDrop : function(){
\r
266 this.dropping = true;
\r
269 // delayed drop so the click event doesn't get fired on a drop
\r
270 endDrop : function(){
\r
271 setTimeout(function(){
\r
272 this.dropping = false;
\r
273 }.createDelegate(this), 50);
\r
277 expand : function(){
\r
278 this.updateExpandIcon();
\r
279 this.ctNode.style.display = "";
\r
283 focus : function(){
\r
284 if(!this.node.preventHScroll){
\r
285 try{this.anchor.focus();
\r
289 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
\r
290 var l = noscroll.scrollLeft;
\r
291 this.anchor.focus();
\r
292 noscroll.scrollLeft = l;
\r
297 <div id="method-Ext.tree.TreeNodeUI-toggleCheck"></div>/**
\r
298 * Sets the checked status of the tree node to the passed value, or, if no value was passed,
\r
299 * toggles the checked status. If the node was rendered with no checkbox, this has no effect.
\r
300 * @param {Boolean} (optional) The new checked status.
\r
302 toggleCheck : function(value){
\r
303 var cb = this.checkbox;
\r
305 cb.checked = (value === undefined ? !cb.checked : value);
\r
306 this.onCheckChange();
\r
313 this.anchor.blur();
\r
318 animExpand : function(callback){
\r
319 var ct = Ext.get(this.ctNode);
\r
321 if(!this.node.isExpandable()){
\r
322 this.updateExpandIcon();
\r
323 this.ctNode.style.display = "";
\r
324 Ext.callback(callback);
\r
327 this.animating = true;
\r
328 this.updateExpandIcon();
\r
331 callback : function(){
\r
332 this.animating = false;
\r
333 Ext.callback(callback);
\r
336 duration: this.node.ownerTree.duration || .25
\r
341 highlight : function(){
\r
342 var tree = this.node.getOwnerTree();
\r
343 Ext.fly(this.wrap).highlight(
\r
344 tree.hlColor || "C3DAF9",
\r
345 {endColor: tree.hlBaseColor}
\r
350 collapse : function(){
\r
351 this.updateExpandIcon();
\r
352 this.ctNode.style.display = "none";
\r
356 animCollapse : function(callback){
\r
357 var ct = Ext.get(this.ctNode);
\r
358 ct.enableDisplayMode('block');
\r
361 this.animating = true;
\r
362 this.updateExpandIcon();
\r
365 callback : function(){
\r
366 this.animating = false;
\r
367 Ext.callback(callback);
\r
370 duration: this.node.ownerTree.duration || .25
\r
375 getContainer : function(){
\r
376 return this.ctNode;
\r
380 getEl : function(){
\r
385 appendDDGhost : function(ghostNode){
\r
386 ghostNode.appendChild(this.elNode.cloneNode(true));
\r
390 getDDRepairXY : function(){
\r
391 return Ext.lib.Dom.getXY(this.iconNode);
\r
395 onRender : function(){
\r
400 render : function(bulkRender){
\r
401 var n = this.node, a = n.attributes;
\r
402 var targetNode = n.parentNode ?
\r
403 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
\r
405 if(!this.rendered){
\r
406 this.rendered = true;
\r
408 this.renderElements(n, a, targetNode, bulkRender);
\r
411 if(this.textNode.setAttributeNS){
\r
412 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
\r
414 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
\r
417 this.textNode.setAttribute("ext:qtip", a.qtip);
\r
419 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
\r
422 }else if(a.qtipCfg){
\r
423 a.qtipCfg.target = Ext.id(this.textNode);
\r
424 Ext.QuickTips.register(a.qtipCfg);
\r
427 if(!this.node.expanded){
\r
428 this.updateExpandIcon(true);
\r
431 if(bulkRender === true) {
\r
432 targetNode.appendChild(this.wrap);
\r
438 renderElements : function(n, a, targetNode, bulkRender){
\r
439 // add some indent caching, this helps performance when rendering a large tree
\r
440 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
\r
442 var cb = typeof a.checked == 'boolean';
\r
444 var href = a.href ? a.href : Ext.isGecko ? "" : "#";
\r
445 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
446 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
\r
447 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
\r
448 '<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
449 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : '/>')) : '',
\r
450 '<a hidefocus="on" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
\r
451 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
\r
452 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
\r
456 if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
\r
457 this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
\r
459 this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
\r
462 this.elNode = this.wrap.childNodes[0];
\r
463 this.ctNode = this.wrap.childNodes[1];
\r
464 var cs = this.elNode.childNodes;
\r
465 this.indentNode = cs[0];
\r
466 this.ecNode = cs[1];
\r
467 this.iconNode = cs[2];
\r
470 this.checkbox = cs[3];
\r
472 this.checkbox.defaultChecked = this.checkbox.checked;
\r
475 this.anchor = cs[index];
\r
476 this.textNode = cs[index].firstChild;
\r
479 <div id="method-Ext.tree.TreeNodeUI-getAnchor"></div>/**
\r
480 * Returns the <a> element that provides focus for the node's UI.
\r
481 * @return {HtmlElement} The DOM anchor element.
\r
483 getAnchor : function(){
\r
484 return this.anchor;
\r
487 <div id="method-Ext.tree.TreeNodeUI-getTextEl"></div>/**
\r
488 * Returns the text node.
\r
489 * @return {HtmlNode} The DOM text node.
\r
491 getTextEl : function(){
\r
492 return this.textNode;
\r
495 <div id="method-Ext.tree.TreeNodeUI-getIconEl"></div>/**
\r
496 * Returns the icon <img> element.
\r
497 * @return {HtmlElement} The DOM image element.
\r
499 getIconEl : function(){
\r
500 return this.iconNode;
\r
503 <div id="method-Ext.tree.TreeNodeUI-isChecked"></div>/**
\r
504 * Returns the checked status of the node. If the node was rendered with no
\r
505 * checkbox, it returns false.
\r
506 * @return {Boolean} The checked flag.
\r
508 isChecked : function(){
\r
509 return this.checkbox ? this.checkbox.checked : false;
\r
513 updateExpandIcon : function(){
\r
515 var n = this.node, c1, c2;
\r
516 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
\r
517 var hasChild = n.hasChildNodes();
\r
518 if(hasChild || n.attributes.expandable){
\r
521 c1 = "x-tree-node-collapsed";
\r
522 c2 = "x-tree-node-expanded";
\r
525 c1 = "x-tree-node-expanded";
\r
526 c2 = "x-tree-node-collapsed";
\r
529 this.removeClass("x-tree-node-leaf");
\r
530 this.wasLeaf = false;
\r
532 if(this.c1 != c1 || this.c2 != c2){
\r
533 Ext.fly(this.elNode).replaceClass(c1, c2);
\r
534 this.c1 = c1; this.c2 = c2;
\r
538 Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
\r
541 this.wasLeaf = true;
\r
544 var ecc = "x-tree-ec-icon "+cls;
\r
545 if(this.ecc != ecc){
\r
546 this.ecNode.className = ecc;
\r
553 onIdChange: function(id){
\r
555 this.elNode.setAttribute('ext:tree-node-id', id);
\r
560 getChildIndent : function(){
\r
561 if(!this.childIndent){
\r
565 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
\r
567 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
\r
569 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
\r
574 this.childIndent = buf.join("");
\r
576 return this.childIndent;
\r
580 renderIndent : function(){
\r
583 var p = this.node.parentNode;
\r
585 indent = p.ui.getChildIndent();
\r
587 if(this.indentMarkup != indent){ // don't rerender if not required
\r
588 this.indentNode.innerHTML = indent;
\r
589 this.indentMarkup = indent;
\r
591 this.updateExpandIcon();
\r
595 destroy : function(){
\r
597 Ext.dd.Registry.unregister(this.elNode.id);
\r
599 delete this.elNode;
\r
600 delete this.ctNode;
\r
601 delete this.indentNode;
\r
602 delete this.ecNode;
\r
603 delete this.iconNode;
\r
604 delete this.checkbox;
\r
605 delete this.anchor;
\r
606 delete this.textNode;
\r
610 Ext.removeNode(this.holder);
\r
611 delete this.holder;
\r
613 Ext.removeNode(this.wrap);
\r
619 <div id="cls-Ext.tree.RootTreeNodeUI"></div>/**
\r
620 * @class Ext.tree.RootTreeNodeUI
\r
621 * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
\r
622 * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
\r
624 * If you are customizing the Tree's user interface, you
\r
625 * may need to extend this class, but you should never need to instantiate this class.<br>
\r
627 Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
\r
629 render : function(){
\r
630 if(!this.rendered){
\r
631 var targetNode = this.node.ownerTree.innerCt.dom;
\r
632 this.node.expanded = true;
\r
633 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
\r
634 this.wrap = this.ctNode = targetNode.firstChild;
\r
637 collapse : Ext.emptyFn,
\r
638 expand : Ext.emptyFn
\r