/*****************************
 *  YUI-addon-YGY-2.js
 * Gestion avancee de boites DRAG et DROP
 * (c) Yves Gerey 2008
 *  11/11/2008 22:56:36
 * Dependances : 
 *  YUI-dragdrop
 *  YUI-treeview 
 *
 * 10/04/2008 : Remplace parametre insertAtEnd (false/undefined/true) par insertionMode (0/1/2)
 * 12/04/2008 : Ajoute gestion arbre.
 * 16/04/2008 : Optimisation "onDragOver". 
 *              Boite fantome disparait si on drag dans zone invalide (-> retour de l'element a sa position d'origine).
 * 20/04/2008 : Ajoute fonction getBoxElements (renvoi liste elements d'une boite)
 * 25/04/2008 : Ajoute autocomplète. 
 * 16/05/2008 : Ajoute parametre arbre_contenant_id dans treeInit.
 * 18/05/2000 : Nouvelles fontions : initialisation autocomplete pour box ou formulaire.
 * 27/05/2008 : Mise au propre : definit toutes les variables comme locales (pour IE)
 *                               decompose attribut "border" (pour IE)
 * 07/06/2008 : Ajout fonction AJAX.
 * 10/06/2008 : Appel de fonction parametrable quand contenue DDBox change (pour notification AJAX)
 * 26/07/2008 : Force la hauteur de la boite ghost d'apres celle de l'element dragge.
 * 26/07/2008 : Nettoie debug
 * 26/09/2008 : verifie si DragDrop present pour initialiser DDM.
 * 28/10/2008 : Ajoute my_alert : alert javascript parametrable.
 * 06/11/2008 : corrige le titre pour qu'il s'affiche dans toute la largeur
 * 11/11/2008 : espaces en plus si croix de fermeture et un titre
 * 25/02/2009 : Tree customisable pour action sur "click" ou sur "hover".
 * *********************************************************************/

/*Debug option for jsl*/
/*jsl:option explicit*/
// alert('debut YUI-addon-YGY-2.js');

var Dom = YAHOO.util.Dom;                // racourcis utilisés de facon globale.
var Event = YAHOO.util.Event;
if ('DragDropMgr' in YAHOO.util) {
	var DDM = YAHOO.util.DragDropMgr;
	// Mode INTERSECT obligatoire pour gestion insertion entre elements. 
	DDM.mode = DDM.INTERSECT;
}
	
	
var panel_alerte ; // Doit etre connu globalement pour etre ferme par setTimeout

// Module de chargement image (ou autre remise à jour DIV).
// Il gere la temporisation, via la fonction change(id, delai)
// Si autre image sélectionnée avant expiration du délai, la demande précédante est annulée.
// 
// Input : div_id, l'identifiant HTML du div à mettre à jour.
function Div_Loader(div_id, delai)
{
  	this.delai = delai         ;  // on rend le délai accessible comme attribut
  	var current_Timeout = null ;

	// Retour connection AJAX : place la reponse du serveur dans le div placé en paramètre
	function fn_image_succes(oResponse) {
		//Dom.get('div_info_general').innerHTML= oResponse.responseText; 
		// alert ("Debug reponse serveur : " + oResponse.responseText) ;
		Dom.get(div_id).innerHTML= oResponse.responseText;
	}
	
	function fn_image_echec(oResponse) {
		// Mettre ici l'action si pas de reponse
		//   - mettre image generique "Pas de photo ?"  (ce n'est pas forcement vrai)
		//   - mettre image generique "Erreur reseau"  (vrai, mais est-ce pertinent pour l'utilisateur ?)
		//   - Ne rien faire...
	}
  					  		  		  
	// Demande chargement div au bout de "delai"
    this.change = function (id, delai)
    {
    	//parametre par default : delai = 0
    	if (!delai) { delai = 0 ; } 
    	//si image deja en attente, on l'annule
    	if (current_Timeout !== null)
    	{
    		clearTimeout(current_Timeout) ;
    	}
    	//on programme chargement image
    	
    	//	alert ( "div : " + target_div + "\n plante : " + plante_id + "\n delai : " + delai) ;
     	current_Timeout = setTimeout(function() 
     		{ 
     			Ajax_appel_serveur("id=" + id,"ajax_chemin_image.php", fn_image_succes, fn_image_echec) ;
     		}, delai );
    }
    
    // Annule changement image.
    this.cancel = function ()
    {
    	//si image deja en attente, on l'annule
    	if (current_Timeout !== null)
    	{
    		clearTimeout(current_Timeout) ;
    		current_Timeout = null ;
    	}
    	
    } 	
}


// Add YG 28/10/2008 : remplace l'alerte javascript
// L'appel de la fonction est générée en PHP, 
// avec titre = '' par defaut 
// et   duree = '' par defaut ou chiffre.
// TODO : detruite panel apres hide()


function chaine_remplace_tout(chaine_depart,remplacer,remplacement)
{
   alert(chaine_depart);
  var aRemplacer = '/'+remplacer+'/g';
  var machaine_depart = chaine_depart ;
  var valRemplacer = remplacement ;
   alert(aRemplacer+" / "+valRemplacer);
 
  chaine_depart = chaine_depart.replace(aRemplacer,valRemplacer);
   alert(chaine_depart);
  return chaine_depart;
}

	
function my_alert(texte, titre, duree) {


// texte = ReplacePlus(texte,'123456789','\\n');
//texte= texte.replace(/123456789/g,"\\n");
texte= texte.replace(/123456789/g,"<br>");

	panel_alerte = new YAHOO.widget.Panel("panel", { fixedcenter:true, visible:true, draggable:true, modal:true,
												   effect    : {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.5} } );

if((!duree)&&(titre))
{
  //ajouter 4 espaces début et fin du titre pour laisser place à la croix de fermeture sur titres courts
  titre="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"+titre+"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
}

if (titre)
{ panel_alerte.setHeader(titre) ; }  // Appeler setHeader avec titre vide enleve le header !
else
{ panel_alerte.setHeader('Alerte') ; }

if (texte.toUpperCase().indexOf('<DIV') == -1)
{
  texte = '<div style="padding:20px;">'+texte+'</div>';
}


	panel_alerte.setBody(texte);
	// panel.setFooter("End of Panel #2");
	
	if (duree) {
		panel_alerte.cfg.queueProperty("close",false)     ;  // Plus de bouton close
		setTimeout("panel_alerte.hide()", duree * 1000)   ; 
		} 
	
	else {
			
		// Ajoute "KeyListener" pour fermeture automatique du panel lors appui sur ESC ou Entree
		var kl = new YAHOO.util.KeyListener(document,     				// Touche detectée sur tout le document, quelque soit le focus
			  								{ keys:[27,13] },  				// ESC ou Entree 							
											{ fn:panel_alerte.hide, 		// Fonction a executer
											  scope:panel_alerte,           // Portee de l'execution de la fonction
											  correctScope:true } );    // "this" est assigne a l'objet en scope

		panel_alerte.cfg.queueProperty("keylisteners", kl);
	}

	panel_alerte.render(document.body);
	
	// on bloque execution tant que le panel est à l'écran
	// TODO !
}


var debugFlag ;


function debug(id, switchOn, append) {
	var el = Dom.get(id) ;

	if (!switchOn) {
		this.print = function(message){} ;
	}
	else {
		if (append) {this.print = function(message) {
			el.innerHTML = el.innerHTML + "<br>" + message ;
		} ;
		}
		else {this.print = function(message) {
			el.innerHTML = message ;
		};
		}
	}
}

var debug0 ;
var debug1 ;
var debug2 ;
var debug3 ;
var debug4 ;
var debug5 ;


// ***********************************************************
//                   AJAX
// ***********************************************************

function Ajax_appel_serveur(requete_texte_ajax,page_traitement_serveur,fn_retour_succes,fn_retour_echec,timeout_serveur,argument) {
	 // exemple requete_texte
	 // "selection=" + encodeURIComponent(getBoxElements('targetBox').join(',')) // encodeURIComponent remplace les carac spéciaux pour qu'ils 'passent'
	 // timeout_serveur en ms (5000 = 5 sec)
	 // callback est le comportement à la réponse du serveur
	 // affichage_reponse : fonction a exécuter
	 
	 if ( !timeout_serveur ) { timeout_serveur=10000 ; }
	 
	 if (!argument) {argument = '' ;}
	 
      // gestion reponse du serveur
      var callback = { 
       
       	
          // EN CAS DE SUCCES DE REPONSE DU SERVEUR, fonction success
	success: fn_retour_succes,
           
           // EN CAS D'ECHEC DE REPONSE DU SERVEUR, fonction failure
	failure: fn_retour_echec,
           
          //on peut passer des arguments. ils seront renvoye dans l'objet reponse. 
          argument: argument, 
           
          //timeout -- if more than timeout_serveur/1000 seconds go by, we'll abort 
          timeout: timeout_serveur
      }; 
       
      //With our callback object ready, it's now time to  
      //make our XHR call using Connection Manager's 
      //asyncRequest method: 
      YAHOO.util.Connect.asyncRequest('POST', page_traitement_serveur, callback, requete_texte_ajax); 
      // testajaxphp est la page serveur qui traitera la requete
      // callback = var de réponse du serveur cf plus haut traitement succès+erreur
      
      /* comment envoyer valeur de plusieurs zones <div id='div_a_envoyer_1'> <div id='div_a_envoyer_2'> avec texte réponse  dans <div id='affiche_reponse'>
     	 YAHOO.util.Connect.asyncRequest('POST', "testajax.php", callback, "zone1=" + encodeURIComponent(Dom.getId('div_a_envoyer_1').firstChild().nodeValue)
		    + "&zone2=" + encodeURIComponent(Dom.getId('div_a_envoyer_2').firstChild().nodeValue)); 
      
      function(oResponse) 
	  	{ 
			Dom.getId('affiche_reponse').firstChild().nodeValue = oResponse.responseText ;
          }
      */
      
      
      // comment choisr où inscrire réponse serveur
      // getBoxElements('targetBox').join(',')  envoie le contenu de targetbox avec une ',' entre chaque élément
      
  } 


// Init autocomplete pour targetBox.

function setAutocompleteTargetBox (oAutoComp, inputEl, targetEl) {

	oAutoComp.itemSelectEvent.subscribe( function(sType, aArgs) {
			var newElement = document.createElement("DIV");
			var newText = document.createTextNode(aArgs[2][0]);
			var attr = document.createAttribute("name");
			attr.value = aArgs[2][1];
			newElement.setAttributeNode(attr);
			// place le texte 
			newElement.appendChild(newText);
			var myTargetEl = Dom.get(targetEl);
			if (myTargetEl) { myTargetEl.getElementsByTagName("span")[0].appendChild(newElement); }
			var myInputEl  = Dom.get(inputEl);
			if (oAutoComp.my_autoClear && myInputEl) { myInputEl.value = "" ; }
			});
}

// Init autocomplete pour formulaire.

function setAutocompleteForm (oAutoComp, inputEl, targetEl) {

	oAutoComp.itemSelectEvent.subscribe( function(sType, aArgs) {
			var myTargetEl = Dom.get(targetEl);
			if (myTargetEl) { myTargetEl.value = aArgs[2][1] }
			var myInputEl  = Dom.get(inputEl);
			if (oAutoComp.my_autoClear && myInputEl) { myInputEl.value = "" ; }
			});
}

// Convertion Arbre -> Tableau pour alimenter auto-complète.
function getArrayFromUlTree(sourceID) {

	var result = [] ;
	var source = Dom.get(sourceID) ;

	function addChildren (node) { // ajoute dans le tableau les elements enfants

		var children = node.childNodes;

		for (var i = 0 ; i < children.length ; ++i) {
			if (children[i].nodeType === 1) {
				result[result.length] = [ YAHOO.lang.trim(children[i].firstChild.nodeValue) ,  //enleve espaces et retour à la ligne ...
					                      children[i].getAttribute('name') ] ;
				if (children[i].nodeName.toLowerCase() == "div" ) { // Descend dans la hierarchie
					addChildren (children[i]) ;
				}
			} 
		}

	}

	if (source) {
		addChildren(source) ;
	}

	return result ;

}

//Renvoi liste des elements d'une boite, identifies par l'attribut 'Name'
//La source doit contenir un element "span" (garanti lors de l'initialisation de la boite)
function getBoxElements(sourceID) {

	var list = [] ; 
	var source = Dom.get(sourceID) ;
	if (source) {

		var spanlist = source.getElementsByTagName("span");

		if (spanlist.length > 0) {

			var nodes = spanlist[0].childNodes ;	

			for (var i = 0 ; i < nodes.length ; ++i) { 

				if (nodes[i].nodeType === 1) {  // On ne s'interesse qu'aux elements. Permet de filtrer les whitespaces, comments... 
					list[list.length] = nodes[i].getAttribute('name') ;
				} 
			}
		}
	}

	return list ;

}

 
//Renvoi liste des ID elements d'une boite, identifies par l'attribut 'Name'
//La source doit contenir un element "span" (garanti lors de l'initialisation de la boite)
function getBoxElementsId(sourceID) {

	var list = [] ; 
	var source = Dom.get(sourceID) ;
	if (source) {

		var spanlist = source.getElementsByTagName("span");

		if (spanlist.length > 0) {

			var nodes = spanlist[0].childNodes ;	

			for (var i = 0 ; i < nodes.length ; ++i) { 

				if (nodes[i].nodeType === 1) {  // On ne s'interesse qu'aux elements. Permet de filtrer les whitespaces, comments... 
					list[list.length] = nodes[i].getAttribute('id') ;
				} 
			}
		}
	}

	return list ;

} 


//Initialisation d'un arbre avec son premier niveau, puis association avec boite Drag.
// div_loader : module s'occupe du changement image.
function treeInit(sourceID, arbre_contenant_id , flagCheckBox,flagDrag,group, div_loader) {
//flagDrag = false ;
	var source = Dom.get(sourceID) ;
	if (source) {

		//instantiate the tree:
		var tree = new YAHOO.widget.TreeView(arbre_contenant_id);
		tree.my_DDRef    = new DDTree(sourceID, group);   // Instancie le gestionnaire DD pour l'arbre
		tree.my_group    = group ;


		//l'arborescence est chargee progressivement, suivant les besoins.
		tree.setDynamicLoad(loadNodeData) ;

		//construit 1er niveau
		buildChildren (tree.getRoot(), source, div_loader) ;


		// Expand and collapse happen prior to the actual expand/collapse,
		// and can be used to cancel the operation
		//tree.subscribe("expand", function(node) {
				// return false; // return false to cancel the expand
		//		});

		if (flagDrag) {
			tree.subscribe("expandComplete", function(node) {
					//On initialise les elements comme draggables
					attachDD(node);
					});
		}

		//tree.subscribe("collapse", function(node) {
		//		});


		// Trees with TextNodes will fire an event for when the label is clicked:
		// NOTE YGY : ne marche pas, car ici on utilse HTMLNode a la place de TextNode 
		//            les evenements generiques ("click", "onClick" ?) restent sans effet.
		// tree.subscribe("labelClick", function(node) {
		//alert(e) ;
		//});

		//The tree is not created in the DOM until this method is called:
		tree.draw();

		//On initialise les elements comme draggables
		if (flagDrag) { attachDD(tree.getRoot()); }

		return tree ;

	}
}

//Construit un niveau d'arborescence
function buildChildren (treeNode,source,div_loader) {

	var sourceNodes = source.childNodes ;	

	for (var i = 0 ; i < sourceNodes.length ; ++i) { 

		var myNode = sourceNodes[i] ;

		if (myNode.nodeType === 1) {  // On ne s'interesse qu'aux elements. Permet de filtrer les whitespaces, comments... 
		
			// on ajoute gestion onClick dans le DIV. Les autres approches n'ont pas fonctionné :
			// - HTMLNode ne déclenche pas l'évélement labelClick.
			// - On ne dispose pas de l'ID du DIV (généré pendant le render ?).  
		
		    var HTMLClick = ''
		    var myValue  = '<DIV name="' + myNode.getAttribute('name') + '">' + myNode.firstChild.nodeValue +  '</DIV>'; // partie texte de l'element
			var myTag    = myNode.nodeName ;
			var myFlagDrag = (myNode.className != "noDrag") ;
			var newNode = {html: myValue  ,
				sourceEl   : myNode ,
				flagDrag   : myFlagDrag ,
				div_loader : div_loader  // Sauvegarde pour niveau d'arborescence suivant
			} ;
			
			
			// attache noeud à l'arbre.
			var curNode = new YAHOO.widget.HTMLNode(newNode, treeNode, false, true);
			
			// recupere name à partir de l'event, ou chaine vide en cas de probleme.
			function get_name(ev)
			{
			  var el = YAHOO.util.Event.getTarget(ev) ; 
			  return el?el.getAttribute('name'):'' ;
			}
				
			if (div_loader) {
				// on réagit aux évenements :
				// "click"     -> change image sans delai.
				// "mouseover" -> change image au bout d'un certain delai
				// "mouseout"  -> annule changement image.
				YAHOO.util.Event.addListener(curNode.contentElId,  //Identifiant du DIV
					"click",				
					function(ev){div_loader.change(get_name(ev));});
					
				YAHOO.util.Event.addListener(curNode.contentElId,  
					"mouseover",	
					function(ev){var name = get_name(ev) ;
					             if (name) div_loader.change(get_name(ev),
					             							 div_loader.delai);});	
				
				YAHOO.util.Event.addListener(curNode.contentElId,  
					"mouseout",		
					function(ev){var name = get_name(ev) ;
					             if (name) div_loader.cancel();});	
			} 
			
			
			if (myTag.toLowerCase() == "div") { // Element est une liste
				curNode.isLeaf = false ;
			} 
			else {
				curNode.isLeaf = true ;
			}

		} 

	}
}

function loadNodeData (node, fnCallBack) {

	buildChildren (node, node.data.sourceEl, node.data.div_loader) ;
	fnCallBack() ;
}

function attachDD (node) {

	var children = node.children;
	var myTree   = node.tree ;

	for (var i = 0 ; i < children.length ; i ++) {
		if (children[i].data.flagDrag) {
			myTree.my_DDRef.addEl(children[i].getContentEl().getElementsByTagName('DIV')[0],myTree.my_group) ;
		}
	}

}

// Les elements DD de l'arbre sont definies dans un objet similaire a DDBox (cf plus bas)
// TODO : version plus propre, avec class de base et heritage

function DDTree (id,  sGroup, config) {

this.id = id ;
this.elements = [] ;   // On stocke les references des objets DD pour changer en lot leurs proprietes.
this.length   = 0  ;   // Nombre d'elements
this.copy     = true ;
//this.el : element conteneur

// initialisation d'un élément. Doit être pouvoir appelée de l'extérieur pour ajout progressif des elements. 
this.addEl = function (el, sGroup, config) {

		var dd = new DDElement(el, sGroup, config) ;
		this.elements[this.length] = dd    ;  
		dd.my_box    = this     ;  // Boite d'origine 
		dd.my_curBox = this     ;  // Boite en cours 
		dd.my_origEl = el ; //lien change pour les clones
		return dd ;
} 


// les clones doivent etre effaces
this.deleteEl = function (el) {
	var dd = DDM.getDDById(el) ;
	if (dd) { dd.unReg() ;}
    el.parentNode.removeChild(el) ;
}



this.el = Dom.get(id) ; // Version cross-browser de document.getElementById(id);


} // Fin function DDTree


// Initialise les elements de la box comme Draggrable
// Et optionnellement, la box elle meme comme Droppable.
function DDBox(id, flagDrop, sGroup, config) {

this.id = id ;
this.elements = [] ;   // On stocke les references des objets DD pour changer en lot leurs proprietes.
this.length   = 0  ;   // Nombre d'elements
this.numCol   = 1  ;   // Decide du mode d'insertion (vertical ou horizontal). La valeur 1 correspond a une liste 
this.insertionMode = 1 ; // 3 etats : 0 -> toujours en haut de conteneur. 1 -> insertion entre elements  2 -> en fin de liste. 
//this.el : element conteneur
//this.droppable  : Vrai si on peut deposer des elements dans la boite (faux par defaut)
//this.reorganizable : Vrai si on peut réarranger l'ordre des elements (faux par defaut)

// initialisation d'un élément. Doit être pouvoir appelée de l'extérieur pour ajout de clones.
this.addEl = function (el, sGroup, config) {

		var dd = new DDElement(el, sGroup, config) ;
		this.elements[this.length] = dd    ;  
		dd.my_box    = this     ;  // Boite d'origine 
		dd.my_curBox = this     ;  // Boite en cours 
		dd.my_rank   = this.length++   ;  // Pour rangement automatique
		dd.my_origEl = el ; //lien change pour les clones
		return dd ;
} 

// les clones doivent etre effaces
this.deleteEl = function (el) {
	var dd = DDM.getDDById(el) ;
	if (dd) { dd.unReg() ;}
	alert ( "dd toujours present : " + DDM.isDragDrop(el) ) ;
    el.parentNode.removeChild(el) ;
}



var el = Dom.get(id) ; // Version cross-browser de document.getElementById(id);

// On doit initialiser comme Draggrable tous les elements du conteneur.
// Ceux-ci sont ranges dans un bloc span, 

var spanlist = el.getElementsByTagName("span");
var span ;

// On le stocke (pour les insertions d'elements), en le creant au besoin
if (spanlist.length > 0) { span = spanlist[0] } 

else {span = document.createElement("span") ;
	  el.appendChild(span) ;
}

this.el = span ;

// Conteneur droppable ?
if (flagDrop) { 
	this.droppable = true ;
	var ddbox = new YAHOO.util.DDTarget(id, sGroup, config) ;
	ddbox.my_isBox = true ;
	ddbox.my_box   = this ;
}

var nodes = span.childNodes ;	

for (var i = 0 ; i < nodes.length ; ++i) { 

	if (nodes[i].nodeType === 1) {  // On ne s'interesse qu'aux elements. Permet de filtrer les whitespaces, comments... 
		this.addEl(nodes[i], sGroup, config)
	} 

} 

} // Fin function DDBox

// On definit les methodes de la class DDBox
DDBox.prototype = { // TODO : verifier si DDBox.prototype.methode = function ne serait pas plus optimise. En tout cas, cela n'est pas equivalent.
addToGroup: function(sGroup) {

				for (var i = 0 ; i < this.elements.length ; i=i+1) {
					this.elements[i].addToGroup(sGroup) ;
				} 
			},
setStyle: function (attribute, value) {
			  
			  for (var i = 0 ; i < this.elements.length ; i=i+1) {
				  var el = this.elements[i].getDragEl() ; //Proxy, typiquement
				  Dom.setStyle(el, attribute, value) ;
			  } 
		  } 
}

//////////////////////////////////////////////////////////////////////////////
// custom drag and drop implementation
//////////////////////////////////////////////////////////////////////////////
DDElement = function(id, sGroup, config) {

DDElement.superclass.constructor.call(this, id, sGroup, config);

this.curX = 0;
this.curY = 0;
this.lastX = 0 ;
this.lastY = 0 ;
this.validDrop = false ;
};

YAHOO.extend(DDElement, YAHOO.util.DDProxy, {

startDrag: function(x, y) {

	// make the proxy look like the source element
	var dragEl = this.getDragEl();
	var clickEl = this.getEl();
//    Dom.setStyle(clickEl, "visibility", "hidden");

	dragEl.innerHTML = clickEl.innerHTML;

	// TODO? : clean way to copy all style 
	Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color"));
	Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor"));
	Dom.setStyle(dragEl, "text-align", Dom.getStyle(clickEl, "text-align"));
	//Dom.setStyle(dragEl, "border-style", Dom.getStyle(clickEl, "border-style")) ;
	//Dom.setStyle(dragEl, "border-width", Dom.getStyle(clickEl, "border-width")) ;
	//Dom.setStyle(dragEl, "border-color", Dom.getStyle(clickEl, "border-color")) ;

    // Definit ghost box (pour afficher pointilles)
	var ghBox = Dom.get('ghost') ;
	if  ( !ghBox ) {
	    ghBox = document.createElement('div');
		ghBox.setAttribute('id', 'ghost'); 
	}

	// Alternative : utiliser 
	// var region = YAHOO.util.Dom.getRegion(clickEl);
	// var elmHeight = region.bottom - region.top;
	// var elmWidth = region.right - region.left;
	Dom.setStyle(ghBox, 'height', clickEl.clientHeight + 'px') ;

    this.my_destEl = ghBox ;

    // on enleve l'element source si deplacement (et non copie)
	if (!this.my_curBox.copy) {
		//clickEl.parentNode.replaceChild(ghBox, clickEl);
		clickEl.parentNode.removeChild(clickEl);
		DDM.refreshCache();
	}

},

endDrag: function(e) {

	var destEl = this.my_destEl;
	var proxy = this.getDragEl();
	if (debugFlag) {
		debug0.print("-----endDrag-- ");
		debug0.print("i am " + this);
		debug0.print("dest" + destEl.id + destEl );
		debug0.print(destEl.parentNode)  ;
	}
    // si destEL n'est pas dans l'arbre : element n'a pas pu etre drope
	if (!destEl.parentNode) { 
		this.onInvalidDrop() ;
		destEl = this.my_destEl ;
	}

	// Show the proxy element and animate it to the src element's location
	Dom.setStyle(proxy, "visibility", "");
	var a = new YAHOO.util.Motion( 
		proxy, { 
			points: { 
				to: Dom.getXY(destEl)
			}
		}, 
		0.2, 
		YAHOO.util.Easing.easeOut ) 
	var proxyid = proxy.id;
	var thisid = this.id;
    var thisobj = this ;

	if (!this.my_box.copy || this.validDrop ) {

		// Element à inserer 
		var srcEL ;
		if (this.my_box.copy) { // on insere clone
			srcEl = this.getEl().cloneNode(true) ;
			srcEl.removeAttribute('id') ; // evite d'avoir 2 fois le meme ID.  YUI en construira un nouveau.

			// Hide the proxy and show the source element when finished with the animation
			a.onComplete.subscribe(function() {
					Dom.setStyle(proxyid, "visibility", "hidden");
					destEl.parentNode.replaceChild(srcEl, destEl );
					// on copie aussi l'instance drag
					var ddgroups = new Array() ;
					for (var i in thisobj.groups) {ddgroups.push(i) ;} 
					var ddgroup1 = "";
					if (ddgroups[0]) {ddgroup1 = ddgroups[0];} 
					var dd = thisobj.my_box.addEl(srcEl , ddgroup1 , thisobj.config) ;
					for (var i = 1 ; i < ddgroups.length ; ++i) {
					dd.addToGroup(ddgroups[i]) ;}
					dd.my_curBox      = thisobj.my_curBox ;
					thisobj.my_curBox = thisobj.my_box ;
					dd.my_origEl = thisobj.getEl() ; // lien direct vers element d'origine.
	                DDM.refreshCache();
					if (thisobj.my_curBox.onChange) {thisobj.my_curBox.onChange() ;} // Si une fonction a ete definie, on l'appelle.
					});
		}
		else {
srcEl = this.getEl();
			// Hide the proxy and show the source element when finished with the animation
			a.onComplete.subscribe(function() {
					Dom.setStyle(proxyid, "visibility", "hidden");
					destEl.parentNode.replaceChild(srcEl, destEl );
					DDM.refreshCache();
					if (thisobj.my_curBox.onChange) {thisobj.my_curBox.onChange() ;} 
					});
		}


	}

	else { // Pas d'insertion element
		a.onComplete.subscribe(function() {
				Dom.setStyle(proxyid, "visibility", "hidden");
				});
	}

	a.animate();


},

onDrag: function(e) {
			// Keep track of the direction of the drag for use during onDragOver
			this.lastY = this.curY ;
			this.curY  = Event.getPageY(e);

			// Dans le cas multi-colonnes. On doit detecter les mouvements horizontaux.
			this.lastX = this.curX ;
			this.curX  = Event.getPageX(e);

		},

onDragDrop: function(e, id) {
/*
// get the drag and drop object that was targeted
var destDD;

if ("string" == typeof id) {
	destDD = YAHOO.util.DDM.getDDById(id);
} else {
	destDD = YAHOO.util.DDM.getBestMatch(id);
}

debug3.print("DRAG DROP - drop ? " + DDM.interactionInfo.drop);
debug4.print("DRAG DROP - box ? " + destDD.my_isBox) ;
*/
},

onDragOut: function(e, id) {

			if (debugFlag) {
				debug0.print("--onDragOut-- " + (this.my_destEl.parentNode) ) ;
				debug0.print("event " + e ) ;
				debug0.print("over" + DDM.interactionInfo.over.length ) ;
				debug0.print("---------")
                // ne change rien si la souris ne bouge pas (NB : event onMouseMove non trouve !)
				if ((this.lastX == this.curX)
						&&	(this.lastY == this.curY) ) {debug0.print ("NO MOVE !!") ;}
			}
	    // Si plus de target, on enleve la boite "ghost"
		if (DDM.interactionInfo.over.length === 0 ) {
			var myparent = this.my_destEl.parentNode ;
			if (myparent) {
				myparent.removeChild(this.my_destEl) ;
			}
			this.validDrop = false ;
		}

},


onDragOver: function(e, id) {

if (debugFlag) {
	debug0.print("Id : " + id) ;
	debug0.print("Id0: " + typeof id[0]) ;
}

	var targetDD =  id[0] ;
	// si plusieurs targets, il faut ecarter la boite (qui sinon se verra toujours selectionnee) 
	for (var i = 1 ; i < id.length ; ++i) {
		var candidat = id[i] ;
	  if (!candidat.my_isBox) {
		  targetDD = candidat ;
		  if (candidat.cursorIsOver) {break;}
	  }
	}

	/*	if ("string" == typeof id) {
		targetDD = YAHOO.util.DDM.getDDById(id);
	} else {

		targetDD = YAHOO.util.DDM.getBestMatch(id);
	}
*/
	var src   = this.my_box  ;
	var srcEl = this.getEl() ;
	var destEl = this.my_destEl;        // ghost box 
	var targetEl = targetDD.getEl();

	if (targetDD.my_isBox) {
		var destBox = targetDD.my_box ;
		var validTarget = true ;
	} else {	// L'element est droppable, mais la boite destination pas forcément.
		var destBox = targetDD.my_curBox ;
		var validTarget = DDM.isLegalTarget(this, destBox)
				       || ((destBox == this.my_box) && destBox.reorganizable) ;
	}
	var p = destBox.el;

	if (debugFlag) {
		debug0.print("--DragOver-- " + (destEl.parentNode) ) ;
		debug1.print("destEl id : " + id + "/" + targetDD);
		debug2.print("srcEl : " + srcEl.id)  ;
	}

	if (!validTarget) { // Inserable nul part : on enleve boite destination "ghost"

		if (destEl.parentNode) { 
			destEl.parentNode.removeChild(destEl) ;
		   	DDM.refreshCache();
		}
		this.validDrop = false ;
		

	}

	else {

		// En cas d'element duplicable, on doit verifier si on peut l'inserer plusieurs fois.
		if (src.copy && !destBox.multi) {

			var elName = srcEl.getAttribute('name') ;
			var elTag  = srcEl.tagName ;
if (debugFlag) {debug0.print("sourceName : " + elName + "(" + elTag + ")" ) ; 
				debug0.print ("Clone : " + (Dom.getElementsBy( function (el) { return (el.getAttribute('name') == elName) ; } , elTag , p)) );}
			if (Dom.getElementsBy( function (el) { return (el.getAttribute('name') == elName) ; } , elTag , p).length !== 0 ) {
			if (debugFlag) {	debug0.print("clone exists");}
				if (destEl.parentNode) { destEl.parentNode.removeChild(destEl) ; }
		        this.validDrop = false ;
		DDM.refreshCache();
				return ;
			}
		}


		if (targetDD.my_isBox) { // on ajoute en fin ou debut de liste

			if (debugFlag) {debug0.print ("insertion " + destBox.insertionMode);}
			if (destBox.insertionMode === 0) {
				if (p.firstChild != destEl) { // On execute insertion + refresh seulement si non deja inseree
					p.insertBefore(destEl , p.firstChild);
					DDM.refreshCache();
				}
			} else {
				if (p.lastChild != destEl) {
					p.appendChild(destEl);
					DDM.refreshCache();
				}
			}

			// On met a jour tant qu'on a destBox sous la main 
			this.my_curBox = destBox ;  

		}

		else { 

			if (debugFlag) {debug4.print("dest Box : " + destBox.id + " this box " + this.my_box.id 
				+ "reorganizable " + destBox.reorganizable) ;}


			if (destBox.insertionMode === 2) { 
				if (p.lastChild !== destEl) {
					p.appendChild(destEl);
					DDM.refreshCache();
				}
			} else if (destBox.insertionMode === 0) {
				if (p.firstChild !== destEl) { 
					p.insertBefore(destEl , p.firstChild);
					DDM.refreshCache();
				}
			} else {

				/* Ou inserer element, suivant type liste/tableau */
				if (destBox.numCol !== 1) { 
					var goingBefore = (this.curX < this.lastX) ; 
				} else { 
					var goingBefore = (this.curY < this.lastY) ; 
				}

				if (goingBefore) {// insert above/left
					if (targetEl.previousSibling !== destEl) {
					p.insertBefore(destEl, targetEl) ;
					DDM.refreshCache();
					}
				} else {// insert below/right
					if (targetEl.nextSibling != destEl) {
					p.insertBefore(destEl, targetEl.nextSibling); 
					DDM.refreshCache();
					}
				}

				this.my_curBox = destBox ;

			}
		}


		this.validDrop = true ;
	}
},

onInvalidDrop: function(e) { // On remet cellule a sa place 

this.validDrop = false ;
this.my_curBox = this.my_box

debug0.print ("----invalid-- copy : " + this.my_box.copy) ;
debug0.print ("orig : " + this.my_origEl.id) ;
debug0.print ("i am " + this) ;

    if (this.my_box.copy) { // Copy : on pointe juste sur element d'origine

		this.my_destEl = this.my_origEl ;
	
	}

	else { // Il faut retrouver la position d'origine.

		var box = this.my_box.el ;			   
		var rank = this.my_rank ;
		var nodes = box.childNodes ;	

		var insertnode = null ;

		for (var i = 0 ; i < nodes.length ; ++i) { 

			if (nodes[i].nodeType === 1) {  // On ne s'interesse qu'aux elements. Permet de filtrer les whitespaces, comments... 
				var ddEl = DDM.getDDById(nodes[i].getAttribute("id"));
				if (ddEl) { // Certains elements peuvent ne pas être DD.
					if(ddEl.my_rank >= rank )  {
						insertnode = nodes[i] ;
						break ; }
				}
			}

		}

		box.insertBefore(this.my_destEl , insertnode) ;	

		DDM.refreshCache();
	}
   }

});


