/**
 * Componente de treeview baseado na estrutura de listas nao ordenadas
 * @author Andre
 */
compono.Treeview = new compono.Object("Treeview");

/**
 * Componentes associados a inicializacao do objeto
 */
compono.Treeview.listeners = [];

/**
 * Selecao default para o objeto
 */
compono.Treeview.defaultSelectionMode="multiple";

/**
 * Me'todo que indica os nodos que devem ser inicializados 
 * @param {String, Element} id Treeview
 */
compono.Treeview.setInit = function(id){
	var ul = this.getByID(id);
	if(ul){
		this.listeners.push(ul);
		ul.selection = this.getSelectionMode(ul);
		ul.selection.init(ul, ul);
	}
}

/**
 * Inicializado dos listeners
 ******************************
 * Retirado por compatibilidade
 ******************************
compono.Treeview.init = function(){
	for(var i=0,u;u=this.listeners[i];i++){
		if(!u.selection)u.selection=this.Selection[ (u.getAttribute("selection").toLowerCase() || this.defaultSelection)];
		u.selection.init(u, u);
	}
}
 */


/**
 * Retorna o objeto selection correspondende ao atributo;
 * Foi criado os me'todos de 'getSelectionMode' para que a treeview funcione sem inicializacao 
 * @param {Element} ul Elemento root da treeview;
 */
compono.Treeview.getSelectionMode = function(ul){
	return this.Selection[ (ul.getAttribute("selectionMode").toLowerCase() || this.defaultSelectionMode)];
}

/**
 * Retorna o objeto selection correspondende ao atributo;
 * Foi criado os me'todos de 'getRoot' para que a treeview funcione sem inicializacao 
 * @param {Element} ul Elemento root da treeview;
 */
compono.Treeview.getRoot = function(trigger){
	var p=trigger.parentNode;
	while(!this.hasClassName('treeview',p)){
		p=p.parentNode;
	}
	return p;
}


/**
 * Interface para o me'todo que abre e fecha o no'
 * Foi criado os me'todos de 'getRoot' e 'getSelectionMode' para que a treeview funcione sem inicializacao
 * @param {Element} trigger Objeto que abre/fecha o no'
 * @param {Event} e Objeto evento gerado
 */
compono.Treeview.toggle = function(o,e){
	if(!o.parentNode.root){
		o.parentNode.root=this.getRoot(o);
		if(!o.parentNode.root.selection){
			o.parentNode.root.selection=this.getSelectionMode(o.parentNode.root);
		}
	}
	if(o.parentNode.root && o.parentNode.root.selection){
		o.parentNode.root.selection.Element.toggle(o, e);
	}
};

/**
 * Interface para o me'todo que ativa/desativa a selecao do objeto
 * @param {Element} check Objeto que executa a acao
 * @param {Event} e Objeto evento gerado
 */
compono.Treeview.check = function(check, e){
	if(check.parentNode.root && check.parentNode.root.selection){
		check.parentNode.root.selection.change(check, e);
	}
};

/**
 * Seta informacoes basicas da janela no storage
 * @param {Node} base Objeto window
 */
compono.Treeview.setStorage = function(base, id, value){
	var o;
	if((o = this.getByID(base.id+"_storage"))){
		if(!base.storage){
			base.storage=compono.Storage.Hidden.ID(o);
		}
		base.storage.set(id, value);
	}
	return o;
}


/**
 * Elemento com os me'todos basicos de abrir/fechar os no's
 * Esse objeto deve ser customizado para cada selecao
 */
compono.Treeview.Element={
	/**
	 * Me'todo que abre o no'
	 * @param {Element} o Elemento clicado
	 * @param {Event} e Objeto evento gerado
	 */
	open:function(o,e){
		var _this=compono.Treeview;
		/* LI */	_this.appendClassName('openup',this.parentNode);
		/* A(+)*/	_this.appendClassName('openup',this);
		/* UL */
		if(!this.parentNode.menu)this.parentNode.menu=_this.getByTagName("ul",this.parentNode);
		_this.appendClassName('openup',this.parentNode.menu);
		/* A LABEL*/
		if(!this.parentNode.label)this.parentNode.label=_this.getByClassName("a","label",this.parentNode);
		 _this.appendClassName('openup',this.parentNode.label);
		this.openup = true;
	},
	/**
	 * Me'todo que fecha o no'
	 * @param {Element} o Elemento clicado
	 * @param {Event} e Objeto evento gerado
	 */
	close:function(o,e){
		var _this=compono.Treeview;
		/* LI */	_this.removeClassName('openup',this.parentNode);
		/* A(+)*/	_this.removeClassName('openup',this);
		/* UL */
		if(!this.parentNode.menu)this.parentNode.menu=_this.getByTagName("ul",this.parentNode);
		_this.removeClassName('openup',this.parentNode.menu);
		/* A LABEL*/
		if(!this.parentNode.label)this.parentNode.label=_this.getByClassName("a","label",this.parentNode);
		_this.removeClassName('openup',this.parentNode.label);
		this.openup = false;
	},
	/**
	 * Me'todo que abre o no' se estiver fechado e fecha se estiver aberto
	 * @param {Element} trigger Elemento clicado
	 * @param {Event} e Objeto evento gerado
	 */
	toggle:function(o,e){
		var _this=compono.Treeview;
		if(!o.open)o.open=o.parentNode.root.selection.Element.open;
		if(!o.close)o.close=o.parentNode.root.selection.Element.close;
		_this.hasClassName("openup",o) ? o.close() : o.open();
		_this.Event.stopPropagation(e);
	}
}


/**
 * Objeto que contem as selecoes possiveis da treeview
 */
compono.Treeview.Selection = {
/**
 * Selecao 'single'
 * Selecao tipo radio, so' um item pode ser selecionado indiferente do nivel deste
 */	
"single":{
		/**
		 * Estados possiveis para o elemento
		 */
		states:["deactive","active"],
		/**
		 * Funcao recursiva que inicializa a treeview para o tipo de selecao 'single'
		 * @param {Element} ul Elemento corrente da recursividade, inicialmente a treeview 
		 * @param {Element} root Elemento base da treeview
		 */
		init:function(ul, root){
			var _this=compono.Treeview;			
			ul.family = false;
			ul.root = root;
			for(var i=0, li; ((li=ul.childNodes[i]) && !_this.hasClassName("separator", li)); i++){
				if(li.nodeType!=1)continue;
				
				li.root = root;
				li.switcher = _this.getByClassName("em","switcher",li);
				li.trigger = _this.getByClassName("span","trigger",li);
				li.trigger.open = this.Element.open;
				li.trigger.close = this.Element.close;
		
				if(_this.hasClassName("sub", li)){
					li.menu = _this.getByTagName("ul",li);
					this.init(li.menu, root);
				}
				if(li.switcher && li.switcher.getAttribute("checked") == "true"){
					this.set(li.switcher,!root.family);
				}
			}
			return ul.family;
		},
		/**
		 * Seta o valor ao radio de cada elemento
		 * @param {Element} switcher Elemento radio
		 * @param {Boolean} value Valor do estado
		 */
		set:function(switcher, value){
			var t=compono.Treeview;
			if(switcher.currentState){
				compono.Treeview.removeClassName("switcher-" + switcher.currentStateName , switcher);
			}
			switcher.currentState = Number(value);
			switcher.currentStateName = this.states[switcher.currentState];
			t.appendClassName("switcher-" + switcher.currentStateName , switcher);
			
			t.setStorage(switcher.parentNode.root, switcher.getAttribute("id"), switcher.currentState)
			switcher.setAttribute("checked", String(value));
			if(value) switcher.parentNode.root.family = switcher;
			return value;
		},
		/**
		 * Me'todo de troca de estado
		 * @param {Element} switcher Element radio
		 * @param {Event} e Evento gerado
		 * @param {Function} callback Funcao executada no change
		 */
		change:function(switcher, e, callback){
			var r = switcher.parentNode.root;
			if(	r && r.family && r.family.nodeType == 1 && r.family !== switcher){
				this.set(r.family, false);
			}
			this.set(switcher, true);
			if(callback && typeof(callback) == "function"){
				callback.apply(switcher, [e])
			}
		},
		/**
		 * Interface para os me'todos de 'open, close, toggle' dos no's
		 */
		Element:compono.Treeview.Element
	},
/**
 * Selecao 'single-level'
 * Permite a selecao de apenas um no' dentro do seu nivel, nao tenho conhecimento dos demais niveis
 */
"single-level":{
		/**
		 * Estados possiveis
		 */
		states:["deactive","active"],
		/**
		 * Funcao recursiva que inicializa a treeview para o tipo de selecao 'single-level'
		 * @param {Element} ul Elemento corrente da recursividade, inicialmente a treeview 
		 * @param {Element} root Elemento base da treeview
		 */
		init:function(ul, root){
			var _this=compono.Treeview;			
			ul.family = false;
			ul.root = root;
			for(var i=0, li; ((li=ul.childNodes[i]) && !_this.hasClassName("separator", li)); i++){
				if(li.nodeType!=1)continue;
				
				li.root = root;
				li.switcher = _this.getByClassName("em","switcher",li);
				li.trigger = _this.getByClassName("span","trigger",li);
				li.trigger.open = this.Element.open;
				li.trigger.close = this.Element.close;
		
				if(_this.hasClassName("sub", li)){
					li.menu = _this.getByTagName("ul",li);
					this.init(li.menu, root);
				}
				if(li.switcher && li.switcher.getAttribute("checked") == "true"){
					this.set(li.switcher,!ul.family);
				}
			}
			return ul.family;
		},
		/**
		 * Seta o valor ao radio de cada elemento
		 * @param {Element} switcher Elemento radio
		 * @param {Boolean} value Valor do estado
		 */
		set:function(switcher, value){
			var t=compono.Treeview;
			if(switcher.currentState){
				t.removeClassName("switcher-" + switcher.currentStateName , switcher);
			}
			switcher.currentState = Number(value);
			switcher.currentStateName = this.states[switcher.currentState];
			t.appendClassName("switcher-" + switcher.currentStateName , switcher);
			
			t.setStorage(switcher.parentNode.root, switcher.getAttribute("id"), switcher.currentState)
			switcher.setAttribute("checked", String(value));
			if(value) switcher.parentNode.parentNode.family = switcher;
			return value;
		},
		/**
		 * Me'todo de troca de estado
		 * @param {Element} switcher Element radio
		 * @param {Event} e Evento gerado
		 * @param {Function} callback Funcao executada no change
		 */
		change:function(switcher, e, callback){
			var r = switcher.parentNode.parentNode;
			if(	r && r.family && r.family.nodeType == 1 && r.family !== switcher){
				this.set(r.family, false);
			}
			this.set(switcher, true);
			if(callback && typeof(callback) == "function"){
				callback.apply(switcher, [e])
			}
		},
		/**
		 * Interface para os me'todos de 'open, close, toggle' dos no's
		 */
		Element:compono.Treeview.Element
	}, 
/**
 * Selecao 'multiple'
 * Permite a selecao de mais de um elemento indiferente de nivel, a selecao dos filhos nao afetam os pais
 */
"multiple":{
		/**
		 * Estados possiveis
		 */
		states:["deactive","active"],
		/**
		 * Funcao recursiva que inicializa a treeview para o tipo de selecao 'single-level'
		 * @param {Element} ul Elemento corrente da recursividade, inicialmente a treeview 
		 * @param {Element} root Elemento base da treeview
		 */
		init:function(ul, root){
			var _this=compono.Treeview;			
			
			ul.family = [];
			ul.family.selected = 0;
			ul.root = root;
			
			for(var i=0, li; ((li=ul.childNodes[i]) && !_this.hasClassName("separator", li)); i++){
				if(li.nodeType!=1)continue;
				
				ul.family.push(li);
				li.root = root;
				li.switcher = _this.getByClassName("em","switcher",li);
				li.trigger = _this.getByClassName("span","trigger",li);
				li.trigger.open = this.Element.open;
				li.trigger.close = this.Element.close;
				
				if(_this.hasClassName("sub", li)){
					li.menu = _this.getByTagName("ul",li);
					var a = this.init(li.menu, root);
					ul.family = ul.family.concat( a );
					ul.family.selected = a.selected;
				}
				if(li.switcher && li.switcher.getAttribute("checked") == "true"){
					this.set(li.switcher, 1, root);
					ul.family.selected++;
				}
			}
			return ul.family;
		},
		/**
		 * Seta o valor ao radio de cada elemento
		 * @param {Element} switcher Elemento radio
		 * @param {Boolean} value Valor do estado
		 */
		set:function(switcher, value, e){
			var t=compono.Treeview;
			if(switcher.currentState){
				t.removeClassName("switcher-" + switcher.currentStateName , switcher);
			}
			switcher.currentState = value;
			switcher.currentStateName = this.states[switcher.currentState];
			t.appendClassName("switcher-" + switcher.currentStateName , switcher);

			switcher.setAttribute("checked", (value == 1 ? "true" : "false"));
			t.setStorage(switcher.parentNode.root, switcher.getAttribute("id"), switcher.currentState);
		},
		/**
		 * Me'todo de troca de estado
		 * @param {Element} switcher Element radio
		 * @param {Event} e Evento gerado
		 * @param {Function} callback Funcao executada no change
		 */
		change:function(switcher, e, callback){
			var s, v, _this=compono.Treeview;
			v = switcher.getAttribute("checked") == "true" ? 0 : 1;
			this.set(switcher, v, e);
			if(callback && typeof(callback) == "function"){
				callback.apply(switcher, [e])
			}
		},
		/**
		 * Interface para os me'todos de 'open, close, toggle' dos no's
		 */
		Element:compono.Treeview.Element
	},
/**
 * Selecao 'multiple-hierarchical'
 * Permite a selecao de mais de um item, a selecao dos filhos sao propagadas aos pais afendando-os
 */	
"multiple-hierarchical":{
		/**
		 * Estados possiveis
		 */
		states:["deactive","active","partial"],
		/**
		 * Funcao recursiva que inicializa a treeview para o tipo de selecao 'single-level'
		 * @param {Element} ul Elemento corrente da recursividade, inicialmente a treeview 
		 * @param {Element} root Elemento base da treeview
		 */
		init:function(ul, root){
			var _this=compono.Treeview;			
			ul.family = [];
			ul.family.selected = 0;
			ul.root = root;
			
			for(var i=0, li; ((li=ul.childNodes[i]) && !_this.hasClassName("separator", li)); i++){
				if(li.nodeType!=1)continue;
				
				ul.family.push(li);
				li.root = root;
				li.switcher = _this.getByClassName("em","switcher",li);
				li.trigger = _this.getByClassName("span","trigger",li);
				li.trigger.open = this.Element.open;
				li.trigger.close = this.Element.close;
		
				if(_this.hasClassName("sub", li)){
					li.menu = _this.getByTagName("ul",li)
					var a = this.init(li.menu, root);
					ul.family = ul.family.concat( a );
					ul.family.selected = a.selected;
					
					if(li.menu.family.selected == li.menu.family.length){
						li.switcher.setAttribute("checked", "true");
					}
					else if(li.menu.family.selected){
						this.set(li.switcher, 2, root)
					}
				}
				if(li.switcher && li.switcher.getAttribute("checked") == "true"){
					this.set(li.switcher, 1, root)
					ul.family.selected++;
				}
			}
			return ul.family;
		},
		/**
		 * Seta o valor ao radio de cada elemento
		 * @param {Element} switcher Elemento radio
		 * @param {Boolean} value Valor do estado
		 */
		set:function(switcher, value, e){
			var t=compono.Treeview;
			if(switcher.currentState){
				t.removeClassName("switcher-" + switcher.currentStateName, switcher);
			}
			switcher.currentState = value;
			switcher.currentStateName = this.states[switcher.currentState];
			t.appendClassName("switcher-" + switcher.currentStateName , switcher);
			
			switcher.setAttribute("checked", (value == 1 ? "true" : "false"));
			t.setStorage(switcher.parentNode.root, switcher.getAttribute("id"), switcher.currentState);
		},
		/**
		 * Me'todo de troca de estado
		 * @param {Element} switcher Element radio
		 * @param {Event} e Evento gerado
		 * @param {Function} callback Funcao executada no change
		 */
		change:function(switcher, e, callback){
			var s, v, _this=compono.Treeview;
			v = switcher.getAttribute("checked") == "true" ? 0 : 1;
			/* se o item clicado tiver filhos e algum desses estiver clicado ento deseleciona*/
			if(switcher.parentNode.menu && switcher.parentNode.menu.family.selected > 0){
				v = 0;
			}
			this.set(switcher, v, e);
			this.propagate(switcher, v, e);
			//this.saveState(switcher, switcher.parentNode.parentNode.root)
			if(callback && typeof(callback) == "function"){
				callback.apply(switcher, [e])
			}
		},
		/**
		 * Me'todo que inicializa a propagacao da selecao atraves da a'rvore
		 * @param {Element} switcher Elemento clicado
		 * @param {Number} value Valor do estado
		 * @param {Event} e Evento gerado
		 */
		propagate:function(switcher, value,e){
			var inc, up, down, pinc, _this=compono.Treeview;
	
			pinc = value > 0 ? 1 : -1;
			up = switcher.parentNode.parentNode;
			down = switcher.parentNode.menu;
		
			if(down){
				if(down.family.selected == 0){
					pinc += down.family.length;
				}else if(down.family.length == down.family.selected){
					pinc += -(down.family.selected);
				}else if(down.family.selected > 0){
					pinc = -(down.family.selected);
				}
				this.propagateToDOWN(down, pinc, value);
			}
			this.propagateToUP(up, pinc);	
		},
		/**
		 * Propaga a selecao para os pais, atribuindo um estado intermedia'rio para estes ('partial')
		 * Quando dos os filhos estao selecionados automaticamente o pai troca de estado para 'active'
		 * Ao selecionar um item que conte'm filhos estes filhos serao selecionados recursivamente
		 * @param {Element} ul Menu selecionado
		 * @param {Number} inc Incrementador para contagem que controla as selecoes automa'ticas
		 */
		propagateToUP:function(ul, inc){
			if(ul.root === ul) return false;
			
			/*	Verifica se a familia est toda selecionada antes do incremento e o inc  negativo;
				para depois de mexer no family atual, a recursividade deletar mais um;	*/
			before = ul.family.length == ul.family.selected && inc < 0;
			ul.family.selected += inc;
			/*	Verifica se a familia est toda seleciona depois do incremento e se inc  negativo
				para depois somar mais um;	*/
			after = ul.family.length == ul.family.selected && inc > 0;
			
			if ( before){	inc--;		}
			if ( after ){	inc++;		}
			
			if(ul.family.selected == 0){
				this.set(	ul.parentNode.switcher, 0);	
			}else if ( ul.family.length == ul.family.selected ){
				this.set(	ul.parentNode.switcher, 1);
			}else{	
				this.set(	ul.parentNode.switcher, 2);
			}
			/*not IE = arguments.callee(	ul.parentNode.parentNode , inc);*/
			this.propagateToUP(	ul.parentNode.parentNode , inc);
			
			return true;
		},
		/**
		 * Me'todo que propaga a selecao aos filhos
		 * @param {Element} ul Elemento
		 * @param {Number} inc Incrementador
		 * @param {Number} value Valor do estado
		 */
		propagateToDOWN:function(ul, inc, value){
			if(!ul)return false;
			
			ul.family.selected = inc > 0 ? ul.family.length : 0;
			
			for(var i=0,li;li=ul.family[i];i++){
				if(li.menu)this.propagateToDOWN( li.menu , inc, value);
				this.set(li.switcher,	value)
			}
			
			return true;
		},
		/**
		 * Interface para os me'todos de 'open, close, toggle' dos no's
		 */
		Element:compono.Treeview.Element
	}	
}
