Go to most recent revision | Blame | Last modification | View Log | RSS feed
/*
* Ext JS Library 0.20
* Copyright(c) 2006-2008, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
// custom menu item to contain Ext trees
Ext.menu.TreeItem = Ext.extend(Ext.menu.Adapter, {
constructor : function(config){
Ext.menu.TreeItem.superclass.constructor.call(this, config.tree, config);
this.tree = this.component;
this.addEvents('selectionchange');
this.tree.on("render", function(tree){
tree.body.swallowEvent(['click','keydown', 'keypress', 'keyup']);
});
this.tree.getSelectionModel().on("selectionchange", this.onSelect, this);
},
onSelect : function(tree, sel){
this.fireEvent("select", this, sel, tree);
}
});
// custom menu containing a single tree
Ext.menu.TreeMenu = Ext.extend(Ext.menu.Menu, {
cls:'x-tree-menu',
keyNav: true,
hideOnClick:false,
plain: true,
constructor : function(config){
Ext.menu.TreeMenu.superclass.constructor.call(this, config);
this.treeItem = new Ext.menu.TreeItem(config);
this.add(this.treeItem);
this.tree = this.treeItem.tree;
this.tree.on('click', this.onNodeClick, this);
this.relayEvents(this.treeItem, ["selectionchange"]);
},
// private
beforeDestroy : function() {
this.tree.destroy();
},
onNodeClick : function(node, e){
if(!node.attributes.isFolder){
this.treeItem.handleClick(e);
}
}
});
// custom form field for displaying a tree, similar to select or combo
Ext.ux.TreeSelector = Ext.extend(Ext.form.TriggerField, {
initComponent : function(){
Ext.ux.TreeSelector.superclass.initComponent.call(this);
this.addEvents('selectionchange');
this.tree.getSelectionModel().on('selectionchange', this.onSelection, this);
this.tree.on({
'expandnode': this.sync,
'collapsenode' : this.sync,
'append' : this.sync,
'remove' : this.sync,
'insert' : this.sync,
scope: this
});
this.on('focus', this.onTriggerClick, this);
},
sync : function(){
if(this.menu && this.menu.isVisible()){
if(this.tree.body.getHeight() > this.maxHeight){
this.tree.body.setHeight(this.maxHeight);
this.restricted = true;
}else if(this.restricted && this.tree.body.dom.firstChild.offsetHeight < this.maxHeight){
this.tree.body.setHeight('');
this.restricted = false;
}
this.menu.el.sync();
}
},
onSelection : function(tree, node){
if(!node){
this.setRawValue('');
}else{
this.setRawValue(node.text);
}
},
initEvents : function(){
Ext.ux.TreeSelector.superclass.initEvents.call(this);
this.el.on('mousedown', this.onTriggerClick, this);
this.el.on("keydown", this.onKeyDown, this);
},
onKeyDown : function(e){
if(e.getKey() == e.DOWN){
this.onTriggerClick();
}
},
validateBlur : function(){
return !this.menu || !this.menu.isVisible();
},
getValue : function(){
var sm = this.tree.getSelectionModel();
var s = sm.getSelectedNode();
return s ? s.id : '';
},
setValue : function(id){
var n = this.tree.getNodeById(id);
if(n){
n.select();
}else{
this.tree.getSelectionModel().clearSelections();
}
},
// private
onDestroy : function(){
if(this.menu) {
this.menu.destroy();
}
if(this.wrap){
this.wrap.remove();
}
Ext.ux.TreeSelector.superclass.onDestroy.call(this);
},
// private
menuListeners : {
show : function(){ // retain focus styling
this.onFocus();
},
hide : function(){
this.focus.defer(10, this);
var ml = this.menuListeners;
this.menu.un("show", ml.show, this);
this.menu.un("hide", ml.hide, this);
}
},
onTriggerClick : function(){
if(this.disabled){
return;
}
this.menu.on(Ext.apply({}, this.menuListeners, {
scope:this
}));
this.menu.show(this.el, "tl-bl?");
this.sync();
var sm = this.tree.getSelectionModel();
var selected = sm.getSelectedNode();
if(selected){
selected.ensureVisible();
sm.activate.defer(250, sm, [selected]);
}
},
beforeBlur : function(){
//
},
onRender : function(){
Ext.ux.TreeSelector.superclass.onRender.apply(this, arguments);
this.menu = new Ext.menu.TreeMenu(Ext.apply(this.menuConfig || {}, {tree: this.tree}));
this.menu.render();
this.tree.body.addClass('x-tree-selector');
},
readOnly: true
});
/*
* Custom tree keyboard navigation that supports node navigation without selection
*/
Ext.tree.ActivationModel = Ext.extend(Ext.tree.DefaultSelectionModel, {
select : function(node){
return this.activate(Ext.tree.ActivationModel.superclass.select.call(this, node));
},
activate : function(node){
if(!node){
return;
}
if(this.activated != node) {
if(this.activated){
this.activated.ui.removeClass('x-tree-activated');
}
this.activated = node;
node.ui.addClass('x-tree-activated');
}
node.ui.focus();
return node;
},
activatePrevious : function(){
var s = this.activated;
if(!s){
return null;
}
var ps = s.previousSibling;
if(ps){
if(!ps.isExpanded() || ps.childNodes.length < 1){
return this.activate(ps);
} else{
var lc = ps.lastChild;
while(lc && lc.isExpanded() && lc.childNodes.length > 0){
lc = lc.lastChild;
}
return this.activate(lc);
}
} else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
return this.activate(s.parentNode);
}
return null;
},
activateNext : function(){
var s = this.activated;
if(!s){
return null;
}
if(s.firstChild && s.isExpanded()){
return this.activate(s.firstChild);
}else if(s.nextSibling){
return this.activate(s.nextSibling);
}else if(s.parentNode){
var newS = null;
s.parentNode.bubble(function(){
if(this.nextSibling){
newS = this.getOwnerTree().selModel.activate(this.nextSibling);
return false;
}
});
return newS;
}
return null;
},
onKeyDown : function(e){
var s = this.activated;
// undesirable, but required
var sm = this;
if(!s){
return;
}
var k = e.getKey();
switch(k){
case e.DOWN:
e.stopEvent();
this.activateNext();
break;
case e.UP:
e.stopEvent();
this.activatePrevious();
break;
case e.RIGHT:
e.preventDefault();
if(s.hasChildNodes()){
if(!s.isExpanded()){
s.expand();
}else if(s.firstChild){
this.activate(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.activate(s.parentNode, e);
}
break;
};
}
});