var XML_VER = "$Revision: 1.105 $";

//---------------- getTechFrm -----------------
function getTechFrm() { return top.document.documentElement.findChildById("tech","div"); }

//---------------- loadXML ------------------
/**
* @return  le Node instancié a partir de la String XML
*/
function loadXML(p_sXml) {
  var oDI = new DOMImplementation();
  oDI.namespaceAware = false; // sinon bloque sur xml:lang
  var oDom = oDI.loadXML(p_sXml);
  var oDomTree = oDom.getDocumentElement();
  return oDomTree;
}

 //---------------- getDocElement ------------------
 /**
  * @return le Node <Document> contenu dans la String XML
  */
function getDocElement(p_sXml) {
  try {
    var oDI = new DOMImplementation();
    oDI.namespaceAware = false; // sinon bloque sur xml:lang
    var oDom = oDI.loadXML(p_sXml);
    var oDomTree = oDom.getDocumentElement().getElementsByTagName("Document").item(0);
    return oDomTree;

  } catch (e) {
    if (bAdmin) {
      if (copy_clip) copy_clip(p_sXml);
      // alert("getDocElement : Failed to create DOM node from String\n<copied to clipboard>");
     }
    throw e;
  }
}

//---------------- builEntityLibFromNode ------------------
/**
 * @param p_oNode représente le noeud <e> de l'entité
 * @return le libellé
 */
function builEntityLibFromNode(p_oNode) {
   var sLib = "";
   var msSns = ["nomfamille","prenom","nom_societe","enseigne","prescr_ville","date_debut","date_fin"
     ,"partner_nom", "num_ordre_mission", "mission_date_debut","mission_date_fin", "mission_com" ];
   var msLibs = new Array(msSns.length);
   var oValSet = p_oNode.selectNodeSet("avl/av");
   for (var ii=0, iiL=oValSet.getLength(); ii<iiL; ii++) {
     var oTmpNode = oValSet.item(ii);
     var sAttrSn = oTmpNode.getAttribute("asn");
     for (var j=0;j<msSns.length;j++) {
       if (sAttrSn==msSns[j]) {
        var sLibAttr = "";
        if (sAttrSn == "date_debut") sLibAttr = "contrat du ";
        if (sAttrSn == "date_fin") sLibAttr = "au ";
        if (sAttrSn == "mission_date_debut") sLibAttr = "mission du ";
        if (sAttrSn == "mission_date_fin") sLibAttr = "au ";
        sLibAttr += oTmpNode.getFirstChild().getFirstChild().getNodeValue();
        msLibs[j] = sLibAttr;
       }
     }
   }
   for (var j=0;j<msLibs.length;j++) {
     if (!msLibs[j]) continue;
     if (sLib!="") sLib += " ";
     sLib += msLibs[j];
   }
   if (sLib=="" && p_oNode.getElementsByTagName("name").length>0) {
     sLib += p_oNode.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
     // ajout du code postal si ville
     if (p_oNode.getAttribute("cp")) sLib += " ["+p_oNode.getAttribute("cp")+"]";
   }
   return sLib;
 }

 //---------------- retrieveHttpParamValue ------------------
 /**
  * @return la valeur du paramètre http
  */
 function retrieveHttpParamValue(oDomTree, p_sParam) {   
   // recup Id de l'input
   var oParList = oDomTree.getElementsByTagName("params").item(0).getElementsByTagName("httpParams").item(0)
     .getElementsByTagName("param");
   var oInput;
   for (j=0; j<oParList.getLength(); j++) {
     var oDOMNode = oParList.item(j);
     if (oDOMNode.getAttribute("name")==p_sParam) return oDOMNode.getFirstChild().getNodeValue();
   }
   return null;
 }

  //---------------- retrieveElementFromHttpParam ------------------
  /**
   * @return l'élément HTML dont l'id est indiqué dans les params http
   */
  function retrieveElementFromHttpParam(oDomTree) {   
    var s = retrieveHttpParamValue(oDomTree,"inputId");
    return document.getElementById(s);
  }

  //---------------- retrieveDataItemIdFromHttpParam ------------------
  function retrieveDataItemIdFromHttpParam(oDomTree) {   
    return retrieveHttpParamValue(oDomTree,"DIId");
  }

  //---------------- buildArrayFromXml ------------------
  /**
   * @return le tableau construit à partir de la String XML fournie
   */
  function buildArrayFromXml(p_xDomTree) {
    // var il = tslog(il);

    // recup du DI
    var xDI = p_xDomTree;
    while (xDI.tagName!="di") xDI = xDI.firstChild;

    var sTypeEnum = "ev";
    if (xDI.getAttribute("ext_table_name")) sTypeEnum = xDI.getAttribute("ext_table_name");
    
    var oList = xDI.getElementsByTagName("evl").item(0).getElementsByTagName(sTypeEnum);
    // il = tslog(il);

    var msList = new Array();
    msList.iDIId = xDI.getAttribute("id");
    msList.sDISn = xDI.getAttribute("sn");
    
    var bByIdx = false;
    for (var j=0, l=oList.getLength(); j<l; j++) {
      var oDOMNode = oList.item(j);
      var sVal     = oDOMNode.getAttribute("id");
      if (sVal==0 || sVal=="") continue; // ignore la valeur par defaut du type 'selectionnez xxx'
      //NB : pas de  vérification que la valeur n'est pas déjà dans la liste à ce niveau car
      // même si valeur présente plusieurs fois, c'est avec des parentId différent =>il faut tous les garder à ce stade

      var sLib     = 'lib non défini';
      if (oDOMNode.getElementsByTagName("l").item(0)) {
        sLib = oDOMNode.getElementsByTagName("l").item(0).getFirstChild().getNodeValue();
      } else if (oDOMNode.getElementsByTagName("name").item(0)) {
        sLib = oDOMNode.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
      }
      var oVal = new ListItem(sVal, sLib);
      msList[j] = oVal;
      oVal.setAllowed(oDOMNode.getAttribute("allowed")=="1");
      // pour permettre une recherche sur les diva disabled ex : presta suspendues
      oVal.setDisabled(oDOMNode.getAttribute("disabled")=="true");
      oVal.setOrderIdx(oDOMNode.getAttribute("index"));
      oVal.setParentId(oDOMNode.getAttribute("parentid"));
      oVal.setDivaId(oDOMNode.getAttribute("divaid"));
      if(!bByIdx && oVal.getOrderIdx()) bByIdx = true;
    }
    if (bByIdx) msList.sort(sortByIndex);
    else msList.sort(sortByValue);

    // il = tslog(il);
    return msList;
  }

  //---------------- sortByValue ------------------
  function sortByValue(x1,x2){
    return (x1.getLabel().replace(/[éèê]/,"e").toUpperCase() > x2.getLabel().replace(/[éèê]/,"e").toUpperCase())? 1 :- 1;
  }
  //---------------- sortByIndex ------------------
  function sortByIndex(x1,x2){
    return ((x1.getOrderIdx()*1) > (x2.getOrderIdx()*1))? 1 :- 1;
  }

// =========================================================================
// @class
// ListItem - définition d'un item d'une liste énumérée (pour remplissage combo par ex)
// =========================================================================
ListItem = Class.create();
ListItem.prototype = {
  m_iId: null,
  m_sLabel: null,
  m_bAllowed: null,
  m_bDisabled: null,
  m_iOrderIdx: null,
  m_iParentId: null,
  m_iDivaId: null,
  initialize: function(p_iId, p_sLib) {
    this.m_iId = p_iId;
    this.m_sLabel = p_sLib;
  },
  getId : function() { return this.m_iId; },
  getLabel : function() { return this.m_sLabel; },
  setAllowed : function(p) { this.m_bAllowed = p; },
  isAllowed : function() { return this.m_bAllowed; },
  setOrderIdx : function(p) { this.m_iOrderIdx = p; },
  getOrderIdx : function() { return this.m_iOrderIdx; },
  setParentId : function(p) { this.m_iParentId = p; },
  getParentId : function() { return this.m_iParentId; },
  setDivaId : function(p) { this.m_iDivaId = p; },
  getDivaId : function() { return this.m_iDivaId; },
  isDisabled : function() { return this.m_bDisabled; },
  setDisabled : function(p) { this.m_bDisabled = p; }
}

  //---------------- fillCombo ------------------
  /**
   * Rempli la combo à partir du tableau de données
   *
   * @param p tableau de définition de la liste des options (cas où la liste est disp
   *   dans la frame tech) ou XML pour le constuire (retour de l'appel au serveur=
   * @param p_oInput l'objet HTMLSelectElement ou son Id
   * @param p_sContainerId si présent, indique l'id du container de l'input (utile si le container est une div afin de
   *      ne pas sélectionner l'input dans le mauvais conteneur, comme par ex dans la frm_but alors qu'on veut celui de frm_frame1)
   */
  function fillCombo(p, p_oInput, p_sCurrentValue, p_sContainerId, p_sFilterValue, p_sType) {
    var msList;
    var oInput = p_oInput;
    if (typeof(oInput)!="object") {
      if (p_sContainerId) {
        var oCont = $O2(p_sContainerId);
        if (!oCont) return; // page plus affichée !
      }
      oInput  = p_sContainerId 
          ? oCont.findChildById(p_oInput)
          : document.getElementById(p_oInput); // dans ce cas p_oInput est une String égale à l'id 
    }
    if (!oInput) throw "fillCombo: input '"+p_oInput+"', type '"+(typeof p_oInput)+"', not found in container '"+p_sContainerId+"'";
    if (typeof(p)=="object") {
      msList = p;

    } else {
      var oDoc = getDocElement(p);
      var iDIId = retrieveDataItemIdFromHttpParam(oDoc);
      msList = buildArrayFromXml(oDoc);
      msDIList[iDIId] = msList;
    }
    var bFilter = oInput.getAttribute && "true"==oInput.getAttribute("filter");
    var sCurValue = p_sCurrentValue ? ODYForm.sVALUE_SEP + p_sCurrentValue + ODYForm.sVALUE_SEP : null;
    var bIsValueSelected = false; // une valeur a t elle été activée ?

    // sélection des valeurs parmi les valeurs "en dur" (ex : "NULL")
    for (var j=0; j<oInput.options.length; j++) {
      var oOpt = oInput.options[j];
      if (!sCurValue || !sCurValue.match(ODYForm.sVALUE_SEP + oOpt.value + ODYForm.sVALUE_SEP)) continue;
      // si c'est la première sélection (ds le cas d'un mutiple), déselectionne le premier "chargement en cours"
      if (!bIsValueSelected) oInput.options[0].selected = false;
      oOpt.selected = true;
      bIsValueSelected = true;
    }

    /* filtrage de la liste :
     *   - en fonction de l'attribut "allowed" si bFilter == true
     *   - en fonction des dépendance au parent si p_sFilterValue est défini
     *   - élimination des doublons
     */
    var iInsertIdx = 0 ;
    var bHasdoublon = false;
    var msFilteredList = new Array();
    for (var j=0, l=msList.length; j<l; j++) {
      var oLI = msList[j];
      // attribut "allowed": 1 si valeur autorisee, 0 sinon
      if (bFilter && !oLI.isAllowed()) continue;
      // si filterValue indiquée (=valeur du champ "parent", ex "type presta" pour "niv presta"), filtre des valeurs
      // NB : p_sFilterValue et oLI.getParentId() font référence à enum_value.id, pas diva.id==asso_id
      if (p_sFilterValue!=null && String(oLI.getParentId())!=String(p_sFilterValue)) continue;
      // verif que la valeur n'est pas déjà présente
      var bFound = false;
      for (var i=0; i<msFilteredList.length ; i++) {
        var oLIT = msFilteredList[i];
        if (parseInt(oLIT.getId())==parseInt(oLI.getId())) { bFound = true; bHasdoublon = true; break; }
      }
      if (bFound) continue;
      msFilteredList[iInsertIdx] = oLI;
      iInsertIdx++;
    }

    // s'il y avait des doublons, on tri selon la valeur dans tous les cas
    if (bHasdoublon) msFilteredList.sort(sortByValue);

    var iMaxOptWidth = 0;
    var iLimitWidth = oInput.getContainer().clientWidth / 3;

    // création de la liste des options de la combo
    for (var j=0, l=msFilteredList.length; j<l; j++) {
      var oLI = msFilteredList[j];
      var oOpt      = new Option(oLI.getLabel());
      oOpt.value    = oLI.getId();
      oOpt["oLI"] = oLI;
      if (sCurValue && sCurValue.match(ODYForm.sVALUE_SEP + oOpt.value + ODYForm.sVALUE_SEP)) {
        // si c'est la première valeur à être sélectionnée, déselectionne le "chargement en cours" 
        if (!bIsValueSelected) oInput.options[0].selected = false;
        oOpt.selected = true;
        bIsValueSelected = true;
      }
      
      // récupération du SN du display
      var oLibInput = oInput.findBrotherById("lib"+oInput.id);
      if (!oLibInput) oLibInput = oInput;
      var sDispSN = oLibInput.getAttribute("dispSN");
      // on affiche l'option en disabled uniquement si on n'est pas dans un form de recherche
      if (oLI.isDisabled() && sDispSN && sDispSN.lastIndexOf("_RECH")==-1 && sDispSN.lastIndexOf("_ETAT")==-1 && sDispSN!="REQUEST") {
        oOpt.disabled = true;
        if (oOpt.style) oOpt.style.color = "#AAAAAA";

      } else if (oOpt.style) oOpt.style.color = "#000000";
      if (bAdmin && !oInput.options) alert(oInput.id + " - " +oInput.tagName+ " - " +findContainer(oInput).id); 
      oInput.options[oInput.options.length] = oOpt;
      var oTextNode = oOpt.childNodes[0]; // maj de la largeur maxi
      var iWidth = oTextNode.nodeValue.getWidth();
      if (iWidth>iMaxOptWidth) iMaxOptWidth = (iWidth>iLimitWidth) ? iLimitWidth : iWidth;
    }

    // pour une liste multiple, ajustement de la taille en fonction du contenu
    if ("select-multiple"==oInput.type && oInput.clientWidth<(iMaxOptWidth+17)) oInput.style.width = (iMaxOptWidth+17) + "px";

    // si pas de valeur sélectionnée, on s'assure qu'il y a une option "vide"
      var oEmptyOpt = oInput.findChildById("emptyOption", "OPTION");
      if (!oEmptyOpt) {
        // insére la vide en première place (à la place de "en cours de chargement")
        var oOpt = new Option("-- --");
        oOpt.id = "emptyOption";
        oOpt.value = "";
        oInput.options[0] = oOpt;
        if (!bIsValueSelected) {     
        oInput.selectedIndex = 0;
      }
    }
    // suppression de "Chargement en cours" si encore présente
    if (oInput.options[0] && oInput.options[0].id=="loading") oInput.options[0] = null;
    if (bIsValueSelected && oInput.onchange) oInput.onchange(); 
    // désactive le mode visuel "En cours de chargement"
    removeCssStyle(oInput,"loading"); // défini dans include.js
    
    if (p_sType=='select_checkable') {
      var sName = oInput.getAttribute("attributename");
      test = new MultipleSelectCheckbox('test');
      test.addGlobalSwitch('<b>Tous</b>', '__global', false);
      test.create(oInput.name, oInput, 'MSC'+oInput.name, 'MSCselect_'+oInput.name);            
    }
    oInput.setAttribute(ODYCont.sATTR_LOADED, "true");
  }

  //---------------- processList ------------------
  /**
   * Charge la liste de l'élément p_sElemId avec les données du DataItem d'id p_sSrcId
   * @param p_sSrcId  id de la source d'alimentation c'est à dire ici du DataItem sous-jacent
   */
  function processList(p_sElemId, p_sSrcId, p_sCurrentValue, p_sFilterValue, p_sType) {
    var oInput  = $O2(p_sElemId);
    if (bAdmin && !oInput) alert("Input '" + p_sElemId + "' not found");
    if (bAdmin && "select-one"!=oInput.type && "select-multiple"!=oInput.type) alert("Input '" + p_sElemId + "' is not a combo but : " + oInput.type);

    if (p_sFilterValue!=null) oInput.options.length = 0; // si filtrage, vide la liste

    // si condition de type "filter" mais pas de valeur du filtre=> pas d'action initiale
    var sCondition = oInput.getCondition();
    if (sCondition && sCondition.indexOf("filter=")==0 && (!p_sFilterValue || ""==p_sFilterValue)) {
      /*
      var oReg = new RegExp("filter=\{(\\d+)\}");
      var oRes = oReg.exec(sCondition);
      if (oRes) {
        var iMasterInputDispiId = oRes[1];
        var oMasterI = oInput.form[ODYForm.sET_SN].getInputByDisplayItemId(iMasterInputDispiId);
        p_sFilterValue = oMasterI.value;
      }
      */
      if (p_sCurrentValue) oInput.value=p_sCurrentValue;
      return;
    }

    addCssStyle(oInput,"loading");
    // remis à true dans fillCombo
    oInput.setAttribute(ODYCont.sATTR_LOADED,"false");

    var moOpt = oInput.options;
    var iNbr = moOpt.length;
    if (iNbr>0) {
      // décale les options pour insérer "Chargement" en première place
      moOpt[iNbr] = new Option("void"); // pour pré-créer l'option supplémentaire
      for (var j=iNbr; j>0; j=j-1) {
        var oOpt = moOpt[j-1];
        var oNewOpt = new Option(oOpt.text);
        oNewOpt.value = oOpt.value;
        oNewOpt.id = oOpt.id;
        oNewOpt.selected = oOpt.selected;
        moOpt[j-1] = new Option("void"); // ne pas mettre null sinon la position est "écrasée"
        moOpt[j] = oNewOpt;
      }
    }
    
    {
      var iInsertIdx = 0;
      var oOpt = new Option("Chargement en cours ...");
      oOpt.id = "loading";
      oOpt.value = "-9";
      oInput.options[iInsertIdx] = oOpt;
      oInput.selectedIndex = iInsertIdx;
    }
    // récupération de la liste des valeurs
    var msList = msDIList[p_sSrcId];
    var oLibInput = oInput.findBrotherById("lib"+oInput.id);
    if (!oLibInput) oLibInput = oInput;
    // éventuelle condition associée au dispi
    var sCriteria = oLibInput.getCondition();
    var bAddCriteria = 0;
    // un filtre sur master ent sera à ajouter ?
    if (sCriteria && sCriteria.lastIndexOf("mainEntId")!=-1) bAddCriteria = 1;
    // rq : si un filtre master ent doit être passé à la construction de la liste,
    // on recharge la liste des valeurs même si elle n'est pas vide
    if (null==msList || !msList || bAddCriteria) {
      // container associé à la liste
      var oContainer = findContainer(oInput);
      // si le container trouvé est "alerte", on monte d'un niveau pour récupérer un id dont l'unicité est garanti
      if (oContainer && "alerte"==oContainer.id) oContainer = oContainer.getContainer();
      var sContId = oContainer && oContainer.id ? oContainer.id : null;
      // filtre master ent à ajouter
      if (bAddCriteria) {
        var oIFAcont = oContainer;
        if (!oIFAcont.id.match("ifa_.*")) oIFAcont = oIFAcont.getContainer();
        var oReg2 = new RegExp("\\{mainEntId\\}","g");
        // construction du filtre : on decode mainEntId
        sCriteria = sCriteria.replace(oReg2, oIFAcont.getAttribute("mainEntId"));
        sCriteria = "&filter=" + encodeURI(sCriteria);
       
      } else sCriteria="";
      
      var sPostData = "DIId=" + p_sSrcId+"&inputId=" + encodeURI(oInput.id) + sCriteria;
      var sExpr = "fillCombo(sResponse,'" + oInput.id + "'," + (p_sCurrentValue ? "'" + p_sCurrentValue + "'" : "null") 
        + ",'" + sContId + "'"
        + "," + (p_sFilterValue ? "'"+p_sFilterValue+"'" : "null") + ",'"+p_sType+"')";
      sendRequest("/loader.svt", 10, "tech", sPostData, sExpr);

    } else fillCombo(msList, oInput, p_sCurrentValue, null, p_sFilterValue, p_sType);
  }

//---------------- processDblList ------------------
/**
 * Initialise la liste double de l'élément p_sElemId avec les données du DataItem d'id p_sSrcId
 * @param p_sSrcId  id de la source d'alimentation c'est à dire ici du DataItem sous-jacent
 */
function processDblList(p_sElemId, p_sSrcId, p_sCurrentValue) {
  var oInput = document.getElementById(p_sElemId); // le hidden
  if (bAdmin && !oInput) alert("Input '" + p_sElemId + "' not found");
  var oList1 = document.getElementById("dblList1_"+p_sElemId); // liste de gauche = les items à sélectionner
  var oList2 = document.getElementById("dblList2_"+p_sElemId); // liste de droite = les items sélectionnés

  addCssStyle(oList1,"loading");
  addCssStyle(oList2,"loading");
  oInput.setAttribute(ODYCont.sATTR_LOADED, "false");
  

  // récupération de la liste des valeurs
  var msList = msDIList[p_sSrcId];
  if (null==msList || !msList) {
    var oContainer = findContainer(oInput);
    var sContId = oContainer && oContainer.id ? "'"+oContainer.id+"'" : "null";
    sendRequest("/loader.svt",10,"tech","DIId=" + p_sSrcId+"&inputId=" + encodeURI(p_sElemId)
      , "fillDblList(sResponse,'" + p_sElemId + "'," + (p_sCurrentValue ? "'" + p_sCurrentValue + "'" : "null") + "," + sContId + ")");
  }
  else fillDblList(msList, oInput, p_sCurrentValue);
}

//---------------- fillDblList ------------------
/**
 * Rempli la combo à partir du tableau de données
 *
 * @param p tableau de définition de la liste des options (cas où la liste est disp
 *   dans la frame tech) ou XML pour le constuire (retour de l'appel au serveur=
 * @param p_oInput l'objet HTMLSelectElement ou son Id
 * @param p_sContainerId si présent, indique l'id du container de l'input (utile si le container est une div afin de
 *      ne pas sélectionner l'input dans le mauvais conteneur, comme par ex dans la frm_but alors qu'on veut celui de frm_frame1)
 */
function fillDblList(p, p_oInput, p_sDefaultValue, p_sContainerId) {
  var msList;
  var oInput;
  if (typeof(p)=="object") {
    msList = p;
    oInput = p_oInput;

  } else { // si callback suite XmlHttpRequest, il faut traiter la response qui est une string XML
    var oDoc = getDocElement(p);
    msList = buildArrayFromXml(oDoc);
    msDIList[retrieveDataItemIdFromHttpParam(oDoc)] = msList;
    oInput  = p_sContainerId 
        ? document.getElementById(p_sContainerId).findChildById(p_oInput)
        : document.getElementById(p_oInput); // dans ce cas p_oInput est une String égale à l'id 
  }

  var bFilter = oInput.getAttribute && "true"==oInput.getAttribute("filter");
  var oList1 = document.getElementById("dblList1_"+oInput.id); // liste de gauche = les items sélectionnés
  var oList2 = document.getElementById("dblList2_"+oInput.id); // liste de droite = les items à sélectionner
  for (var j=0, l=msList.length; j<l; j++) {
    var oLI = msList[j];
    // attribut "allowed": 1 si valeur autorisee, 0 sinon
    if (bFilter && oLI[2]!="1") continue;
    addDblListItem(oList1,oLI[0],oLI[1], oInput);
  }
  // désactive le mode visuel "En cours de chargement"
  removeCssStyle(oList1, "loading");
  removeCssStyle(oList2, "loading");
  oInput.setAttribute("bLoaded","true");
  if (arguments.length>=3) oInput.value = arguments[2];
}

//------------------ createXMLHttpRequest ------------------
/**
 *  affiche des infos sur l'input
 */
function createXMLHttpRequest() {
  var ua;

  if(window.XMLHttpRequest) {
    try {
      ua = new XMLHttpRequest();
      ua.overrideMimeType('text/xml');
    } catch(e) {
      ua = false;
    }
  } else if(window.ActiveXObject) {
    try {
      ua = new ActiveXObject("Microsoft.XMLHTTP");
    } catch(e) {
      ua = false;
    }
  }
  return ua;
}
// tableau des requetes en cours non terminées
var msRequest = new Array();

//------------------ sendRequest ------------------
/**
 * @param p_sUrl  URL de la requête à exécuter
 * @param p_sSubmit  paramètre http à envoyer sous forme d'une chaine "param1=value;param2=val2;..."
 * @param p_sResultExpr  expression à exécuter au retour. Le contenu de la réponse est disponible
 *   dans la variable "sResponse". Si null, sResponse est retournée telle quelle et l'appel est synchro
 * @return false si pb, le texte de la réponse si ok pour une synchro, null sinon
 */
function sendRequest(p_sUrl, p_iStep, p_sFrm, p_sSubmit, p_sResultExpr) {
  var req = Ajax.getTransport();

  if (!req) return false; // createXMLHttpRequest return false si erreur
  var bAsyncCall = (typeof p_sResultExpr!="undefined" && p_sResultExpr!=null); // si pas d'expression à évaluer, appel synchro
  msRequest.push(req);
  var sUrl = p_sUrl;
  if (-1!=p_iStep) sUrl = addUrlParam(sUrl,"step",p_iStep,true);
  sUrl = addUrlParam(sUrl,"frm", p_sFrm,true);
  req.open('POST', sUrl, bAsyncCall);
  req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  if (!p_sSubmit) p_sSubmit="a="; // pour éviter pb squid : 411 Length Required
  req.setRequestHeader("Content-length", p_sSubmit.length);
  req.setRequestHeader("Connection", "close");
  req.onreadystatechange = function() {
    if (req.readyState == 4) processResponse(req, sUrl, p_sResultExpr);
  }
  req.send(p_sSubmit);
  if(!bAsyncCall) return processResponse(req, sUrl, p_sResultExpr);
}

//------------------ processResponse ------------------
/** traite le retour d'une XMLHttpRequest, qu'elle soit synchro ou asynchro 
 * @return true si p_sResultExpr définie et exécutée, le texte de la réponse sinon
 */
function processResponse(req, p_sUrl, p_sResultExpr) {
  // reconstruction d'un tableau sans la req en cours
  var ms = new Array();
  for (var i=0, l=msRequest.length ; i<l ; i++) {
    if (msRequest[i]!=req) ms.push(msRequest[i]);
  }
  msRequest = ms;
  var sResponse = req.responseText;
  switch (req.status) {
    // Page-not-found error
    case 404:
      if (bAdmin) alert('sendRequest : Error '+req.status+' : Not Found. The requested URL ' +  p_sUrl + ' could not be found.');
      break;
    // Display results in a full window for server-side errors
    case 500:
      handleErrFullPage(sResponse);
      break;
    default:
       // Call JS alert for custom error or debug messages
       if (sResponse.indexOf('XXXError:') > -1 || sResponse.indexOf('XXXDebug:') > -1) {
          alert(sResponse);

       } else { // Call the desired result function
         if (!p_sResultExpr) return sResponse;
         try {
           eval(p_sResultExpr);
           return true;

         } catch(exc) {
           var sMsg = 'sendRequest['+p_sUrl+'] : Failed to execute script.\n'
               + O2Exception.errorToStr(exc)
               + '\n\n Executing script : ' + p_sResultExpr
               //  + '\n Response : ' + typeof(sResponse) + ', <copied to clipboard>.'
               ;
           O2Exception.recordError(sMsg);
           if (bAdmin) {
             // if (copy_clip) copy_clip(sResponse);
             alert(sMsg);
           }
           // throw exc;  throw inutile : on est ds onreadyStateChange => l'exception reste invisible
         }
       }
       break;
  }
  return false;
}

//------------------ display ------------------
function display(p_oContainer, p_iStep, p_sUrl, p_sSubmit, p_bSynchro) {
  displayInId(p_oContainer.id, p_iStep, p_sUrl, p_sSubmit, p_bSynchro);
}

//------------------ displayInId ------------------
/**
 * @param p_sContainerId id du container HTML (objet DOM)
 * @param p_sUrl  URL de la page à afficher
 * @param p_sSubmit  paramètres http à envoyer sous forme d'une chaine "param1=value;param2=val2;..."
 */
function displayInId(p_sContainerId, p_iStep, p_sUrl, p_sSubmit, p_bSynchro) {
  var oCont = typeof(p_sContainerId)=="string" ? findContainerById(p_sContainerId) : p_sContainerId;

  if (!oCont) return new O2Exception("Le container '" + p_sContainerId + "' n'existe pas.").display();

  oCont.showLoading();

  oCont.setAttribute("reloadCmd", "displayInId('" + oCont.id + "', "+p_iStep+",'" + p_sUrl + "'" +
    (p_sSubmit?",'" + p_sSubmit + "'":"") + ")");
  var sUrl = addUrlParam(p_sUrl, "out", "div", false);
  // NB : oCont.id!=p_sContainerId  car p_sContainerId peut être "parent" ou "_parent"
  var sExpr = "doDisplayInId('" + oCont.id + "', sResponse)";
  try {
    if (p_bSynchro) { // si demande d'appel synchro
      var sResponse = sendRequest(sUrl, p_iStep, p_sContainerId, p_sSubmit);
      eval(sExpr);

    } else sendRequest(sUrl, p_iStep, p_sContainerId, p_sSubmit, sExpr);

  } catch (e) {
    if (oCont) oCont.unsetLoading();
    throw e;
  }
}

//------------------ doDisplayInId ------------------
/**
 * @param p_sContainerId id du container HTML ou objet DOM de ce container
 */
function doDisplayInId(p_sContainerIdOrObj, p_sResponse) {
  var oCont = (typeof p_sContainerIdOrObj=="object") ? p_sContainerIdOrObj : findContainerById(p_sContainerIdOrObj);
  if (!oCont) return; // le chargement de la page peut avoir été interrompu ou la page changée
  // réinitialise la liste des actions à exécuter sur le onLoad
  oCont.msOnLoadFunctionList = new Array();
  oCont.innerHTML = p_sResponse;
  oCont.setLoaded();
  if (sDbg) patchDbgLinks(oCont);
  executeJS(oCont);
}

//------------------ executeJS ------------------
function executeJS(p_oCont) {
  var vScript = p_oCont.getElementsByTagName("script");
  for (var i=0, l = vScript.length; i<l; i++) {
    var oScript = vScript[i];
    if (!oScript) continue;
    try {
      var sScript = oScript.textContent||oScript.text;
      if (!sScript || sScript.match(/<!--[\s\n\r]*-->/)) continue;

      var iIdx = 0, iNext = 0, sModifiedS = "";
      var re = /[\(\)]/g;

      // ajoute (ou remplace) le paramètre "contId" aux appels de addOnLoad
      while (-1!=(iNext=sScript.indexOf("addOnLoad",iIdx))) {
        iNext = iNext + "addOnLoad".length + 1;
        // ajoute ce qui précède
        sModifiedS += sScript.substring(iIdx, iNext);
        var iParentCount = 1, ii=0;
        re.lastIndex = iNext;
        // cherche la parenthèse fermante
        while(iParentCount!=0 && ii<100 && (result = re.exec(sScript))!=null) {
          iParentCount += (result[0]=="(") ? +1 : -1;
          ii++;
        }
        var iIdxCloseParent = re.lastIndex;
        if (iIdxCloseParent<=iNext) throw "erreur";
        // ajoute (ou remplace) le paramètre "contId"
        sModifiedS += sScript.substring(iNext, iIdxCloseParent-1) + ",'" + p_oCont.id + "'"
           + sScript.substring(iIdxCloseParent-1,iIdxCloseParent);
        iIdx = iIdxCloseParent+1;
      }
      sModifiedS += sScript.substring(iIdx);
      eval(sModifiedS);

    } catch(e) {
       var sMsg = "executeJS[" + p_oCont.id + "] : Failed to execute script.\n"
         + O2Exception.errorToStr(e) 
         + "\nexecuting script : " + sModifiedS
       ;
      O2Exception.recordError(sMsg);
      if (bAdmin) copy_clip(sMsg);
      throw e;
    }
  }
  // exécution des scripts ajoutés par addOnLoad dans le container
  windowOnLoad(p_oCont);
}

//------------------ handleErrFullPage ------------------
function handleErrFullPage(strIn) {
  var errorWin;

  // Create new window and display error
  try {
    errorWin = window.open('', 'errorWin');
    errorWin.document.body.innerHTML = strIn;
  } catch(e) {
    // If pop-up gets blocked, inform user
    alert('An error occurred, but the error message cannot be' +
     ' displayed because of your browser\'s pop-up blocker.\n' +
     'Please allow pop-ups from this Web site.');
  }
}

//------------------ processSpecialCaractere ------------------
function processSpecialCaractere(p_sChaine) {
  p_sChaine= p_sChaine.replace(/\u20AC/g, 'EUR');
  p_sChaine= p_sChaine.replace(/\u2019/g, '\'');
  p_sChaine= p_sChaine.replace(/\u0153/g, 'oe');
  return p_sChaine;
}

//------------------ formData2QueryString ------------------
function formData2QueryString(p_oForm) {
  var submitContent = '';
  var formElem;
  var lastElemName = '';
  
  for (var i = 0; i < p_oForm.elements.length; i++) {
    formElem = p_oForm.elements[i];
    switch (formElem.type) {
      // Text fields, hidden form elements
      case 'text':
      case 'hidden':
      case 'password':
      case 'textarea':
       if (formElem.getAttribute("dataItemTypeSn")=='select_checkable') {
          var sCurrentValue = getCurrentValue(formElem,";");
          if (sCurrentValue) submitContent += formElem.name + '=' + escape(sCurrentValue) + '&';
        } else if(formElem.value && (!formElem.getAttribute("dataTypeId")  || formElem.getAttribute("dataItemTypeSn")!='upload')) {
          //il faut exclure les zone de type upload
          var sValue = escape(processSpecialCaractere(formElem.value));
          sValue = sValue.replace(/\+/g, '%2B'); // les "+" sont sinon considérés comme des espaces encodés !
          submitContent += (formElem.name + '=' + sValue + '&');
       }
       break;
      case 'select-one':
        if (formElem.value) submitContent += formElem.name + '=' + escape(formElem.value) + '&';
        break;

      case 'select-multiple':
        if (formElem.selectedIndex!=-1) {
          var iNb = 0;
          submitContent += formElem.name + '=';
          $A(formElem.options).each(function(oOpt) {
            if (!oOpt.selected) return;
            if (iNb>0) submitContent += ODYForm.sVALUE_SEP;
            submitContent += oOpt.value;
            iNb++;
          });
          submitContent += '&';
        }
        break;
        
      // Radio buttons
      case 'radio':
        if (formElem.checked) {
          submitContent += formElem.name + '=' + escape(formElem.value) + '&'
        }
        break;
        
      // Checkboxes
      case 'checkbox':
        if (formElem.checked) {
          // Continuing multiple, same-name checkboxes
          if (formElem.name == lastElemName) {
            // Strip of end ampersand if there is one
            if (submitContent.lastIndexOf('&') == submitContent.length-1) {
              submitContent = submitContent.substr(0, submitContent.length - 1);
            }
            // Append value as comma-delimited string
            submitContent += ',' + escape(formElem.value);

          } else {
            submitContent += formElem.name + '=' + escape(formElem.value);
          }
          submitContent += '&';
          lastElemName = formElem.name;
        }
        break;
        
    }
  }
  // Remove trailing separator
  submitContent = submitContent.substr(0, submitContent.length - 1);
  return submitContent;
}
