<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js"><span id='Ext-tree-Panel'>/**
|
</span> * The TreePanel provides tree-structured UI representation of tree-structured data.
|
* A TreePanel must be bound to a {@link Ext.data.TreeStore}. TreePanel's support
|
* multiple columns through the {@link #columns} configuration.
|
*
|
* Simple TreePanel using inline data:
|
*
|
* @example
|
* var store = Ext.create('Ext.data.TreeStore', {
|
* root: {
|
* expanded: true,
|
* children: [
|
* { text: "detention", leaf: true },
|
* { text: "homework", expanded: true, children: [
|
* { text: "book report", leaf: true },
|
* { text: "algebra", leaf: true}
|
* ] },
|
* { text: "buy lottery tickets", leaf: true }
|
* ]
|
* }
|
* });
|
*
|
* Ext.create('Ext.tree.Panel', {
|
* title: 'Simple Tree',
|
* width: 200,
|
* height: 150,
|
* store: store,
|
* rootVisible: false,
|
* renderTo: Ext.getBody()
|
* });
|
*
|
* For the tree node config options (like `text`, `leaf`, `expanded`), see the documentation of
|
* {@link Ext.data.NodeInterface NodeInterface} config options.
|
*/
|
Ext.define('Ext.tree.Panel', {
|
extend: 'Ext.panel.Table',
|
alias: 'widget.treepanel',
|
alternateClassName: ['Ext.tree.TreePanel', 'Ext.TreePanel'],
|
requires: ['Ext.tree.View', 'Ext.selection.TreeModel', 'Ext.tree.Column', 'Ext.data.TreeStore'],
|
<span id='Ext-tree-Panel-cfg-viewType'> viewType: 'treeview',
|
</span><span id='Ext-tree-Panel-cfg-selType'> selType: 'treemodel',
|
</span>
|
<span id='Ext-tree-Panel-property-treeCls'> treeCls: Ext.baseCSSPrefix + 'tree-panel',
|
</span>
|
<span id='Ext-tree-Panel-cfg-deferRowRender'> deferRowRender: false,
|
</span>
|
<span id='Ext-tree-Panel-cfg-rowLines'> /**
|
</span> * @cfg {Boolean} rowLines
|
* False so that rows are not separated by lines.
|
*/
|
rowLines: false,
|
|
<span id='Ext-tree-Panel-cfg-lines'> /**
|
</span> * @cfg {Boolean} lines
|
* False to disable tree lines.
|
*/
|
lines: true,
|
|
<span id='Ext-tree-Panel-cfg-useArrows'> /**
|
</span> * @cfg {Boolean} useArrows
|
* True to use Vista-style arrows in the tree.
|
*/
|
useArrows: false,
|
|
<span id='Ext-tree-Panel-cfg-singleExpand'> /**
|
</span> * @cfg {Boolean} singleExpand
|
* True if only 1 node per branch may be expanded.
|
*/
|
singleExpand: false,
|
|
<span id='Ext-tree-Panel-property-ddConfig'> ddConfig: {
|
</span> enableDrag: true,
|
enableDrop: true
|
},
|
|
<span id='Ext-tree-Panel-cfg-animate'> /**
|
</span> * @cfg {Boolean} animate
|
* True to enable animated expand/collapse. Defaults to the value of {@link Ext#enableFx}.
|
*/
|
|
<span id='Ext-tree-Panel-cfg-rootVisible'> /**
|
</span> * @cfg {Boolean} rootVisible
|
* False to hide the root node.
|
*/
|
rootVisible: true,
|
|
<span id='Ext-tree-Panel-cfg-displayField'> /**
|
</span> * @cfg {String} displayField
|
* The field inside the model that will be used as the node's text.
|
*/
|
displayField: 'text',
|
|
<span id='Ext-tree-Panel-cfg-root'> /**
|
</span> * @cfg {Ext.data.Model/Ext.data.NodeInterface/Object} root
|
* Allows you to not specify a store on this TreePanel. This is useful for creating a simple tree with preloaded
|
* data without having to specify a TreeStore and Model. A store and model will be created and root will be passed
|
* to that store. For example:
|
*
|
* Ext.create('Ext.tree.Panel', {
|
* title: 'Simple Tree',
|
* root: {
|
* text: "Root node",
|
* expanded: true,
|
* children: [
|
* { text: "Child 1", leaf: true },
|
* { text: "Child 2", leaf: true }
|
* ]
|
* },
|
* renderTo: Ext.getBody()
|
* });
|
*/
|
root: null,
|
|
<span id='Ext-tree-Panel-property-normalCfgCopy'> // Required for the Lockable Mixin. These are the configurations which will be copied to the
|
</span> // normal and locked sub tablepanels
|
normalCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible', 'scroll'],
|
<span id='Ext-tree-Panel-property-lockedCfgCopy'> lockedCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible'],
|
</span>
|
<span id='Ext-tree-Panel-property-isTree'> isTree: true,
|
</span>
|
<span id='Ext-tree-Panel-cfg-hideHeaders'> /**
|
</span> * @cfg {Boolean} hideHeaders
|
* True to hide the headers.
|
*/
|
|
<span id='Ext-tree-Panel-cfg-folderSort'> /**
|
</span> * @cfg {Boolean} folderSort
|
* True to automatically prepend a leaf sorter to the store.
|
*/
|
|
<span id='Ext-tree-Panel-cfg-store'> /**
|
</span> * @cfg {Ext.data.TreeStore} store (required)
|
* The {@link Ext.data.TreeStore Store} the tree should use as its data source.
|
*/
|
|
arrowCls: Ext.baseCSSPrefix + 'tree-arrows',
|
<span id='Ext-tree-Panel-property-linesCls'> linesCls: Ext.baseCSSPrefix + 'tree-lines',
|
</span><span id='Ext-tree-Panel-property-noLinesCls'> noLinesCls: Ext.baseCSSPrefix + 'tree-no-lines',
|
</span><span id='Ext-tree-Panel-property-autoWidthCls'> autoWidthCls: Ext.baseCSSPrefix + 'autowidth-table',
|
</span>
|
<span id='Ext-tree-Panel-method-constructor'> constructor: function(config) {
|
</span> config = config || {};
|
if (config.animate === undefined) {
|
config.animate = Ext.isBoolean(this.animate) ? this.animate : Ext.enableFx;
|
}
|
this.enableAnimations = config.animate;
|
delete config.animate;
|
|
this.callParent([config]);
|
},
|
|
<span id='Ext-tree-Panel-method-initComponent'> initComponent: function() {
|
</span> var me = this,
|
cls = [me.treeCls],
|
store = me.store,
|
view;
|
|
if (me.useArrows) {
|
cls.push(me.arrowCls);
|
me.lines = false;
|
}
|
|
if (me.lines) {
|
cls.push(me.linesCls);
|
} else if (!me.useArrows) {
|
cls.push(me.noLinesCls);
|
}
|
|
if (Ext.isString(store)) {
|
store = me.store = Ext.StoreMgr.lookup(store);
|
} else if (!store || Ext.isObject(store) && !store.isStore) {
|
store = me.store = new Ext.data.TreeStore(Ext.apply({
|
root: me.root,
|
fields: me.fields,
|
model: me.model,
|
folderSort: me.folderSort
|
}, store));
|
} else if (me.root) {
|
store = me.store = Ext.data.StoreManager.lookup(store);
|
store.setRootNode(me.root);
|
if (me.folderSort !== undefined) {
|
store.folderSort = me.folderSort;
|
store.sort();
|
}
|
}
|
|
// I'm not sure if we want to this. It might be confusing
|
// if (me.initialConfig.rootVisible === undefined && !me.getRootNode()) {
|
// me.rootVisible = false;
|
// }
|
|
me.viewConfig = Ext.apply({
|
rootVisible: me.rootVisible,
|
animate: me.enableAnimations,
|
singleExpand: me.singleExpand,
|
node: store.getRootNode(),
|
hideHeaders: me.hideHeaders
|
}, me.viewConfig);
|
|
// If the user specifies the headers collection manually then dont inject our own
|
if (!me.columns) {
|
if (me.initialConfig.hideHeaders === undefined) {
|
me.hideHeaders = true;
|
}
|
me.addCls(me.autoWidthCls);
|
me.columns = [{
|
xtype : 'treecolumn',
|
text : 'Name',
|
width : Ext.isIE6 ? '100%' : 10000, // IE6 needs width:100%
|
dataIndex: me.displayField
|
}];
|
}
|
|
if (me.cls) {
|
cls.push(me.cls);
|
}
|
me.cls = cls.join(' ');
|
|
me.callParent();
|
|
// TreeModel has to know about the TreeStore so that pruneRemoved can work properly upon removal
|
// of nodes.
|
me.selModel.treeStore = me.store;
|
|
view = me.getView();
|
|
// Relay events from the TreeView.
|
// An injected LockingView relays events from its locked side's View
|
me.relayEvents(view, [
|
<span id='Ext-tree-Panel-event-checkchange'> /**
|
</span> * @event checkchange
|
* Fires when a node with a checkbox's checked property changes
|
* @param {Ext.data.NodeInterface} node The node who's checked property was changed
|
* @param {Boolean} checked The node's new checked state
|
*/
|
'checkchange',
|
<span id='Ext-tree-Panel-event-afteritemexpand'> /**
|
</span> * @event afteritemexpand
|
* @inheritdoc Ext.tree.View#afteritemexpand
|
*/
|
'afteritemexpand',
|
<span id='Ext-tree-Panel-event-afteritemcollapse'> /**
|
</span> * @event afteritemcollapse
|
* @inheritdoc Ext.tree.View#afteritemcollapse
|
*/
|
'afteritemcollapse'
|
]);
|
|
// If there has been a LockingView injected, this processing will be performed by the locked TreePanel
|
if (!view.isLockingView) {
|
// If the root is not visible and there is no rootnode defined, then just lets load the store
|
if (!view.rootVisible && !me.getRootNode()) {
|
me.setRootNode({
|
expanded: true
|
});
|
}
|
}
|
},
|
|
<span id='Ext-tree-Panel-method-bindStore'> // @private
|
</span> // Hook into the TreeStore.
|
// Do not callParent in TreePanel's bindStore
|
// The TreeStore is only relevant to the tree - the View has its own NodeStore
|
bindStore: function(store, initial) {
|
var me = this;
|
|
me.store = store;
|
|
// Connect to store. Return a Destroyable object
|
me.storeListeners = me.mon(store, {
|
destroyable: true,
|
load: me.onStoreLoad,
|
rootchange: me.onRootChange,
|
clear: me.onClear,
|
scope: me
|
});
|
|
// Relay store events. relayEvents always returns a Destroyable object.
|
me.storeRelayers = me.relayEvents(store, [
|
<span id='Ext-tree-Panel-event-beforeload'> /**
|
</span> * @event beforeload
|
* @inheritdoc Ext.data.TreeStore#beforeload
|
*/
|
'beforeload',
|
|
<span id='Ext-tree-Panel-event-load'> /**
|
</span> * @event load
|
* @inheritdoc Ext.data.TreeStore#load
|
*/
|
'load'
|
]);
|
|
// Relay store events with prefix. Return a Destroyable object
|
me.storeRelayers1 = me.mon(store, {
|
destroyable: true,
|
|
<span id='Ext-tree-Panel-event-itemappend'> /**
|
</span> * @event itemappend
|
* @inheritdoc Ext.data.TreeStore#append
|
*/
|
append: me.createRelayer('itemappend'),
|
|
<span id='Ext-tree-Panel-event-itemremove'> /**
|
</span> * @event itemremove
|
* @inheritdoc Ext.data.TreeStore#remove
|
*/
|
remove: me.createRelayer('itemremove'),
|
|
<span id='Ext-tree-Panel-event-itemmove'> /**
|
</span> * @event itemmove
|
* @inheritdoc Ext.data.TreeStore#move
|
*/
|
move: me.createRelayer('itemmove', [0, 4]),
|
|
<span id='Ext-tree-Panel-event-iteminsert'> /**
|
</span> * @event iteminsert
|
* @inheritdoc Ext.data.TreeStore#insert
|
*/
|
insert: me.createRelayer('iteminsert'),
|
|
<span id='Ext-tree-Panel-event-beforeitemappend'> /**
|
</span> * @event beforeitemappend
|
* @inheritdoc Ext.data.TreeStore#beforeappend
|
*/
|
beforeappend: me.createRelayer('beforeitemappend'),
|
|
<span id='Ext-tree-Panel-event-beforeitemremove'> /**
|
</span> * @event beforeitemremove
|
* @inheritdoc Ext.data.TreeStore#beforeremove
|
*/
|
beforeremove: me.createRelayer('beforeitemremove'),
|
|
<span id='Ext-tree-Panel-event-beforeitemmove'> /**
|
</span> * @event beforeitemmove
|
* @inheritdoc Ext.data.TreeStore#beforemove
|
*/
|
beforemove: me.createRelayer('beforeitemmove'),
|
|
<span id='Ext-tree-Panel-event-beforeiteminsert'> /**
|
</span> * @event beforeiteminsert
|
* @inheritdoc Ext.data.TreeStore#beforeinsert
|
*/
|
beforeinsert: me.createRelayer('beforeiteminsert'),
|
|
<span id='Ext-tree-Panel-event-itemexpand'> /**
|
</span> * @event itemexpand
|
* @inheritdoc Ext.data.TreeStore#expand
|
*/
|
expand: me.createRelayer('itemexpand', [0, 1]),
|
|
<span id='Ext-tree-Panel-event-itemcollapse'> /**
|
</span> * @event itemcollapse
|
* @inheritdoc Ext.data.TreeStore#collapse
|
*/
|
collapse: me.createRelayer('itemcollapse', [0, 1]),
|
|
<span id='Ext-tree-Panel-event-beforeitemexpand'> /**
|
</span> * @event beforeitemexpand
|
* @inheritdoc Ext.data.TreeStore#beforeexpand
|
*/
|
beforeexpand: me.createRelayer('beforeitemexpand', [0, 1]),
|
|
<span id='Ext-tree-Panel-event-beforeitemcollapse'> /**
|
</span> * @event beforeitemcollapse
|
* @inheritdoc Ext.data.TreeStore#beforecollapse
|
*/
|
beforecollapse: me.createRelayer('beforeitemcollapse', [0, 1])
|
});
|
|
// TreeStore must have an upward link to the TreePanel so that nodes can find their owning tree in NodeInterface.getOwnerTree
|
store.ownerTree = me;
|
|
if (!initial) {
|
me.view.setRootNode(me.getRootNode());
|
}
|
},
|
|
<span id='Ext-tree-Panel-method-unbindStore'> // @private
|
</span> // TODO: Decide whether it is possible to reconfigure a TreePanel.
|
unbindStore: function() {
|
var me = this,
|
store = me.store;
|
|
if (store) {
|
Ext.destroy(me.storeListeners, me.storeRelayers, me.storeRelayers1);
|
delete store.ownerTree;
|
}
|
},
|
|
<span id='Ext-tree-Panel-method-onClear'> onClear: function(){
|
</span> this.view.onClear();
|
},
|
|
<span id='Ext-tree-Panel-method-setRootNode'> /**
|
</span> * Sets root node of this tree.
|
* @param {Ext.data.Model/Ext.data.NodeInterface/Object} root
|
* @return {Ext.data.NodeInterface} The new root
|
*/
|
setRootNode: function() {
|
return this.store.setRootNode.apply(this.store, arguments);
|
},
|
|
<span id='Ext-tree-Panel-method-getRootNode'> /**
|
</span> * Returns the root node for this tree.
|
* @return {Ext.data.NodeInterface}
|
*/
|
getRootNode: function() {
|
return this.store.getRootNode();
|
},
|
|
<span id='Ext-tree-Panel-method-onRootChange'> onRootChange: function(root) {
|
</span> this.view.setRootNode(root);
|
},
|
|
<span id='Ext-tree-Panel-method-getChecked'> /**
|
</span> * Retrieve an array of checked records.
|
* @return {Ext.data.NodeInterface[]} An array containing the checked records
|
*/
|
getChecked: function() {
|
return this.getView().getChecked();
|
},
|
|
<span id='Ext-tree-Panel-method-isItemChecked'> isItemChecked: function(rec) {
|
</span> return rec.get('checked');
|
},
|
|
<span id='Ext-tree-Panel-method-expandNode'> /**
|
</span> * Expands a record that is loaded in the tree.
|
* @param {Ext.data.Model} record The record to expand
|
* @param {Boolean} [deep] True to expand nodes all the way down the tree hierarchy.
|
* @param {Function} [callback] The function to run after the expand is completed
|
* @param {Object} [scope] The scope of the callback function.
|
*/
|
expandNode: function(record, deep, callback, scope) {
|
return this.getView().expand(record, deep, callback, scope || this);
|
},
|
|
<span id='Ext-tree-Panel-method-collapseNode'> /**
|
</span> * Collapses a record that is loaded in the tree.
|
* @param {Ext.data.Model} record The record to collapse
|
* @param {Boolean} [deep] True to collapse nodes all the way up the tree hierarchy.
|
* @param {Function} [callback] The function to run after the collapse is completed
|
* @param {Object} [scope] The scope of the callback function.
|
*/
|
collapseNode: function(record, deep, callback, scope) {
|
return this.getView().collapse(record, deep, callback, scope || this);
|
},
|
|
<span id='Ext-tree-Panel-method-expandAll'> /**
|
</span> * Expand all nodes
|
* @param {Function} [callback] A function to execute when the expand finishes.
|
* @param {Object} [scope] The scope of the callback function
|
*/
|
expandAll : function(callback, scope) {
|
var me = this,
|
root = me.getRootNode(),
|
animate = me.enableAnimations;
|
if (root) {
|
if (!animate) {
|
Ext.suspendLayouts();
|
}
|
root.expand(true, callback, scope || me);
|
if (!animate) {
|
Ext.resumeLayouts(true);
|
}
|
}
|
},
|
|
<span id='Ext-tree-Panel-method-collapseAll'> /**
|
</span> * Collapse all nodes
|
* @param {Function} [callback] A function to execute when the collapse finishes.
|
* @param {Object} [scope] The scope of the callback function
|
*/
|
collapseAll : function(callback, scope) {
|
var me = this,
|
root = me.getRootNode(),
|
animate = me.enableAnimations,
|
view = me.getView();
|
|
if (root) {
|
if (!animate) {
|
Ext.suspendLayouts();
|
}
|
scope = scope || me;
|
if (view.rootVisible) {
|
root.collapse(true, callback, scope);
|
} else {
|
root.collapseChildren(true, callback, scope);
|
}
|
if (!animate) {
|
Ext.resumeLayouts(true);
|
}
|
}
|
},
|
|
<span id='Ext-tree-Panel-method-expandPath'> /**
|
</span> * Expand the tree to the path of a particular node.
|
* @param {String} path The path to expand. The path should include a leading separator.
|
* @param {String} [field] The field to get the data from. Defaults to the model idProperty.
|
* @param {String} [separator='/'] A separator to use.
|
* @param {Function} [callback] A function to execute when the expand finishes. The callback will be called with
|
* (success, lastNode) where success is if the expand was successful and lastNode is the last node that was expanded.
|
* @param {Object} [scope] The scope of the callback function
|
*/
|
expandPath: function(path, field, separator, callback, scope) {
|
var me = this,
|
current = me.getRootNode(),
|
index = 1,
|
view = me.getView(),
|
keys,
|
expander;
|
|
field = field || me.getRootNode().idProperty;
|
separator = separator || '/';
|
|
if (Ext.isEmpty(path)) {
|
Ext.callback(callback, scope || me, [false, null]);
|
return;
|
}
|
|
keys = path.split(separator);
|
if (current.get(field) != keys[1]) {
|
// invalid root
|
Ext.callback(callback, scope || me, [false, current]);
|
return;
|
}
|
|
expander = function(){
|
if (++index === keys.length) {
|
Ext.callback(callback, scope || me, [true, current]);
|
return;
|
}
|
var node = current.findChild(field, keys[index]);
|
if (!node) {
|
Ext.callback(callback, scope || me, [false, current]);
|
return;
|
}
|
current = node;
|
current.expand(false, expander);
|
};
|
current.expand(false, expander);
|
},
|
|
<span id='Ext-tree-Panel-method-selectPath'> /**
|
</span> * Expand the tree to the path of a particular node, then select it.
|
* @param {String} path The path to select. The path should include a leading separator.
|
* @param {String} [field] The field to get the data from. Defaults to the model idProperty.
|
* @param {String} [separator='/'] A separator to use.
|
* @param {Function} [callback] A function to execute when the select finishes. The callback will be called with
|
* (bSuccess, oLastNode) where bSuccess is if the select was successful and oLastNode is the last node that was expanded.
|
* @param {Object} [scope] The scope of the callback function
|
*/
|
selectPath: function(path, field, separator, callback, scope) {
|
var me = this,
|
root,
|
keys,
|
last;
|
|
field = field || me.getRootNode().idProperty;
|
separator = separator || '/';
|
|
keys = path.split(separator);
|
last = keys.pop();
|
if (keys.length > 1) {
|
me.expandPath(keys.join(separator), field, separator, function(success, node){
|
var lastNode = node;
|
if (success && node) {
|
node = node.findChild(field, last);
|
if (node) {
|
me.getSelectionModel().select(node);
|
Ext.callback(callback, scope || me, [true, node]);
|
return;
|
}
|
}
|
Ext.callback(callback, scope || me, [false, lastNode]);
|
}, me);
|
} else {
|
root = me.getRootNode();
|
if (root.getId() === last) {
|
me.getSelectionModel().select(root);
|
Ext.callback(callback, scope || me, [true, root]);
|
} else {
|
Ext.callback(callback, scope || me, [false, null]);
|
}
|
}
|
}
|
});
|
</pre>
|
</body>
|
</html>
|