/*
 * Ext JS Library 0.30
 * Copyright(c) 2006-2009, 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;
        };
    }
});