

/**
 * Cette classe gère le contexte des pages de formulaire des moteurs (mev et meh)
 */
(function() {
	// Raccourci vers window
	var window = this,
			// Raccourci vers window.ev
			ev = window.ev,
			// Raccourci vers evData (données d'environement en provenance du Java)
			evData = window.evData || {},
			// Raccourci vers window.ev.log
			LOG = ev && ev.log,
			// Raccourci vers window.ev.dom
			DOM = ev && ev.dom,
			// Raccourci vers window.ev.tools
			TOOLS = ev && ev.tools,
			// Raccourci vers fonctions utiles
			element = DOM && DOM.element,
			document = window.document,
			// raccourci vers le requestManager
			requestManager = ev && ev.requestManager,
			Date = window.Date,
			// check de la version de MEV
			versionMEV = evData.versionMEV || window.versionMEV || 1;

	// Si les namespaces/classes nécessaires ne sont pas chargées : exception
	if (!ev) {throw new Error("Le namespace 'ev' doit exister");}
	if (!requestManager) {throw new Error("Le 'ev.requestManager' doit exister");}
	// On s'assure que le namespace ev.me existe
	if (!ev.me) { ev.me = {}; }
	//Si la classe ContextFormulaire est définie on sort
	if (ev.me.ContextFormulaire) {return;}

	// on s'assure que la version de MEV est un nombre
	versionMEV = parseInt(versionMEV, 10);

	/**
	 * Compte client courant.
	 */
	var compte,

			/*
			 * Tableau de listener de compte.<br>
			 * Ces listeners seront notifiés lorsque le
			 * compte sera disponible.
			 */
			compteListeners = [],

			/*
			 * Chemin du moteur courant.
			 */
			enginePath,

			/*
			 * Chemin du service de recherche du moteur courant.
			 */
			searchPath,

			/*
			 * Jeu de critères de la recherche actuelle.
			 * (Quelque soit le moteur)
			 */
			criteres,

			/*
			 * Tableau de listener de criteres.<br>
			 * Ces listeners seront notifiés lorsque les
			 * critères sont disponibles.
			 */
			crListeners = [],

			/*
			 * Expression régulière représentant la fin d'une URL
			 * (à partir de l'ancre s'il y en a une).
			 */
			REGEXP_HASH_PARAMS = /(#([^\/]*(\/([^\/]*))*)?)?$/,

			/*
			 * Expression régulière permettant de retrouver
			 * les criteres dans l'URL (s'ils y sont).
			 */
			REGEXP_URL_CRITERES = /#(([^\/]*\/)*)(cr:([^\/]*))((\/[^\/]*)*)$/,

			/*
			 * Expression régulière permettant de voir lorsque
			 * le serveur est un serveur local (de dev ou de
			 * pré-prod).
			 */
			REGEXP_LOCAL_DN = /(seoul|tokyo|test)\.[^\.]*\.l[^\.:]*(:[0-9]+)?\/.*$/,

			/*
			 * Chemin de la page de résultats seulement pour vol.
			 * FIXME A mettre dans mev (ou trouver un autre système
			 *   hierarchique)
			 */
			URL_RESULTS;

	var requestM;

	/**
	 * Envoi le navigateur vers une page d'erreur dont le code est donné.
	 * @param {!number} errCode code status de la page d'erreur demandée.
	 * @param {string=} msg un message d'information (optionnel).
	 */
	function sendError(errCode, msg) {
		if (msg) {
			LOG.fatal(msg + ' [' + errCode + ']');
		}
		//    window.location.href='/errors/'+errCode+'.html';
	}

	/**
	 * Méthode d'envoi de l'événement de réception
	 * de compte valable.<br>
	 * @param {Object} compte compte disponible.
	 */
	function fireCompteEvent(compte) {
		var l = compteListeners.length;
		// si aucun listener, on sort
		if (!l) {return;}
		while (l) {
			--l;
			try {
				compteListeners[l].onCompteEvent(compte);
			}
			catch (e) {
				LOG.error('me.ContextFormulaire.fireCompteEvent()> Erreur sur listener de compte : ' + e);
			}
		}
	}

	/**
	 * Methode executée une fois que le compte est en session.
	 * @param {Object} r tableau de resultat retourné par le serveur.
	 */
	function onCompteSet(r) {
		if (r.exception) {
			sendError(500, 'me.ContextFormulaire.__onCompteSet()_compte_undefined');
			return;
		}
		if (r.compte) {
			compte.updateFrom(r.compte);
		}
		fireCompteEvent(compte);
	}

	/**
	 * Méthode d'envoi de l'événement de réception
	 * de critères valables.<br>
	 * @param {Object} cr jeu de critères disponible.
	 */
	function fireCriteresEvent(cr) {
		var l = crListeners.length;
		// si aucun listener, on sort
		if (!l) {return;}
		while (l) {
			--l;
			try {
				crListeners[l].onCriteresEvent(cr);
			}
			catch (e) {
				LOG.error('me.ContextFormulaire.__fireCriteresEvent()> Erreur sur listener de critères : ' + e);
			}
		}
	}

	/**
	 * Méthode de stockage d'un jeu de critères reçu.<br>
	 * Une fois les critères stockés, on envoie un événement.
	 * @param {Object} cr jeu de critères valide.
	 */
	function onCriteresOk(cr) {
		criteres.updateFrom(cr);
		fireCriteresEvent(criteres);
	}

	var onCriteresReceived = {
		/**
		 * Méthode de traitement de critères.<br>
		 * Si les critères sont reçus, on envoie un événement.
		 * Si les critères sont mauvais ou non reçu, on va tenter de les poster
		 * à nouveau vers le serveur (à partir de l'URL).
		 * FIXME à factoriser + placer parties vols dans le package ev.mev
		 * @param {Object} r resultat de la requete RJS.
		 */
		onSuccess: function(r) {
			if (!r.exception) {
				onCriteresOk(r.criteres);
				return;
			}
			sendError(500, 'me.ContextFormulaire.__onCriteresReceived()> Wrong criteres in URL!');
		},

		/**
		 * Méthode appelée lorsque la requête est en erreur. cf ev.requestManager.invokeEra.
		 * @param {Object} e evenement ou erreur.
		 */
		onError: function(e) {
			// sendError(500, e); FIXME erreur 500 ??
		}
	};

	/**
	 * FIXME à mettre dans le Context vol
	 * Envoi les criteres au serveur.
	 *
	 * @param {Object} cr jeu de critères sous forme d'un objet JSON.
	 * @param {(Object|Function)} callback fonction à exécuter après la création de critères en session.
	 */
	function sendCriteres(cr, callback) {
		criteres = new ev.mev.Criteres();
		criteres.type = cr.type;
		criteres.paxAdultes = cr.paxAdultes;
		criteres.paxEnfants = cr.paxEnfants;
		criteres.paxBebes = cr.paxBebes;
		criteres.classe = cr.classe;
		criteres.dataDepartAller = cr.dataDepartAller;
		criteres.dataArriveeAller = cr.dataArriveeAller;
		if (!cr.dataDepartAller) {
			criteres.departAller = cr.departAller;
		}
		if (!cr.dataArriveeAller) {
			criteres.arriveeAller = cr.arriveeAller;
		}
		criteres.dateAller = cr.dateAller;
		switch (criteres.type) { // type autre que aller simple
			case 1: // aller retour, on recopie les lieux de l'aller
				criteres.dataDepartRetour = criteres.dataArriveeAller;
				criteres.dataArriveeRetour = criteres.dataDepartAller;
				criteres.departRetour = criteres.arriveeAller;
				criteres.arriveeRetour = criteres.departAller;
				criteres.dateRetour = cr.dateRetour;
				break;
			case 2: // parcours circulaire
				criteres.dataDepartRetour = cr.dataDepartRetour;
				criteres.dataArriveeRetour = cr.dataArriveeRetour;
				if (!cr.dataDepartRetour) {
					criteres.departRetour = cr.departRetour;
				}
				if (!cr.dataArriveeRetour) {
					criteres.arriveeRetour = cr.arriveeRetour;
				}
				criteres.dateRetour = cr.dateRetour;
				break;
			// case 0: // aller simple
			default: // ou autre (valeur erronée)
				criteres.dataDepartRetour = '';
				criteres.dataArriveeRetour = '';
				criteres.departRetour = '';
				criteres.arriveeRetour = '';
				criteres.dateRetour = '';
				break;
		}
		// Requete ERA de validation des lieux.
		// Set du critere si valide.
		// creation du critere.
		/*
		if(criteres.departAller && criteres.arriveeAller) {
			interrogationDirectValidation(criteres.departAller,criteres.arriveeAller,callback,criteres);
		}else {
			requestManager.invokeEra(searchPath+"/criteres/creer.rjs?"+criteres.inUrlParams(), callback);
		}
		 */
		requestManager.invokeEra(searchPath + '/criteres/creer.rjs?' + criteres.inUrlParams(), callback);
	}

	//  /**
	//   *  Méthode à utiliser pour activer l'interrogation par ville en mode accès direct sur page de résultat.
	//   *  - Envoi une requete de validation des deux lieux données en paramètres.
	//   *  - Si matching direct des lieux, mise à jour du critere avec ces données + creation du critere coté serveur ( Requête ERA )
	//   *   + ( TODO ) Mise à jour des ancres de filtres aeroports éventuels.
	//   *  - si pas de matching direct, envoi d'une erreur 500, criteres non valide.
	//   */
	//  function interrogationDirectValidation(departAller,arriveeAller,callback,criteres) {
	//    var mLocator = new ev.rjs.Locator(10000, 'DAVC');
	//    // Callback sur la requête de validation des lieux.
	//    function onReceive(r) {
	//      if (r.dataMEVArriveeAller && r.dataMEVDepartAller) {
	//        if (r.dataMEVDepartAller2) {
	//          criteres.dataDepartAller = r.dataMEVDepartAller2;
	//        }
	//        else {
	//          criteres.dataDepartAller = r.dataMEVDepartAller;
	//        }
	//        if (r.dataMEVArriveeAller2) {
	//          criteres.dataArriveeAller = r.dataMEVArriveeAller2;
	//        }
	//        else {
	//          criteres.dataArriveeAller = r.dataMEVArriveeAller;
	//        }
	//        // Mise à jour du critere et creation de celui ci..;
	//        requestManager.invokeEra(searchPath + '/criteres/creer.rjs?' + criteres.inUrlParams(), callback);
	//      }
	//      else {
	//        sendError(500, 'me.ContextFormulaire.__onCriteresReceived()> Wrong criteres in URL!');
	//      }
	//    }
	//    // Envoi de la requête de validation des lieux de départ et d'arrivée.
	//    var url = '/vols/geo/lieux/valider.rjs?departAller=' + departAller + '&arriveeAller=' + arriveeAller + '&id=DAVC';
	//    requestManager.invokeEra(mLocator, url, onReceive, true);
	//  }

	/**
	 * Méthode de récupération/vérification de champ
	 * de formulaire.
	 *
	 * @param {string} f id du champ demandé.
	 * @return {Element} le champ demandé.
	 * @throws Error si le champ n'existe pas
	 */
	function fld(f) {
		var fe = element(f);
		if (!fe) {throw 'Le champ \'' + f + '\' (du formulaire) doit exister.';}
		return fe;
	}

	/**
	 * Méthode conversion des informations disponibles
	 * dans le formulaire, en jeu de critères.
	 * FIXME pas à sa place ! à mettre dans mev
	 * @param {Object} frm formulaire à lire.
	 */
	function formToCriteres(frm) {
		// objet Critères à retourner
		var c = new ev.mev.Criteres();
		// FIXME traiter les parcours circulaires (2)
		if (fld('typeMEVAS').checked) {
			c.type = 0;
		}
		else {
			c.type = 1;
		}
		c.paxAdultes = fld('paxMEVAdultes').value;
		c.paxEnfants = fld('paxMEVEnfants').value;
		c.paxBebes = fld('paxMEVBebes').value;
		c.classe = 0; // classe voyageur indiférente
		if (fld('classeMEVEco').checked) {
			c.classe = 1;
		}
		else if (fld('classeMEVPre').checked) {
			c.classe = 2;
		}
		else if (fld('classeMEVAff').checked) {
			c.classe = 3;
		}
		// Aller
		c.dateAller = Date.convertSelectorToStringFr('jourMEVAller', 'moisMEVAller');
		/** Gestion des formulaires ancien format
				 *  Remplace les deux lignes suivantes, laissées en commentaires pour le moment au cas ou....
					c.dataDepartAller=fld("dataMEVDepartAller").value;
			c.dataArriveeAller=fld("dataMEVArriveeAller").value;
			 */
		var dataMEVDepartAllerNode = element('dataMEVDepartAller') || element('iataMEVDepartAller');
		if (!dataMEVDepartAllerNode) {
			throw new Error('Le champ \'' + dataMEVDepartAllerNode + '\' (du formulaire) doit exister.');
		}
		/**Recherche si données présentes dans les attributs aData et vdata du champs texte de saisie du départ.*/
		var node = element('lieuMEVDepartAller');
		if (node) {
			var adata = node.getAttribute('adata');
			var vdata = node.getAttribute('vdata');
			if (adata && vdata) {
				//LOG.warn('ContextFormulaire__adata && vdata');
				c.dataDepartAller = vdata;
				c.dataDepartAller2 = adata;
			}else {// FIXME on continu d'utiliser l'ancien format pour les datas, utilisation du champs caché....
				c.dataDepartAller = dataMEVDepartAllerNode.value;
			}
		}
		var dataMEVArriveeAllerNode = element('dataMEVArriveeAller') || element('iataMEVArriveeAller');
		if (!dataMEVArriveeAllerNode) {
			throw new Error('Le champ \'' + dataMEVArriveeAllerNode + '\' (du formulaire) doit exister.');
		}
		//Recherche si données présentes dans les attributs aData et vdata du champs texte de saisie du départ.
		var node2 = element('lieuMEVArriveeAller');
		if (node2) {
			var adata2 = node2.getAttribute('adata');
			var vdata2 = node2.getAttribute('vdata');
			if (adata2 && vdata2) {
				c.dataArriveeAller = vdata2;
				c.dataArriveeAller2 = adata2;
			}else {// FIXME on continu d'utiliser l'ancien format pour les datas, utilisation du champs caché....
				c.dataArriveeAller = dataMEVArriveeAllerNode.value;
			}
		}
		c.departAller = fld('lieuMEVDepartAller').value;
		c.arriveeAller = fld('lieuMEVArriveeAller').value;
		// Retour
		c.dateRetour = '';
		c.dataDepartRetour = '';
		c.dataArriveeRetour = '';
		c.departRetour = '';
		c.arriveeRetour = '';
		if (c.type) {
			c.dateRetour = Date.convertSelectorToStringFr('jourMEVRetour', 'moisMEVRetour');
			if (c.type === 2) {
				// les champs de formulaire ne sont nécessaires que lors des parcours circulaires
				c.dataDepartRetour = fld('dataMEVDepartRetour').value;
				c.dataArriveeRetour = fld('dataMEVArriveeRetour').value;
				c.departRetour = fld('lieuMEVDepartRetour').value;
				c.arriveeRetour = fld('lieuMEVArriveeRetour').value;
			}
		}

		return c;
	}
	/**
	 * Fonction permettant de formater une chaine
	 * de 2 caractères pour le nombre donné.
	 * @param {Integer} n nombre à formater.
	 */
	function fmt2dgt(n) {
		if (n < 10) {
			return '0' + n;
		}
		return n;
	}
	/**
	 * Convertit les valeurs des chaines de date
	 * (jour et mois/année) en date sous forme texte
	 * au format fr_FR (dd/m/yyyy)
	 * @param {String} day jour au format dd.
	 * @param {String} monthYear mois et année au format MM/yyyy.
	 */
	function convertToStringFr(day, monthYear) {
		try {
			var monthYearArray = monthYear.split('/'),
					year = parseInt(monthYearArray[1], 10),
					month = parseInt(monthYearArray[0], 10);
			return fmt2dgt(day) + '/' + fmt2dgt(month) + '/' + year;
		}
		catch (e) {
			LOG.error(e);
			return null;
		}
	}

	/**
	 *  Parcours la chaine passée en paramètre et remplace les valeurs suivantes par le caractère correspondant.
	 *    - %E9 - 'é'
	 *    - %E8 - 'è'
	 *    - %EF - 'ï'
	 *    - %FC - 'ü'
	 *
	 * @param {!string} url URL à traiter.
	 */
	function unscapeSpecialChar(url) {
		return TOOLS.decodeURI(url);
		//url = url.replace(/%E9/g, 'é').replace(/%E8/g, 'è').replace(/%E0/g, 'à').replace(/%E1/g, 'á').replace(/%EF/g, 'ï').replace(/%FC/g, 'ü').replace(/%ED/g, 'í').replace(/%FA/g, 'ú');
		//url = url.replace(/%/g, ''); // empeche les erreurs JS pour les caractères non gérés.
		//return url;
	}
	/**
	 * Méthode conversion des informations disponibles
	 * dans l'URL en jeu de critères.
	 * FIXME pas à sa place ! à mettre dans mev
	 * @param {!string} url URL à lire.
	 */
	function urlToCriteres(url) {
		// objet Critères à retourner

		var c = new ev.mev.Criteres(), params = TOOLS.getUrlParameters(decodeURIComponent(unscapeSpecialChar(url)));

		if (params.type === 0 || params.type === '0') {
			c.type = 0;
		}
		else {
			c.type = params.type && parseInt(params.type, 10) || 1;
		}
		c.paxAdultes = params.paxAdultes;
		c.paxEnfants = params.paxEnfants;
		c.paxBebes = params.paxBebes;
		c.classe = params.classe;
		c.dataDepartAller = (params.departAllerData && params.departAllerData.replace(/\+/g, ' ')) || (params.departAllerIata && params.departAllerIata.replace(/\+/g, ' '));
		c.dataArriveeAller = (params.arriveeAllerData && params.arriveeAllerData.replace(/\+/g, ' ')) || (params.arriveeAllerIata && params.arriveeAllerIata.replace(/\+/g, ' '));

		if (params.departAllerData2) {
			c.dataDepartAller2 = params.departAllerData2;
		}
		if (params.arriveeAllerData2) {
			c.dataArriveeAller2 = params.arriveeAllerData2;
		}
		/** Ces deux champs doivent contenir le texte du data2 si défini, c'est à dire intérgogation par ville d'un aeroport.*/
		c.departAller = ev.mev.Criteres.getTextFromData(c.dataDepartAller) || params.departAller;
		c.arriveeAller = ev.mev.Criteres.getTextFromData(c.dataArriveeAller) || params.arriveeAller;
		c.dateAller = params.dateAller || params.jourAller && params.moisAller && convertToStringFr(params.jourAller, params.moisAller);
		c.dataDepartRetour = params.departRetourData && params.departRetourData.replace(/\+/g, ' ');
		c.dataArriveeRetour = params.arriveeRetourData && params.arriveeRetourData.replace(/\+/g, ' ');
		c.departRetour = ev.mev.Criteres.getTextFromData(c.dataDepartRetour) || params.departRetour;
		c.arriveeRetour = ev.mev.Criteres.getTextFromData(c.dataArriveeRetour) || params.arriveeRetour;
		c.dateRetour = params.dateRetour || params.jourRetour && params.moisRetour && convertToStringFr(params.jourRetour, params.moisRetour);
		return c;
	}

	/**
	 * Permet d'extraire le nom de lieu depuis une chaine de code correctement structurée...
	 * Format du code ville de Londres : v:4173|c:LON|t:Londres,
	 * @param {Object} dataMEV code complet correctement formé correspondant au lieu.
	 */
	function extractNomLieuFromReponse(dataMEV) {
		var refString = '|t:';
		var indexNomLieu = dataMEV.indexOf(refString);
		var nomLieu = dataMEV.substring(indexNomLieu + refString.length, dataMEV.length);
		return nomLieu;
	}
	/**
	 * FIXME à mettre dans le Context vol
	 * Convertir les valeurs du formulaire en criteres de recherche
	 * Puis lancer un rjs setCritere (qui lance la recherche).<br>
	 * <b>NOTA:</b> C'est la fonction de soumission de formulaire pour MEV3.
	 *
	 * @param {Element} frm Formulaire à soumettre.
	 */
	function doSubmitFormMEV3(frm) {
		LOG.debug('me.ContextFormulaire#_doSubmitFormMEV3(' + frm.id + '): start...');
		var c = formToCriteres(frm), unknowns = [];
		if (!c.dataDepartAller) {
			unknowns.push('departAller=' + c.departAller);
		}
		if (!c.dataArriveeAller) {
			unknowns.push('arriveeAller=' + c.arriveeAller);
		}
		if (c.type === 2) { // les retours pour les parcours circulaires seulement
			if (!c.dataDepartRetour) {
				unknowns.push('departRetour=' + c.departRetour);
			}
			if (!c.dataArriveeRetour) {
				unknowns.push('arriveeRetour=' + c.arriveeRetour);
			}
		}

		if (unknowns.length) {
			unknowns = unknowns.join('&');
			if (requestM) {
				requestM.check();
			}
		}
		else {
			//      LOG.info('Vrai submit du formulaire...');
			//      requestManager.addFieldSIDtoForm(frm.ev_SID);
			//      frm.submit();
			// Envoi vers page de résultats avec encodage de l'URL (pour être sûr d'avoir la session ERA sur la page suivante)
			//FIXME [ygally] à valider

			/*FIXME[poblin]: Ce hack permet d'envoyer sur une page de résultat MEV2 en utilisant un formulaire MEV3
			 * C'est temporaire pour des besoins de l'intégration (cf avec Bruno). C'est déstiné à être supprimé.
			 */
			switch (versionMEV) {
				case 3:
					var url = URL_RESULTS + '?' + c.inUrlParams();
					if (evData.esvVolRecherche) {
						url = evData.esvVolRecherche + '?' + c.inUrlParams();
					}

					//TEMPORAIRE: patch pour valider la transmission du clientId sur la page info (cf http://bug.lcom/issues/1012)
					var clientIdUrl = TOOLS.getParameter('clientId');
					if (clientIdUrl) {
						url += '&clientId=' + clientIdUrl;
					}
					LOG.debug('me.ContextFormulaire#_doSubmitFormMEV3(' + frm.id + ' ; version ' + versionMEV + '): form submission... ' + requestManager.encodeAnchor(url));
					//fin TEMPORAIRE
					window.location.href = requestManager.encodeAnchor(url);
					//window.location.href=requestManager.encodeAnchor(URL_RESULTS+'?'+c.inUrlParams());
					break;
				case 2:
					LOG.debug('me.ContextFormulaire#_doSubmitFormMEV3(' + frm.id + ' ; version ' + versionMEV + '): form submission... ' + (requestManager.encodeAnchor(URL_RESULTS + '?clientId=' + (TOOLS.getParameter('clientId') || '') + '&' + c.inUrlParams())));
					window.location.href = requestManager.encodeAnchor(URL_RESULTS + '?clientId=' + (TOOLS.getParameter('clientId') || '') + '&' + c.inUrlParams());
					break;
				default:
					LOG.error("me.ContextFormulaire#doSubmitFormMEV3: Cette version du moteur de vol n'existe pas: " + versionMEV);
					break;
			}
		}
		LOG.debug('me.ContextFormulaire#_doSubmitFormMEV3(' + frm.id + '): end');
	}

	/**
	 * FIXME à mettre dans le Context vol
	 * Méthode d'enregistrement de critères.
	 * Elle utilise l'URL pour récupérer les paramètres.
	 * @param {(Object|Function)} c fonction de (c)allback à exécuter après la création de critères en session.
	 */
	function setCriteres(c) {
		sendCriteres(urlToCriteres(window.location.search), c);
	}

	/**
	 * FIXME à mettre dans le Context vol
	 *
	 * Prépare la soumission du formulaire pour MEV3.
	 */
	function initFormMEV3() {
		var e = element('formMEV');
		if (e) {
			e.action = URL_RESULTS;
		}
		// déclaration du système de proposition. Cf documentation.
		var propManager = new ev.me.PropositionManager.ValidationRequest({'str': 'lieuMEVDepartAller', 'code': 'dataMEVDepartAller'});
		var propManager2 = new ev.me.PropositionManager.ValidationRequest({'str': 'lieuMEVArriveeAller', 'code': 'dataMEVArriveeAller'});
		// requestM permet de controler les champs auquel on a associé le système de proposition. cf Documentation.
		requestM = new ev.me.PropositionManager.RequestManager('formMEV', 'submitMEV');
		requestM.addField(propManager);
		requestM.addField(propManager2);
		requestM.setOnAfter(doSubmitFormMEV3); // set de la méthode submit du formulaire.
		/**
		 * Surcharge de la méthode de soumission du
		 * formulaire MEV.
		 *
		 * @param {Element} frm Formulaire à soumettre.
		 */
		window.doSubmitFormMEV = function(frm) {
			LOG.debug('me.ContextFormulaire#doSubmitFormMEV(' + frm.id + '): start...');
			doSubmitFormMEV3(frm);
			LOG.debug('me.ContextFormulaire#doSubmitFormMEV(' + frm.id + '): end');
			// on retourne 'false' dans tous les cas car il ne faut pas que l'événement remonte
			return false;
		};
		LOG.debug('me.ContextFormulaire#initFormMEV3(): Surcharge OK - formMEV#doSubmitFormMEV(Element) -> me.ContextFormulaire#doSubmitFormMEV(Element)');
	}
	/**
	 * Package contenant les fonctions d'accès aux service ERA utilisé dans ce fichier.
	 */
	ev.era = {};
	/**
	 * Lance la requête ERA de création de compte et appel la méthode listener donnée en paramètre à reception de la réponse. Le listener prend en paramètre
	 * le résultat de la réponse.
	 * @param {Object} _param.site id du site.
	 * @param {Object} _param.code code client.
	 * @param {Object} _param.force paramètre force, à déterminer.
	 * @param {Object} _param.listener méthode à appeler à la réception de la réponse et en cas de timeout répété.
	 * @param {Object} _param.parentController instance de l'objet qui appel cette méthode.
	 */
	ev.era.setCompte = function(_param) {
		var nbRequest = 0,
				regexp = /^\d{0,6}$/,
				urlInfo = evData.esvVolInfo || 'infos.jsp';
		if (!_param.listener) {
			LOG.error('era.setCompte_no_receive_method_specified');
			return;
		}
		// Parse de la réponse et appel du listener avec la liste de recherche en paramètre.
		function onSuccess(r) {
			_param.listener(r);
		}
		// Méthode appelée lorsque la requête est en erreur. Cf. ev.requestManager.invokeEra.
		function onError(r) {
			_param.listener({exception: 'MAX_RETRY_REACHED'});
		}
		// Envoi
		if (!(_param.site && _param.site.toString().match(regexp))) {
			requestManager.invokeEra(enginePath + '/compte/choisir.rjs?urlInfos=' + urlInfo + '&force=' + _param.force, {onSuccess: onSuccess, onError: onError});
		}else if (_param.code && _param.code.toString().match(regexp)) {
			requestManager.invokeEra(enginePath + '/compte/choisir.rjs?urlInfos=' + urlInfo + '&site=' + _param.site + '&codeClient=' + _param.code + '&force=' + _param.force, {onSuccess: onSuccess, onError: onError});
		}else {
			requestManager.invokeEra(enginePath + '/compte/choisir.rjs?urlInfos=' + urlInfo + '&site=' + _param.site + '&force=' + _param.force, {onSuccess: onSuccess, onError: onError});
		}
	};

	/**
	 * Classe permettant de stoker les
	 * informations générales du moteur
	 * MEV3.
	 */
	ev.me.ContextFormulaire = {
		/**
		 * Crée un compte en session quelque soit les arguments.
		 * @param {Object} _site identifiant numérique du site (compte client courant).
		 * @param {Object} _code code client du compte client.
		 */
		setCompte: function(_site, _code, _force) {
			ev.era.setCompte({site: _site, code: _code, force: _force, listener: onCompteSet});
		},
		/**
		 * Récupération du jeu de critère en session.
		 * Si on les a déjà récupérés, on garde le jeu stocké.
		 */
		retreiveCriteres: function() {
			if (!criteres || !criteres.isSet()) {
				setCriteres(onCriteresReceived);
			}
			else {
				fireCriteresEvent(criteres);
			}
		},
		/**
		 * Permet de récupérer le compte client courant de la page.
		 */
		getCompte: function() {
			return compte;
		},

		/**
		 * Permet de récupérer le jeu de criteres courant de la page.
		 */
		getCriteres: function() {
			return criteres;
		},

		/**
		 * Permet d'ajouter un listener de critères au
		 * contexte.<br>
		 * Ce listener doit contenir une méthode
		 * #onCriteresEvent(Criteres).<br>
		 * Le type de jeu de critères n'est pas contrôlé.
		 * Il pourra être de type vol, hotel, voiture,...
		 * sans distinction, et sera envoyé au listener.
		 * @param {Object} l un listener de critères.
		 * @throws If l is null or undefined.
		 * @throws If l does not contain a #onCriteresEvent method.
		 */
		addCriteresListener: function(l) {
			if (!l) {throw new Error('me.ContextFormulaire#addCriteresListener()> Un listener ne doit pas être nul.');}
			if (typeof(l.onCriteresEvent) !== 'function') {throw new Error('me.ContextFormulaire#addCriteresListener()> Un listener doit posséder une méthode #onCriteresEvent().');}

			// Si l'objet criteres est encore non défini (ou pas initilialisé)
			// on ajoute le listener à la liste (il sera notifié dés que les critères sont bons)
			if (!criteres || !criteres.isSet()) {
				crListeners.push(l);
			}
			else {
				l.onCriteresEvent(criteres);
			}
		},

		/**
		 * Permet d'ajouter un listener de commpte au
		 * contexte.<br>
		 * Ce listener doit contenir une méthode
		 * #onCompteEvent(Compte).<br>
		 * @param {Object} l un listener de compte.
		 * @throws If l is null or undefined.
		 * @throws If l does not contain a #onCompteEvent method.
		 */
		addCompteListener: function(l) {
			if (!l) {throw new Error('me.ContextFormulaire#addCompteListener()> Un listener ne doit pas être nul.');}
			if (typeof(l.onCompteEvent) !== 'function') {throw new Error('me.ContextFormulaire#addCompteListener()> Un listener doit posséder une méthode #onCompteEvent().');}

			// Si l'objet compte est encore non défini (ou pas initilialisé)
			// on ajoute le listener à la liste (il sera notifié dés que le compte sera bon)
			if (!compte || !compte.isSet()) {
				compteListeners.push(l);
			}
			else {
				l.onCompteEvent(compte);
			}
		},
		/**
		 * Permet de choisir l'url de la page de résultats.
		 * @param {String} path chemin vers la page à utiliser.
		 */
		setUrlResults: function(path) {
			URL_RESULTS = path;
		},
		/**
		 * Accesseur publique sur la propriété URL_RESULTS.
		 * @return {String} URL_RESULTS chemin de la page de résultats à utiliser.
		 */
		getUrlResults: function() {
			return URL_RESULTS;
		},

		/**
		 * Initialisation du formulaire en fonction des paramètres de recherche.
		 * L'initialisation du formulaire utilise les informations issues du crit
		 * @param {!string} url URL de la page.
		 * @param {!Object} critere jeu de critères.
		 */
		initMEV3FormDataFromCritere: function(url,critere) {
			/**On génère un critere à partir de l'URL pour véri*/
			var critereURL = urlToCriteres(url);

			if (!critere) {
				throw 'Initialisation du formulaire impossible';
			}
			/** Initialisation du type */
			switch (critere.type) {
				case 2: fld('typeMEVPC').checked = true; break;
				case 1: fld('typeMEVAR').checked = true; break;
				//case 0:
				default: fld('typeMEVAS').checked = true; break;
			}

			/** Initialisation du nmbre de passager */
			fld('paxMEVAdultes').value = critere.paxAdultes;
			fld('paxMEVEnfants').value = critere.paxEnfants;
			fld('paxMEVBebes').value = critere.paxBebes;
			/** Initialisation de la classe de voyage*/
			if (critere.classe == 1) {
				fld('classeMEVEco').checked = true;
			}
			else if (critere.classe == 2) {
				fld('classeMEVPre').checked = true;
			}
			else if (critere.classe == 3) {
				fld('classeMEVAff').checked = true;
			}
			/** Initialisation des champs DATA destination et depart. */
			/** fld("iataMEVDepartAller") || fld("dataMEVDepartAller") => Pour gestion des anciens formulaire...*/
			var dataMEVDepartAller = element('iataMEVDepartAller') || element('dataMEVDepartAller');
			dataMEVDepartAller.value = critere.dataDepartAller || critere.iataDepartAller;
			var dataMEVArriveeAller = element('iataMEVArriveeAller') || element('dataMEVArriveeAller');
			dataMEVArriveeAller.value = critere.dataArriveeAller || critere.iataArriveeAller;

			/** La valeure de data2 dans les deux champs qui suivent si data définis !!!!!!!!!**/
			/*
			fld("lieuMEVDepartAller").value =critere.getNomVilleDepartAller();
			fld("lieuMEVArriveeAller").value = critere.getNomVilleArriveeAller();
			 */

			if (critereURL.dataDepartAller2 && critereURL.dataDepartAller2.length > 0) {
				fld('lieuMEVDepartAller').value = critereURL.getNomLieuDepartAller();
			}else {
				fld('lieuMEVDepartAller').value = critere.getNomLieuDepartAller();
			}

			if (critereURL.dataArriveeAller2 && critereURL.dataArriveeAller2.length > 0) {
				fld('lieuMEVArriveeAller').value = critereURL.getNomLieuArriveeAller();
			}else {
				fld('lieuMEVArriveeAller').value = critere.getNomLieuArriveeAller();
			}




			/** Set les valeures de vdata et adata ( optionnel pour ce dernier ).**/
			fld('lieuMEVDepartAller').setAttribute('vdata', critere.dataDepartAller || critere.iataDepartAller);
			if (critereURL.dataDepartAller2) {
				fld('lieuMEVDepartAller').setAttribute('adata', critereURL.dataDepartAller2);
			}
			fld('lieuMEVArriveeAller').setAttribute('vdata', critere.dataArriveeAller || critere.iataArriveeAller);
			if (critereURL.dataArriveeAller2) {
				fld('lieuMEVArriveeAller').setAttribute('adata', critereURL.dataArriveeAller2);
			}



			/** Initialisation des dates */
			var dateAller = Date.convertStringFrToDate(critere.dateAller);
			Date.convertDateToSelector(dateAller, 'jourMEVAller', 'moisMEVAller');
			if (critere.type == 1) {
				var dateRetour = Date.convertStringFrToDate(critere.dateRetour);
				Date.convertDateToSelector(dateRetour, 'jourMEVRetour', 'moisMEVRetour');
			}
			/** Initialisation des champs data dans le cas des parcours circulaire*/
			//OUCH ça demande d'utiliser du MEV2 dans MEV3
			window.initMEVDates(critere.dateAller, critere.dateRetour);
			if (critere.type === 2) {
				// les champs de formulaire ne sont nécessaires que lors des parcours circulaires
				fld('dataMEVDepartRetour').value = critere.dataDepartRetour;
				fld('dataMEVArriveeRetour').value = critere.dataArriveeRetour;
				fld('lieuMEVDepartRetour').value = critere.departRetour;
				fld('lieuMEVArriveeRetour').value = critere.arriveeRetour;
			}
			/** Mis à jour des styles calendrier, en particulier pour le cas des aller simple..*/
			window.setMEVType();
		},

		/**
		 * Initialise le contexte de la page formulaire
		 * avec les paramètres donnés.
		 * @param {Object} _tckSiteId code site de la page.
		 * @param {Object} _refVols répertoire /mev/ (mappé dans la bonne langue si besoin).
		 * @param {Object} _refHotels répertoire /meh/ (mappé dans la bonne langue si besoin).
		 */
		load: function(_tckSiteId, _refVols, _refHotels) {
			if (!_tckSiteId) {
				throw new Error('me.ContextFormulaire#initialize()> Le paramètre tckSiteId est nécessaire!');
			}
			//FIXME gérer vols et hotels ??? surcharge ?!
			if (_refVols) {
				// seulement pour http://www.easyvols.org/avionfr/mev2/
				if (/^http:\/\/www\.easyvols\.org\/avionfr\/mev2\/$/.test(_refVols) && REGEXP_LOCAL_DN.test(window.location.href)) {
					this.setUrlResults(window.location.href.replace(REGEXP_LOCAL_DN, RegExp.$1 + '.mev.lorg' + RegExp.$2 + '/avionfr/mev2/results.jsp'));
				}
				// seulement pour http://www.easyvols.fr/mev2/
				else if (/^http:\/\/www\.easyvols\.fr\/mev3\/$/.test(_refVols) && REGEXP_LOCAL_DN.test(window.location.href)) {
					this.setUrlResults(window.location.href.replace(REGEXP_LOCAL_DN, RegExp.$1 + '.mev.lfr' + RegExp.$2 + '/mev3/results.jsp'));
				}
				// pour tous les autres
				else {
					this.setUrlResults(_refVols + 'results.jsp');
				}
			}
			else { // si rien n'est précisé voici le chemin par défaut
				this.setUrlResults(evData.esvVolRecherche || '/mev/results.jsp');
			}

			// Initialisation des chemins utiles pour ERA (vols)
			enginePath = '/vols';
			searchPath = enginePath + '/recherche/MEV';

			var clientId,
					esvCodeClientName = 'esvCodeClient',
					regexCookieCodeClient = new RegExp('(?:; )?' + esvCodeClientName + '=([^;]*);?');

			clientId = (window.identification && window.identification.clientId) ||
					TOOLS.getParameter('clientId') ||
					window.CODE_CLIENT ||
					(regexCookieCodeClient.test(document.cookie) && RegExp.$1);

			// on initialise le compte client
			compte = new ev.me.Compte();
			if (clientId) {
				this.setCompte(_tckSiteId, clientId, true);
			}else {
				this.setCompte(_tckSiteId, 1, true);
			}

			// Initialisation MEH
			if( ev && ev.meh) {
				ev.meh.Context().setBaseXML('/rjs/meh'); // FIXME [ygally] surement obsolete
				if (_refHotels) {
					if (/^http:\/\/www\.easyvols\.org\/avionfr\/mev2\/$/.test(_refVols) && REGEXP_LOCAL_DN.test(window.location.href)) {
						ev.meh.Context().setPageResults(window.location.href.replace(REGEXP_LOCAL_DN, RegExp.$1 + '.mev.lorg' + RegExp.$2 + '/avionfr/meh2/results.jsp'));
					}
					else {
						ev.meh.Context().setPageResults(_refHotels + 'results.jsp');
						//          ev.meh.Context().setPageResults(_refHotels+"resultsMeh3.jsp");
					}
				}
				else if (evData.esvHotelRecherche) {
					if (clientId) {
						ev.meh.Context().setCompteBySiteAndCode(_tckSiteId, clientId);
						ev.meh.Context().setPageResults(evData.esvHotelRecherche + '?clientId=' + clientId);
					}
					else {
						ev.meh.Context().setCompteBySiteAndCode(_tckSiteId, 1);
						ev.meh.Context().setPageResults(evData.esvHotelRecherche);
					}
				}	
			}
			
			// On initialise le formulaire (partie commune aux 2 versions du moteur)
			window.initFormMEV();

			// On initialise le formulaire MEV3
			initFormMEV3();

			// Initialisation du jeu de critères courant
			criteres = new ev.mev.Criteres();
		}
	};

	LOG.debug('ev.me.ContextFormulaire ok');
}());

