var NTrees=[];

/**
 * constructeur
 * @param name            (string)  nom identifiant de l'arbre
 * @param nodes           (tableau) description des noeuds racines (cf tree_nodes.js)
 * @param format          (tableau) description du format (cf tree_format.js)
 */
function COOLjsTreePRO(name, nodes, format) {
	if (und(name)) {
		// définition des sous-classes -> on arrête de suite 
		return;
	}
	// définition du nom de constructeur
	this.constructorName = "COOLjsTreePRO";

	this.format = format;
	this.document = document;  // par défaut, utilisation du document courant
	this.name=name;
	this.nodesDef = nodes;
	this.bw = new bw_check();
	this.ver3 = this.bw.ver3;
	this.ns4 = this.bw.ns4;
	this.fmt = new COOLjsTreeFmtPRO(format, this);
	if (und(NTrees)) {
		NTrees=[];
	}
	NTrees[this.name] = this;
	// Tableau contenant TOUS les noeuds de l'arbre
	this.Nodes = [];
	// création de la racine
	var l_rootObj = {"def":{"format":format.nodeFormat}}
	this.rootNode = new this.nodeConstructor(this, null, l_rootObj);
	
	this.selectedNode = null;
	this.selUIdPath = null;
	this.shadowedNode = null;
	this.maxWidth = 0;
	this.maxHeight = 0;
    this.ondraw = null;
    // on conserve le code HTML définissant les noeuds de l'arbre
    this.htmlCode = "";
    // éventuel div précisant l'endroit où contruire l'arbre
    this.div = null;
}

/**
 * Définition du constructeur des noeuds
 */
COOLjsTreePRO.prototype.nodeConstructor = COOLjsTreeNodePRO;

/**
 *
 */
COOLjsTreePRO.prototype.setVisible = function(a_visible) {
	this.conteneur.style.visibility = a_visible ? "visible" : "hidden";
/*
	if (this.fmt && this.fmt.back) {
		this.fmt.back.setVisible(a_visible);
	}
*/	
}


/**
 *
 */
COOLjsTreePRO.prototype.moveTo=function(x,y) {
	this.fmt.back.top = y;
	this.fmt.back.left = y;
	this.fmt.back.moveTo(x,y);
	this.fmt.top = y;
	this.fmt.left = x;
	this.draw();
}

/**
 * getNodeByName(name) --> recherche d'un noeud par son nom.
 * @param nm nom du noeud à rechercher.
 * @return la référence du noeud dans Nodes s'il existe, null sinon.
 */
COOLjsTreePRO.prototype.nbn=function(nm) {
	for (var i = 0; i < this.Nodes.length; i++) {
		if (und(this.Nodes[i])) {
			continue;
		}
		if (this.Nodes[i].text == nm) {
			return this.Nodes[i];
		}
	}
	return null;
}

/**
 * Construction des noeuds fournis.
 */
COOLjsTreePRO.prototype.buildNodes=function(a_addedNodes, a_has2Clear) {
	var s = "";
	if (a_has2Clear) {
		this.rootNode.next = null;
	}
	// on écrit le code HTML représentant les noeuds
	var l_node;
	for (var i = 0; i < a_addedNodes.length; i++){
		l_node = a_addedNodes[i];
		l_node.initImages();
		if (this.ver3 && l_node.hasChildren()) {
			l_node.expanded = true;
		}
		s += l_node.init();
	}
	// on ajoute ce code à la page
	if (this.fmt.rel) {
		var l_contName = 'cls' + this.name + '_back';
		var l_conteneur;
		if (this.bw.ns4) {
			// on regarde si le layer principal existe déjà 
			l_conteneur = this.document.layers[l_contName];
			if (und(l_conteneur)) {
				// premier affichage
				var bgc = this.fmt.back.color == "" ? "" : ' bgcolor="' + this.fmt.back.color + '" ';
				s = '<ilayer ' + bgc + ' id="' + l_contName + '" width="' + this.fmt.rels[0] + '" height="' + this.fmt.rels[1] + '">' + s + '</ilayer>';
			}
		} else {
			// on regarde si le div principal existe déjà 
			l_conteneur = this.document.all? this.document.all[l_contName] : this.document.getElementById(l_contName);
			if (und(l_conteneur)) {
				// premier affichage
				var bgc = this.fmt.back.color == "" ? "" : " background-color:" + this.fmt.back.color + ";";
				s='<div id="' + l_contName + '" style="position:relative;left:0px;' + bgc + 'top:0px;width:' + this.fmt.rels[0] + 'px;height:' + this.fmt.rels[1] + 'px;">' + s +'</div>';
			}
		}
		if (und(l_conteneur)) {
			// Ecriture du code HTML généré dans la page ou le div
			if (this.div) {
				xbSetInnerHTML(this.div, s);
			} else {
				this.document.write(s);
			}
		} else {
			// écriture du code dans le conteneur
			if (a_has2Clear) {
				xbSetInnerHTML(l_conteneur, s);
			} else {
				// on ajoute simplement les nouveaux noeuds
				this.tmp = xbGetElementById("tmp");
				xbSetInnerHTML(this.tmp, s);
			}
		}
		this.conteneur = xbGetElementById(l_contName);
	} else {
		// Ecriture du code HTML généré dans la page
		if (a_has2Clear) {
			this.htmlCode = "";
		}
		this.htmlCode += s;
		this.document.write(this.htmlCode);
	}
	this.fmt.back.el = this.bw.ns4 ? this.document.layers[this.fmt.back.name] : this.document.all ? this.document.all[this.fmt.back.name] : this.document.getElementById(this.fmt.back.name);
	
	this.manageDragDrop(a_addedNodes, a_has2Clear);
}

/**
 * Définition des caractères drag&drop des noeuds fournis
 * @param a_addedNodes	tableau des noeuds à traiter
 * @param a_has2Clear	true s'il faut effacer les anciens noeuds
 */
COOLjsTreePRO.prototype.manageDragDrop = function(a_addedNodes, a_has2Clear) {
	if (!this.ver3) {
		var l_node;
		var l_el;
		for (var i = 0; i < a_addedNodes.length; i++) {
			l_node = a_addedNodes[i];
			// on définit la partie graphique
			l_el = this.getEl(l_node);
			if (l_el.parentNode == this.tmp) {
				// il s'agit d'un noeud ajouté -> on le rajoute dans le conteneur
				this.conteneur.appendChild(l_el);
			}
			// on l'abonne au mécanisme de drag & drop
			if (l_node.isDragable()) {
				var l_drag = getChildByTag(l_el, "div");
				l_drag._node = l_node;
				l_drag.createGhost = l_node.createGhost;
				l_drag.clearCSS = l_node.clearCSS;
				if (window.setDragable) {
					window.setDragable(l_drag);
				}
			}
			if (l_node.isDropable()) {
				if (window.removeDropListener) {
					removeDropListener(l_el.id);
				}
				l_el._node = l_node;
				l_el.highlight = l_node.highlight;
				if (window.addDropListener) {
					addDropListener(l_el);
				}
			}
		}
	}
}

/**
 * Définition de l'élément HTML associé (par son ID).
 */
COOLjsTreePRO.prototype.getEl=function(node) {
	if (this.ns4) {
		node.el = this.fmt.rel ? this.fmt.back.el.layers[node.id() + "d"] : this.document.layers[node.id() + "d"];
	}
	else {
		node.el = this.document.all ? this.document.all[node.id()+"d"] : this.document.getElementById(node.id() + "d");
	}
	return node.el;
}

/**
 * Affichage de l'arbre complet
 */
COOLjsTreePRO.prototype.draw=function() {
	if ( this.ver3 ) {
		return;
	}
	this.currTop = this.fmt.top;
	this.maxHeight = 0;
	this.maxWidth = 0;
	for (var i = 0; i < this.rootNode.children.length; i++) {
		this.rootNode.children[i].draw(true);
	}
    if (this.fmt.rel && this.fmt.resize || !this.fmt.rel) {
        this.fmt.back.resize(this.maxWidth - this.fmt.left, this.maxHeight - this.fmt.top);
    }
	if (this.ondraw != null) {
		this.ondraw(this);
	}
	
	// on indique éventuellement le noeud sélectionné
	if (this.selectedNode) {
		var l_node = this.selectedNode;
		if (l_node.getFormatProp("selected.change") == true) {
			l_node.setColors(l_node.getFormatProp("selected.bgcolor"), 
							 l_node.getFormatProp("selected.color"), 
							 l_node.getFormatProp("selected.css"));
		}
	}
}

/**
 * Ajoute au noeud parent fourni de nouveaux noeuds
 * @param a_parent  noeud à compléter
 * @return tableau des fils chargés
 */ 
COOLjsTreePRO.prototype._loadChildren = function(a_parent) {
	// on flag le chargement		
	a_parent.childrenLoaded = true;
	// on ne fait rien d'autre
	return null;
}

/**
 * Ouverture/fermeture d'un noeud.
 * @param a_index 	(int)		indice du noeud dans le tableau global de l'arbre this.Nodes[].
 * @param a_nd    	(boolean)	commande empêchant le draw du noeud.
 * @param a_sel   	(boolean)	commande la surbrillance du noeud lors de sa sélection.
 * @param a_forced	(boolean)	si true, force l'ouverture du noeud
 */
COOLjsTreePRO.prototype.expandNode=function(a_index, a_nd, a_sel, a_forced) {
	writeMyTrace(window.name);
	if ( this.ver3 ) {
		return;
	}
	// on conserve l'ancien noeud sélectionné
	var l_oldSelected = this.selectedNode;
	
	var l_node = this.Nodes[a_index];
	if (und(l_node)) {
		// pas de noeud
		return;
	}
	var l_pNode = l_node.parentNode ? l_node.parentNode : null;
	if (a_sel == true) {
		this.selectNode(a_index);
	}
	if (l_node.childrenLoaded == false) {
		var l_added = true;
		// on essaie de charger les enfants, si la méthode existe
		if (this.loadChildren) {
			var l_loaded = this.loadChildren(l_node);
			l_added = l_loaded[0];
			if (l_added == true) {
				this.addDynamicNodes(l_loaded[1], l_node, false);
				l_node.childrenLoaded = true;
				// on met éventuellement à jour les images du noeud
				if (!l_node.hasChildren()) {
					// le noeud est ouvert, mais n'a pas de fils 
					l_node.expanded = true;
					// -> on change simplement les images
					l_node.updateImages();
				}
			} else {
				// le chargement est en cours -> il faut à tout prix arrêter le script
				return;
			}
		} else {
			// la méthode n'existe pas -> on flag pour la prochaine fois
			l_node.childrenLoaded = true;
			l_node.expanded = true;
			// et on change simplement les images
			l_node.updateImages();
		}
		if (!l_added) {
			// les noeuds sont en cours de chargement -> on arrête là
			return;
		}
	}
	
	if (l_node.hasChildren() || (l_node.getParamProp("first") == true)) {
		var l_shadow = false;
		if (und(a_forced) || (a_forced == false)) {
			l_node.expanded = !l_node.expanded;
		} else {
			// on force l'ouverture
			l_node.expanded = true;
		}
		l_node.updateImages();
		if (!l_node.expanded) {
			l_node.hideChildren();
			if (l_node.getFormatProp("singleBranch") == true) {
				// on désactive la partie expanded
				if (this.expandedNode == l_node) {
					this.clearExpandedNode();
				}
			}
		} else {
			if (l_node.getFormatProp("singleBranch") == true) {
				// on "désélectionne" éventuellement le noeud sélectionné
				l_shadow = (l_oldSelected != l_node);
				
				// une seule branche ouverte autorisée
				for (var i = 0; i < this.Nodes.length; i++) {
					if (und(this.Nodes[i])) {
						continue;
					}
					this.Nodes[i].show(false);
					if ( this.Nodes[i] != l_node && this.Nodes[i].parentNode == l_pNode) {
						this.Nodes[i].expanded = false;
						this.Nodes[i].updateImages();
					}
				}
			}
		}
        if (!a_nd) {
        	this.draw();
        }
        if (l_shadow) {
	    	this.shadowNode(l_oldSelected);
	    }
        if (l_node.expanded) {
	    	this.expNode(l_node);
	    }
		if (!a_nd && this.fmt.cook) {
			this.saveState();
		}

	}
}

/**
 * Gestion de la couleur de sélection d'un noeud.
 * @param index du noeud sélectionné.
 * @param force
 */
COOLjsTreePRO.prototype.selectNode=function(a_index , a_force) {
	var l_node = this.Nodes[a_index];
	if (und(l_node) || !l_node.isSelectable()) {
		// on ne fait rien
		return;
	}
	if (a_force || this.selectedNode != l_node ) {
		// on nettoie le shadow
		this.clearShadowNode();

		var l_oldSel = this.selectedNode;
		if (l_oldSel && (l_oldSel.getFormatProp("selected.change") == true)) {
			l_oldSel.setColors("", "");
		}

		this.selectedNode = l_node;
		this.selUIdPath = l_node.getUIdPath();
		if (l_node.getFormatProp("selected.change") == true) {
			l_node.setColors(l_node.getFormatProp("selected.bgcolor"), 
							 l_node.getFormatProp("selected.color"), 
							 l_node.getFormatProp("selected.css"));
		}

		if (this.onSelectNode != null) {
			this.onSelectNode(l_oldSel, l_node);
		}
	}
}

/**
 * Désélectionne le noeud courant
 */
COOLjsTreePRO.prototype.unselectNode = function() {
	// on regarde le shadow
	this.clearShadowNode();
	
	// on regarde le selected
	var l_oldSel = this.selectedNode;
	this.selectedNode = null;
	this.selUIdPath = null;
	if (l_oldSel) {
		if (l_oldSel.getFormatProp("selected.change") == true) {
			l_oldSel.setColors("", "");
		}
		if (this.onSelectNode != null) {
			this.onSelectNode(l_oldSel, null);
		}
	}
}

/**
 * Passe le noeud courant en mode "shadow"
 * @param a_oldSel (TreeNode)	ancien noeud sléectionné (si null, utilise le noeud sélectionné)	
 */
COOLjsTreePRO.prototype.shadowNode = function(a_oldSel) {
	var l_oldSel = (und(a_oldSel)) ? this.selectedNode : a_oldSel;
	if (l_oldSel) {
		//this.selectedNode = null;
		if (l_oldSel.getFormatProp("shadow.change") == true) {
			l_oldSel.setColors(l_oldSel.getFormatProp("shadow.bgcolor"), 
							   l_oldSel.getFormatProp("shadow.color"), 
							   l_oldSel.getFormatProp("shadow.css"));
		}
		this.shadowedNode = l_oldSel;
	}
}

/**
 * Repasse le noeud "shadow" en affichage normal
 */
COOLjsTreePRO.prototype.clearShadowNode = function() {
	var l_shadow = this.shadowedNode;
	if (!und(l_shadow)) {
		this.shadowedNode = null;
		if (l_shadow.getFormatProp("shadow.change") == true) {
			l_shadow.setColors("", "");
		}
	}
}

/**
 * Passe le noeud courant en mode "shadow"
 * @param a_oldSel (TreeNode)	ancien noeud sléectionné (si null, utilise le noeud sélectionné)	
 */
COOLjsTreePRO.prototype.expNode = function(a_node) {
	this.clearExpandedNode();
	if (!a_node.isSelectable()) {
		if (a_node.getFormatProp("expanded.change") == true) {
			a_node.setColors(a_node.getFormatProp("expanded.bgcolor"), 
							   a_node.getFormatProp("expanded.color"), 
							   a_node.getFormatProp("expanded.css"));
		}
		this.expandedNode = a_node;
	}
}

/**
 * Repasse le noeud "shadow" en affichage normal
 */
COOLjsTreePRO.prototype.clearExpandedNode = function() {
	var l_expanded = this.expandedNode;
	if (!und(l_expanded)) {
		this.expandedNode = null;
		if (l_expanded.getFormatProp("expanded.change") == true) {
			l_expanded.setColors(l_expanded.getFormatProp("expanded.bgcolor"), 
									l_expanded.getFormatProp("expanded.color"), 
									l_expanded.getFormatProp("css"));
		}
	}
}

/**
 * Modifie le texte du noeud "sélectionné"
 * @param a_nodeText  texte à utiliser
 */
COOLjsTreePRO.prototype.updateText = function(a_nodeText) {
	var l_node = this.selectedNode;
	if (l_node == null) {
		// peut-être le shadow ?
		l_node = this.shadowedNode;
	}
	if (l_node != null) {
		// on en tient un
		l_node.setText(a_nodeText);
	}
}

/**
 * Ajout d'un noeud. Cet ajout se fait toujours en fin de liste (emplacement Nodes[Nodes.length])
 * @param a_node référence du noeud à ajouter.
 * @return référence du noeud ajouté.
 */
COOLjsTreePRO.prototype.addNode=function(a_node) {
	var l_parentNode = a_node.parentNode;
	a_node.next = null;
	if (l_parentNode == null) {
		l_parentNode = this.rootNode;
	}			
	// insertion d'un noeud -> on l'ajoute au parent
	l_parentNode.addChild(a_node);

	// on ajoute le noeud
	a_node.index = this.Nodes.length;
	this.Nodes[this.Nodes.length] = a_node;
	// on définit l'arbre courant
	a_node.treeView = this;

	return a_node;
}

/**
 * Instanciation récursive d'un objet noeud à partir du tableau TREExx_NODES.
 * @param a_parent		noeud parent du nouveau noeud.
 * @param a_object		objet contenant la description du noeud.
 * @param a_addedNodes	tableau des noeuds ajoutés (à compléter)
 */
COOLjsTreePRO.prototype.readOne=function(a_parent, a_object, a_addedNodes) {
	if (und(a_object)) {
		return;
	}
	var l_node = this.addNode(new this.nodeConstructor(this, a_parent, a_object));
	
	// on ajoute le noeud créé au tableau
	a_addedNodes[a_addedNodes.length] = l_node;
	
	// Récupération des sous-noeuds contenus dans les cases
	var l_subNodes = a_object.sub;
	if (!und(l_subNodes)) {
		// il y a bien un noeud fils défini
		var l_nb = l_subNodes.length;
		for (var i = 0; i < l_nb; i++) {
			a_parent = l_node;
			this.readOne(l_node, l_subNodes[i], a_addedNodes);
		}
		// on indique qu'un fils a été chargé
		l_node.childrenLoaded = true;
	}
}

/**
 * Iteration de la fonction readOne(par, arr, tree) sur tous les noeuds de premier niveau de l'arbre.
 * @param a_nodes   		tableau contenant la définition des noeuds (cf tree_nodes.js).
 * @param a_parent  		noeud parent
 * @param a_addedNodes   	tableau contenant les noeuds ajoutés  (à compléter)
 */
COOLjsTreePRO.prototype.readNodes=function(a_nodes, a_parent, a_addedNodes) {
	if (und(a_nodes) || und(a_nodes[0])) {// || und(a_nodes[0][0])) {
		return;
	}
	for (var i = 0; i < a_nodes.length; i++) {
		this.readOne(a_parent, a_nodes[i], a_addedNodes);
	}
}


/**
 * Suppression d'un noeud. 
 * @param a_node référence du noeud à supprimer.
 */
COOLjsTreePRO.prototype.removeNode=function(a_node) {
	var l_parentNode = a_node.parentNode;
	if (l_parentNode == null) {
		l_parentNode = this.rootNode;
	}			
	// suppression du noeud -> on le supprime de la liste de son parent
	l_parentNode.removeChild(a_node);

	// on rafraîchit les images du noeud parent
	l_parentNode.updateImages();
	
	// on réaffiche l'arbre
	this.draw();
}

/**
 * Déplacement du noeud fourni vers le noeud parent fourni.
 * @param a_node 		 référence du noeud à déplacer.
 * @param a_newParent  référence du nouveau noeud parent.
 */
COOLjsTreePRO.prototype.moveNode=function(a_node, a_newParent) {
	if (a_node == a_newParent) {
		// impossible de rattacher le noeud courant à lui-même !!!
		return;
	}
	
	// on détermine le parent actuel du noeud
	var l_parentNode = a_node.parentNode;
	if (l_parentNode == null) {
		l_parentNode = this.rootNode;
	}			
	// on teste le parent
	if (l_parentNode == a_newParent) {
		// pas de changement de parent -> on ne fait rien	
		return;
	}
	
	// suppression du noeud -> on le supprime de la liste de son parent
	l_parentNode.removeChild(a_node);
	// on rafraîchit les images de l'ancien noeud parent
	l_parentNode.updateImages();
	
	// on ajoute le noeud à son nouveau parent
	a_node.parentNode = a_newParent;
	a_node.next = null;
	a_newParent.addChild(a_node);
	// on ouvre le noeud
	a_newParent.expanded = true;
	// on rafraîchit les images du nouveau noeud parent
	a_newParent.updateImages();
	
	// on réaffiche l'arbre
	this.draw();
}

/**
 * Fermeture de tous les noeuds de l'arbre.
 * @param  rd  (boolean)  redraw
 */
COOLjsTreePRO.prototype.collapseAll=function( rd ) {
	if ( this.ver3 ) {
		return;
	}
	for (var i=0; i < this.Nodes.length; i++) {
		if (und(this.Nodes[i])) {
			continue;
		}
		if (this.Nodes[i].parentNode!=this.rootNode) {
			this.Nodes[i].show(false);
		}
		this.Nodes[i].expanded = false;
		this.updateImages(this.Nodes[i]);
	}
	if (this.fmt.cook) {
		this.saveState();
	}
	if (rd) {
		this.draw();
	}
}

/**
 * Ouverture de tous les noeuds de l'arbre.
 * @param  rd  (boolean)  redraw
 */
COOLjsTreePRO.prototype.expandAll=function(rd) {
	if ( this.ver3 ) {
		return;
	}
	for (var i = 0; i < this.Nodes.length; i++){
		if (und(this.Nodes[i])) {
			continue;
		}
		if (this.Nodes[i].hasChildren()){
			this.Nodes[i].expanded=true;
			this.updateImages(this.Nodes[i]);
		}
	}
	if (this.fmt.cook) {
		this.saveState();
	}
	if (rd) {
		this.draw();
	}
}

/**
 * Ajoute dynamiquement les noeuds spécifiés dans a_node2Add au noeud parent fourni.
 * @param a_node2Add	tableau des descriptions des noeuds à ajouter
 * @param a_parent		noeud parent
 * @param a_draw		true s'il faut redessiner l'arbre
 */
COOLjsTreePRO.prototype.addDynamicNodes = function(a_nodes2Add, a_parent, a_draw) {
	if (und(a_nodes2Add)) {
		// on arrête de suite
		return;
	}
	var l_addedNodes = [];
	// on ajoute les noeuds fournis au noeud parent
	this.readNodes(a_nodes2Add, a_parent, l_addedNodes);
	// on construit les nouveaux noeuds, et on les ajoute à l'arbre courant
	this.buildNodes(l_addedNodes, false);

	if (a_draw) {
		// on le réaffiche
		this.draw();
	}
}


/**
 * Initialisation de l'arbre.
 * @param	a_div	(HTMLElement)	éventuel div destiné à contenir l'arbre
 */
COOLjsTreePRO.prototype.init = function(a_div) {
	var l_newNodes = [];
	
	if (a_div) {
		this.div = a_div;
	}
	
	// lecture des noeuds
	this.readNodes(this.nodesDef, null, l_newNodes);
	// initialisation du fond
	if ( !this.ver3 && !this.fmt.rel ) {
		this.fmt.back.init();
	}
	// construction de l'arbre
	this.buildNodes(l_newNodes, true);
/*		
		if (this.fmt.cook) {
			// récupération du cookie
			this.restoreState();
		}
*/
	if (!this.fmt.rel) {
		this.draw();
	}
}

/**
 * Rechargement de l'arbre à partir d'un nouveau tableau de noeuds
 * @param a_vars	tableau de tableaux contenant les nouvelles données
 * 					a_vars[0] = nouveaux noeuds à afficher
 * @param a_newNodes  tableau des nouveaux noeuds construits
 */
COOLjsTreePRO.prototype.reloadData=function(a_vars, a_newNodes) {
	this.shadowedNode = null;
	// on supprime les anciens noeuds
	this.Nodes.length = 0;
	// on supprime les fils de la racine	
	if (!und(this.rootNode.children)) {
		this.rootNode.children.length = 0;
	}

	// on recharge l'arbre
	var l_newNodes = und(a_newNodes) ? [] : a_newNodes;

	// lecture des noeuds
	this.readNodes(a_vars[0], null, l_newNodes);
	// reconstruction de l'arbre
	this.buildNodes(l_newNodes, true);
/*		
	if (this.fmt.cook) {
		// récupération du cookie
		this.restoreState();
	}
*/		
	// on essaie de rouvrir l'arbre jusqu'au noeud sélectionné précédemment,
	// s'il existe encore
	this.expandToSelectedNode();
	
	// on le redessine
	this.draw();
}

/**
 * Essaie de rouvrir l'arbre jusqu'au noeud sélectionné précédemment,
 * s'il existe encore
 * @param a_forced (boolean) si true, force l'ouverture du noeud
 */
COOLjsTreePRO.prototype.expandToSelectedNode = function(a_forced) {
	// on vérifie l'existence d'un noeud "sélectionné"
	if (und(this.selUIdPath)) {
		return;
	}
	// on essaie d'ouvrir le maximum de noeuds du path
	var l_uid;
	var l_node, l_parent = null;
	for (var i = 0; i < this.selUIdPath.length -1; i++) {
		l_uid = this.selUIdPath[i];
		l_node = this.getNodeByUId(l_uid);
		if (l_node == null) {
			// non trouvé 
			if (l_uid == 0) {
				// ->on continue quand même
				continue;
			} else {
				// -> on peut arrêter
				break;
			}
		} else {
			// on ouvre le noeud
			this.expandNode(l_node.index, true, false, a_forced);
		}
		l_parent = l_node;
	}
	// on sélectionne le dernier noeud visible 
	var l_sel = this.getNodeByUId(this.selUIdPath[this.selUIdPath.length -1]);
	if ((l_node == null) || (l_sel == null)) {
		l_sel = l_parent;
	}
	if (l_sel != null) {
		// on désactive la callback de sélection
		var l_callBack = this.onSelectNode;
		this.onSelectNode = null;
		this.selectNode(l_sel.index, true);
		// on réactive la callback
		this.onSelectNode = l_callBack;
	}
}

/**
 * Renvoie le noeud correspondant à l'uId spécifié, null s'il n'existe plus.
 * @param a_uId		(String)  id unique du noeud
 * @return le node
 */
COOLjsTreePRO.prototype.getNodeByUId = function(a_uId) {
	var l_node;
	for (var i=this.Nodes.length; --i >= 0; ) {
		l_node = this.Nodes[i];
		if (und(l_node)) {
			continue;
		}
		if (l_node.uid() == a_uId) {
			return l_node;
		}
	}
	// pas trouvé -> renvoie null
	return null;
}

/**
 * Nettoie l'arbre, en nettoyant notamment les éléments dropables.
 * Supprime la référence dans la table principale.
 */
COOLjsTreePRO.prototype.clear = function() {
	if (!this.ver3) {
		var l_node;
		for (var i = 0; i < this.Nodes.length; i++) {
			l_node = this.Nodes[i];
			if (l_node.isDropable()) {
				if (window.removeDropListener) {
					// on le désabonne
					removeDropListener(this.getEl(l_node).id);
				}
			}
		}
	}

	delete NTrees[this.name];
}

/**
 *
 */
COOLjsTreePRO.prototype.getCookie=function(sName){
	var aCookie = this.document.cookie.split("; ");
	for (var i = 0; i < aCookie.length ; i++){
		var aCrumb=aCookie[i].split("=");
		if (sName==aCrumb[0]) return unescape(aCrumb[1]);
	}
	 return null;
}

/**
 * Mémorisation de l'état de l'arbre dans les cookies.
 */
COOLjsTreePRO.prototype.saveState=function() {
	var state="";
	for (var i=0; i < this.Nodes.length; i++) {
		if (und(this.Nodes[i])) {
			continue;
		}
		state +=this.Nodes[i].expanded ? '1' : '0';
	}
	/* Uncomment this to keep cookies during month
	var d = new Date();
	d.setMonth(d.getMonth() + 15);
	document.cookie = this.name + 'State=' + state + '; path=/' + '; expires=' + d.toGMTString();
	*/
	this.document.cookie = this.name + 'State=' + state + '; path=/';
}

/**
 * Restitution de l'état de l'arbre à la dernière visite de la page. Cet état
 * a été mémorisé dans les cookies du navigateur.
 */
COOLjsTreePRO.prototype.restoreState=function() {
	var state = this.getCookie(this.name + 'State');
	if (state == null) {
		return;
	}
	var l_node;
	for (var i = 0; i < this.Nodes.length; i++) {
		l_node = this.Nodes[i];
		if (und(l_node)) {
			continue;
		}
		if (state.charAt(i) == '1' && l_node.hasChildren()) {
			// on regarde ce qu'en dit le format de base
			if (!und(l_node.getParamProp("expanded"))) {
				// défini -> on ne fait rien
			} else {
				this.expandNode(i, true);
			}
		}
	}
}

/**
 * Supprime la mémorisation de l'état de l'arbre dans les cookies.
 */
COOLjsTreePRO.prototype.clearState=function() {
	this.document.cookie = this.name + 'State=';
}

/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/
var NODE_TYPE_FOLDER = "folder";
var NODE_TYPE_PAGE = "page";

/**
 * constructeur d'un noeud
 */
function COOLjsTreeNodePRO(a_treeView, a_parentNode, a_object) {
	if (und(a_treeView)) {
		// définition des sous-classes -> on arrête de suite 
		return;
	}
	this.index = -1;
	this.treeView = a_treeView;
	this.parentNode = a_parentNode;
	this.text = a_object.code;
	if (this.text != null && this.text.length > 0 && this.text.charAt(1) == '_') {
		// on supprime les premiers caractères pour l'affichage (mais on le conserve pour le tri)
		this.printedText = this.text.substring(2);
	} else {
		this.printedText = this.text;
	}	
	
	this.url = a_object.url || "";
	this.target = a_object.target || "";
	
	// on récupère les informations étendues
	this.expanded = false;
	
	var l_supp = a_object.def;
	if (!und(l_supp)) {
	 	this.format = l_supp["format"];
		this.status = this.getFormatProp("status") || false;
		this.order = this.getFormatProp("childrenOrder");
		
		this.subFormat = l_supp["subFormat"];
		this.setParams(l_supp["params"]);
		this.expanded = this.getParamProp("expanded") || false;
	}
	// collection des noeuds fils
    this.children = [];
    // chargement des fils effectué ?
    this.childrenLoaded = !(this.getFormatProp("virtualChildren") || false); //false;
}

/**
 * Définit les paramètres du noeud.
 * Méthode surchargée par les classes dérivées.
 * @param a_params	tableau des paramètres
 */
COOLjsTreeNodePRO.prototype.setParams = function(a_params) {
	this.params = a_params;
}

/**
 * Renvoie la valeur de la propriété de format spécifiée par son nom.
 * Remonte récursivement les noeuds jusqu'à l'arbre, en s'arrêtant dès qu'une valeur
 * a été trouvée.
 * @param a_propName	(String)	nom de la propriété (éventuellement composé, avec . comme sép)
 */
COOLjsTreeNodePRO.prototype.getFormatProp = function(a_propName) {
	// on décompose le nom de la propriété
	var l_names = a_propName.split('.');
	var i, l_nb = l_names.length;
	
	var l_val;
	var l_node = this;
	var l_format = this.format;
	do {
		if (l_format) {
			// on regarde si la propriété existe
			l_val = l_format;
			for (i = 0; i < l_nb; i++) {
				l_val = l_val[l_names[i]];
				if (und(l_val)) {
					// propriété non définie -> on arrête
					break;
				}
			}
		}
		if (und(l_val)) {
			// pas encore de valeur -> on continue
			if (und(l_node)) {
				// on arrête là
				return null;
			}
			if (und(l_node.parentNode)) {
				// noeud racine -> on utilise le format par défaut de l'arbre lui-même
				l_format = l_node.treeView.format.nodeFormat;
				l_node = null;
			} else {
				l_node = l_node.parentNode;
				l_format = l_node.subFormat;
			}
		} else {
			// on arrête là
			return l_val;
		}
	} while (true);
	
	return null;
}

/**
 * Renvoie la valeur de la propriété de paramétrage spécifiée par son nom.
 * @param a_paramName	(String)	nom de la propriété
 */
COOLjsTreeNodePRO.prototype.getParamProp = function(a_paramName) {
	return (this.params) ? this.params[a_paramName] : null;
}

/**
 * Renvoie le path du noeud, sous forme d'un tableau de uId.
 * @return [uIds]
 */
COOLjsTreeNodePRO.prototype.getUIdPath=function() {
	if (this.parentNode == null) {
		// on s'arrête là ...
		return [this.uid()];
	} else {
		var l_path = this.parentNode.getUIdPath();
		l_path[l_path.length] = this.uid();
		return l_path;
	}
}

/**
 * Renvoie le paramètre type s'il existe
 */
COOLjsTreeNodePRO.prototype.getType=function() {
	return this.getParamProp("type");
}

/**
 * Renvoie true si le paramètre type vaut NODE_TYPE_FOLDER.
 * true par défaut
 */
COOLjsTreeNodePRO.prototype.isFolder=function() {
	var l_type = this.getType();
	if (und(l_type)) {
		// par défaut, on renvoie toujours false
		return this.getFormatProp("virtualChildren");
	} else {
    	return (l_type.toLowerCase() == NODE_TYPE_FOLDER);
    }
}

/**
 * Renvoie true si le paramètre type vaut NODE_TYPE_PAGE.
 * false par défaut (on considère qu'il s'agit toujours d'un noeud si le type n'est pas spécifié)
 */
COOLjsTreeNodePRO.prototype.isPage=function() {
	var l_type = this.getType();
	if (und(l_type)) {
		// par défaut, on renvoie toujours false
		return false;
	} else {
    	return (l_type.toLowerCase() == NODE_TYPE_PAGE);
    }
}

/**
 * Récupération de l'identifiant étendu du noeud.
 * @return l'identifiant unique de l'objet associé
 */
COOLjsTreeNodePRO.prototype.uid=function() {
	return this.getParamProp("uId");
}

/**
 * Récupération de l'identifiant de l'objet associé au noeud.
 * @return l'id des objets JCF
 */
COOLjsTreeNodePRO.prototype.xid=function() {
	return this.getParamProp("xId");
}

/**
 * Renvoie true si le noeud est sélectionnable.
 * true par défaut
 */
COOLjsTreeNodePRO.prototype.isSelectable = function() {
	var l_sel = this.getParamProp("sel");
	return und(l_sel) ? true : l_sel;
}

/**
 * Renvoie true si le noeud est dragable.
 * false par défaut
 */
COOLjsTreeNodePRO.prototype.isDragable = function() {
	var l_drag = this.getParamProp("drag");
	return und(l_drag) ? false : l_drag;
}

/**
 * Renvoie true si le noeud est dropable.
 * false par défaut
 */
COOLjsTreeNodePRO.prototype.isDropable = function() {
	var l_drop = this.getParamProp("drop");
	return und(l_drop) ? false : l_drop;
}

/**
 * Vérification de l'existence de noeuds fils.
 */
COOLjsTreeNodePRO.prototype.hasChildren=function() {
	// VLT, le 05/02/2003
	/* LS le 06/03/2004
	if (this.isFolder()) {
		return true;
	}
	*/
	var bHasChildren = ( (this.children != null ) &&  (this.children.length > 0) );
	return bHasChildren;
}

/**
 * Vérification de l'existence éventuelle de noeuds fils.
 */
COOLjsTreeNodePRO.prototype.hasVirtualChildren=function() {
	var l_real = this.hasChildren();
	
	if (l_real) {
		// il y en a vraiment
		return true; 
	} else {
		// LSP, le 12/08/2003
		if (this.isPage()) {
			return false;
		}
		// on regarde si on les a déjà chargés
		if (this.childrenLoaded == false) {
			// si les fils n'ont pas encore été chargés, on suppose par défaut qu'il peut en exister
			return true;
		}
		return false;
	}
}


/**
 * Renvoie le ghost pour le drag.
 * this est l'élément div fils de node.el !!!
 * this._node est le COOLjsTreeNodePro associé
 */
COOLjsTreeNodePRO.prototype.createGhost = function() {
	var l_ghost = this.cloneNode(false);
	// on le "remplit"
	var l_str = '<img src="' + (this._node.expanded ? this._node.eimg : this._node.cimg) + 
				'" width="' + this._node.wbtn + '" height="' + this._node.hbtn + '"/>';
	l_str += this._node.printedText.replace(/ /g, "&nbsp;");
	xbSetInnerHTML(l_ghost, l_str);
	l_ghost.className = this._node.getFormatProp('ghost.css');

	return l_ghost;
}

/**
 * Surligne (si a_highlight vaut true) le noeud courant lors d'un survol lors du drag.
 * this est l'élément node.el !!!
 * @param a_highlight	true s'il faut surligner le noeud
 */
COOLjsTreeNodePRO.prototype.highlight = function(a_highlight) {
	// on recherche le span interne
	var l_span = getChildByTag(this, "span");
	// on modifie son style
	if (a_highlight) {
		l_span.oldCSS = l_span.className;
		l_span.className = this._node.getFormatProp('highlight.css');
	} else {
		if (!und(l_span.oldCSS)) {
			l_span.className = l_span.oldCSS;
			l_span.oldCSS = null;
		}
	}
}

/**
 * "Nettoie" le noeud courant (correspondant au noeud de départ) lors d'un doDragEnd
 * this est l'élément node.el !!!
 * @param a_highlight	true s'il faut surligner le noeud
 */
COOLjsTreeNodePRO.prototype.clearCSS = function() {
	// on recherche le span interne
	var l_span = getChildByTag(this, "span");
	l_span.className = l_span.className.split('_')[0];
}

/**
 * Renvoie le niveau d'indentation
 */
COOLjsTreeNodePRO.prototype.level = function() {
	var node = this;
	var i = 0;
	while(node.parentNode != null) {
		i++;
		node = node.parentNode;
	}
	return i;
}


/**
 * Définition des images en fonction du format
 */
COOLjsTreeNodePRO.prototype.initImages=function() {
	if (!this.treeView) {
		return;
	}
	var l_hasChild = this.hasVirtualChildren();
	this.like = "explorer";
	this.explorer = this.getFormatProp(this.like) ? true : false;
	var l_butExt = "";
	if (this.explorer) {
		l_butExt = this.next == null ? "Bottom" : "";
		// on regarde le paramètre "first"
		if (this.getParamProp("first") == true) {
			l_butExt = "First";
		}
		
		this.showBut = true;
		this.wbtn = this.getFormatProp(this.like + ".width");
		this.hbtn = this.getFormatProp(this.like + ".height");
		
		this.showItem = true;
		this.wit = this.wbtn;
		this.hit = this.hbtn;
	}
	else {
		this.like = "generic";
		
		this.showBut = this.getFormatProp(this.like + ".button") ? true : false;
		if (this.showBut) {
			this.wbtn = this.getFormatProp(this.like + ".button.width");
			this.hbtn = this.getFormatProp(this.like + ".button.height");
		}
		this.showItem = this.getFormatProp(this.like + ".item") ? true : false;
		if (this.showItem) {
			this.wit = this.getFormatProp(this.like + ".item.width");
			this.hit = this.getFormatProp(this.like + ".item.height");
		}
	}
	// répertoire commun
	this.dirName = this.getFormatProp("imgsDir");
	
	// image indent
	this.bind = this.getImgSrc(this.like + ".indent.blank");
	this.lind = this.getImgSrc(this.like + ".indent.line");
	
	// image bouton +/-
	var l_img;
	if (this.showBut) {
		if (l_butExt == "First") {
			// on affiche toujours le bouton 
			l_hasChild = true;
		} else {
			l_img = this.getImgSrc(this.like + ".button.join" + l_butExt);
		}
		this.cbtn = l_hasChild ? this.getImgSrc(this.like + ".button.plus" + l_butExt) : l_img;
		this.ebtn = l_hasChild ? this.getImgSrc(this.like + ".button.minus" + l_butExt) : l_img; 
	}
		
	// images item
	if (this.showItem) {
		// on réétudie le caractère folder : on affiche une image "folder" si
		// le noeud a été spécifiquement indiqué comme tel 
		l_hasChild = l_hasChild || this.isFolder();	
		l_img = this.getImgSrc(this.like + ".item.page");
		this.cimg = l_hasChild ? this.getImgSrc(this.like + ".item.folder") : l_img;
		this.eimg = l_hasChild ? this.getImgSrc(this.like + ".item.folderOpen") : l_img;
	}
}

/**
 * Renvoie l'url "réelle" de l'image, en remplaçant éventuellement le caractère '#' par le nom
 * du répertoire fourni.
 * @param	a_propName	(String)	nom de la propriété correspondant à une image
 */
COOLjsTreeNodePRO.prototype.getImgSrc = function(a_propName) {
	var l_src = this.getFormatProp(a_propName);
	if (!und(l_src) && (l_src.charAt(0) == '#')) {
		return this.dirName + l_src.substring(1);
	} else {
		return l_src;
	}
}


/**
 * Enrobage du code HTML du noeud dans un DIV (IE) ou LAYER (Netscape), avec ajout d'élements de style.
 */
COOLjsTreeNodePRO.prototype.init=function() {
	return !this.treeView.ver3 ? this.treeView.ns4 ? '<layer id="' + this.id() + 'd" z-index="' + this.index + 10 + '" visibility="hidden">' + this.getContent() + '</layer>':
	'<div id="' + this.id() + 'd" style="position:absolute;visibility:hidden;width:1px;z-index:' + this.index + 10 + ';">' + this.getContent() + '</div>' :	this.getContent();
}

/**
 *
 */
COOLjsTreeNodePRO.prototype.getH=function() {
	if (!this.h) {
		this.h = this.treeView.ns4 ? this.el.clip.height:this.treeView.bw.dom&&!this.treeView.bw.operaOld? this.el.firstChild.offsetHeight:this.el.offsetHeight;
	}
	return this.h;
}

/**
 *
 */
COOLjsTreeNodePRO.prototype.getW=function() {
	if(!this.w) {
		this.w = this.treeView.ns4 ? this.el.clip.width : this.treeView.bw.dom && !this.treeView.bw.operaOld ? this.el.firstChild.offsetWidth : this.el.offsetWidth;
	}
	return this.w;
}

/**
 * Récupération de l'identifiant normalisé du noeud (pour nommage des éléments HTML)
 */
COOLjsTreeNodePRO.prototype.id=function() {
	var s = 'nt_' + this.treeView.name + '_' + this.index + '_'; 
	return s;
}


/**
 * Ajoute le noeud fourni comme fils du noeud courant.
 * Si format.order vaut "alpha", insert le noeud pour respecter l'ordre alphabétique,
 * sinon ajoute à la fin
 * @param  node (COOLjsTreeNodePRO)
 */ 
COOLjsTreeNodePRO.prototype.addChild=function(node) {
	if ((this.order == "alpha") && this.hasChildren()) {
		// on classe
		var l_text= node.text.toUpperCase();
		var j = 0;
		// on compare avec les noeuds présents
		while (j < this.children.length && (this.children[j].text.toUpperCase() <= l_text)) {
			j++;
		}

		if (j == this.children.length) {
			// on définit le chaînage
			if (j > 0) {
				this.children[this.children.length - 1].next = node;
			}
			// on ajoute juste à la fin
			this.children[this.children.length] = node;
		} else {
			// il faut décaler ...
    		for (var k=this.children.length; --k >= j; ) {
    			// on copie plus loin
    			this.children[k + 1] = this.children[k];
    		}
    		// on insère le nouveau noeud
    		this.children[j] = node;
    		// on re-définit la chaîne
    		if (j > 0) {
    			this.children[j - 1].next = node;
    		}
    		node.next = this.children[j + 1];
		}
	} else {
		// on définit le chaînage
		if ((this.children != null ) &&  (this.children.length > 0)) {
			this.children[this.children.length - 1].next = node;
		}
		this.children[this.children.length] = node;
	}
}

/**
 * Supprime le noeud fourni comme fils du noeud courant.
 * @param  node (COOLjsTreeNodePRO)
 */ 
COOLjsTreeNodePRO.prototype.removeChild=function(node) {
	var j = 0;
	// on recherche l'index dans la table des fils
	while (j < this.children.length && this.children[j] != node) {
		j++;
	}
	// on a la position dans j
	// on décale ...
	for (var k=j; k < this.children.length -1; k++) {
		// on écrase
		this.children[k] = this.children[k + 1];
	}
	// on re-définit la chaîne
	if (j > 0) {
		this.children[j - 1].next = this.children[j];
	}
	// on limite la taille du tableau
	this.children.length -= 1;
	// on annule bien le suivant du dernier fils
	if (this.children.length > 0) {
		this.children[this.children.length - 1].next = null;
	} else {
		// plus de fils
		this.expanded = false;
	}
	
}

/**
 * Renvoie true si le noeud fourni est acceptable comme fils du noeud courant.
 * @param  node (COOLjsTreeNodePRO)
 */ 
COOLjsTreeNodePRO.prototype.acceptChild=function(node) {
	var l_nodeId = node.xid();
	if (this.xid() == l_nodeId) {
		// noeud identique -> on ne fait rien !
		return false;
	}
	// on teste les aïlleuls
	var l_parent = this.parentNode;
	while (l_parent != null) {
		if (l_parent.xid() == l_nodeId) {
			// déplacement d'un père vers un fils -> on ne fait rien !
			return false;
		}
		l_parent = l_parent.parentNode;
	}
	return true;
}


/**
 * Construction du contenu HTML du noeud.
 */
COOLjsTreeNodePRO.prototype.getContent=function() {
	var s= '<table border="0" width="' + this.treeView.fmt.table.width + 
				'" cellpadding="' + this.treeView.fmt.table.padding + 
				'" cellspacing="' + this.treeView.fmt.table.spacing + 
				'" class="cls' + this.treeView.name + '_back' + this.level() + '"><tr>';
	s += this.getTreeContent();
	s += '</tr></table>';
	
	return s;
}

/**
 * Construction du contenu HTML du noeud.
 */
COOLjsTreeNodePRO.prototype.getTreeContent=function() {
	var s= '';
	// indentation
	s += this.blankSquares();
	
	// bouton +/-
	if (this.showBut) {
		s += this.explorer ? this.buttonSquare() 
						   : (this.hasChildren() ? this.buttonSquare() 
						   						 : this.blankSquares(this.wbtn));
	}
	
	// item
	if (this.showItem) {
		s += this.itemSquare();
	}
	var l_tree = 'NTrees[\'' + this.treeView.name + '\']'; 
	var l_onc;
	if (this.isSelectable()) {
		l_onc = l_tree + '.selectNode(' + this.index + ')';
	} else {
		l_onc = l_tree + '.expandNode(' + this.index + ', 0, 1)';
	}
	this.defaultCSS = this.getFormatProp("css");
	var l_evts = '';
	if (window.nodeChange) {
		l_evts = 'onmouseover="nodeChange(this, \'o\')" onmouseout="nodeChange(this)"';
	} else {
		l_evts = this.writeStatus();
	}

	var l_txt = '&nbsp;' + this.printedText;

	if (this.url) {
		// lien direct par url
		var l_onm = this.hasVirtualChildren() ? this.writeStatus() : '';
		var l_targ = this.target ? ' target="' + this.target + '"' : '';
		l_txt = ' class="' + this.defaultCSS + '"' + l_evts + '><a href="' + this.url + '" ' + l_targ + l_onm + ' onclick="' + l_onc + ';"' + '>' + l_txt + '</a>';
	} else {
		l_txt = ' class="' + this.defaultCSS + '" onclick="' + l_onc + ';" ' + 
					l_evts + '>' + l_txt;
	}

	var l_tag = this.treeView.ns4 ? 'ilayer' : 'div';
	s += '<td nowrap="1" width="100%"><' + l_tag + ' id="' + this.id()+'a" ' + l_txt + '</' + l_tag + '></td>';

	return s;
}

/**
 * Positionnement du noeud dans la page.
 */
COOLjsTreeNodePRO.prototype.moveTo=function( x, y ) {
	if (this.treeView.ns4) {
		this.el.moveTo(x,y);
	}
	else {
		this.el.style.left = x;
		this.el.style.top = y;
	}
}

/**
 * Affichage du noeud.
 * @param boolean sh commande de visibilité.
 */
COOLjsTreeNodePRO.prototype.show=function(sh) {
	this.visible = sh;
	var vis = this.treeView.ns4 ? (sh ? 'show': 'hide') : (sh ? 'visible': 'hidden');
	if (this.treeView.ns4) {
		this.el.visibility = vis;
	}
	else {
		this.el.style.visibility = vis;
	}
}

/**
 *
 */
COOLjsTreeNodePRO.prototype.hideChildren=function() {
	this.show(false);
	for (var i=0; i < this.children.length; i++) {
		this.children[i].hideChildren();
	}
}

/**
 * Choix de l'emplacement d'affichage du noeud.
 */
COOLjsTreeNodePRO.prototype.draw=function() {
	// Ajustement des coordonnées du noeud
	var ll = this.treeView.fmt.left;
	var left = this.treeView.fmt.rel && this.treeView.bw.operaOld ? this.treeView.fmt.left + this.treeView.operaLeft: this.treeView.fmt.left;
	var top = this.treeView.fmt.rel && this.treeView.bw.operaOld ? this.treeView.currTop + this.treeView.operaTop : this.treeView.currTop;
	this.moveTo(left, top);
	// Ajustement de la largeur max de l'arbre
	var w = this.getW();
	this.show(true);
	if (ll + w > this.treeView.maxWidth) {
		this.treeView.maxWidth = ll + w;
	}
	// Ajustement de la hauteur max de l'arbre
	this.treeView.currTop += this.getH();
	if (this.treeView.currTop > this.treeView.maxHeight) {
		this.treeView.maxHeight = this.treeView.currTop;
	}
	// Placement des noeuds fils, à condition qu'ils existent et qu'ils soient ouverts
	if (this.expanded && this.hasChildren() ) {
		for (var i = 0; i < this.children.length; i++) {
			this.children[i].draw();
		}
	}
}

/**
 * Renvoie l'élément graphique associé au texte visualisé
 */
COOLjsTreeNodePRO.prototype.getElt2Color = function() {
	if (und(this.el2color)) {
		var l_el = this.treeView.bw.ns4 ? 
							this.el.layers[this.id()+'a'] : 
							this.treeView.document.all ? 
									this.treeView.document.all[this.id()+'a'] : 
									this.treeView.document.getElementById(this.id()+'a');
		if (!und(l_el)) {
			this.el2color = getChildByTag(l_el, "span");
		}
	}
	return this.el2color;
}

/**
 * Modifie l'affichage du noeud.
 * @param a_bgColor		(String) couleur de fond
 * @param a_fgColor		(String) couleur de texte
 * ou alors
 * @param a_cssName		(String) nom de la classe CSS à appliquer
 */
COOLjsTreeNodePRO.prototype.setColors=function(a_bgColor, a_fgColor, a_cssName) {
	var l_el = this.getElt2Color();
	if (und(l_el)) {
		// on ne fait rien
		return;
	}
	if (this.treeView.bw.ns4) {
		l_el.bgColor = a_bgColor;
		l_el.color = a_fgColor;
	} else {
		if (l_el.style) {
			var l_css = und(a_cssName) ? (a_bgColor == "") : true;
			if (l_css) {
				// on passe en CSS
				if (a_bgColor == "") {
					// pas de couleur définie -> CSS initiale
					l_el.className = this.defaultCSS; 
				} else {
					// utilisation de la CSS fournie
					l_el.className = a_cssName;
				}
			} else {
				// on passe en couleur
				l_el.style.backgroundColor = a_bgColor;
				l_el.style.color = a_fgColor;
			}
		}
	}
}

COOLjsTreeNodePRO.prototype.setText=function(a_newText) {
	var l_el = this.getElt2Color();
	if (l_el.innerHTML) {
		l_el.innerHTML = a_newText;
	}	
}

/**
 * Définition des images associées au 'node', en fonction des paramètres de format
 */
COOLjsTreeNodePRO.prototype.getImg=function() {
	var l_tree = this.treeView;
	if (l_tree.ns4) {
		if (this.showBut)
			this.nb = this.el.document.images[this.id() + "nb"];
		if (this.showItem)
			this.ni = this.el.document.images[this.id() + "ni"];
	}
	else {
		if (this.showBut)
			this.nb = this.el.all ? this.el.all[this.id() + "nb"] : l_tree.document.getElementById(this.id() + "nb");
		if (this.showItem)
			this.ni = this.el.all ? this.el.all[this.id() + "ni"] : l_tree.document.getElementById(this.id() + "ni");
	}
}


/**
 * Définition de la source des images en fonction de l'état du noeud et du format de l'arbre
 */
COOLjsTreeNodePRO.prototype.updateImages=function() {
	// on récupère les éléments images
	this.getImg();
	
	// on réinitialise la définition des images, pour prendre en compte les éventuelles
	// modifications (au niveau des noeuds fils)
	this.initImages();
	
	// on "change" les images
	var l_butSrc = this.expanded ? this.ebtn : this.cbtn;
	var l_itemSrc = this.expanded ? this.eimg : this.cimg;
	if (this.showBut && this.nb && this.nb.src != l_butSrc)
		this.nb.src = l_butSrc;
	if (this.showItem && this.ni && this.ni.src != l_itemSrc)
		this.ni.src = l_itemSrc;
}

/**
 * Affiche l'indentation correcte.
 */
COOLjsTreeNodePRO.prototype.blankSquares = function(a_width) {
	var l_level = this.level();
	if (l_level == 0) {
		return '';
	}

	if (this.explorer) {
		var l_res = '';
		var l_img;
		var l_pNode = this;
		for (var i = l_level; i > 0; i--) {
			if (l_pNode.parentNode != null) {
				l_pNode = l_pNode.parentNode;
			}
			l_img = ((l_pNode.next == null) || (l_pNode.getParamProp("first") == true)) 
						? l_pNode.bind : l_pNode.lind;
			l_res = '<td width="' + l_pNode.wbtn + '"><img src="' + l_img + 
						'" width="' + l_pNode.wbtn + '" height="' + l_pNode.hbtn + '"/></td>' + l_res;
		}
		return l_res;
	}
	else {
		var l_width = 0;
		if (und(a_width)) {
			var l_pNode = this;
			// on calcule récursivement l'indentation
			for (var i = l_level; i > 0; i--) {
				if (l_pNode.parentNode != null) {
					l_pNode = l_pNode.parentNode;
				}
				l_width += l_pNode.wit | 0;
			}
		} else {
			l_width = a_width;
		}
		if (l_width > 0) {
			return '<td width="' + l_width + '"><img src="' + this.bind + '" width="' + l_width + '" height="1"/></td>\n';
		} else {
			return '';
		}
	}
}

/**
 * Affiche éventuellement des informations dans la barre de status
 */
COOLjsTreeNodePRO.prototype.writeStatus = function(){
	return this.status ? ' onmouseout="window.status=\'\'" onmouseover="window.status=\'' + this.text + '\'"' : '';
}

/**
 * Affiche l'image correspondant au bouton
 */
COOLjsTreeNodePRO.prototype.buttonSquare = function(){
	var img = this.expanded ? this.ebtn : this.cbtn;
	var s = '<td valign="middle" width="' + this.wbtn + '">';

	var i = '<img class="buttonSquare" name="' + this.id() + 'nb" id="' + this.id() + 'nb" src="' + img + '"' + ' width="' + this.wbtn + '" height="' + this.hbtn + '"';
	return (this.hasVirtualChildren() || (this.getParamProp("first") == true))
			? s + i + this.writeStatus() +' onclick="NTrees[\'' + this.treeView.name + '\'].expandNode(' + this.index + ')"/></td>' 
			: s + i + '/></td>';
	
}

/**
 * Affiche l'image correspondant à l'item
 */
COOLjsTreeNodePRO.prototype.itemSquare = function() {
	var l_img = this.expanded ? this.eimg : this.cimg;
	var l_strImg = '<img class="itemSquare" id="' + this.id() + 'ni" name="' + this.id() + 'ni" src="' + l_img + '"' + ' width="' + this.wit + '" height="' + this.hit + '"';
	// status
	l_strImg += this.writeStatus();
	
	// on regarde si on doit mettre un lien sur l'image
	if (this.treeView.imageClick != null) {
		// oui
		l_strImg += ' onclick="NTrees[\'' + this.treeView.name + '\'].imageClick(\'' + this.index + '\')"/>';
	} else if (!this.isSelectable()) {
		// noeud non sélectionnable -> un clic sur l'image ouvre/ferme le noeud
		l_strImg += ' onclick="NTrees[\'' + this.treeView.name + '\'].expandNode(' + this.index + ', 0, 1)"/>';
	}
	return "<td valign=\"middle\" width=\"" + this.wit + "\">" + l_strImg + "</td>";
}





function COOLjsTreeFmtPRO(fmt, tree) {
	this.init(fmt, tree);
}

/*
 * Initialisation du formatage de l'arbre.
 */
COOLjsTreeFmtPRO.prototype.init=function(a_fmt, a_tree ) {
	this.left = a_fmt.left || 0;	// left position
	this.top = a_fmt.top || 0;	// top position

	if (!a_tree.ver3)			// tree background color ("" - transparent)
		this.back = new COOLjsTreeBackPRO(this.left, this.top, a_fmt.background.color  || "", 'cls'+a_tree.name+'_back');
		
	this.table = a_fmt.table;	// format de la table

	this.cook = a_fmt.cook || false;	// if true, state will be saved in cookies
	this.rel = a_fmt.relative || true;	// if true, relative position will be used. 
										// (tree will be opened in place where init() was called)
	this.rels = [a_fmt.width, a_fmt.height]; // width and height of initial rectangle for relative positioning 
	this.resize = a_fmt.background.resize;	//resize background 
											//works only under IE4+, NS6+ for relative positioning

}

function COOLjsTreeBackPRO( aleft, atop, color, name) {
	this.bw = new bw_check();
	this.ver3 = this.bw.ver3;
	this.ns4 = this.bw.ns4;
	this.left = aleft;
	this.top = atop;
	this.name = name;
	this.color = color;
	this.dim = [0, 0];
}

COOLjsTreeBackPRO.prototype.resize=function(w,h) {
	if ((w > 0) && (h > 0)) {
		this.dim = [w, h];
	}
	if (this.ns4)
		this.el.resizeTo(w,h);
	else{
		this.el.style.width= (w > 0) ? w : 0;
		this.el.style.height= (h > 0) ? h : 0;
		// on informe éventuellement la fenêtre parente
		if (window.resizeDetailIFrame) {
			resizeDetailIFrame();
		}
	}
}

/**
 *
 */
COOLjsTreeBackPRO.prototype.init=function(){
	if (this.ns4) {
		var bgc = this.color == "" ? "" : ' bgcolor="' + this.color + '" ';
		this.document.write('<layer ' + bgc + ' top="' + this.top + '" left="' + this.left + '" id="' + this.name + '" z-index="0"></layer>');
		this.el = this.document.layers[this.name];
	}
	else {
		var bgc = this.color == "" ? "" : " background-color:" + this.color + ";";
		this.document.write('<div id="'+this.name+'" style="' + bgc + 'position:absolute;z-index:0;top:' + this.top + 'px;left:' + this.left + 'px"></div>');
		this.el = this.document.all ? this.document.all[this.name] : this.document.getElementById(this.name);
	}
}

/**
 *
 */
COOLjsTreeBackPRO.prototype.setVisible = function(a_visible) {
	this.el.style.visibility = a_visible ? "visible" : "hidden";
/*
	if (a_visible) {
		this.resize(this.dim[0], this.dim[1]);
	} else {
		this.resize(0, 0);
	}
*/
}


/*
 *
 *
 */
function bw_check() {
	var is_major = parseInt(navigator.appVersion);
	this.nver = is_major;
	this.ver = navigator.appVersion;
	this.agent = navigator.userAgent;
	this.dom = document.getElementById ? 1 : 0;
	this.opera = window.opera ? 1 : 0;
	this.ie5 = (this.ver.indexOf("MSIE 5") > -1 && this.dom && !this.opera) ? 1 : 0;
	this.ie6 = (this.ver.indexOf("MSIE 6") > -1 && this.dom && !this.opera) ? 1 : 0;
	this.ie4 = (document.all && !this.dom && !this.opera) ? 1 : 0;
	this.ie = this.ie4||this.ie5||this.ie6;
	this.mac = this.agent.indexOf("Mac") > -1;
	this.ns6 = (this.dom&&parseInt(this.ver) >= 5) ? 1 : 0;
	this.ie3 = (this.ver.indexOf("MSIE") && (is_major < 4));
	this.hotjava = (this.agent.toLowerCase().indexOf('hotjava') != -1) ? 1 : 0;
	this.ns4 = (document.layers && !this.dom && !this.hotjava) ? 1 : 0;
	this.bw = (this.ie6 || this.ie5 || this.ie4 || this.ns4 || this.ns6 || this.opera);
	this.ver3 = (this.hotjava || this.ie3);
	this.opera7 = ((this.agent.toLowerCase().indexOf('opera 7') > -1) || (this.agent.toLowerCase().indexOf('opera/7')>-1));
	this.operaOld = this.opera && !this.opera7;
	return this;
};

/*
 * Pré-chargement de plusieurs images, contenues dans un tableau.
 * @param arg tableau contenant les images à pré-charger.
 */
function pldImg(arg) {
	for (var i in arg) {
		var im = new Image();
		im.src = arg[i];
	}
}

/*
 * Assemblage récursif des fils d'un noeud donné.
 * @param node noeud à traiter.
 */
function assemlbleChildren(node) {
	if (node.children.length > 0 ) {
		node.children[node.children.length - 1].next = null;
	}
	for (var i = 0; i < node.children.length; i++) {
		if ( i + 1 < node.children.length ) {
			node.children[i].next = node.children[i + 1];
		}
		if (node.children[i].children.length > 0) {
			assemlbleChildren(node.children[i]);
		}
	}
	return;
}

/*
 *
 *
 */
function RedrawAllTrees() {
	for (var tree in NTrees) {
		NTrees[tree].draw();
	}
}

window.oldCTOnLoad = window.onload;
/*
 *
 *
 */
function CTOnLoad() {
	var bw = new bw_check();
	if (bw.ns4 || bw.operaOld){
		window.origWidth=window.innerWidth;
		window.origHeight=window.innerHeight;
	}
	if (bw.operaOld) {
		if (!window.operaResizeTimer) {
			resizeHandler();
		}
		for (var tree in NTrees) {
			for (var j=0;j<NTrees[tree].Nodes.length;j++) {
				if (NTrees[tree].fmt.rel) {
					var l=NTrees[tree].fmt.back.el.offsetParent.offsetLeft;
					var t=NTrees[tree].fmt.back.el.offsetParent.offsetTop;
					var par=NTrees[tree].fmt.back.el.offsetParent;
					while (par!=document.body){
						l +=par.offsetLeft;
						t +=par.offsetTop;
						par=par.offsetParent;
					}
					NTrees[tree].operaTop=t;
					NTrees[tree].operaLeft=l;
				}
				if (NTrees[tree].Nodes[j].visible) {
					NTrees[tree].Nodes[j].el.style.visibility='visible';
				}
			}
			NTrees[tree].draw();
		}
	}
	if (typeof(window.oldCTPOnLoad)=='function') {
		window.oldCTPOnLoad();
	}
	if (bw.ns4) {
		window.onresize = resizeHandler;
	}
}

window.onload = new CTOnLoad();
/*
 *
 *
 */
function resizeHandler() {
	if (window.reloading) {
		return;
	}
	var reload = window.innerWidth != window.origWidth || window.innerHeight != window.origHeight;
	window.origWidth = window.innerWidth;window.origHeight = window.innerHeight;
	if (window.operaResizeTimer) {
		clearTimeout(window.operaResizeTimer);
	}
	if (reload) {
		window.reloading = 1;
		document.location.reload();
		return;
	}
	if (new bw_check().operaOld) {
		window.operaResizeTimer = setTimeout('resizeHandler()', 500);
	}
}

/**
 * Modifie la css du noeud spécifié de l'arbre.
 * @param a_node	(HTML span)	contenu du noeud
 * @param a_state	(char)		nouvel état désiré ('o' ou 'n')
 */
function nodeChange(a_node, a_state) {
	if (window.isOnDrag && window.isOnDrag == true) {
		// drag en cours -> on ne fait rien
		return;
	}
	// on récupère sa css, que l'on décompose
	var l_cssDefs = a_node.className.split('_');
		// l_cssDefs[0] : préfixe, cad nom générique de la css
		// l_cssDefs[1] : état précédent
	// on la modifie
	var l_newCss = l_cssDefs[0];
	if (a_state == 'o') {
		l_newCss += '_over';
	}
	a_node.className = l_newCss;
}

