app/assets/javascripts/ext/src/widgets/tree/TreeSelectionModel.js
/*!
* Ext JS Library 3.3.0
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
/**
* @class Ext.tree.DefaultSelectionModel
* @extends Ext.util.Observable
* The default single selection for a TreePanel.
*/
Ext.tree.DefaultSelectionModel = Ext.extend(Ext.util.Observable, {
constructor : function(config){
this.selNode = null;
this.addEvents(
/**
* @event selectionchange
* Fires when the selected node changes
* @param {DefaultSelectionModel} this
* @param {TreeNode} node the new selection
*/
'selectionchange',
/**
* @event beforeselect
* Fires before the selected node changes, return false to cancel the change
* @param {DefaultSelectionModel} this
* @param {TreeNode} node the new selection
* @param {TreeNode} node the old selection
*/
'beforeselect'
);
Ext.apply(this, config);
Ext.tree.DefaultSelectionModel.superclass.constructor.call(this);
},
init : function(tree){
this.tree = tree;
tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
tree.on('click', this.onNodeClick, this);
},
onNodeClick : function(node, e){
this.select(node);
},
/**
* Select a node.
* @param {TreeNode} node The node to select
* @return {TreeNode} The selected node
*/
select : function(node, /* private*/ selectNextNode){
// If node is hidden, select the next node in whatever direction was being moved in.
if (!Ext.fly(node.ui.wrap).isVisible() && selectNextNode) {
return selectNextNode.call(this, node);
}
var last = this.selNode;
if(node == last){
node.ui.onSelectedChange(true);
}else if(this.fireEvent('beforeselect', this, node, last) !== false){
if(last && last.ui){
last.ui.onSelectedChange(false);
}
this.selNode = node;
node.ui.onSelectedChange(true);
this.fireEvent('selectionchange', this, node, last);
}
return node;
},
/**
* Deselect a node.
* @param {TreeNode} node The node to unselect
* @param {Boolean} silent True to stop the selectionchange event from firing.
*/
unselect : function(node, silent){
if(this.selNode == node){
this.clearSelections(silent);
}
},
/**
* Clear all selections
* @param {Boolean} silent True to stop the selectionchange event from firing.
*/
clearSelections : function(silent){
var n = this.selNode;
if(n){
n.ui.onSelectedChange(false);
this.selNode = null;
if(silent !== true){
this.fireEvent('selectionchange', this, null);
}
}
return n;
},
/**
* Get the selected node
* @return {TreeNode} The selected node
*/
getSelectedNode : function(){
return this.selNode;
},
/**
* Returns true if the node is selected
* @param {TreeNode} node The node to check
* @return {Boolean}
*/
isSelected : function(node){
return this.selNode == node;
},
/**
* Selects the node above the selected node in the tree, intelligently walking the nodes
* @return TreeNode The new selection
*/
selectPrevious : function(/* private */ s){
if(!(s = s || this.selNode || this.lastSelNode)){
return null;
}
// Here we pass in the current function to select to indicate the direction we're moving
var ps = s.previousSibling;
if(ps){
if(!ps.isExpanded() || ps.childNodes.length < 1){
return this.select(ps, this.selectPrevious);
} else{
var lc = ps.lastChild;
while(lc && lc.isExpanded() && Ext.fly(lc.ui.wrap).isVisible() && lc.childNodes.length > 0){
lc = lc.lastChild;
}
return this.select(lc, this.selectPrevious);
}
} else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
return this.select(s.parentNode, this.selectPrevious);
}
return null;
},
/**
* Selects the node above the selected node in the tree, intelligently walking the nodes
* @return TreeNode The new selection
*/
selectNext : function(/* private */ s){
if(!(s = s || this.selNode || this.lastSelNode)){
return null;
}
// Here we pass in the current function to select to indicate the direction we're moving
if(s.firstChild && s.isExpanded() && Ext.fly(s.ui.wrap).isVisible()){
return this.select(s.firstChild, this.selectNext);
}else if(s.nextSibling){
return this.select(s.nextSibling, this.selectNext);
}else if(s.parentNode){
var newS = null;
s.parentNode.bubble(function(){
if(this.nextSibling){
newS = this.getOwnerTree().selModel.select(this.nextSibling, this.selectNext);
return false;
}
});
return newS;
}
return null;
},
onKeyDown : function(e){
var s = this.selNode || this.lastSelNode;
// undesirable, but required
var sm = this;
if(!s){
return;
}
var k = e.getKey();
switch(k){
case e.DOWN:
e.stopEvent();
this.selectNext();
break;
case e.UP:
e.stopEvent();
this.selectPrevious();
break;
case e.RIGHT:
e.preventDefault();
if(s.hasChildNodes()){
if(!s.isExpanded()){
s.expand();
}else if(s.firstChild){
this.select(s.firstChild, e);
}
}
break;
case e.LEFT:
e.preventDefault();
if(s.hasChildNodes() && s.isExpanded()){
s.collapse();
}else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
this.select(s.parentNode, e);
}
break;
};
}
});
/**
* @class Ext.tree.MultiSelectionModel
* @extends Ext.util.Observable
* Multi selection for a TreePanel.
*/
Ext.tree.MultiSelectionModel = Ext.extend(Ext.util.Observable, {
constructor : function(config){
this.selNodes = [];
this.selMap = {};
this.addEvents(
/**
* @event selectionchange
* Fires when the selected nodes change
* @param {MultiSelectionModel} this
* @param {Array} nodes Array of the selected nodes
*/
'selectionchange'
);
Ext.apply(this, config);
Ext.tree.MultiSelectionModel.superclass.constructor.call(this);
},
init : function(tree){
this.tree = tree;
tree.mon(tree.getTreeEl(), 'keydown', this.onKeyDown, this);
tree.on('click', this.onNodeClick, this);
},
onNodeClick : function(node, e){
if(e.ctrlKey && this.isSelected(node)){
this.unselect(node);
}else{
this.select(node, e, e.ctrlKey);
}
},
/**
* Select a node.
* @param {TreeNode} node The node to select
* @param {EventObject} e (optional) An event associated with the selection
* @param {Boolean} keepExisting True to retain existing selections
* @return {TreeNode} The selected node
*/
select : function(node, e, keepExisting){
if(keepExisting !== true){
this.clearSelections(true);
}
if(this.isSelected(node)){
this.lastSelNode = node;
return node;
}
this.selNodes.push(node);
this.selMap[node.id] = node;
this.lastSelNode = node;
node.ui.onSelectedChange(true);
this.fireEvent('selectionchange', this, this.selNodes);
return node;
},
/**
* Deselect a node.
* @param {TreeNode} node The node to unselect
*/
unselect : function(node){
if(this.selMap[node.id]){
node.ui.onSelectedChange(false);
var sn = this.selNodes;
var index = sn.indexOf(node);
if(index != -1){
this.selNodes.splice(index, 1);
}
delete this.selMap[node.id];
this.fireEvent('selectionchange', this, this.selNodes);
}
},
/**
* Clear all selections
*/
clearSelections : function(suppressEvent){
var sn = this.selNodes;
if(sn.length > 0){
for(var i = 0, len = sn.length; i < len; i++){
sn[i].ui.onSelectedChange(false);
}
this.selNodes = [];
this.selMap = {};
if(suppressEvent !== true){
this.fireEvent('selectionchange', this, this.selNodes);
}
}
},
/**
* Returns true if the node is selected
* @param {TreeNode} node The node to check
* @return {Boolean}
*/
isSelected : function(node){
return this.selMap[node.id] ? true : false;
},
/**
* Returns an array of the selected nodes
* @return {Array}
*/
getSelectedNodes : function(){
return this.selNodes.concat([]);
},
onKeyDown : Ext.tree.DefaultSelectionModel.prototype.onKeyDown,
selectNext : Ext.tree.DefaultSelectionModel.prototype.selectNext,
selectPrevious : Ext.tree.DefaultSelectionModel.prototype.selectPrevious
});