TreeNodeUI.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:19k
源码类别:

JavaScript

开发平台:

JavaScript

  1. /*!  * Ext JS Library 3.1.0  * Copyright(c) 2006-2009 Ext JS, LLC  * licensing@extjs.com  * http://www.extjs.com/license  */ /**
  2.  * @class Ext.tree.TreeNodeUI
  3.  * This class provides the default UI implementation for Ext TreeNodes.
  4.  * The TreeNode UI implementation is separate from the
  5.  * tree implementation, and allows customizing of the appearance of
  6.  * tree nodes.<br>
  7.  * <p>
  8.  * If you are customizing the Tree's user interface, you
  9.  * may need to extend this class, but you should never need to instantiate this class.<br>
  10.  * <p>
  11.  * This class provides access to the user interface components of an Ext TreeNode, through
  12.  * {@link Ext.tree.TreeNode#getUI}
  13.  */
  14. Ext.tree.TreeNodeUI = function(node){
  15.     this.node = node;
  16.     this.rendered = false;
  17.     this.animating = false;
  18.     this.wasLeaf = true;
  19.     this.ecc = 'x-tree-ec-icon x-tree-elbow';
  20.     this.emptyIcon = Ext.BLANK_IMAGE_URL;
  21. };
  22. Ext.tree.TreeNodeUI.prototype = {
  23.     // private
  24.     removeChild : function(node){
  25.         if(this.rendered){
  26.             this.ctNode.removeChild(node.ui.getEl());
  27.         } 
  28.     },
  29.     // private
  30.     beforeLoad : function(){
  31.          this.addClass("x-tree-node-loading");
  32.     },
  33.     // private
  34.     afterLoad : function(){
  35.          this.removeClass("x-tree-node-loading");
  36.     },
  37.     // private
  38.     onTextChange : function(node, text, oldText){
  39.         if(this.rendered){
  40.             this.textNode.innerHTML = text;
  41.         }
  42.     },
  43.     // private
  44.     onDisableChange : function(node, state){
  45.         this.disabled = state;
  46.         if (this.checkbox) {
  47.             this.checkbox.disabled = state;
  48.         }        
  49.         if(state){
  50.             this.addClass("x-tree-node-disabled");
  51.         }else{
  52.             this.removeClass("x-tree-node-disabled");
  53.         } 
  54.     },
  55.     // private
  56.     onSelectedChange : function(state){
  57.         if(state){
  58.             this.focus();
  59.             this.addClass("x-tree-selected");
  60.         }else{
  61.             //this.blur();
  62.             this.removeClass("x-tree-selected");
  63.         }
  64.     },
  65.     // private
  66.     onMove : function(tree, node, oldParent, newParent, index, refNode){
  67.         this.childIndent = null;
  68.         if(this.rendered){
  69.             var targetNode = newParent.ui.getContainer();
  70.             if(!targetNode){//target not rendered
  71.                 this.holder = document.createElement("div");
  72.                 this.holder.appendChild(this.wrap);
  73.                 return;
  74.             }
  75.             var insertBefore = refNode ? refNode.ui.getEl() : null;
  76.             if(insertBefore){
  77.                 targetNode.insertBefore(this.wrap, insertBefore);
  78.             }else{
  79.                 targetNode.appendChild(this.wrap);
  80.             }
  81.             this.node.renderIndent(true, oldParent != newParent);
  82.         }
  83.     },
  84. /**
  85.  * Adds one or more CSS classes to the node's UI element.
  86.  * Duplicate classes are automatically filtered out.
  87.  * @param {String/Array} className The CSS class to add, or an array of classes
  88.  */
  89.     addClass : function(cls){
  90.         if(this.elNode){
  91.             Ext.fly(this.elNode).addClass(cls);
  92.         }
  93.     },
  94. /**
  95.  * Removes one or more CSS classes from the node's UI element.
  96.  * @param {String/Array} className The CSS class to remove, or an array of classes
  97.  */
  98.     removeClass : function(cls){
  99.         if(this.elNode){
  100.             Ext.fly(this.elNode).removeClass(cls);  
  101.         }
  102.     },
  103.     // private
  104.     remove : function(){
  105.         if(this.rendered){
  106.             this.holder = document.createElement("div");
  107.             this.holder.appendChild(this.wrap);
  108.         }  
  109.     },
  110.     // private
  111.     fireEvent : function(){
  112.         return this.node.fireEvent.apply(this.node, arguments);  
  113.     },
  114.     // private
  115.     initEvents : function(){
  116.         this.node.on("move", this.onMove, this);
  117.         if(this.node.disabled){
  118.             this.onDisableChange(this.node, true);            
  119.         }
  120.         if(this.node.hidden){
  121.             this.hide();
  122.         }
  123.         var ot = this.node.getOwnerTree();
  124.         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
  125.         if(dd && (!this.node.isRoot || ot.rootVisible)){
  126.             Ext.dd.Registry.register(this.elNode, {
  127.                 node: this.node,
  128.                 handles: this.getDDHandles(),
  129.                 isHandle: false
  130.             });
  131.         }
  132.     },
  133.     // private
  134.     getDDHandles : function(){
  135.         return [this.iconNode, this.textNode, this.elNode];
  136.     },
  137. /**
  138.  * Hides this node.
  139.  */
  140.     hide : function(){
  141.         this.node.hidden = true;
  142.         if(this.wrap){
  143.             this.wrap.style.display = "none";
  144.         }
  145.     },
  146. /**
  147.  * Shows this node.
  148.  */
  149.     show : function(){
  150.         this.node.hidden = false;
  151.         if(this.wrap){
  152.             this.wrap.style.display = "";
  153.         } 
  154.     },
  155.     // private
  156.     onContextMenu : function(e){
  157.         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
  158.             e.preventDefault();
  159.             this.focus();
  160.             this.fireEvent("contextmenu", this.node, e);
  161.         }
  162.     },
  163.     // private
  164.     onClick : function(e){
  165.         if(this.dropping){
  166.             e.stopEvent();
  167.             return;
  168.         }
  169.         if(this.fireEvent("beforeclick", this.node, e) !== false){
  170.             var a = e.getTarget('a');
  171.             if(!this.disabled && this.node.attributes.href && a){
  172.                 this.fireEvent("click", this.node, e);
  173.                 return;
  174.             }else if(a && e.ctrlKey){
  175.                 e.stopEvent();
  176.             }
  177.             e.preventDefault();
  178.             if(this.disabled){
  179.                 return;
  180.             }
  181.             if(this.node.attributes.singleClickExpand && !this.animating && this.node.isExpandable()){
  182.                 this.node.toggle();
  183.             }
  184.             this.fireEvent("click", this.node, e);
  185.         }else{
  186.             e.stopEvent();
  187.         }
  188.     },
  189.     // private
  190.     onDblClick : function(e){
  191.         e.preventDefault();
  192.         if(this.disabled){
  193.             return;
  194.         }
  195.         if(this.fireEvent("beforedblclick", this.node, e) !== false){
  196.             if(this.checkbox){
  197.                 this.toggleCheck();
  198.             }
  199.             if(!this.animating && this.node.isExpandable()){
  200.                 this.node.toggle();
  201.             }
  202.             this.fireEvent("dblclick", this.node, e);
  203.         }
  204.     },
  205.     onOver : function(e){
  206.         this.addClass('x-tree-node-over');
  207.     },
  208.     onOut : function(e){
  209.         this.removeClass('x-tree-node-over');
  210.     },
  211.     // private
  212.     onCheckChange : function(){
  213.         var checked = this.checkbox.checked;
  214.         // fix for IE6
  215.         this.checkbox.defaultChecked = checked;        
  216.         this.node.attributes.checked = checked;
  217.         this.fireEvent('checkchange', this.node, checked);
  218.     },
  219.     // private
  220.     ecClick : function(e){
  221.         if(!this.animating && this.node.isExpandable()){
  222.             this.node.toggle();
  223.         }
  224.     },
  225.     // private
  226.     startDrop : function(){
  227.         this.dropping = true;
  228.     },
  229.     
  230.     // delayed drop so the click event doesn't get fired on a drop
  231.     endDrop : function(){ 
  232.        setTimeout(function(){
  233.            this.dropping = false;
  234.        }.createDelegate(this), 50); 
  235.     },
  236.     // private
  237.     expand : function(){
  238.         this.updateExpandIcon();
  239.         this.ctNode.style.display = "";
  240.     },
  241.     // private
  242.     focus : function(){
  243.         if(!this.node.preventHScroll){
  244.             try{this.anchor.focus();
  245.             }catch(e){}
  246.         }else{
  247.             try{
  248.                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
  249.                 var l = noscroll.scrollLeft;
  250.                 this.anchor.focus();
  251.                 noscroll.scrollLeft = l;
  252.             }catch(e){}
  253.         }
  254.     },
  255. /**
  256.  * Sets the checked status of the tree node to the passed value, or, if no value was passed,
  257.  * toggles the checked status. If the node was rendered with no checkbox, this has no effect.
  258.  * @param {Boolean} (optional) The new checked status.
  259.  */
  260.     toggleCheck : function(value){
  261.         var cb = this.checkbox;
  262.         if(cb){
  263.             cb.checked = (value === undefined ? !cb.checked : value);
  264.             this.onCheckChange();
  265.         }
  266.     },
  267.     // private
  268.     blur : function(){
  269.         try{
  270.             this.anchor.blur();
  271.         }catch(e){} 
  272.     },
  273.     // private
  274.     animExpand : function(callback){
  275.         var ct = Ext.get(this.ctNode);
  276.         ct.stopFx();
  277.         if(!this.node.isExpandable()){
  278.             this.updateExpandIcon();
  279.             this.ctNode.style.display = "";
  280.             Ext.callback(callback);
  281.             return;
  282.         }
  283.         this.animating = true;
  284.         this.updateExpandIcon();
  285.         
  286.         ct.slideIn('t', {
  287.            callback : function(){
  288.                this.animating = false;
  289.                Ext.callback(callback);
  290.             },
  291.             scope: this,
  292.             duration: this.node.ownerTree.duration || .25
  293.         });
  294.     },
  295.     // private
  296.     highlight : function(){
  297.         var tree = this.node.getOwnerTree();
  298.         Ext.fly(this.wrap).highlight(
  299.             tree.hlColor || "C3DAF9",
  300.             {endColor: tree.hlBaseColor}
  301.         );
  302.     },
  303.     // private
  304.     collapse : function(){
  305.         this.updateExpandIcon();
  306.         this.ctNode.style.display = "none";
  307.     },
  308.     // private
  309.     animCollapse : function(callback){
  310.         var ct = Ext.get(this.ctNode);
  311.         ct.enableDisplayMode('block');
  312.         ct.stopFx();
  313.         this.animating = true;
  314.         this.updateExpandIcon();
  315.         ct.slideOut('t', {
  316.             callback : function(){
  317.                this.animating = false;
  318.                Ext.callback(callback);
  319.             },
  320.             scope: this,
  321.             duration: this.node.ownerTree.duration || .25
  322.         });
  323.     },
  324.     // private
  325.     getContainer : function(){
  326.         return this.ctNode;  
  327.     },
  328. /**
  329.  * Returns the element which encapsulates this node.
  330.  * @return {HtmlElement} The DOM element. The default implementation uses a <code>&lt;li></code>.
  331.  */
  332.     getEl : function(){
  333.         return this.wrap;  
  334.     },
  335.     // private
  336.     appendDDGhost : function(ghostNode){
  337.         ghostNode.appendChild(this.elNode.cloneNode(true));
  338.     },
  339.     // private
  340.     getDDRepairXY : function(){
  341.         return Ext.lib.Dom.getXY(this.iconNode);
  342.     },
  343.     // private
  344.     onRender : function(){
  345.         this.render();    
  346.     },
  347.     // private
  348.     render : function(bulkRender){
  349.         var n = this.node, a = n.attributes;
  350.         var targetNode = n.parentNode ? 
  351.               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
  352.         
  353.         if(!this.rendered){
  354.             this.rendered = true;
  355.             this.renderElements(n, a, targetNode, bulkRender);
  356.             if(a.qtip){
  357.                if(this.textNode.setAttributeNS){
  358.                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
  359.                    if(a.qtipTitle){
  360.                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
  361.                    }
  362.                }else{
  363.                    this.textNode.setAttribute("ext:qtip", a.qtip);
  364.                    if(a.qtipTitle){
  365.                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
  366.                    }
  367.                } 
  368.             }else if(a.qtipCfg){
  369.                 a.qtipCfg.target = Ext.id(this.textNode);
  370.                 Ext.QuickTips.register(a.qtipCfg);
  371.             }
  372.             this.initEvents();
  373.             if(!this.node.expanded){
  374.                 this.updateExpandIcon(true);
  375.             }
  376.         }else{
  377.             if(bulkRender === true) {
  378.                 targetNode.appendChild(this.wrap);
  379.             }
  380.         }
  381.     },
  382.     // private
  383.     renderElements : function(n, a, targetNode, bulkRender){
  384.         // add some indent caching, this helps performance when rendering a large tree
  385.         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
  386.         var cb = Ext.isBoolean(a.checked),
  387.             nel,
  388.             href = a.href ? a.href : Ext.isGecko ? "" : "#",
  389.             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">',
  390.             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
  391.             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
  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" class="x-tree-node-anchor" href="',href,'" tabIndex="1" ',
  395.              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '><span unselectable="on">',n.text,"</span></a></div>",
  396.             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
  397.             "</li>"].join('');
  398.         if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
  399.             this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
  400.         }else{
  401.             this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
  402.         }
  403.         
  404.         this.elNode = this.wrap.childNodes[0];
  405.         this.ctNode = this.wrap.childNodes[1];
  406.         var cs = this.elNode.childNodes;
  407.         this.indentNode = cs[0];
  408.         this.ecNode = cs[1];
  409.         this.iconNode = cs[2];
  410.         var index = 3;
  411.         if(cb){
  412.             this.checkbox = cs[3];
  413.             // fix for IE6
  414.             this.checkbox.defaultChecked = this.checkbox.checked;
  415.             index++;
  416.         }
  417.         this.anchor = cs[index];
  418.         this.textNode = cs[index].firstChild;
  419.     },
  420. /**
  421.  * Returns the &lt;a> element that provides focus for the node's UI.
  422.  * @return {HtmlElement} The DOM anchor element.
  423.  */
  424.     getAnchor : function(){
  425.         return this.anchor;
  426.     },
  427.     
  428. /**
  429.  * Returns the text node.
  430.  * @return {HtmlNode} The DOM text node.
  431.  */
  432.     getTextEl : function(){
  433.         return this.textNode;
  434.     },
  435.     
  436. /**
  437.  * Returns the icon &lt;img> element.
  438.  * @return {HtmlElement} The DOM image element.
  439.  */
  440.     getIconEl : function(){
  441.         return this.iconNode;
  442.     },
  443. /**
  444.  * Returns the checked status of the node. If the node was rendered with no
  445.  * checkbox, it returns false.
  446.  * @return {Boolean} The checked flag.
  447.  */
  448.     isChecked : function(){
  449.         return this.checkbox ? this.checkbox.checked : false; 
  450.     },
  451.     // private
  452.     updateExpandIcon : function(){
  453.         if(this.rendered){
  454.             var n = this.node, 
  455.                 c1, 
  456.                 c2,
  457.                 cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow",
  458.                 hasChild = n.hasChildNodes();
  459.             if(hasChild || n.attributes.expandable){
  460.                 if(n.expanded){
  461.                     cls += "-minus";
  462.                     c1 = "x-tree-node-collapsed";
  463.                     c2 = "x-tree-node-expanded";
  464.                 }else{
  465.                     cls += "-plus";
  466.                     c1 = "x-tree-node-expanded";
  467.                     c2 = "x-tree-node-collapsed";
  468.                 }
  469.                 if(this.wasLeaf){
  470.                     this.removeClass("x-tree-node-leaf");
  471.                     this.wasLeaf = false;
  472.                 }
  473.                 if(this.c1 != c1 || this.c2 != c2){
  474.                     Ext.fly(this.elNode).replaceClass(c1, c2);
  475.                     this.c1 = c1; this.c2 = c2;
  476.                 }
  477.             }else{
  478.                 if(!this.wasLeaf){
  479.                     Ext.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
  480.                     delete this.c1;
  481.                     delete this.c2;
  482.                     this.wasLeaf = true;
  483.                 }
  484.             }
  485.             var ecc = "x-tree-ec-icon "+cls;
  486.             if(this.ecc != ecc){
  487.                 this.ecNode.className = ecc;
  488.                 this.ecc = ecc;
  489.             }
  490.         }
  491.     },
  492.     
  493.     // private
  494.     onIdChange: function(id){
  495.         if(this.rendered){
  496.             this.elNode.setAttribute('ext:tree-node-id', id);
  497.         }
  498.     },
  499.     // private
  500.     getChildIndent : function(){
  501.         if(!this.childIndent){
  502.             var buf = [],
  503.                 p = this.node;
  504.             while(p){
  505.                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
  506.                     if(!p.isLast()) {
  507.                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
  508.                     } else {
  509.                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
  510.                     }
  511.                 }
  512.                 p = p.parentNode;
  513.             }
  514.             this.childIndent = buf.join("");
  515.         }
  516.         return this.childIndent;
  517.     },
  518.     // private
  519.     renderIndent : function(){
  520.         if(this.rendered){
  521.             var indent = "",
  522.                 p = this.node.parentNode;
  523.             if(p){
  524.                 indent = p.ui.getChildIndent();
  525.             }
  526.             if(this.indentMarkup != indent){ // don't rerender if not required
  527.                 this.indentNode.innerHTML = indent;
  528.                 this.indentMarkup = indent;
  529.             }
  530.             this.updateExpandIcon();
  531.         }
  532.     },
  533.     destroy : function(){
  534.         if(this.elNode){
  535.             Ext.dd.Registry.unregister(this.elNode.id);
  536.         }
  537.         
  538.         Ext.each(['textnode', 'anchor', 'checkbox', 'indentNode', 'ecNode', 'iconNode', 'elNode', 'ctNode', 'wrap', 'holder'], function(el){
  539.             if(this[el]){
  540.                 Ext.fly(this[el]).remove();
  541.                 delete this[el];
  542.             }
  543.         }, this);
  544.         delete this.node;
  545.     }
  546. };
  547. /**
  548.  * @class Ext.tree.RootTreeNodeUI
  549.  * This class provides the default UI implementation for <b>root</b> Ext TreeNodes.
  550.  * The RootTreeNode UI implementation allows customizing the appearance of the root tree node.<br>
  551.  * <p>
  552.  * If you are customizing the Tree's user interface, you
  553.  * may need to extend this class, but you should never need to instantiate this class.<br>
  554.  */
  555. Ext.tree.RootTreeNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
  556.     // private
  557.     render : function(){
  558.         if(!this.rendered){
  559.             var targetNode = this.node.ownerTree.innerCt.dom;
  560.             this.node.expanded = true;
  561.             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
  562.             this.wrap = this.ctNode = targetNode.firstChild;
  563.         }
  564.     },
  565.     collapse : Ext.emptyFn,
  566.     expand : Ext.emptyFn
  567. });