var SELECTED_NODE = "selected_node";
var CHILDREN	  = "children";
var CLEAR_LEFT_TREES	= "clearLeftTrees";
var CLEAR_DET_TREES 	= "clearDetailTrees";
var UNSELECT_TREES		= "unselectTrees";

var LEFTTREES_NO_BLOCKED = "leftTreesNoBlocked";

var VISU_LIB	= "visuLib";
var VISU_TYPE	= "visuType"; //Permet d'affiner le message warning de suppression selon le type d'élément à supprimer

var MENU_ITEMS_VAR = 'MENU_ITEMS';
var ONGLETS_ITEMS_VAR = 'TAB_ITEMS';

var K_TREES = '';
var K_SEARCH = '';
var DIV_NAME = '';

var DIV_NAMES = ["leftTrees", "rightTrees"];

var TRANSFERT_PAGE_NAME = "pageTransfert";

var B_NEW_NAV		= true,
	B_DETAIL_FORM	= true,
	B_NO_BLOCK		= true;
	
var CMD_SHOW_TREES = 'showTrees';

var EXTRANET_LOGIN = '../modules/extranet_login.htm';

/**
 * Constructeur du controlleur
 */
function Controller() {
	// tableau contenant le nom des champs "de travail", 
	// utilisés par la méthode executeCommand pour la requête
	this.workFields = [];

	this.isBlocked = false;
	this.working = false;
}

/**
 * Initialisation du contrôleur, et demande d'affichage des arbres
 */
Controller.prototype.init = function() {
	// tableau conteant les noms des arbres affichés à gauche
	this.leftTreeNames = [];
	this.leftTreesDiv = xbGetElementById("leftTrees");
	this.leftTreesCache = this.createCacheDiv("leftTrees_cache", "cache");
	// tableau conteant les noms des arbres affichés à droite
	this.rightTreeNames = [];
	this.rightTreesDiv = xbGetElementById("rightTrees");

	// on récupère la(es) page(s) de détail
	this.detailHautIFrameElt = xbGetElementById("pageDetailHaut_parent");
	if (und(this.detailHautIFrameElt)) {
		// pas de différenciation haut / bas
		this.detailHautIFrameElt = xbGetElementById("pageDetail_parent");
	}
	this.detailCacheHautOndrag = this.createCacheDiv("detailCacheHaut_ondrag", "vitre");

	this.detailBasIFrameElt = xbGetElementById("pageDetailBas_parent");
	if (!und(this.detailBasIFrameElt)) {
		this.detailCacheBasOndrag = this.createCacheDiv("detailCacheBas_ondrag", "vitre");
	}
	
	// on crée un div tmp pour la création des éléments d'arbre ajouté sur un loadChildren
	this.createCacheDiv("tmp");
	
	// on crée un div "ghost_content" pour l'affichage de l'élément dragué
	if (!und(window.dd)) {
		var l_divGhost = document.createElement("div");
		l_divGhost.id = "ghost_content";
		l_divGhost.name = "ghost_content";
		document.body.appendChild(l_divGhost);
		
		// on définit le div temporaire pour les fantômes drag & drop
		dd.ghost_content = xbGetElementById("ghost_content");
		
		// on définit la callback de blocage de la (des) page(s) détail
		dd.updateDetailOnDrag = this.updateDetailOnDrag;
	}
	
	// flag indiquant si le target du formulaire doit être réinitialisé
	this.reInitTarget = false;

	this.selectedTree = null;
	this.leftSelTree = null;

	// menu modules
	var l_transfert = getPageTransfert();
	if (l_transfert['EXTRANET_ITEMS']) {
		window[MENU_MODULES].show(l_transfert['EXTRANET_ITEMS']);
	}
	
	// demande de chargement des arbres gauches
	this.executeCommand(CMD_SHOW_TREES);
}

/**
 * Crée un div "cache" utilisé pour la gestion des événements
 * @param	a_name		(String)	nom du div
 * @param	a_className	(String)	éventuel nom de la classe CSS à utiliser
 */
Controller.prototype.createCacheDiv = function(a_name, a_className) {
	// on crée le div
	var l_div = document.createElement("div");
	l_div.id = a_name;
	l_div.name = a_name;
	// on définit sa classe éventuelle
	if (!und(a_className)) {
		l_div.className = a_className;
	}
	
	// on définit ses éléments de style
	l_div.style.position = "absolute"; 
	l_div.style.width = "0px"; 
	l_div.style.height = "0px"; 
	l_div.style.visibility = "hidden"; 
	l_div.style.zIndex = "100000000";
	
	document.body.appendChild(l_div);
	
	return l_div;
}

/**
 * Exécute la commande relative aux paramètres passés.
 * @param a_command  commande à effectuer (peut contenir plusieurs "morceaux" séparés par ':')
 * @return true si la commande a été traitée, false sinon 
 */
Controller.prototype.executeCommand = function(a_command) {
	writeMyTrace("ctrl.execute " + a_command + " from " + window.name);
	// si un tranfert est en cours, on ne fait rien
	if (this.working == true) {
		// on ne fait rien
		return true;
	}
	// on analyse la chaîne de commande
	var l_commands = a_command.split(":");
	// par défaut, une seule commande
	var l_command = l_commands[0];
	
	if ('login' == l_command) {
		return this.gotoUrl("../control?_action=show&nav=true&key=core." + l_command);
		
	} else if ('logout' == l_command) {
		return this.gotoUrl("../control?_action=" + l_command);
		
	} else if ('accueil' == l_command) {
		// on redirige vers le login, en cas de page utilisateur "variable" en fonction de l'utilisateur
		return this.gotoUrl("../control?_action=show&nav=true&key=core.login");

	} else if ('accueilExtranet' == l_command) {
		// on redirige vers la page utilisateur 
		return this.gotoUrl("../control?_action=show&nav=true&key=core.utilisateur");
		
	} else if (CMD_SHOW_TREES  == l_command) {
		// premier chargement des arbres
		if (K_TREES != "") {
			return this.sendRequest(K_TREES, 'show');
		} else {
			// pas d'arbres initiaux -> on considère que c'est bon
			return true;
		}

	} else if ('clic_tree' == l_command) {
		// clic dans un arbre
		// on récupère les paramètres à passer au serveur
		var l_params = [];
		l_params[0] = ['tree', l_commands[1]];
		l_params[1] = ['node_id', l_commands[2]];
		if (l_commands.length == 4) {
			l_params[2] = ['parent_id', l_commands[3]];
		} else {
			l_params[2] = ['parent_id', null];
		}
		// on envoie au serveur
		return this.sendRequest(K_TREES, 'clic_tree', l_params);

	} else if ('clic_detail' == l_command) {
		// clic dans un arbre à droite
		return this.sendRequest(null, l_command,
						 [['tree', l_commands[1]] , ['node_id', l_commands[2]]]);

	} else if ('popup_detail' == l_command) {
		// clic dans la popup de navigation
		return this.sendRequest(null, l_command, [['node_id', l_commands[1]]]);

	} else if ('children' == l_command) {
		// chargement des noeuds fils
		// on conserve le nom de l'arbre courant
		this.currentTree4Children = l_commands[1]
		// on envoie au serveur
		return this.sendRequest(null, l_command, [['node_id', l_commands[2]]]);

	} else if ('clic_tab' == l_command) {
		// clic sur un onglet
		// teste la commande
		if ('module' == l_commands[1]) {
			// passage vers un module donné
			return this.gotoUrl("../control?_action=show&nav=true&key=modules." + l_commands[2]);
		}
		return this.sendRequest(null, l_command, [['selectedTab', l_commands[1]]], B_NEW_NAV);

	} else if ('rechercher' == l_command) {
		// affichage de l'écran de recherche
		var l_params = [];
		if (l_commands.length == 2) {
			l_params[0] = ['search_type', l_commands[1]];
		}
		return this.sendRequest(K_SEARCH, l_command, l_params, B_NEW_NAV);

	} else if ('afficher' == l_command) {
		// affichage de l'élément précédemment sélectionné
		return this.sendRequest(null, l_command, B_NEW_NAV); 

	} else if ('ajouter' == l_command) {
		// on "shadow" le noeud éventuellement sélectionné à gauche
		if (this.leftSelTree && NTrees[this.leftSelTree]) {
			NTrees[this.leftSelTree].shadowNode();
		}
		var l_params = [];
		l_params[0] = ['selectedTab', l_commands[1]];
		if (l_commands.length == 3) {
			l_params[1] = ['new_type', l_commands[2]];
		}
		// création d'un nouvel objet
		return this.sendRequest(null, l_command, l_params);

	} else if ('modifier' == l_command) {
		// modification de l'objet sélectionné
		return this.sendRequest(null, l_command);

	} else if (l_command.indexOf('supprimer') == 0) {
		if (this.confirmDelete(l_command)) {
			return this.sendRequest(null, l_command, [['node_id', und(l_node) ? 0 : l_node.xid()]], B_NEW_NAV);
		} else {
			// le traitement a été "effectué"
			return true;
		}

	} else if ('associer' == l_command) {

		return this.sendRequest(null, l_command, 
						[['drag_id', l_commands[1]], ['drop_id', l_commands[2]]]);

	} else if ('dissocier' == l_command) {
		var l_tree = NTrees[this.selectedTree];
		if (l_tree == null) {
			alert('Aucun arbre selectionné ! ');
			// le traitement a été "effectué"
			return true;
		}
		var l_node = l_tree.selectedNode;
		if (l_node == null) {
			alert('Aucun objet sélectionné !');
			// le traitement a été "effectué"
			return true;
		}
		if (confirm(i18n(window._CONFIRM_DISSO, 'Etes-vous sûr de vouloir dissocier ') + '"' + purgeNodeText(l_node.text) + '" ?')) {
			return this.sendRequest(null, l_command, 
							[['tree', this.selectedTree], ['node_id', l_node.xid()]]);
		} else {
			// le traitement a été "effectué"
			return true;
		}

	} else if ('reactiver' == l_command) {
		// réactivation du fantôme sélectionné
		return this.sendRequest(null, l_command) 

	} else if ('clic_sort' == l_command) {
		// on trie la colonne spécifié
		return this.sendRequest(null, l_command, 
						[['formName', l_commands[1]], ['data', l_commands[2]], ['sort', l_commands[3]]],
						false, DETAIL_FORM);
		
	}
	return false;
}

/**
 * Demande confirmation de la destruction (cf a_command) du noeud  'a_node'.
 * @param a_command  commande de destruction à effectuer
 * @return true si la destruction est confirmée
 */
Controller.prototype.confirmDelete = function(a_command) {
	var l_text = purgeNodeText(this.visuLib);
	return confirm(i18n(window._CONFIRM_DELETE, 'Etes-vous sûr de vouloir supprimer ') + '"' + l_text + '" ?')
}

/**
 * Envoie une requête au serveur.
 * @param a_key		(String)	clé de la page (éventuellement nulle -> utilisation de celle définie par défaut)
 * @param a_action	(String)	action à réaliser (éventuellement nulle)
 * @param a_params	(tableau)	tableau de paramètres supplémentaires [[key1, value1], [key2, value2], ...]
 * @param a_nav		(boolean)	true s'il faut commencer un nouveau processus d'historique
 *									(par défaut, false : continuité du processus courant)
 * @param a_detail	(boolean)	true s'il faut utiliser le formulaire 'detail' 
 *									(par défaut, false : utilisation du formulaire 'command')
 * @param a_noBlock	(boolean)	true s'il ne faut pas bloquer la vue courante (cf action vers pop-up)
 *									(par défaut, false : blocage activé)
 * @return true si la requête est partie, false sinon.
 */
Controller.prototype.sendRequest = function(a_key, a_action, a_params, a_nav, a_detail, a_noBlock) {
	// on nettoie les champs de formulaire utilisés lors de la dernière requête
	this.clearWorkFields();

	// Récupère le formulaire adéquat
	var l_form, l_window;
	if (a_detail == true) {
		// 'detailForm'
		l_window = getPageDetail();
		l_form = this.getDetailForm();
	} else {
		// 'commandForm'
		l_window = getPageCommand();
		l_form = this.getCommandForm();
	}		
	
	if (und(l_form)) {
		// pas de formulaire -> on arrête de suite
		return false;
	}
	// on renseigne les paramètres
	if (!und(a_key) && (a_key != null)) {
		l_form.key.value = a_key;
	}
	if (!und(a_action)) {
		l_form._action.value = a_action;
	}
	if (!und(a_params)) {
		var l_nb = a_params.length;
		var l_param;
		var l_paramName;
		for (var i = 0; i < l_nb; i++) {
			l_paramName = a_params[i][0];
			// on conserve le nom du paramètre, pour pouvoir "purger"
			this.workFields[this.workFields.length] = l_paramName;
			setHiddenValue(l_form, l_window, l_paramName, a_params[i][1]);
		}
	}
	if (a_nav == true) {
		// on ajoute un paramètre signifiant qu'il faut commencer un nouveau pocessus d'historique
		this.workFields[this.workFields.length] = 'nav';
		setHiddenValue(l_form, l_window, 'nav', 'true');
	}
	
	if (a_noBlock == true) {
		// on conserve cette demande, pour réinitialiser le target du formulaire au prochain appel
		this.reInitTarget = true;
	} else {
		if (this.reInitTarget) {
			this.getDetailForm().target = TRANSFERT_PAGE_NAME;
			this.reInitTarget = false;
		}
		// on lance la méthode de contrôle des nouvelles données
		this.state2commit(a_action);
	}
	
	l_form.submit();
	return true;
}

/**
 * Appelée sur clic dans un arbre de gauche
 * @param a_oldSel		(COOLjsTreeNodePRO)	ancien noeud sélectionné
 * @param a_node		(COOLjsTreeNodePRO)	nouveau noeud sélectionné
 */
Controller.prototype.clicTree = function(a_oldSel, a_node) {
	// ATTENTION : this n'est pas le controller, mais l'arbre !!!
	if (!und(a_node)) {
		if (controller.working == true) {
			// on ne fait rien
			return;
		}
		var l_selId = a_node.xid();
		if ("none" != l_selId) {
			// on a bien un noeud sélectionné
			var l_treeName = this.name;
			controller.selectedTree = l_treeName;
			controller.leftSelTree = l_treeName;
			controller.unSelectTreesExcept(l_treeName);
			var l_cmdStr = 'clic_tree:' + l_treeName + ':' + l_selId;
			controller.executeCommand(l_cmdStr);
		}
	}
}

/**
 * Appelée sur clic dans un arbre de droite
 * @param a_oldSel		(COOLjsTreeNodePRO)	ancien noeud sélectionné
 * @param a_node		(COOLjsTreeNodePRO)	nouveau noeud sélectionné
 */
Controller.prototype.clicDetail = function(a_oldSel, a_node) {
	// ATTENTION : this n'est pas le controller, mais l'arbre !!!
	if (!und(a_node)) {
		var l_selId = a_node.xid();
		if ("none" != l_selId) {
			// on "shadow" le noeud éventuellement sélectionné à gauche
			if (controller.leftSelTree) {
				NTrees[controller.leftSelTree].shadowNode();
			}
			var l_treeName = this.name;
			// on conserve la sélection
			controller.selectedTree = l_treeName;
			// on désélectionne les autres arbres droites
			controller.unselectRightTreesExcept(l_treeName);			
			// on informe le serveur, pour la mise à jour du menu
			controller.executeCommand('clic_detail:' + l_treeName + ':' + l_selId);
		}
	}
	return;
}

/**
 * Modifie l'apparence de la barre de statut pour faire informer d'un traitement en cours
 */
Controller.prototype.updateProgress = function() {
	this.progress += '.';
	window.defaultStatus = this.progress;
	// non -> on ressaye dans 1 seconde
	this.timeoutId = window.setTimeout('controller.updateProgress()', 1000);
}

/**
 * Met à jour l'état du contrôleur juste avant un submit du formulaire
 * @param  a_action   action éventuellement associée
 * @return true si le contrôleur est déjà bloqué
 */
Controller.prototype.state2commit = function(a_action) {
	// on reteste le blocage ou non de la vue, pour l'utilisation directe de cette fonction
	// par les boutons
	if (this.working == true) {
		// on bloque
		return true;
	}
/*
	// teste la remise en état du target 
	if (this.reInitTarget) {
		this.getDetailForm().target = TRANSFERT_PAGE_NAME;
		this.reInitTarget = false;
	}
*/	
	// on lance la méthode de contrôle des nouvelles données
	this.working = true;
	this.progress = i18n(window._CONTACT_SERVEUR, 'Interrogation du serveur ');
	this.updateProgress();
	
	return false;		// la commande peut partir
}

/**
 * Récupération des données depuis la page de transfert vers la page affichée et les zones de détail, 
 * entre autre par remplissage des différents panels (div).
 */
Controller.prototype.transfertData = function() {
	// dans tous les cas, on nettoie les champs utilisés pour la transmission de la précédente requête
	this.clearWorkFields();
	
	// on teste le contenu de la frame de transfert
	if (this.filterTransfert() == false) {
		// un problème est survenu, la redirection a normalement déjà eu lieu
		return;
	}
	
	// on récupère la page de transfert
	var l_transfert = getPageTransfert();
	
	// noeuds fils
	if (l_transfert[CHILDREN]) {
		this.childrenLoaded();
		// on regarde l'éventuel message
		if (!und(l_transfert.message)) {
			alert(l_transfert.message);
		}
		// on regarde une éventuelle popup
		this.displayPopup();
		
		// on arrête là
		this.endTransfert();
		
		if (l_transfert.pagePopup && this.popup) {
			this.popup.focus();
		}
		return;
	}

	// on met à jour les éléments du formulaire de commande, 
	// à partir de ceux du formulaire de transfert
	this.updateCommandForm();

	// on récupère la variable de blocage
	var l_newBlocked = l_transfert.blockView || false;
	if (l_newBlocked != this.isBlocked) {
		// changement d'état de blocage
		if (l_newBlocked == true) {
			// on bloque de suite 
			this.isBlocked = l_newBlocked;
			this.changeStatus();
		}
	}

	// fonction de traitements spécifiques
	if (!this.specificTransf()) {
		if (l_transfert.displayDetail) {
			// on a demandé l'affichage du détail 
			// -> on affichera les modifications de navigation
			// une fois la page détail chargée (via appel de detailLoaded)
			
			// on recharge la page détail
			this.reloadDetail();
		} else {
			// on affiche les modifications
			this.updateNavigation();
			// on traite également les éventuels arbres droite (en mise à jour seule)
			this.manageTrees(l_transfert, "right");

			// on regarde une éventuelle popup
			this.displayPopup();
		}
		// on regarde si on a explicitement demandé la suppression d'un arbre droite
		this.deleteRightTree(l_transfert);
	}
	// on relocalise éventuellement la frame principale
	if (!und(l_transfert.hash)) {
		if (l_transfert.hash == 'top') {
			top.window.scroll(0, 0);
		}
		// pb avec Safari / MAC : plantage 
		//window.location.hash = l_transfert.hash;
	}
	

	// affichage éventuel du message
	if (!und(l_transfert.message)) {
		alert(l_transfert.message);
	}
	
	this.endTransfert();

	// affichage éventuel de la question
	if (!und(l_transfert.question)) {
		if (confirm(l_transfert.question[0])) {
			// la réponse est favorable -> on exécute de suite la commande spécifiée
			this.executeCommand(l_transfert.question[1]);
		} else {
			// la réponse est négative -> on regarde s'il faut executer une autre commande
			var l_cmdAnnul = l_transfert.question[2];
			if (l_cmdAnnul != '') {
				this.executeCommand(l_cmdAnnul);
			}
		}
	}
	
	// éventuelle commande suivante
	if (!und(l_transfert.nextCommand)) {
		// une commande a été définie -> on l'exécute directement
		executeCommand(l_transfert.nextCommand);
	}
	
	// éventuelle fonction de sélection automatique
	var l_onloadAutoSelect = l_transfert.onloadAutoSelect;
	if (!und(l_onloadAutoSelect)) {
		// une fonction de sélection auto est définie -> on l'appelle directement
		l_onloadAutoSelect();
	}
}

/**
 * Remplissage du panel spécifié : utilisée pour l'affichage des rubriques dynamiques
 * (publiques, donc pour l'instant, sans utilisation d'iframe pageDetail)
 * @param a_id id du div à traiter
 * @return ok si un div correspondant est trouvé
 */
Controller.prototype.fillPanel = function(a_id, a_fill) {
	// récupération du div de la page cachée
	var l_response = xbGetElementById(a_id, getPageTransfert());
	if (!checkVariable(l_response)) {
		// pas le bon id
		return null;
	}	
	// récupération du div de la page affichée
	var l_panel = xbGetElementById(a_id);
	if (!checkVariable(l_panel)) {
		// pas le bon panel (c'est plus grave ...)
		return null;
	}
	// copie
	if (a_fill) {
		xbSetInnerHTML(l_panel, l_response.innerHTML);
	}
	// on renvoie "ok" puisque tout s'est bien passé
	return "ok";
}

/**
 * Met à jour tous les éléments de navigation en fonction des données transférées.
 */
Controller.prototype.updateNavigation = function() {
	var l_transfert = getPageTransfert();

	// menu ?
	if (window.MENU_BAR && window[MENU_BAR] && l_transfert[MENU_ITEMS_VAR]) {
		window[MENU_BAR].reloadData(l_transfert[MENU_ITEMS_VAR]);
	}

	// barre d'onglets
	var l_tabs = l_transfert[ONGLETS_ITEMS_VAR];
	if (!und(l_tabs) && window[TAB_BAR]) {
		window[TAB_BAR].show(l_tabs);
	}

	// libellé de sélection ?
	if (!und(l_transfert[VISU_LIB])) {
		this.visuLib = l_transfert[VISU_LIB];
		// on nettoie éventuellement
		var l_pos = this.visuLib.indexOf('$$');
		if (l_pos > 0) {
			// on supprime les premiers caractères pour l'affichage
			this.visuLib = this.visuLib.substring(l_pos + 2);
		}
	}
	// type de la sélection ?
	if (!und(l_transfert[VISU_TYPE])) {
		this.visuType = l_transfert[VISU_TYPE];
	}

	// désélection des arbres ?
	if (l_transfert[UNSELECT_TREES] == true) {
		this.selectedTree = null;
		this.leftSelTree = null;
		this.unSelectTreesExcept('');
	}

	// arbres gauches
	// suppression ?
	if (l_transfert[CLEAR_LEFT_TREES] == true) {
		this.clearLeftTrees();
	}
	// affichage
	var l_selectedName = this.manageTrees(l_transfert, "left");
	if (l_selectedName != null) {
		this.selectedTree = l_selectedName;
		this.leftSelTree = l_selectedName;
		this.unSelectTreesExcept(l_selectedName);
	}
}

/**
 * Gère l'affichage des arbres de la partie spécifiée par a_side, en utilisant les données
 * présente dans la page fournie.
 * @param	a_page		(window)	page contenant les données
 * @param	a_side		(String)	partie à traiter ("left"|"right")
 * @return le nom de l'arbre sélectionné
 */
Controller.prototype.manageTrees = function(a_page, a_side) {
	var l_selectedName = null;
	// on récupère le tableau contenant le nom des arbres dans la page fournie
	var l_treeNames = a_page[a_side + "Trees"];
	if (l_treeNames) {
		// il y a bien de tels arbres à gérer
		// on récupère le tableau des noms des arbres affichés	
		var l_visuTabName = this[a_side + "TreeNames"];
		// on parcourt le tableau des arbres transmis
		var l_treeName;
		var l_data;
		var l_create;
		var l_div;
		for (var i = 0; i < l_treeNames.length; i++) {
			l_treeName = l_treeNames[i];
			if (und(l_treeName)) {
				alert(l_treeName + " n'existe plus ...");
				continue;
			}
			// on regarde de quel type de données on dispose
			l_data = a_page[l_treeName + "_DATA_update"];
			if (l_data) {
				// données de mise à jour -> on ne fait que recharger l'arbre
				if (window[l_treeName].reloadData(l_data)) {
					l_selectedName = l_treeName;
				}
			} else {
				l_data = a_page[l_treeName + "_DESC"];
				if (l_data) {	// a priori, toujours le cas
					// données de création
					// on construit la chaîne de caractères permettant la création de l'arbre
					l_create = 'l_visTree = new ' + l_data[0] + '("' + l_treeName + '"';
					for (var j = 0; j < l_data[1].length; j++) {
						l_create += ', ' + l_data[1][j];
					}
					l_create += ')';
					// on construit un nouvel arbre
					eval(l_create);
					// on conserve son nom pour les utilisations ultérieures
					window[l_treeName] = l_visTree;
					l_visuTabName[l_visuTabName.length] = l_treeName;
					// on ajoute la fonction de sélection
					this.setSelectFunc(l_visTree, l_treeName, a_side);
					
					// on construit le nouveau div allant contenir l'arbre
					l_div = document.createElement("div");
					// on définit son nom
					l_div.id = a_side + '_' + l_treeName;
					// on définit son style
					l_div.className = a_side + "Tree";
					// on l'ajoute au div contenant les arbres
					this[a_side + "TreesDiv"].appendChild(l_div);
					
					// on initialise l'arbre dans ce div
					l_visTree.init(l_div);
					
					// on affiche l'arbre dans ce div
					l_visTree.draw();
				}
			}
			var l_updateRoot = a_page[l_treeName + "_updateRoot"];
			if (l_updateRoot) {
				window[l_treeName].updateRootName(l_updateRoot);
			}
		}
	}
}

/**
 * Supprime l'éventuel arbre droit précisé dans la page fournie.
 * Si le nom précisé est "all", on détruit tous les arbres.
 * @param	a_page		(window)	page contenant les données
 */
Controller.prototype.deleteRightTree = function(a_page) {
	var l_right2del = a_page.deleteRightTree;
	if (l_right2del) {
		// on le recherche
		// on récupère le tableau de tous les arbres
		var l_trees = window.NTrees;
		// on parcourt le tableau des noms des arbres droite
		var l_treeName;
		for (var i = this.rightTreeNames.length; --i >= 0; ) {			
			l_treeName = this.rightTreeNames[i];
			if ((l_right2del == l_treeName) || (l_right2del == "all")) {
				// c'est le bon -> on le supprime 
				if (l_trees[l_treeName]) {
					l_trees[l_treeName].clear();
				}
				// on le supprime de la vue
				if (window[l_treeName]) {
					window[l_treeName] = null;
				}
				// on le supprime du tableau
				this.rightTreeNames[i] = null;
				
				// on le supprime de la vue
				this.rightTreesDiv.removeChild(xbGetElementById('right_' + l_treeName));
			}
		}
	}
}

/**
 * Met à jour tous les éléments de navigation en fonction des données transférées.
 * @param	a_withTree	(boolean)	si true, appel de la méthode d'affichage des arbres droite
 */
Controller.prototype.detailLoaded = function(a_withTree) {
	// on réinitialise le pointeur vers le formulaire détail
	this.detailForm = null;

	// on récupère la variable de blocage
	var l_newBlocked = getPageTransfert().blockView || false;

	// nettoie les éventuels arbres droite
	this.clearRightTrees();
	if (l_newBlocked == false) {
		var l_transfert = getPageTransfert();
		if (a_withTree == true) {
			this.manageTrees(getPageDetail(), "right");
			if (!und(l_transfert.selectedTree)) {
				this.rightSelect(l_transfert.selectedTree);
			}
		}
	}	
	// on met à jour les éléments de navigation (même si la vue est bloquée ...)
	this.updateNavigation();
	
	if (l_newBlocked != this.isBlocked) {
		// changement d'état de blocage
		if (l_newBlocked == false) {
			// on débloque maintenant seulement
			this.isBlocked = l_newBlocked;
			this.changeStatus();
		}
	} else {
		// on met à jour la cache des arbres gacuhe, pour prendre en compte la nouvelle dimension
		this.updateLeftTreeCache();
		// on bloque les onglets (ils peuvent avoir été modifiés)
		if (window.TAB_BAR) {
			// on "bloque" les onglets, s'ils existent et ont été modifiés
			var l_onglets = window[TAB_BAR];
			if (!und(l_onglets) && !und(getPageTransfert()[ONGLETS_ITEMS_VAR])) {
				l_onglets.setBlocked(this.isBlocked);
			}
		}
		
	}

	// on regarde une éventuelle popup
	this.displayPopup();
}

/**
 * Repositionne la frame principale pour visualiser l'éventuelle "ancre" XXX spécifiée dans la page
 * détail (du haut en cas de pages multiples) par un tag <span id="XXX">.
 */
Controller.prototype.relocate2STHash = function() {
	// on récupère l'éventuelle "ancre"
	var l_stHash = getPageTransfert().stHash;
	if (l_stHash) {
		// on récupère le tag <span correspondant
		var l_span = xbGetElementById(l_stHash, getPageDetail());
		if (l_span) {
			// on l'a bien trouvé -> on récupère sa position dans la page
			var l_spanY = getClientTop(l_span);
			// on ajoute la position de la td englobante
			l_spanY += getClientTop(this.detailHautIFrameElt);
			// on scroll
			window.scrollTo(0, l_spanY);
		}
	}
}

/**
 * Met à jour la barre de statut, pour indiquer la fin du traitement
 */
Controller.prototype.endTransfert = function() {
	// on arrête le processus de mise à jour
	window.clearTimeout(this.timeoutId);
	
	window.defaultStatus = i18n(window._UPDATE_OK, 'Mise à jour effectuée');
	this.working = false;
}

/**
 * Définit l'éventuelle fonction appellée sur un clic dans l'arbre fourni.
 * @param a_tree		(COOLjsTreePRO) arbre
 * @param a_treeName	(String)		nom de l'arbre
 * @param a_side		(String)		position de l'arbre (gauche|droite)
 */
Controller.prototype.setSelectFunc = function(a_tree, a_treeName, a_side) {
	if ("left" == a_side) {
		// par défaut, clic de sélection
		a_tree.onSelectNode = this.clicTree;
		
	} else {
		// par défaut, clic détail
		a_tree.onSelectNode = this.clicDetail;
	}
}

/**
 * Sélectionne un noeud dans l'arbre droite spécifié par son nom.
 * Désélectionne tous les autres arbres droite.
 * @param a_treeName	(String)	nom de l'arbre droite sélectionné
 */
Controller.prototype.rightSelect = function(a_treeName) {
	// on "shadow" le noeud éventuellement sélectionné à gauche
	if (this.leftSelTree && window.NTrees[controller.leftSelTree]) {
		window.NTrees[this.leftSelTree].shadowNode();
		this.leftSelTree = null;
	}
	// on déselectionne les arbres de droites
	this.unselectRightTreesExcept(a_treeName);

	// on conserve la sélection
	this.selectedTree = a_treeName;
}

/**
 * Méthode à remplacer pour effectuer des transferts spécifiques.
 * @return true s'il faut arrêter la procédure de transfert
 *		   false sinon (valeur par défaut)
 */
Controller.prototype.specificTransf = function() {
	return false;
}

/**
 * Nettoyage des paramètres de requêtage des formulaires 'detail' et 'command'.
 */
Controller.prototype.clearWorkFields = function() {
	// on "nettoie" tous les champs de workFields 
	var l_detailFrame = getPageDetail(),
		l_commandFrame = getPageCommand();
	var l_elt;
	for (var i = 0; i < this.workFields.length; i++) {
		l_elt = xbGetElementById(this.workFields[i], l_detailFrame);
		if (l_elt && l_elt.value) {
			// on "nettoie" et renomme le champ (pour compatibilité Safari)
			// -> le champ '_poubelle_' n'est jamais utilisé
			l_elt.value = null;
			l_elt.name = '_poubelle_';
			l_elt.parentNode.removeChild(l_elt);
		}
		l_elt = xbGetElementById(this.workFields[i], l_commandFrame);
		if (l_elt && l_elt.value) {
			// on "nettoie" et renomme le champ (pour compatibilité Safari)
			// -> le champ '_poubelle_' n'est jamais utilisé
			l_elt.value = null;
			l_elt.name = '_poubelle_';
			l_elt.parentNode.removeChild(l_elt);
		}
	}
	this.workFields.length = 0;
}

/**
 * Mise à jour les éléments du formulaire de commande, 
 * à partir de ceux du formulaire de transfert
 */
Controller.prototype.updateCommandForm = function() {
	// on récupère les deux objets
	var l_transfert = getPageTransfert();
		l_commandForm = this.getCommandForm();
	// process courant
	if (l_transfert.process) {
		l_commandForm.process.value = l_transfert.process;
	}
	// key du formulaire
	if (l_transfert.key) {
		l_commandForm.key.value = l_transfert.key;
	}
}

/**
 * Ajoute de nouveaux noeuds à un arbre.
 */
Controller.prototype.childrenLoaded = function() {
	var l_transfert = getPageTransfert();
	// on récupère l'arbre concerné
	// d'abord dans la fenêtre principale
	var l_tree = window[this.currentTree4Children];
	if (und(l_tree)) {
		// puis dans la fenêtre de détail
		l_tree = getPageDetail().NTrees[this.currentTree4Children];
	}
	this.currentTree4Children = null;

	l_tree.childrenLoaded(l_transfert.children);
}

/**
 * Teste le contenu de la page de transfert et filtre le processus de transfert : si un pb
 * quelconque est survenu, redirige éventuellement vers la bonne page et renvoie false.
 * Si tout est normal, renvoie true, et permet ainsi au reste de l'algorithme de transfert de
 * se dérouler.
 */
Controller.prototype.filterTransfert = function() {
	var l_transfertPage = getPageTransfert();
	
	if (l_transfertPage != null) {
		if (l_transfertPage.transfert) {
			// c'est bon, on a bien récupéré une page de transfert (detail ou no_action)
			return true;
		} else {
			// on regarde s'il existe un formulaire de login (signe d'un timeout de session ...)
			var l_loginForm = xbGetElementById("loginForm", l_transfertPage);
			if (l_loginForm != null) {
				// c'est le cas -> on réaffiche la page de login 
				this.gotoUrl(EXTRANET_LOGIN);
				return false;
			}
			// on regarde s'il existe un message d'erreur
			if (l_transfertPage.errorMsg) {
				// message d'erreur -> on l'affiche	
				alert("erreur dans result : " + l_transfertPage.errorMsg);
				return false;
			}
			return false;
		}
	} else {
		return false;
	}
}

/**
 * Renvoie la variable contenue dans la frame de transfert correspondant au nom spécifié.
 * @param	a_varName	(String)	nom de la variable demandée
 */
Controller.prototype.getTransfertVariable = function(a_varName) {
	return getPageTransfert()[a_varName];
}

/**
 * Renvoie l'objet formulaire "detailForm" de la page détail
 */	
Controller.prototype.getDetailForm = function() {
	if (und(this.detailForm)) {
		var l_detailPage = getPageDetail();
		
		if (l_detailPage != null) {
			this.detailForm = xbGetElementById("detailForm", l_detailPage);
		}
	} 
	return this.detailForm;
}

/**
 * Renvoie l'objet formulaire "commandForm" de la frame commande
 */	
Controller.prototype.getCommandForm = function() {
	if (und(this.commandForm)) {
		this.commandForm = getCommandForm();
	} 
	return this.commandForm;
}

/**
 * Affiche le message d'erreur
 */
Controller.prototype.alertError = function() {
	var l_transfertPage = getPageTransfert();
	if (l_transfertPage != null) {
		// on recherche le message d'erreur
		var l_errorMsg = l_transfertPage.errorMsg;
		if (l_errorMsg) {
			// message d'erreur -> on l'affiche	
			alert("erreur dans result : " + l_errorMsg);
		}
	}
	// fin du travail
	this.endTransfert();
}

/**
 * Désélectionne tous les arbres non selectionnés
 * @param a_selectedTreeName	nom de l'arbre réellement sélectionné
 */
Controller.prototype.unSelectTreesExcept = function(a_selectedTreeName) {
	var l_trees = window.NTrees;
	// parcours des arbres
	for (var l_treeName in l_trees) {			
		// si diff de selectedTree
		if (l_treeName != a_selectedTreeName) {
			// déselectionne
			l_trees[l_treeName].unselectNode();
		}
	}
}

/**
 * Supprime tous les arbres appartenant à la partie gauche
 */
Controller.prototype.clearLeftTrees = function() {
	this.clearTrees(this.leftTreeNames, this.leftTreesDiv);
}

/**
 * Supprime tous les arbres appartenant à la partie droite
 */
Controller.prototype.clearRightTrees = function() {
	this.clearTrees(this.rightTreeNames, this.rightTreesDiv);
}


/**
 * Supprime tous les arbres précisés dans les paramètres fournis.
 * @param	a_names		(String[])	noms des arbres à détruire
 * @param	a_div		(HTLMDiv)	div à nettoyer
 */
Controller.prototype.clearTrees = function(a_names, a_div) {
	if (a_names && !und(a_div)) {
		// on récupère le tableau de tous les arbres
		var l_trees = window.NTrees;
		// on parcourt le tableau des noms des arbres droite
		var l_treeName;
		for (var i = a_names.length; --i >= 0; ) {			
			l_treeName = a_names[i];
			if (und(l_treeName)) {
				// on passe
				continue;
			}
			// on le supprime 
			if (l_trees[l_treeName]) {
				l_trees[l_treeName].clear();
			}
			// on le supprime de la vue
			if (window[l_treeName]) {
				window[l_treeName] = null;
			}
		}
		// on nettoie le tableau
		a_names.length = 0;
		// on supprime tous les éléments contenus dans le div d'affichage des arbres droites
		var l_divs = a_div.childNodes;
		for (var i = l_divs.length; --i >= 0; ) {
			a_div.removeChild(l_divs[i]);
		}
	}
}

/**
 * Désélectionne tous les arbres appartenant à la partie détail
 */
Controller.prototype.unselectRightTreesExcept = function(a_selectedTreeName) {
	var l_trees = window.NTrees;
	// parcours des arbres
	var l_left;
	// on parcourt le tableau des noms des arbres droite
	var l_treeName;
	for (var i = this.rightTreeNames.length; --i >= 0; ) {
		l_treeName = this.rightTreeNames[i]; 
		if (und(l_treeName)) {
			// on passe
			continue;
		}
		// on désélectionne si diff de a_selectedTreeName
		if ((l_treeName != a_selectedTreeName) && (l_trees[l_treeName])) {
			l_trees[l_treeName].unselectNode();
		}
	}
}

/**
 * Active / Inactive les div des arbres et toute forme de navigation
 * (pour les 'désactiver' en mode modification).
 */
Controller.prototype.changeStatus = function() {
	if (this.isBlocked == true) {
		// on veut bloquer -> on change le gestionnaire de resize (en conservant l'ancien)
		this.oldOnResize = window.onresize;
		window.onresize = this.updateAllTreeCaches;
		// on conserve l'éventuel booléen de non blocage des arbres gauche
		if (und(this.leftTreesNoBlocked)) {
			this.leftTreesNoBlocked = getPageTransfert()[LEFTTREES_NO_BLOCKED];
		}
	} else {
		// on remet l'ancien gestionnaire
		if (!und(this.oldOnResize)) {
			window.onresize = this.oldOnResize;
		}
		this.leftTreesNoBlocked = null;
	}
	this.divBlocked = this.isBlocked;
	// on "bloque" les arbres
	this.updateAllTreeCaches();
	
	// on "bloque" le menu des modules, s'il existe
	if (window.MENU_MODULES) {
		var l_menuMod = window[MENU_MODULES];
		if (!und(l_menuMod)) {
			l_menuMod.setBlocked(this.isBlocked);
		}
	}
		
	// on "bloque" le menu action, s'il existe
	if (window.MENU_BAR) {
		var l_menuAct = window[MENU_BAR];
		if (!und(l_menuAct)) {
			// on regarde si un nouveau menu est défini ou non
			if (!this.isBlocked || und(getPageTransfert()[MENU_ITEMS_VAR])) {
				// non -> on peut bloquer
				l_menuAct.setBlocked(this.isBlocked);
			}
		}
	}
	
	if (window.TAB_BAR) {
		// on "bloque" les onglets, s'ils existent
		var l_onglets = window[TAB_BAR];
		if (!und(l_onglets)) {
			l_onglets.setBlocked(this.isBlocked);
		}
	}
}

/**
 * Active / Inactive les div des arbres, pour les 'désactiver' en mode modification.
 * ATTENTION, this n'est pas forcément le controller (callback sur redimensionnement de la fenêtre)
 */
Controller.prototype.updateAllTreeCaches = function() {
	controller.updateLeftTreeCache();
	controller.updateRightTreeCache();
}

/**
 * Active / Inactive le cache des arbres gauche
 */
Controller.prototype.updateLeftTreeCache = function() {
	// on regarde si on n'a pas demandé explicitement le non blocage des arbres gauches
	// ou s'il n'y a pas d'arbre gauches ...
	if (this.leftTreesNoBlocked || und(this.leftTreesCache)) {
		// on ne fait rien
		return;
	}

	if (this.divBlocked == true) {
		if (this.leftTreesDiv) {
			// on récupère les caractéristiques du div visible
			var l_position = getClientPosition(this.leftTreesDiv);
			// on les affecte au div invisible 
			this.leftTreesCache.style.left = l_position[0];
			this.leftTreesCache.style.top = l_position[1];
			this.leftTreesCache.style.width = this.leftTreesDiv.offsetWidth + "px"; 
			var l_height = parseInt(this.leftTreesDiv.offsetHeight) - parseInt(this.leftTreesDiv.offsetTop);
			if (l_height > 0) {
				this.leftTreesCache.style.height = l_height + "px";
			}
			// on le rend visible
			this.leftTreesCache.style.visibility = 'visible';
		}
	} else {	
		this.leftTreesCache.style.visibility = 'hidden';
		this.leftTreesCache.style.width = "0px";
		this.leftTreesCache.style.height = "0px";
	}
}

/**
 * Active / Inactive le cache de la (des) page(s) détail.
 * @param	a_blocked		(boolean)	true si la page détail est "bloquée"
 *											c'est-à-dire insensible au survol 
 */
Controller.prototype.updateDetailOnDrag = function(a_blocked) {
	// ATTENTION : this n'est pas Controller, mais 'dd'
	controller.updateDetailOnDrag4Page(controller.detailHautIFrameElt, 
									   controller.detailCacheHautOndrag,
									   a_blocked,
									   getPageDetail());
									   
	if (!und(controller.detailBasIFrameElt)) {
		controller.updateDetailOnDrag4Page(controller.detailBasIFrameElt, 
										   controller.detailCacheBasOndrag,
										   a_blocked,
										   getPageDetailBas());
	}
}

/**
 * Active / Inactive le cache de la page détail spécifiée.
 * @param	a_detailElt		(HTMLElt)	élément correspondant à la page
 * @param	a_detailCache	(HTMLElt)	élément correspondant au cache de la page
 * @param	a_blocked		(boolean)	true si la page détail est "bloquée",
 *											c'est-à-dire insensible au survol 
 * @param	a_pageDetail	(window)	page détail concernée
 */
Controller.prototype.updateDetailOnDrag4Page = function(a_detailElt, a_detailCache, a_blocked, a_pageDetail) {
	if (a_blocked == true) {
		this["oldMouseOver" + a_pageDetail.id] = a_pageDetail.document.body.onmouseover;
		this["oldMouseMove" + a_pageDetail.id] = a_pageDetail.document.body.onmousemove;
		a_pageDetail.document.body.onmouseover = this.disabledOnMouse;
		a_pageDetail.document.body.onmousemove = this.disabledOnMouse;
		// on récupère les caractéristiques de l'iframe de détail
		var l_position = getClientPosition(a_detailElt);
		// on les affecte au div invisible 
		a_detailCache.style.left = l_position[0];
		a_detailCache.style.top = l_position[1];
		a_detailCache.style.width = a_detailElt.offsetWidth + "px";
		a_detailCache.style.height = a_detailElt.offsetHeight + "px";
		// on le rend visible
		a_detailCache.style.visibility = 'visible';
	} else {	
		a_pageDetail.document.body.onmouseover = this["oldMouseOver" + a_pageDetail.id];
		a_pageDetail.document.body.onmousemove = this["oldMouseMove" + a_pageDetail.id];

		a_detailCache.style.visibility = 'hidden';
		a_detailCache.style.width = "0px";
		a_detailCache.style.height = "0px";
	}
}

/**
 * Active / Inactive le div des arbres droit
 */
Controller.prototype.updateRightTreeCache = function() {
	if (this.divBlocked == true) {
		this.clearRightTrees();
	}
}

/**
 * Callback permettant de désactiver complètement les événements de souris
 * dans les pages de détail lors d'un drag & drop.
 * @param	a_event		(Event)		événement
 * @return false pour ne pas propager l'événement
 */
Controller.prototype.disabledOnMouse = function(a_event) {
	return false;
}


/**
 * Redirige vers la page fournie, en gérant éventuellement le process
 * @param	a_url	(String)	url de redirection
 */
Controller.prototype.gotoUrl = function(a_url) {
	if (this.isBlocked == true) {
		// la page est bloquée -> on alerte l'utilisateur
		this.alertBlocked();
		// et on s'arrête là
		return true;
	}
		
	// on ferme la popup
	this.closePopup();

	// on récupère le champ process
	var l_form = this.getCommandForm();
	if (!und(l_form)) {
		var l_process = l_form.process.value;
		if (!und(l_process)) {
			// on a bien une valeur -> on l'ajoute à l'url
			// on regarde si l'url contient déjà des paramètres
			var l_pos = a_url.indexOf("?");
			// si oui, on ajoute ('&') le paramètre, sinon, on crée une chaîne de param ('?')
			a_url += ((l_pos > 0) ? '&' : '?') + 'process=' + l_process;
		}
	}
	// on y va
	window.location.href = a_url;
	
	// on renvoie true, même si a priori, pas trop de risque d'y passer, puisqu'on a changé de page
	return true;	
}

/**
 * Recharge la frame de détail, en gérant le process et la clé
 */
Controller.prototype.reloadDetail = function() {
	// on récupère la frame de transfert
	var l_transfert = getPageTransfert();
	
	// on veut rediriger vers la page de détail
	var l_url = '..' + l_transfert.pageDetail;
	
	// on regarde s'il existe déjà un paramètre
	l_url += (l_url.indexOf('?') < 0) ? '?' : '&';
	// on ajoute les champs process et key à l'url
	l_url += 'process=' + l_transfert.process + '&key=' + l_transfert.key + 
				'&ts=' + new Date().getTime();
				
	// on redirige la ou les pageDetail
	var l_page = getPageDetailHaut();
	var l_multi = l_transfert.multiPage || false;
	if (l_transfert.blockView || (l_multi == false) || und(l_page)) {
		// on considère que si la vue est bloquée, seule la partie haute est affichée
		// -> une seule page détail
		getPageDetail().location.href = l_url;
		// on "nettoie" éventuellement la page détail du bas
		if (!und(this.detailBasIFrameElt)) {
			getPageDetailBas().location.href = '../modules/_blank.htm';
			this.detailBasIFrameElt.style.height = 0;
		}
	} else {
		// différentiation haut / bas
		l_page.location.href = l_url + '&_detail=haut';	
		l_page = getPageDetailBas();
		if (!und(l_page)) {
			l_page.location.href = l_url + '&_detail=bas';	
		}
	}
	// on remodifie le titre du document (compatibilité IE 5.0)
	if (window.LABELS) {
		var l_titre = LABELS[0];
		if (!und(l_titre)) {
			window.document.title = l_titre;
		}
	}
}

/**
 * Affiche éventuellement la popup demandée, en gérant le process et la clé
 */
Controller.prototype.displayPopup = function() {
	// on récupère la frame de transfert
	var l_transfert = getPageTransfert();
	
	// on regarde si une page de popup est spécifiée
	var l_url = l_transfert.pagePopup;
	if (und(l_url)) {
		// non -> on regarde si on veut en fermer une
		var l_name = l_transfert.killPopup;
		if (!und(l_name)) {
			// on ferme la popup courante
			this.closePopup();
		}
		// on arrête là
		return;
	}
	// il y a bien une popup à ouvrir
	// on désélmectionne les arbres
	this.unSelectTreesExcept('');
	
	// on ouvre la page demandée dans une popup
	l_url = '..' + l_url;
	
	// on regarde s'il existe déjà un paramètre
	l_url += (l_url.indexOf('?') < 0) ? '?' : '&';
	// on ajoute les champs process et key à l'url
	l_url += 'process=' + l_transfert.process + '&key=' + l_transfert.key + 
				'&ts=' + new Date().getTime();
				
	// on ouvre la popup
	var l_size = l_transfert.popupSize;
	if (und(l_size)) {
		// taille par défaut
		l_size = [300, 300];
	}
	// on détermine la position (par défaut, absolue par rapport à l'écran)
	var l_top = 100, l_left = 100;
	if (l_transfert.popupRelative) {
		// position relative par rapport à la page courante
		if (top.screenX) {
			// on a accès aux coordonnées de la fenêtre
			l_left += top.screenX;
			l_top += top.screenY;
		} else if (top.screenLeft) {
			l_left += top.screenLeft;
			l_top += top.screenTop;
		}
	}
	if ((this.popup != null) && !this.popup.closed) {
		this.popup.close();
	}
	this.popup = openWindow(l_url, "popup", l_size[0], l_size[1], l_left, l_top, "yes", "yes");
	
	if ((this.popup != null) && this.popup.focus) {
		this.popup.focus();
	}
}

/**
 * Ferme l'éventuelle popup.
 */
Controller.prototype.closePopup = function() {
	if (this.popup) {
		this.popup.close();
		this.popup = null;
	}
}

/**
 * Affichage d'un message d'alerte concernant le blocage de fonctionnalité en fonction
 * de l'état de la page (pb de formulaire en modification essentiellement)
 */
Controller.prototype.alertBlocked = function() {
	alert(i18n(window.ACTION_IMPOSSIBLE, "Action impossible. Veuillez tout d'abord annuler l'action en cours."));
}

/**
 * Renvoie la frame de détail.
 * @return	la frame
 */
function getPageDetail() {
	var l_page = window.pageDetail;
	if (und(l_page)) {
		// si 2 pages détail, on renvoie par défaut celle du haut : c'est celle qui contient
		// le seul formulaire detailForm
		l_page = window.pageDetailHaut;
	}
	return l_page;
}

/**
 * Renvoie la frame de détail du haut
 * @return	la frame
 */
function getPageDetailHaut() {
	return window.pageDetailHaut;
}

/**
 * Renvoie la frame de détail du bas
 * @return	la frame
 */
function getPageDetailBas() {
	return window.pageDetailBas;
}

/**
 * Renvoie la frame de transfert.
 * @return	la frame
 */
function getPageTransfert() {
	return window.pageTransfert;
}

/**
 * Renvoie la frame d'exécution de commande.
 * @return	la frame
 */
function getPageCommand() {
	return window.pageCommand;
}

/**
 * Renvoie l'objet formulaire "commandForm" de la frame commande
 */	
function getCommandForm() {
	var l_commandPage = getPageCommand();
	
	if (l_commandPage != null) {
		return xbGetElementById("commandForm", l_commandPage);
	}
	return null;
}

/**
 * Renvoie le texte fourni en supprimant toutes les balises HTML.
 * @param a_text 	(String)	chaîne à purger
 * @return la chaîne purgée
 */
function purgeNodeText(a_text) {
	// on découpe le texte initial, pour ne conserver que les morceaux 
	// ne comportant pas de signes < ou >
	var l_tiles = a_text.split(/<[^>]*>/);
	// on reconstruit la chaîne
	var l_text = '';
	for (i = l_tiles.length; --i >= 0; ) {
		l_text = l_tiles[i] + l_text;
	}
	// on nettoie éventuellement
	var l_pos = l_text.indexOf('$$');
	if (l_pos > 0) {
		// on supprime les premiers caractères pour l'affichage
		l_text = l_text.substring(l_pos + 2);
	}
	return l_text;
}

/**
 * Internationalisation : si la variable a_var est définie, la renvoie, sinon, renvoie
 * la valeur par défaut
 * @param a_var		(variable cf window._XXX)	contenu internationalisé
 * @param a_default	(String)					teste par défaut (en français)
 */
function i18n(a_var, a_default) {
	if (a_var) {
		return a_var;
	} else {
		return a_default;
	}
} 
	
