/* * Based on: * Ext JS Library 1.1.1 * Copyright(c) 2006-2007, Ext JS, LLC. * * Originally Released Under LGPL - original licence link has changed is not relivant. * * Fork - LGPL * <script type="text/javascript"> */ /** * @class Roo.tree.TreeNode * @extends Roo.data.Node * @cfg {String} text The text for this node * @cfg {Boolean} expanded true to start the node expanded * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true) * @cfg {Boolean} allowDrop false if this node cannot be drop on * @cfg {Boolean} disabled true to start the node disabled * @cfg {String} icon The path to an icon for the node. The preferred way to do this * is to use the cls or iconCls attributes and add the icon via a CSS background image. * @cfg {String} cls A css class to be added to the node * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images * @cfg {String} href URL of the link used for the node (defaults to #) * @cfg {String} hrefTarget target frame for the link * @cfg {String} qtip An Ext QuickTip for the node * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip) * @cfg {Boolean} singleClickExpand True for single click expand on this node * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI) * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox * (defaults to undefined with no checkbox rendered) * @constructor * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node */ Roo.tree.TreeNode = function(attributes){ attributes = attributes || {}; if(typeof attributes == "string"){ attributes = {text: attributes}; } this.childrenRendered = false; this.rendered = false; Roo.tree.TreeNode.superclass.constructor.call(this, attributes); this.expanded = attributes.expanded === true; this.isTarget = attributes.isTarget !== false; this.draggable = attributes.draggable !== false && attributes.allowDrag !== false; this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false; /** * Read-only. The text for this node. To change it use setText(). * @type String */ this.text = attributes.text; /** * True if this node is disabled. * @type Boolean */ this.disabled = attributes.disabled === true; this.addEvents({ /** * @event textchange * Fires when the text for this node is changed * @param {Node} this This node * @param {String} text The new text * @param {String} oldText The old text */ "textchange" : true, /** * @event beforeexpand * Fires before this node is expanded, return false to cancel. * @param {Node} this This node * @param {Boolean} deep * @param {Boolean} anim */ "beforeexpand" : true, /** * @event beforecollapse * Fires before this node is collapsed, return false to cancel. * @param {Node} this This node * @param {Boolean} deep * @param {Boolean} anim */ "beforecollapse" : true, /** * @event expand * Fires when this node is expanded * @param {Node} this This node */ "expand" : true, /** * @event disabledchange * Fires when the disabled status of this node changes * @param {Node} this This node * @param {Boolean} disabled */ "disabledchange" : true, /** * @event collapse * Fires when this node is collapsed * @param {Node} this This node */ "collapse" : true, /** * @event beforeclick * Fires before click processing. Return false to cancel the default action. * @param {Node} this This node * @param {Roo.EventObject} e The event object */ "beforeclick":true, /** * @event checkchange * Fires when a node with a checkbox's checked property changes * @param {Node} this This node * @param {Boolean} checked */ "checkchange":true, /** * @event click * Fires when this node is clicked * @param {Node} this This node * @param {Roo.EventObject} e The event object */ "click":true, /** * @event dblclick * Fires when this node is double clicked * @param {Node} this This node * @param {Roo.EventObject} e The event object */ "dblclick":true, /** * @event contextmenu * Fires when this node is right clicked * @param {Node} this This node * @param {Roo.EventObject} e The event object */ "contextmenu":true, /** * @event beforechildrenrendered * Fires right before the child nodes for this node are rendered * @param {Node} this This node */ "beforechildrenrendered":true }); var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI; /** * Read-only. The UI for this node * @type TreeNodeUI */ this.ui = new uiClass(this); // finally support items[] if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) { return; } Roo.each(this.attributes.items, function(c) { this.appendChild(Roo.factory(c,Roo.Tree)); }, this); delete this.attributes.items; }; Roo.extend(Roo.tree.TreeNode, Roo.data.Node, { preventHScroll: true, /** * Returns true if this node is expanded * @return {Boolean} */ isExpanded : function(){ return this.expanded; }, /** * Returns the UI object for this node * @return {TreeNodeUI} */ getUI : function(){ return this.ui; }, // private override setFirstChild : function(node){ var of = this.firstChild; Roo.tree.TreeNode.superclass.setFirstChild.call(this, node); if(this.childrenRendered && of && node != of){ of.renderIndent(true, true); } if(this.rendered){ this.renderIndent(true, true); } }, // private override setLastChild : function(node){ var ol = this.lastChild; Roo.tree.TreeNode.superclass.setLastChild.call(this, node); if(this.childrenRendered && ol && node != ol){ ol.renderIndent(true, true); } if(this.rendered){ this.renderIndent(true, true); } }, // these methods are overridden to provide lazy rendering support // private override appendChild : function() { var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments); if(node && this.childrenRendered){ node.render(); } this.ui.updateExpandIcon(); return node; }, // private override removeChild : function(node){ this.ownerTree.getSelectionModel().unselect(node); Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments); // if it's been rendered remove dom node if(this.childrenRendered){ node.ui.remove(); } if(this.childNodes.length < 1){ this.collapse(false, false); }else{ this.ui.updateExpandIcon(); } if(!this.firstChild) { this.childrenRendered = false; } return node; }, // private override insertBefore : function(node, refNode){ var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments); if(newNode && refNode && this.childrenRendered){ node.render(); } this.ui.updateExpandIcon(); return newNode; }, /** * Sets the text for this node * @param {String} text */ setText : function(text){ var oldText = this.text; this.text = text; this.attributes.text = text; if(this.rendered){ // event without subscribing this.ui.onTextChange(this, text, oldText); } this.fireEvent("textchange", this, text, oldText); }, /** * Triggers selection of this node */ select : function(){ this.getOwnerTree().getSelectionModel().select(this); }, /** * Triggers deselection of this node */ unselect : function(){ this.getOwnerTree().getSelectionModel().unselect(this); }, /** * Returns true if this node is selected * @return {Boolean} */ isSelected : function(){ return this.getOwnerTree().getSelectionModel().isSelected(this); }, /** * Expand this node. * @param {Boolean} deep (optional) True to expand all children as well * @param {Boolean} anim (optional) false to cancel the default animation * @param {Function} callback (optional) A callback to be called when * expanding this node completes (does not wait for deep expand to complete). * Called with 1 parameter, this node. */ expand : function(deep, anim, callback){ if(!this.expanded){ if(this.fireEvent("beforeexpand", this, deep, anim) === false){ return; } if(!this.childrenRendered){ this.renderChildren(); } this.expanded = true; if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){ this.ui.animExpand(function(){ this.fireEvent("expand", this); if(typeof callback == "function"){ callback(this); } if(deep === true){ this.expandChildNodes(true); } }.createDelegate(this)); return; }else{ this.ui.expand(); this.fireEvent("expand", this); if(typeof callback == "function"){ callback(this); } } }else{ if(typeof callback == "function"){ callback(this); } } if(deep === true){ this.expandChildNodes(true); } }, isHiddenRoot : function(){ return this.isRoot && !this.getOwnerTree().rootVisible; }, /** * Collapse this node. * @param {Boolean} deep (optional) True to collapse all children as well * @param {Boolean} anim (optional) false to cancel the default animation */ collapse : function(deep, anim){ if(this.expanded && !this.isHiddenRoot()){ if(this.fireEvent("beforecollapse", this, deep, anim) === false){ return; } this.expanded = false; if((this.getOwnerTree().animate && anim !== false) || anim){ this.ui.animCollapse(function(){ this.fireEvent("collapse", this); if(deep === true){ this.collapseChildNodes(true); } }.createDelegate(this)); return; }else{ this.ui.collapse(); this.fireEvent("collapse", this); } } if(deep === true){ var cs = this.childNodes; for(var i = 0, len = cs.length; i < len; i++) { cs[i].collapse(true, false); } } }, // private delayedExpand : function(delay){ if(!this.expandProcId){ this.expandProcId = this.expand.defer(delay, this); } }, // private cancelExpand : function(){ if(this.expandProcId){ clearTimeout(this.expandProcId); } this.expandProcId = false; }, /** * Toggles expanded/collapsed state of the node */ toggle : function(){ if(this.expanded){ this.collapse(); }else{ this.expand(); } }, /** * Ensures all parent nodes are expanded */ ensureVisible : function(callback){ var tree = this.getOwnerTree(); tree.expandPath(this.parentNode.getPath(), false, function(){ tree.getTreeEl().scrollChildIntoView(this.ui.anchor); Roo.callback(callback); }.createDelegate(this)); }, /** * Expand all child nodes * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes */ expandChildNodes : function(deep){ var cs = this.childNodes; for(var i = 0, len = cs.length; i < len; i++) { cs[i].expand(deep); } }, /** * Collapse all child nodes * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes */ collapseChildNodes : function(deep){ var cs = this.childNodes; for(var i = 0, len = cs.length; i < len; i++) { cs[i].collapse(deep); } }, /** * Disables this node */ disable : function(){ this.disabled = true; this.unselect(); if(this.rendered && this.ui.onDisableChange){ // event without subscribing this.ui.onDisableChange(this, true); } this.fireEvent("disabledchange", this, true); }, /** * Enables this node */ enable : function(){ this.disabled = false; if(this.rendered && this.ui.onDisableChange){ // event without subscribing this.ui.onDisableChange(this, false); } this.fireEvent("disabledchange", this, false); }, // private renderChildren : function(suppressEvent){ if(suppressEvent !== false){ this.fireEvent("beforechildrenrendered", this); } var cs = this.childNodes; for(var i = 0, len = cs.length; i < len; i++){ cs[i].render(true); } this.childrenRendered = true; }, // private sort : function(fn, scope){ Roo.tree.TreeNode.superclass.sort.apply(this, arguments); if(this.childrenRendered){ var cs = this.childNodes; for(var i = 0, len = cs.length; i < len; i++){ cs[i].render(true); } } }, // private render : function(bulkRender){ this.ui.render(bulkRender); if(!this.rendered){ this.rendered = true; if(this.expanded){ this.expanded = false; this.expand(false, false); } } }, // private renderIndent : function(deep, refresh){ if(refresh){ this.ui.childIndent = null; } this.ui.renderIndent(); if(deep === true && this.childrenRendered){ var cs = this.childNodes; for(var i = 0, len = cs.length; i < len; i++){ cs[i].renderIndent(true, refresh); } } } });