var INCLUDE_VER = "$Revision: 1.168 $";

var iSCROLLER_WIDTH;
var sTOOLTIP_DIV_ID = "tooltip";

// définitions communes
var msOnLoadFunctionList = new Array();
var bLoaded = false;
var sDbg;
var bAdmin;
var sAccueil;//initialisé dans le fs_main

/* *********************************************************************** */
/*                                STRING                                   */  
/* *********************************************************************** */

// ----------- trim ----------
String.prototype.trim = function() { return this.replace(/(^\s*)|(\s*$)/g, ""); }

// ----------- count ----------
String.prototype.count=function(s1) { 
  return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}

// ----------- escapeJS ----------
/** echappe une String de sorte qu'elle puisse être incluse entre 2 p_sActiveQuote, c'est à dire :
    - remplace tous les "\" par "\\"
    - remplace tous les "p_sActiveQuote" par "\p_sActiveQuote"
  */
String.prototype.escapeJS = function(p_sActiveQuote) {
  if (p_sActiveQuote=='"') p_sActiveQuote = '\"';
  var re = new RegExp("([\\\\" + p_sActiveQuote +"])","g");
  return this.replace(re,"\\$1");
}
// ----------- supprCR ----------
/** remplace les retours à la ligne par le texte fourni, "" si non spécifié  */
String.prototype.supprCR = function(p_sCRReplace) {
  return this.replace(/\r|\n|\\n/g,(p_sCRReplace||""));
}

//------------------ removeItemFromList ------------------
/** Supprime de la liste (de type string avec séparateur p_cSep) l'item p_sItem. */
String.prototype.removeItemFromList = function (p_cSep, p_sItem) {
  var ms = this.split(p_cSep);
  var sNewList = "";
  for(var i=0; i<ms.length;i++) {
    if (ms[i]==p_sItem) continue;
    sNewList += ms[i] + p_cSep;
  }
  return sNewList=="" ? sNewList : sNewList.substring(0, sNewList.length-1);
}
//------------------ addItemToList ------------------
/** Ajoute à la liste (de type string avec séparateur p_cSep) l'item p_sItem. */
String.prototype.addItemToList = function(p_cSep, p_sItem) {
  var ms = this.split(p_cSep);
  for (var i=0; i<ms.length;i++) if (ms[i]==p_sItem) return this;
 
  var sNewList = (""==this)
    ? p_sItem
    : this + p_cSep + p_sItem
  return sNewList;
}

//------------------ getWidth ------------------
/** Calcule la largeur de cette string, une fois les éventuels styles appliqués. */
String.prototype.getWidth = function(p_msStyleObject){
  var test = document.createElement("spanSW");
  document.body.appendChild(test);
  test.style.visibility = "hidden";
  if (p_msStyleObject) for(var i in p_msStyleObject) test.style[i] = p_msStyleObject[i];
  test.innerHTML = this;
  var w = test.offsetWidth;
  document.body.removeChild(test);
  return w;
}

/* *********************************************************************** */
/*                                GENERAL                                  */  
/* *********************************************************************** */

//--------------  isDef  --------------
function isDef() { return (arguments.length>0 && typeof window[arguments[0]]!='undefined'); }

//--------------  getClassName  --------------
/** @return la classe du paramètre s'il est de type objet, false sinon */
function getClassName(obj) {
  if (typeof(obj)!="object" || obj === null) return false;
  if (obj._class) return obj._class; // cas des objets de DOMImplementation
  var msRes = /(\w+)\(/.exec(obj.constructor.toString());
  if (!msRes)  msRes = /\[([\w ]+)\]/.exec(obj.constructor.toString());
  return msRes[1];
}

//--------------  $O2  --------------
/** wrapper de la fonction $() qui boucle jusqu'à trouver un élément valide
 * @param element        id de l'objet cherché
 * @param p_oContainer   (optionnel) si défini, la recherche est d'abord faire dans ce container
 * @param p_sTag         (optionnel) précise le tag de l'élément cherché
 */
function $O2(element, p_oContainer, p_sTag) {
  var oElem;
  do {
    if (oElem) { // si on revient ici avec oElem défini, c'est qu'il n'était pas valide
      element = oElem.id;
      delete oElem;
    }
    if (p_oContainer) oElem = p_oContainer.findChildById(element, p_sTag);
    else if (isDef("oCC") && oCC) oElem = oCC.findChildById(element, p_sTag); 
    if (!oElem) oElem = $(element);
    if (!oElem) return;
    oElem = $(oElem);
  } while (!oElem.isValid());
  return oElem;
}

//--------------  checkVersion  --------------
function checkVersion(p_sSrcName, p_sExpectedVersion) {
  var v ;
  var sFileName = p_sSrcName;
  if (sFileName.match(/\/.*/)) sFileName = new RegExp("\/.*\/([a-zA-Z0-9_]*)(\.js)?").exec(sFileName)[1];
  try {  v = eval(sFileName.toUpperCase() + "_VER"); } catch(e) {}
  var sExpectedVersion = "$" + "Revision: " + p_sExpectedVersion + " $";
  if (v!=sExpectedVersion) {
    var sPath = p_sSrcName;
    if (!sPath.match(/\/.*/)) sPath = "/com/js/" + sPath;
    // reloadJS(sPath + ".js", p_sExpectedVersion);
    var sMsg = "Attention un fichier n'est pas à jour !\n\n";
    if (sDbg) sMsg += "("+p_sSrcName+".js : version '" + v +"' au lieu de '" + p_sExpectedVersion + "') \n\n";
    sMsg += " MERCI DE VIDER LE CACHE (menu 'Outils / Effacer mes traces') ET/OU DE RECHARGER LA PAGE (touche F5).";
    alert(sMsg);
  }
}

//--------------  windowOnLoad  --------------
function windowOnLoad(p_oCont) {
  var oWin = p_oCont && p_oCont.document ? p_oCont : window;
  var bIsIframe = (oWin.frameElement!=null); // si cette property est définie, on est dans une Iframe
  if (bIsIframe && this!=oWin) { // si on est dans une iframe, appel de la fonction sur l'objet window de l'iframe
    oWin.windowOnLoad(p_oCont);
    return;
  }  
  var msTbl = p_oCont ? p_oCont.msOnLoadFunctionList : msOnLoadFunctionList;
  if (msTbl) {
    for (var i=0, l=msTbl.length;i<l;i++) {
      var sScript = msTbl[i];
      try {
        // NB : surtout pas oWin.eval car KO sous IE
        eval(sScript);

      } catch(e) {
        var sMsg = "windowOnLoad[" +(p_oCont ? p_oCont.id : "-") +"] : Failed to execute script.\n"
               + O2Exception.errorToStr(e)
               + "\n\n Executing script : " + sScript
               ;
           O2Exception.recordError(sMsg);
       if (bAdmin && copy_clip) copy_clip(sMsg);
       throw e;
      }
    }
  }
  if (p_oCont && p_oCont.setAttribute) p_oCont.setAttribute(ODYCont.sATTR_LOADED, "true");
  else bLoaded = true;
}

//--------------  addOnLoad  --------------
// ajoute l'expression à la liste des expression à exécuter sur l'événement "onLoad" de la page
function addOnLoad(p_sExpr, p_sContId) {
  if (!p_sContId) throw "addOnLoad : target container id mandatory ! Use \"_XXX\" for a frame named XXX.\n" + p_sExpr;
  if (!p_sContId.startsWith("_")) {
    var oCont = findContainerById(p_sContId);
    // alert("addOnLoad: '" + p_sExpr + "' for container '" + p_sContId +"', msOnLoadFunctionList="+oCont.msOnLoadFunctionList); 
    if (!oCont.msOnLoadFunctionList) oCont.msOnLoadFunctionList = new Array();
    oCont.msOnLoadFunctionList.push(p_sExpr);
    
  } else msOnLoadFunctionList.push(p_sExpr);
}

//--------------  initCC  --------------
// initialisation de oCC (le current Container)
function initCC(p_sTS) {
  // the current container
  oCC = $(p_sTS).getContainer();
  oCC["ts"] = p_sTS;
}

//--------------  getCurrentScript  --------------
function getCurrentScript() {
  var scripts = document.getElementsByTagName('script');
  return scripts[scripts.length - 1];
}

//--------------  makeDev  --------------
function makeDev(p_oLink) {
  var sCurrentUrl = this; // compte tenu du format de l'appel, le context est initialisé avec this=document.location.href
  var sPrefix;
  if (sCurrentUrl.match("https?://dev")) sPrefix = "dev";
  else if (sCurrentUrl.match("https?://test")) sPrefix = "test";
  else return;

  var sProt = "http://";
  if (!p_oLink.href.match(sProt + ".*")) return;
  var sRef = p_oLink.href.substring(sProt.length);
  if (sRef.match(sPrefix + "\..*")) return;
  if (sRef.match("www\..*")) sRef = sRef.substring(4);
  p_oLink.href = sProt + sPrefix + "." + sRef;
}

//--------------  setCookie ----------------
function setCookie(name, value, days){
  var expire = new Date ();
  expire.setTime (expire.getTime() + (24 * 60 * 60 * 1000) * days);
  document.cookie = name + "=" + escape(value) + "; expires=" +expire.toGMTString();
}

//--------------  getCookie ----------------
function getCookie(name){
  var startIndex = document.cookie.indexOf(name);
  if (startIndex != -1){
    var endIndex = document.cookie.indexOf(";", startIndex);
    if (endIndex == -1) endIndex = document.cookie.length;{
      return unescape(document.cookie.substring(startIndex+name.length+1, endIndex));
    }
  }
  else {
    return null;
  }
}

//--------------  deleteCookie ----------------
function deleteCookie (name) {
var exp=new Date();
exp.setTime (exp.getTime() - 100000);
var cval=getCookie (name);
document.cookie=name+"="+cval+"; expires="+exp.toGMTString();
} 

/* *********************************************************************** */
/*                        TOOLTIP - OVERLIB                                */  
/* *********************************************************************** */

//--------------  getOverlibDiv ----------------
function getOverlibDiv(e, p_bCreateIfNotExist) {
  var oCaller = e ? e.target : null;
  var oDivParent = oCaller ? oCaller.getContainer() : document.body;
  var oDiv = oDivParent.findChildById(sTOOLTIP_DIV_ID);
  if (p_bCreateIfNotExist && !oDiv) { // premier appel : création de la div
    oDiv = document.createElement('div');
  	Element.extend(oDiv) ;
    oDiv.id = sTOOLTIP_DIV_ID;
    oDivParent.appendChild(oDiv);
    // pour forcer la div à se cacher lors d'un clic
    oDiv.listenTo("click", function(e) {  hideOverlib(this, e); }, false);
    if (true || p_bPersistent) oDiv.listenTo("mouseout", ndDiv, false);
    else delete oDiv.onmouseout;
    oDiv.hide();
  }
  return oDiv;
}

//--------------  overlib ----------------
function overlib(e, p_sMsg, p_bPersistent, p_sCssStyle) {
  var oCaller = e ? e.target : null;
  if(!oCaller) oCaller = e.srcElement ; // IE considère que e.target est undefined, il faut utiliser e.srcElement
  Element.extend(oCaller) ;
  var oDiv = getOverlibDiv(e, true);

  // si tooltip déjà visible, on le cache juste
  if (oDiv.visible()) return hideOverlib(oDiv, e);

  oDiv.innerHTML = p_sMsg;
  oDiv.className = p_sCssStyle ? p_sCssStyle : "overlib";
  var sInputId;
  if (oCaller) {
    try {
      sInputId = oCaller.getAttribute("inputId");
    } catch(e) {
      sInputId = oCaller.parentNode.getAttribute("inputId");
    }
  }
  // si référence vers le champ d'input (sauf hidden), utilisation de cet input (et non du "?") pour positionner la div
  if (oCaller.getAttribute && oCaller.getAttribute("inputId")) {
    var oInput = oCaller.findBrotherById(oCaller.getAttribute("inputId"));
    if (oInput.type!="hidden") oCaller = oInput; 
  }
  
  var oCont = oDiv.getContainer();
  // visible avant dimensionnement
  oDiv.show();
  // sauvegarde le zindex actuel du container
  oDiv["containerZindex"] = oCont.style.zIndex;
  // passe le container au meme zindex que l'overlib pour etre sûr que le fond sera visible
  if(e.target) oCont.style.zIndex = oDiv.getStyle("zIndex"); 
  else oCont.style.zIndex = oDiv.style.zIndex ;// Hack car IE ne fonctionne pas avec 'oDiv.getStyle("zIndex")'

  if (oCaller) {
    var oTmpcont = oCaller.getContainer();
   
    // offset par rapport au premier parent qui est "absolute" ou "relative" ou "fixed"
    // donc en principe par rapport à oTmpcont
    var offset = oCaller.offsetFromIncludingScroll(oCont);

    // calcul des max et min possibles en X et Y
    var oMaxContX = oCont;
    var iOffsetFromMaxX = 0;
    while (oMaxContX && oMaxContX.getStyle("overflow-x")=="visible") {
      iOffsetFromMaxX += oMaxContX.offsetLeft;
      oMaxContX = oMaxContX.getContainer();
    }
    var iXMin = oMaxContX ? -iOffsetFromMaxX : oCont.clientWidth;
    var iXMax = oMaxContX ? oMaxContX.clientWidth-iOffsetFromMaxX : oCont.clientWidth;

    var oMaxContY = oCont;
    var iOffsetFromMaxY = 0; // position du container de oDiv par rapport au premier container qui va clipper 
    while (oMaxContY && oMaxContY.getStyle("overflow-y")=="visible") {
      iOffsetFromMaxY += oMaxContY.offsetTop;
      oMaxContY = oMaxContY.getContainer();
    }
    var iYMin = oMaxContY ? -iOffsetFromMaxY : oCont.clientHeight;
    var iYMax = oMaxContY ? oMaxContY.clientHeight - iOffsetFromMaxY: oCont.clientHeight;

    var iMargin = -2;
    // prise en compte de la hauteur éventuellement scrollée (cumulée)
    var iScrollTop = 0, ii = 0;
    do {
       iScrollTop += oTmpcont.scrollTop;
       oTmpcont = oTmpcont.getContainer();
       ii++;
    } while (oTmpcont && oTmpcont.tagName!="BODY" && oTmpcont!=oCont && ii<15)

    // dans un multi scrollé, il ne faut pas soustraire iScrollTop : ex: help pour "Parc Duo" dans mat remis EA
    var iLeft = offset[0];
    var iWidth = oDiv.getDimensions().width;
    if ((iLeft+iWidth)>(iXMax-iMargin)) { // si dépassement à droite, cale le bord droit de l'overlib sur le max du container
      iLeft = iXMax - iWidth;
    }
    if (iLeft<(iXMin+iMargin)) iLeft = 0 + iMargin;
    
    var iTop = offset[1] + iMargin + oCaller.getHeight()/* - iScrollTop */;
    var iHeight = oDiv.getDimensions().height;
    if ((iTop+iHeight)>(iYMax-iMargin)) { // si dépassement en bas, cale sur le haut du caller
      iTop = offset[1] - iHeight - iMargin;
      // if (iTop<iMargin) iTop = iMargin;
    }
    oDiv.style.left = iLeft + "px";
    oDiv.style.top  = iTop  + "px";
  }
  oDiv["persist"] = p_bPersistent ? true : false;
}

//--------------  hideOverlib ----------------
function hideOverlib(p_oDiv, e) {
  var oDiv = p_oDiv ? p_oDiv : getOverlibDiv(e, false);
  if (!oDiv) return;
  oDiv.hide();
  oDiv.getContainer().style.zIndex = oDiv["containerZindex"];
}

//--------------  nd ----------------
function nd(e) {
  var oDiv = getOverlibDiv(e, false);
  if (!oDiv) return;
  var eLeavedObject = e.currentTarget;
  Position.prepare();
  if (e && Position.withinIncludingScrolloffsets(oDiv, e.clientX, e.clientY)) return;
  if (!(oDiv.getAttribute && oDiv["persist"]) || eLeavedObject==oDiv) hideOverlib(oDiv, e);
}

//--------------  ndDiv ----------------
/** pour le onMouseout de la div elle-meme : ne réagit pas si mouseout déclenché sur un élément qui est dans la div elle meme */
function ndDiv(e) {
  var oDiv = getOverlibDiv(e, false);
  var oNewOver = e.explicitOriginalTarget;
  if (oNewOver==oDiv || findParentByTagName(oNewOver,"div")==oDiv) return;
  hideOverlib(oDiv, e);
}

//--------------  findParentByTagName ----------------
/**
 * @param p_oNode le node racine de la recherche
 * @param p_sTagNamePattern le tag parent cherché
 * @return l'objet DOM ayant le tag
 */
function findParentByTagName(p_oNode, p_sTagNamePattern) {
  if(null == p_oNode) return null ;
  var oElt = p_oNode;
  if (oElt.nodeName=="#text") oElt = oElt.parentNode;
  return $(oElt).findParentByTagName(p_sTagNamePattern);
}

//--------------  findChildById ----------------
/**
 * @param p_oParentNode le node racine de la recherche, qui peut aussi être un objet window ou document
 * @param p_sId l'id cherché
 * @return l'objet DOM ayant l'id
 */
function findChildById(p_oParentNode, p_sId, p_sTagName, p_bInRecusion) {
  var oElt = p_oParentNode;
  if (oElt.documentElement) oElt = documentElement;
  else if (oElt.frames) oElt = oElt.document.documentElement;
  return oElt.findChildById(p_sId, p_sTagName); 
}

//--------------  isContainer ----------------
/** retrun true si le node est une div avec attribut "container=true" ou un "BODY" ou un "HTML" */
function isContainer(p_oNode) {
  return ODYCont.isContainer(p_oNode);
}

function findContainer(p_oNode) { return ODYCont.findContainer(p_oNode); }

//--------------  findContainee ----------------
/**
 * Cherche le premier container contenu qui est soit un document soit une DIV avec le style css "cont"
 * @param p_oContainer le node contenant
 * @param p_sNamePattern le nom du container contenu cherché 
 * @return l'objet DOM container contenu dans p_oContainer
 */
function findContainee(p_oContainer, p_sNamePattern) {
  if (p_oContainer.document || p_oContainer.tagName=="FRAMESET") return findChildFrame(p_oContainer, p_sNamePattern);
  try {
    var reg=new RegExp(p_sNamePattern, "g");
    // première passe sur les fils direct pour otpimisation
    for (var i=0, vChildren = p_oContainer.childNodes, l=vChildren.length ; i<l ; i++) {
      var oChild = vChildren[i];
      if (oChild && oChild.tagName=="DIV" && ODYCont.isContainer(oChild) && oChild.id.match(reg)) return oChild;
    }
    (p_oContainer.select('div') || []).each(function(oDiv) {
        if (oDiv && ODYCont.isContainer(oDiv) && oDiv.id.match(reg)) return oDiv;
      })
  } catch(e) {
    alert("findContainee: " + p_oContainer + ", " +e);
  }
  return null;
}

//--------------  findChildFrame ----------------
/**
 * @param p_oParent la frame parente (window, document ou frameset)
 * @param p_sNamePattern expression régulière du nom de la frame à trouver
 * @return la frame fille dont le nom match l'expression
 */
function findChildFrame(p_oParent, p_sNamePattern) {
  var oParent = p_oParent;
  if (oParent.ownerDocument) oParent = oParent.ownerDocument;
  if (oParent.defaultView) oParent = oParent.defaultView;
  if (!oParent.frames) return;
  var reg=new RegExp(p_sNamePattern, "g");
  for (var i=0, fl=oParent.frames.length; i<fl;i++) {
    var oFrm = oParent.frames[i];
    if (oFrm.name.match(reg)) return oFrm;
  }
  for (var i=0, fl=oParent.frames.length; i<fl;i++) {
    var oFrm = findChildFrame(oParent.frames[i],p_sNamePattern);
    if (oFrm) return oFrm;
  }
  return;
}

//--------------  findContainerById ----------------
/**
 * Cherche le container avec l'id ou le nom indiqué. Cette fonction délègue à findFrameOrElementById
 * sauf si p_sNameOrId vaut "parent" ou "_parent"
 *
 * @param p_sNameOrId id cherché
 * @param p_oRoot départ de la recherche. Si null, window.top est utilisé
 * @return l'objet trouvé de type documentElement si c'est une frame ou objet DOM sinon
 */
function findContainerById(p_sNameOrId, p_oRoot) {
  if (p_sNameOrId=="parent" || p_sNameOrId=="_parent") {
    var oCaller = findFunctionCallerObject(findContainerById);
    var oCont = findContainer(oCaller);
    oCont = findContainer(oCont);

  } else if (p_sNameOrId=="fiche.parent") {
    var oCaller = findFunctionCallerObject(findContainerById);
    var oCont = oCaller.getContainer().getContainer();
    oCont = getDivFiche(oCont); 

  } else {
    oCont = findFrameOrElementById(p_sNameOrId, p_oRoot);
  }
  if (oCont && !ODYCont.isContainer(oCont)) throw "'"+p_sNameOrId+"' was found but is not identified as a container !";
  return oCont;
}

//--------------  findFrameOrElementById ----------------
/**
 * Cherche l'élément ou la frame avec l'id ou le nom indiqué
 * @param p_sNameOrId id cherché
 * @param p_oRoot départ de la recherche. Si null, window.top est utilisé
 * @return l'objet trouvé : le body si c'est une frame, l'objet DOM sinon
 */
function findFrameOrElementById(p_sNameOrId, p_oRoot) {
  var oRoot = p_oRoot ? p_oRoot : window; // NB: pas window.top sinon pb avec iframe
  //recherche d'une frame avec ce nom
  var oFrm = findChildFrame(oRoot, p_sNameOrId);
  if (oFrm) return oFrm.document.body;

  // sinon recherche div
  var oDom = oRoot.tagName ? oRoot.findChildById(p_sNameOrId) : oRoot.document.getElementById(p_sNameOrId);
  if (oDom) return oDom;
  if (oRoot.frames) {
    for (var i=0, fl=oRoot.frames.length; i<fl;i++) {
      var oFrm = oRoot.frames[i];
      oDom = findFrameOrElementById(p_sNameOrId, oFrm);
      if (oDom) return oDom;
    }
  }
  return null;
}

//--------------  Event.retrieve ----------------
/**
 * Récupère l'event courant
 */
Object.extend(Event, {
  retrieve: function(p_oFunction) {
    var ev = window.event;  // pour IE
    if (ev) return ev;
    try {
      var i=0; // sécurité
      var oCaller =  p_oFunction ? p_oFunction.caller : arguments.callee;
      while(true && i<20) {
        i++;
        if (!oCaller) break;
        if (oCaller.arguments && oCaller.arguments[0] && oCaller.arguments[0].originalTarget) return oCaller.arguments[0];
        if (oCaller==oCaller.caller) break;
        oCaller = oCaller.caller;
      }
    } catch (e) { }
    return null;
  }
});

//--------------  findFunctionCallerObject ----------------
/**
 * @param p_oFunction la fonction dont on cherche l'origine de l'appel, ou null pour détection automatique
 * @return l'élément DHTML d'où émane l'appel de la fonction, null si l'appel ne résulte pas de l'activation d'un 
 *  handler (donc pas d'event)
 */
function findFunctionCallerObject(p_oFunction) {
  var oCaller = p_oFunction ? p_oFunction.caller : arguments.callee;
  var i=0; // sécurité
  var oCal = window.event && Event.element(window.event);  // pour IE
  if (oCal) return oCal;
  try {
    while(true && i<20) {
      i++;
      if (!oCaller) break;
      if (oCaller.arguments && oCaller.arguments[0] && oCaller.arguments[0].originalTarget) {
        oCal= oCaller.arguments[0].originalTarget;
        if (!getClassName(oCal).match(/.*HTML.*/)) oCal = null;
        break;
      } 
      if (oCaller==oCaller.caller) break;
      oCaller = oCaller.caller;
 
    }
  } catch (e) { }
  return oCal;
}


//--------------  getElementLeft  --------------
function getElementLeft(eElement) {
   if (!eElement && this) eElement = this;
   var DL_bIE = document.all ? true : false; // initialize var to identify IE
   var nLeftPos = eElement.offsetLeft;       // initialize var to store calculations
   var eParElement = eElement.offsetParent;  // identify first offset parent element
   while (eParElement != null) {             // move up through element hierarchy
     if(DL_bIE)  {
       if( (eParElement.tagName != "TABLE") && (eParElement.tagName != "BODY") ) {                                   // if parent is not a table or the body, then...
            nLeftPos += eParElement.clientLeft; // append cell border width to calcs
         }
      } else {
         if(eParElement.tagName == "TABLE") {
            var nParBorder = parseInt(eParElement.border); // get its border as a number
            if(isNaN(nParBorder)) {          // if no valid border attribute, then...
                                             // check the table's frame attribute
               var nParFrame = eParElement.getAttribute('frame');
               if(nParFrame != null) {       // if frame has ANY value, then...
                  nLeftPos += 1;             // append one pixel to counter
               }
            } else if(nParBorder > 0) {      // if a border width is specified, then...
               nLeftPos += nParBorder;       // append the border width to counter
            }
         }
      }
      nLeftPos += eParElement.offsetLeft;    // append left offset of parent
      eParElement = eParElement.offsetParent; // and move up the element hierarchy
   }                                         // until no more offset parents exist
   return nLeftPos;                          // return the number calculated
}

//--------------  getElementTop  --------------
function getElementTop(eElement) {
   if (!eElement && this) eElement = this;
   var DL_bIE = document.all ? true : false; // initialize var to identify IE
   var nTopPos = eElement.offsetTop;         // initialize var to store calculations
   var eParElement = eElement.offsetParent;  // identify first offset parent element

   while (eParElement!=null && !isContainer(eParElement)) {             // move up through element hierarchy
      if(DL_bIE) {
         if( (eParElement.tagName != "TABLE") && (eParElement.tagName != "BODY") ) {
                                              // if parent a table cell, then...
            nTopPos += eParElement.clientTop; // append cell border width to calcs
         }
      } else {                                // if browser is Gecko, then...
         if(eParElement.tagName == "TABLE") { // get its border as a number
            var nParBorder = parseInt(eParElement.border);
            if(isNaN(nParBorder)) {          // if no valid border attribute, then...
                                            // check the table's frame attribute
               var nParFrame = eParElement.getAttribute('frame');
               if(nParFrame != null)         // if frame has ANY value, then...
               {
                  nTopPos += 1;              // append one pixel to counter
               }
            } else if(nParBorder > 0) {      // if a border width is specified, then...
               nTopPos += nParBorder;        // append the border width to counter
            }
         }
      }
      nTopPos += eParElement.offsetTop;      // append top offset of parent
      eParElement = eParElement.offsetParent; // and move up the element hierarchy
   }                                         // until no more offset parents exist
   return nTopPos;                           // return the number calculated
}      

//------------------ showInputInfo ------------------
/**
 *  affiche des infos sur l'input
 */
function showInputInfo(p_oInput) {
  var s = "DisplayItem [" + p_oInput.getDisplayItemId() + " - " + p_oInput.name + "]"
   + ", Group [" + p_oInput.getAttribute("groupId") + "]"
   + ", Tab [" + p_oInput.getAttribute("tabId") + "]"
   + ", Display [" + p_oInput.getAttribute("dispId") + "]"
   + ", tabIdx=" + p_oInput.tabIndex
   + ", Attribute [" + p_oInput.getAttributeId() + " - " + p_oInput.getAttribute("attributeName") + "]"
   + " : originValue=" + p_oInput.getAttribute("originValue")
   + " : value=" + p_oInput.value + ", type=" + p_oInput.type
   + ", filter=" + p_oInput.getAttribute("filter")
   + ", cond=" + p_oInput.getAttribute("cond")
   + ", readonly=" + (null!=p_oInput.getAttribute("readonly"));
  if ("select-one"==p_oInput.type) {
    s += ", nboptions=" + p_oInput.options.length;
    s += " :\n"
    for (var j=0; j<p_oInput.options.length; j++) {
      s += "     " + p_oInput.options[j].value + " - " + p_oInput.options[j].text + "\n";
    }
  }
  if (p_oInput.oMask) s += ", mask=" + p_oInput.oMask;
  alert(s);
}

//------------------ getInputValue ------------------
function getInputValue(p_oInput) {
  if (!p_oInput) return;
  if (p_oInput.length) { // radio button
    for (var i=0; i < p_oInput.length; i++) if (p_oInput[i].checked) return p_oInput[i].value;

  } else return p_oInput.value;
}

//------------------ isArray ------------------
/**
 * Return a boolean value telling whether // the first argument is an Array object. 
 */
function isArray() {
  if (typeof arguments[0]!='object') return false;
  var criterion = arguments[0].constructor.toString().match(/array/i); 
  return (criterion != null);
}

//------------------ addUrlParam ------------------
/**
 * @param  p_bReplaceIfExist si true, la valeur du paramètre est modifiée lorsque celui-ci existe déjà dans l'url.
         Sinon l'url est renvoyée telle quelle si le param existe déjà
 * @return l'url à laquelle le paramètre indiqué a été ajouté. Si param déjà présent
 */
function addUrlParam(p_sUrl,p_sParam,p_sValue,p_bReplaceIfExist) {
  var bParamExist = (null!=p_sUrl.match("(\\?|&)" + p_sParam + "="));
  // si existe et pas de remplacement, no change
  if (bParamExist && !p_bReplaceIfExist) return p_sUrl;
  // si param n'existe pas, ajout simple
  if (!bParamExist) return (p_sUrl + (-1==p_sUrl.indexOf("?") ? "?" : "&") + p_sParam + "=" + escape(p_sValue));
  // si existe et il faut le remplacer ...
  var reg=new RegExp("((\\?|&)" + p_sParam + "=)[\\w\\.]*($|&)", "g");
  return p_sUrl.replace(reg, "$1" + p_sValue + "$3");
}

//------------------ getUrlParam ------------------
/**
 * @param  p_bReplaceIfExist si true, la valeur du paramètre est modifiée lorsque celui-ci existe déjà dans l'url.
         Sinon l'url est renvoyée telle quelle si le param existe déjà
 * @return l'url à laquelle le paramètre indiqué a été ajouté. Si param déjà présent
 */
function getUrlParam(p_sUrl, p_sParam, p_sValueIfNotPresent) {
  var reg = new RegExp("(\\?|&)" + p_sParam + "=([\\w\\.]*)($|&)", "g");
  var result       = reg.exec(p_sUrl);
  var bParamExist = (null!=result);
  // si existe et pas de remplacement, no change
  if (!bParamExist) return p_sValueIfNotPresent;
  return result[2];
}

//------------------ isCssStylesheetLoaded ------------------
function isCssStylesheetLoaded(p_sUrl) {
  var sCss = escape(p_sUrl);
  var vLinks = oCC.ownerDocument.styleSheets;
  var theCss;
  for (var i=0,l=vLinks.length; i<l;i++) {
    var oLink = vLinks[i];
    if (!oLink.href || oLink.href.indexOf(sCss)==-1) continue;
    theCss = oLink;
    break;
  }
  try {
    theCss.cssRules;
    return true;

  } catch (e) {
    return false;
  }
}

//------------------ addCssStylesheet ------------------
function addCssStylesheet(p_sCss, p_sMedia, p_sVersion) {
  var sCss = escape(p_sCss);
  if (p_sVersion) sCss = addUrlParam(sCss,"ver",p_sVersion, true);
  var oHeadElt = document.getElementsByTagName("head")[0];
  var vLinks = oHeadElt.getElementsByTagName("link");
  for (var i=0,l=vLinks.length; i<l;i++) {
    var oLink = vLinks[i];
    if (oLink.href && oLink.href.indexOf(sCss)!=-1) return;
  }
  if(document.createStyleSheet) document.createStyleSheet(p_sCss); // IE
  
  else {
    // var styles = "@import url('" + p_sCss + "');";
    var newSS=document.createElement('link');
    newSS.rel="stylesheet";
    newSS.type="text/css"
    if (p_sMedia) newSS.media = p_sMedia; 
    newSS.href=sCss;
    oHeadElt.appendChild(newSS);
  }
}

//------------------ reloadCSS ------------------
function reloadCSS(p_sUrl, p_sVersion) {
  var sUrl = escape(p_sUrl);
  var oHeadElt = document.getElementsByTagName("head")[0];
  var oCSS;
  (oHeadElt.select('link') || []).each(function(oLink) {
    if (!oLink || !oLink.href || ""==oLink.href || oLink.href.indexOf(sUrl)==-1) return;
    oCSS = oLink;
  });
  if (oCSS) {
    var sMedia = oCSS.media;
    oHeadElt.removeChild(oCSS);
    // oCSS.delete();
    addCssStylesheet(p_sUrl, sMedia, p_sVersion);
  }
}

//------------------ addJS ------------------
/**
 * @param p_sOnLoadExpr expression à exécuter quand le script a été chargé
 */
function addJS(p_sUrl, p_sOnLoadExpr, p_sVersion) {
  var sUrl = p_sUrl.indexOf("://")==-1 ? escape(p_sUrl) : p_sUrl;
  if (p_sVersion) sUrl = addUrlParam(sUrl,"ver",p_sVersion, true);
  var oHeadElt = document.getElementsByTagName("head")[0];
  var bExist = false;
  (oHeadElt.select('script') || []).each(function(oScript) {
    if (oScript && oScript.src && oScript.src.indexOf(sUrl)!=-1) bExist = true;
   });
  if (bExist) return;
  var newJS=document.createElement('script');
  newJS.src=sUrl;
  newJS.type="text/javascript";
  if (p_sOnLoadExpr) newJS.onload = function() { eval(p_sOnLoadExpr) };
  oHeadElt.appendChild(newJS);
}

//------------------ reloadJS ------------------
function reloadJS(p_sUrl, p_sVersion) {
  var sUrl = escape(p_sUrl);
  var oHeadElt = document.getElementsByTagName("head")[0];
  var oS;
  (oHeadElt.select('script') || []).each(function(oScript) {
    if (!oScript || !oScript.src || ""==oScript.src || oScript.src.indexOf(sUrl)==-1) return;
    oS = oScript;
  });
  if (oS) {
    oHeadElt.removeChild(oS);
    // oS.delete();
    addJS(p_sUrl, null, p_sVersion);
  }
}

//------------------ addCssStyle ------------------
/** @return true si le style a été ajouté, false sinon, ce qui se produit lorsque le style était déjà présent */
function addCssStyle(p_oElem, p_sStyle) {
  var sStyle = p_oElem.className;
  var sNewStyle = (sStyle||"").addItemToList(" ",p_sStyle);
  p_oElem.className = sNewStyle;
  return sNewStyle!=sStyle;
}

//------------------ removeCssStyle ------------------
/** @return true si le style a été supprimé, false sinon, ce qui se produit lorsque le style n'était pas déjà présent */
function removeCssStyle(p_oElem, p_sStyle) {
  var sStyle = p_oElem.className;
  var sNewStyle = (sStyle||"").removeItemFromList(" ",p_sStyle);
  p_oElem.className = sNewStyle;
  return sNewStyle!=sStyle;
}

/* *********************************************************************** */
/*                                NAVIGATE                                 */  
/* *********************************************************************** */

//--------------  decodeFrameDesig ----------------
function decodeFrameDesig(p_oCallee, p_sFrmDesig) {
  var oTgt = $O2(p_sFrmDesig);
  if (oTgt) return oTgt;
  // NB : oCallee est null dans le cas d'un callback suite à une XMLHttpRequest
  var oCont = p_oCallee ? findContainer(p_oCallee) : null;
  if (!p_sFrmDesig) oTgt = oCont; // par défaut, correspond au cas où p_sFrmDesig n'est pas défini

  if ("parent"==p_sFrmDesig || "_parent"==p_sFrmDesig) {
    if (null==oCont) throw "Can't navigate to 'parent' container inside a callback";
    oTgt = findContainer(oCont);
    p_sFrmDesig = null; //  pour force reinit de la variable ci-dessous

  } else if ("fiche.parent"==p_sFrmDesig) {
    if (null==oCont) throw "Can't navigate to 'fiche.parent' container inside a callback";
    oTgt = oCont.getContainer().getDivFiche();
    p_sFrmDesig = null; //  pour force reinit de la variable ci-dessous

  } else if ("fiche"==p_sFrmDesig) {
    if (null==oCont) throw "Can't navigate to 'fiche' container inside a callback";
    oTgt = oCont.getDivFiche();
    p_sFrmDesig = null; //  pour force reinit de la variable ci-dessous
  }
  return oTgt;
}

//--------------  checkedNavigateIfNoError ----------------
/**
 * Appelle navigateIfNoError() après vérification qu'aucune modifications non enregistrées existe.
 * @return false si le navigate a été annulé car modif en cours, le résultat de navigateIfNoError() sinon
 */
function checkedNavigateIfNoError(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_sFollowingExpr, p_oOF) {
  try {
    if (!ODYForm.checkUnsavedModif()) return false;
  } catch(e) {
     if (isDef("sDbg")) alert(e);
  }
  var oCont = $O2(p_oFrmIdOrObj);
  if (oCont) oCont.showLoading();
  return navigateIfNoError(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_sFollowingExpr, p_oOF);
}

//--------------  navigateIfNoError ----------------
/**
 * Affiche l'url indiquée dans le container
 * @param  p_sUrl
 * @param  p_iStep         step de servlet
 * @param  p_oFrmIdOrObj   id du container cible ou l'objet container cible lui même
 * @param p_sFollowingExprOrStep expression (ou juste step:frame) à exécuter en second si la première nav est ok
 * @return false si une alerte est affichée (donc s'il y a eu des "erreurs"), la réponse du navigate() sinon
 */
function navigateIfNoError(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_sFollowingExprOrStep, p_oOF) {
  // NB : si erreur gérée par ErrorFilter, navigate return la valeur booleenne "false"
  var oRep = navigate(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, "ifNoError");
  var sClass = getClassName(oRep);

  // si erreur, navigation annulée, reste à afficher les messages
  if (oRep && sClass.indexOf("DOMElement")!=-1) {
    var vErrList = oRep.selectNodeSet("//errorl/formError[@level='ERROR']");
    if (!vErrList || vErrList.length==0) vErrList = oRep.selectNodeSet("//data/error");
    var bHasError = (vErrList.length>0);
    var vWarningList = oRep.selectNodeSet("//errorl/formError[@level='WARNING']");
    var bHasWarning = (vWarningList.length>0);
    var vMsgList = oRep.selectNodeSet("//errorl/formError");
    var bHasMsg = bHasError || (vMsgList.length>0);
  }

  // ---- retraitement de p_sFollowingExprOrStep
  var sFollowing = p_sFollowingExprOrStep;
  if (/^\d+(:[\w_]*)?$/.test(sFollowing)) {
    var ms = String(sFollowing).split(/:/);
    var iStep = ms[0];
    var sFrm = (/.*:$/.test(sFollowing)) ? "" : ms[1];
    if (null == sFrm) sFrm = p_oFrmIdOrObj;
    sFollowing = "navigate('" + p_sUrl + "',"+ iStep + ",'"+ sFrm+"')";
  }

  if (bHasMsg) { // si msg à afficher
    var sFollow = bHasError ? null : sFollowing;
    var oCont;
    if (p_oOF) {
      oCont = p_oOF.getContainer();
      p_oOF.setRunningResponse(oRep);
    }
    if (!oCont) {
      var oCallee = findFunctionCallerObject();
      oCont = oCallee ? oCallee.getContainer() : null;
    }
    if (!oCont) oCont = $O2('edi');
    if (!oCont) oCont = window.document.body;
    if(!oCont) {
      if (bAdmin) alert("navigateIfNoError : pas de container trouvé : window.body="+window.document.body+".");
      return false;
    }
    ODYForm.doShowErrors(ODYForm.buildErrorsArray(oRep, p_oOF), oCont, sFollow);
    return false;
  }
  
  if (p_sFollowingExprOrStep && oRep) {
    try { eval(sFollowing); }
    catch (e) {
      if (bAdmin) alert(e);
      throw e;
    }
  }
  return oRep;
}

//--------------  checkedNavigate ----------------
/**
 * Appelle navigate() après vérification qu'aucune modifications non enregistrées existe.
 * @return false si le navigate a été annulé car modif en cours, le résultat de navigate() sinon
 */
function checkedNavigate(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_bNoDisplay) {
  try {
    if (!ODYForm.checkUnsavedModif()) return false;
  } catch(e) {
     if (isDef("sDbg")) alert(e);
  }
  return navigate(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_bNoDisplay);
}

//--------------  navigate ----------------
/**
 * Affiche l'url indiquée dans le container
 * @param  p_sUrl
 * @param  p_iStep         step de servlet
 * @param  p_oFrmIdOrObj   id du container cible ou l'objet container cible lui même
 * @return l'objet container target ou si p_bNoDisplay le contenu de la réponse du serveur
 */
function navigate(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_bNoDisplay) {
  var oTgt;
  var sFrm;

  var bNavIfNoErr = ("ifNoError"==p_bNoDisplay);
  var bNoDisplay = p_bNoDisplay;
  // dans le cas d'un appel depuis "navigateIfNoError", p_bNoDisplay vaut "ifNoError" ; dans ce cas on peut 
  // déterminer si un affichage et requis selon le nom de la frame : si commence par "js:" => pas de display
  if (bNavIfNoErr) bNoDisplay = p_oFrmIdOrObj && p_oFrmIdOrObj.match(/js:.*/);

  try {
    var bNewWin = false;
    
    if (typeof(p_oFrmIdOrObj)=="object" && p_oFrmIdOrObj!=null) {
      oTgt = p_oFrmIdOrObj;

    } else {
      sFrm = p_oFrmIdOrObj;
      // si pas de frame en argument, recherche en paramètre dans l'url
      var sFrmFromUrl = getUrlParam(p_sUrl, "frm");
      if (!sFrm && sFrmFromUrl) sFrm = sFrmFromUrl;

      // rech de la frame cible
      var oTgt = document.getElementById(sFrm);
      if (!sFrm) oTgt = window; // si le nom indiqué est null ou "", la target est la fenêtre principale
      // traitement du cas particulier "parent". NB :  ce traitement doit être fait meme ds le cas "noDisplay"
      if (!oTgt && ("parent"==sFrm || "_parent"==sFrm || "fiche.parent"==sFrm)) {
        var oCallee = findFunctionCallerObject();
        oTgt = decodeFrameDesig(oCallee, sFrm);
        sFrm = null; // pour forcer reinit de la variable avec nom réel de la frm, cf ci-après
      }

      if (!bNoDisplay) { // si target pas trouvée, cherche dans les parents
        var oParent = window;
        while (!oTgt && oParent!=oParent.parent) {
          oParent = oParent.parent;
          oTgt = oParent.document.getElementById(sFrm);
          if (!oTgt) oTgt = findChildFrame(oParent, sFrm);
        }
        if (!oTgt) bNewWin = true;

      } else { // si "noDisplay" demandé
        if (oTgt) sFrm = oTgt.id;
        if (sFrm.length<3 || sFrm.substring(0,3)!="js:") sFrm = "js:" + sFrm;
      }
    }
    // construction de l'Url finale
    var sUrl = p_sUrl;

    // navigation
    if (oTgt && oTgt.contentWindow) oTgt = oTgt.contentWindow; // cas d'une IFrame

    var bTgtIsWindow = (oTgt && typeof(oTgt.status)=="string");
    if (!bNoDisplay) {
      if (!bTgtIsWindow) { // si target est une window, il ne faut surtout pas prendre sont éventuel id comme nom de frame
        if (!sFrm) sFrm = oTgt.id; 
        if (!sFrm) sFrm = oTgt.name;
      } 
      sUrl = addUrlParam(sUrl,"frm",sFrm,true);
    }

    // si pas de step en argument, recherche en paramètre dans l'url
    if (p_iStep!=0 && !p_iStep) p_iStep = getUrlParam(p_sUrl, "step");

    if (bNoDisplay || bNavIfNoErr) { // pour NavIfNoErr, on fait aussi l'appel "en background" pour vérifier si erreurs
      var oResult = sendRequest(sUrl, p_iStep, sFrm, p_sSubmit, null);
      // si on est dans un cas "noDisplay" autre "ifNoError", on renvoie systématiquement le result brut
      if (!bNavIfNoErr) return oResult;
      if (oResult==false) return oResult; // cas d'un 404 par ex.

      try {  // si le contenu est du XML, return systématique
        var xDoc = getDocElement(oResult);
        if (xDoc) return xDoc; // si pas d'exception et que xDoc défini, le contenu est du XML

      } catch (e) {}
    }

    // on n'ouvre la fenêtre qu'après navigation, comme ça si erreur, fen pas ouverte
    if (bNewWin) {
      oTgt = window.open("/com/blank.htm", p_oFrmIdOrObj);
      bTgtIsWindow = true;
    }
	
	  // si on en est là, c'est qu'il va y avoir navigation
    var oNav = new ODYNavigation(oTgt, p_iStep, sUrl, p_sSubmit);
    ODYNavigation.record(oNav);

    if (bNavIfNoErr) { // forcement "ifNoError", dans ce cas pas d'appel à display sinon l'url est appelée 2 fois
      doDisplayInId(oTgt, oResult);
      // si exception catchée par ErrorFilter, génération de HTML par parsing de /com/error.xsl
      if (typeof(oResult)=="string" && oResult.indexOf('id="errorPage"')!=-1) return false;

    } else if (bTgtIsWindow) {
      if (-1!=p_iStep) sUrl = addUrlParam(sUrl, "step", p_iStep, true);
      if (p_sSubmit) throw "Submitted data not handled when not in div";
      oTgt.document.location.href = sUrl;

    } else {
      display(oTgt, p_iStep, sUrl, p_sSubmit);
    }

  } catch (e) {
    if (bAdmin) alert(e);
  }
  return oTgt;
}

//--------------  navigateWithProgress ----------------
/**
 * Affiche l'url indiquée dans le container
 * @param  p_sUrl
 * @param  p_iStep         step de servlet
 * @param  p_oFrmIdOrObj   id du container cible ou l'objet container cible lui même
 * @return l'objet container target
 */
function navigateWithProgress(p_sUrl, p_iStep, p_oFrmIdOrObj, p_sSubmit, p_sFollowingExpr) {
  if (!p_oFrmIdOrObj) throw "navigateWithProgress : IllegalArgument : Pas de frame indiquée : p_oFrmIdOrObj='" + p_oFrmIdOrObj + "'";
  if (!p_iStep) throw "navigateWithProgress : IllegalArgument : Pas de step indiqué : p_iStep='" + p_iStep + "'";
  var sFrm;
  if (typeof(p_oFrmIdOrObj)=="object") {
    sFrm = p_oFrmIdOrObj.id;

  } else {
    sFrm = p_oFrmIdOrObj;
    var oTgt = document.getElementById(sFrm);
    var oCallee = findFunctionCallerObject();
    if (!oTgt && ("parent"==sFrm || "_parent"==sFrm || "fiche.parent"==sFrm)) {
      oTgt = decodeFrameDesig(oCallee, sFrm);
      sFrm = null; // pour forcer reinit de la variable avec nom réel de la frm, cf ci-après
    }
  }

  // construction de l'Url finale
  var sUrl = p_sUrl;

  if (!sFrm) sFrm = oTgt.id; 
  if (!sFrm) sFrm = oTgt.name; 
  sUrl = addUrlParam(sUrl,"frm",sFrm,true);

  updateProgress(sFrm,3000,sUrl,p_iStep,p_sSubmit,p_sFollowingExpr.escapeJS("'")); // sendRequest(sUrl, p_iStep, sFrm, p_sSubmit, null);
  return oTgt;
}

//------------------- updateProgress --------------------
function updateProgress(p_sSpanId, p_iReloadIntervalMs, p_sUrl, p_iStep, p_sSubmit, p_sFollowingExpr) {
  var oSpan = $O2(p_sSpanId);
  if (!oSpan) return;
  if (!p_sUrl) p_sUrl = "/requester/request.svt";
  if (!p_iStep) p_iStep = "31";
  var sUrl = addUrlParam(p_sUrl,"ns","1"); // ajout du flag "ns" pour "NotSynchronized"
  var sExpr = "displayProgress(sResponse,'" + p_sSpanId + "'," + p_iReloadIntervalMs + ",'"+sUrl+"','"+p_iStep+"','"
    + p_sSubmit + "','"+p_sFollowingExpr+"')";

  if (p_sSpanId.indexOf("prog_")==0) p_sSpanId = oSpan.getContainer().id;//spécif tdb

  try {
    sendRequest(p_sUrl, p_iStep, p_sSpanId, p_sSubmit, sExpr);

  } catch (e) {
    throw e;
  }
}

//------------------- displayProgress --------------------
/**
 *  Frames : panel       -> content -> panel_contenu    |-> indic_inter_qty [panel_indic.xsl]
 *           [panel.xsl]               [panel_tdb.xsl]  |->indic_trans_rate
 */
function displayProgress(p_sXml, p_sSpanId, p_iReloadIntervalMs, p_sUrl, p_iStep, p_sSubmit, p_sFollowingExpr) {
  var oSpan = $O2(p_sSpanId);
  if (!oSpan) return;

  var xDoc = getDocElement(p_sXml);
  var vErrList = xDoc.selectNodeSet("//errorl/formError[@level='ERROR']");
  if (vErrList && vErrList.length>0) {
    //var oCallee = findFunctionCallerObject();
    ODYForm.doShowErrors(ODYForm.buildErrorsArray(xDoc, null), oSpan, null);
    return;
  }
  var sIndicSN = p_sSpanId.substring(5);
  //cas du tableau de bord
  var xProg = xDoc.selectNodeSet("//indicator[@sn='"+sIndicSN+"']/progress").item(0);
  //cas de tous les traitements concernant la factu
  if (null==xProg) xProg = xDoc.selectNodeSet("//actionProgress/progress").item(0);
  
  if (null==xProg) { // calcul terminé
    if (p_sSpanId.indexOf("prog_")==0) {
      if (oSpan && oSpan.isValid()) oSpan.getContainer().reload();
      return;
    }
    var xCount = xDoc.selectNodeSet("//indicator[@sn='"+sIndicSN+"']/count").item(0);
    if (null==xCount) xCount = xDoc.selectNodeSet("//actionProgress/count").item(0);
    if (null==xCount) return;
    var iNbEnregOk = xCount.getAttribute("nbEnregOk");
    var oMsgDiv = "Tout s'est bien passé : "+iNbEnregOk+" enregistrement(s) traité(s)";
    //ATTENTION eval systématique sur p_sFollowingExpr, cette string doit donc correspondre à une fct javascript!
    ODYForm.doShowEndAction(oSpan, oMsgDiv, eval(p_sFollowingExpr));
    return;//affichage du résultat
  } 
  var iCur = xProg.getAttribute("cur");
  var iTot = xProg.getAttribute("tot");
  var iProg = Math.round(xProg.getFirstChild().getNodeValue()*100)/100;

  var sPanInnerHtml = iCur + " / " + iTot + " (" + iProg 
    + '%) (<a href="#" onclick="eval(getContainer().getAttribute(\'reloadCmd\').replace(/30/,\'32\'))">stop</a>)'
    + '&#160;<img src="/portail/img/indicator_tiny_red.gif" alt="Working..."/>';
  if (p_sSpanId.indexOf("prog_")==0) {
    oSpan.innerHTML = sPanInnerHtml;
  } else {
    ODYForm.doShowWaiting(oSpan, sPanInnerHtml);
  }
  var iInt = p_iReloadIntervalMs;
  if (p_iReloadIntervalMs && iInt < 1000) iInt = 1000;
  if (p_sSpanId.indexOf("prog_")==0) {
    if (p_iReloadIntervalMs) updateProgress.delay((p_iReloadIntervalMs/1000.0), p_sSpanId, p_iReloadIntervalMs, p_sUrl, p_iStep, p_sSubmit, p_sFollowingExpr.replace(/'/g,"\\'"));
  } else {
    if (p_iReloadIntervalMs) updateProgress.delay(2, p_sSpanId, p_iReloadIntervalMs, p_sUrl, p_iStep, p_sSubmit, p_sFollowingExpr.replace(/'/g,"\\'"));
  }  
}

//--------------  showAlert ----------------
/**
 * Affiche si besoin le msg d'alerte
 * @param  p_sAlertMsg message d'alerte à diffuser
 * @param  p_sTimeBeforeRestart durée restante avant redémarrage, accolée au message si présent
 */
function showAlert(p_sAlertMsg, p_sTimeBeforeRestart) {
  var oDiv = $O2("msgsystem");
  if (!oDiv) return;
  var sMsg = p_sAlertMsg;
  if (p_sTimeBeforeRestart) sMsg += p_sTimeBeforeRestart;
  oDiv.findChildById("contenumsg","div").innerHTML = sMsg;
  oDiv.style.display = "block";
}

//--------------  navWithIndicator ----------------
/** Navigation avec activation d'un indicateur et controle pour éviter double soumission. Une variable "ev" pointe sur
  * l'event en cours
  */
function navWithIndicator(p_oClickedButton, p_sExpr) {
  if (p_oClickedButton.clicked == 1) {
    alert("Merci de patienter quelques instants, votre demande est en cours de traitement.");
    return false;
  }
  p_oClickedButton.clicked = 1;
  // si un bouton "annuler" existe, désactive son action
  var oCancelButton = p_oClickedButton.findBrotherById("stop","a");
  if (oCancelButton) oCancelButton.style.display = "none";
  $O2('indicator').style.display = "";
  var sFunc = "doNavWithIndicator('"+p_oClickedButton.id+"','"+String(p_sExpr).escapeJS("'")+"')";
  // alert (sFunc);
  setTimeout(sFunc, 100);
  return false;
}

//--------------  doNavWithIndicator ----------------
function doNavWithIndicator(p_sClickedButtonId, p_sExpr) {
  try {
    var ev = Event.retrieve(doNavWithIndicator); 
    eval(p_sExpr);
    //alert(p_sExpr);

  } catch (e) {
    if (bAdmin) alert(e);
    throw e;

  } finally {
    var oBtn = $O2(p_sClickedButtonId); 
    if (oBtn) oBtn.clicked = 0;
    var oIndic = $O2('indicator'); 
    if (oIndic) oIndic.style.display = "none";
  }
}

// =========================================================================
// @class
// ODYNavigation - Information sur une navigation
//
// @param 
// : oTgt  le container dans lequel afficher
// : p_iStep  le step visé
// : sUrl  l'url affichée
// =========================================================================

ODYNavigation = function(oTgt, p_iStep, sUrl,p_sSubmit) {
  this.m_iTS = new Date().getTime();
  this.m_iIdx;
  this.m_oTargetContainer = oTgt;
  this.m_iStep = p_iStep;
  this.m_sUrl = sUrl;
  this.m_sSubmit = p_sSubmit;
  this.m_bValid = true;
}

ODYNavigation.maxHistory = 50;
 
//************************* STATIC ********************************

//------------- record ------------------
ODYNavigation.record = function (p_oNav, p_bIsRewind) {
  var sFrm = p_oNav.m_oTargetContainer.id;
  if (sFrm && sFrm.indexOf("task_contenu")!=-1) {//onglet TODO, ne rien ajouter dans l'historique
    return;
  }
  var oPrev = ODYNavigation.getHistory(1);
  p_oNav.m_iIdx = oPrev ? oPrev.m_iIdx +1 : 0; // compteur du nombre de navigations
  var moHist = ODYNavigation.prototype.history;
  moHist.push(p_oNav);
  // si l'historique a atteint sa taille maxi, purge le premier item
  if (moHist.length > ODYNavigation.maxHistory) moHist.splice(0,1);
  if (!p_bIsRewind) ODYNavigation.pushIfRequired(p_oNav);
}

//------------- pushIfRequired ------------------
/** empile l'étape ssi il y a changement de contexte */
ODYNavigation.pushIfRequired = function (p_oNav) {

  var bIsEdit   = p_oNav.m_sUrl.indexOf("/edit.svt")==0;
  var bIsSearch = p_oNav.m_sUrl.indexOf("/query.svt")==0;
  var iStep = p_oNav.m_iStep;
  // si la navigation a lieu dans une autre fenêtre, exit
  if (!p_oNav.m_oTargetContainer || !p_oNav.m_oTargetContainer.id) return;
  var sFrm = p_oNav.m_oTargetContainer.id;
  /* Ignorés : 
    edit : step 6 (passage suivant / précédent)
           step 40 (exécution action sur entité)
           step 70 (showList multi)
           step 71 (showList multi tab)
           step 75 (ajout multi)
           step 85 (delete multi)
           step 98 (grille planning)
           step 99 (grille planning)
    search : step 30 (navigation ds liste par page, séquence ou modif nbre pages)
             step 70 (génération état)
    toutes les étapes en wizard
   */
  if ( 
       (bIsEdit   && (iStep==6 || iStep==40 || iStep==70 || iStep==71 || iStep==75 || iStep==85 || iStep==98 || iStep==99) )
     ||(bIsSearch && (iStep==30 || iStep==70))
     || sFrm.indexOf("wizard")!=-1 || sFrm.indexOf("if_")==0 || sFrm.indexOf("ifa_")==0
     || sFrm.indexOf("annuaire")==0 || sFrm.indexOf("task_contenu")!=-1
     ) {
    return;
  }
  if (bIsEdit && iStep==25) {
    p_oNav.m_iStep = 10;
    p_oNav.m_bValid = false; // l'étape sera à compléter avec l'id de l'entité
  }
  var moStack = ODYNavigation.prototype.stack;
  moStack.push(p_oNav);
  // si la pile a atteint sa taille maxi, purge le premier item
  if (moStack.length > ODYNavigation.maxHistory) moStack.splice(0,1);
}

//------------- setIdOfLastEntity ------------------
ODYNavigation.setIdOfLastEntity = function (p_iId, p_iETId) {
  var moStack = ODYNavigation.prototype.stack;
  var oEditionNav = moStack[moStack.length-1];
  if (oEditionNav.m_bValid) return;
  oEditionNav.m_sUrl += "&eid=" + p_iId + "&etid=" + p_iETId;
  oEditionNav.m_bValid = true;
}

//------------- contextBack ------------------
/** Retourne au contexte précédent
 * @param p_bIsSaveAction indique que la navigation se fait suite à un enregistrement d'entité
 */
ODYNavigation.contextBack = function(p_bIsSaveAction) {
  var moStack = ODYNavigation.prototype.stack;

  var oCurNav    = moStack.pop();
  var bIsEdit   = oCurNav.m_sUrl.indexOf("/edit.svt")==0;
  var bIsSearch = oCurNav.m_sUrl.indexOf("/query.svt")==0;
  var iStep = oCurNav.m_iStep;
  var sFrm = oCurNav.m_oTargetContainer.id;
  // si enregistrement suite clonage, retour au contexte avant l'entité qui a été clonée
  if (p_bIsSaveAction && bIsEdit && 95==iStep) moStack.pop();

  var oTargetNav = moStack[moStack.length-1];
  while (!oTargetNav || !oTargetNav.m_bValid) {
    if (!oTargetNav) { // si CurNav était le premier élément => retour à la home
      oTargetNav = new ODYNavigation();
      oTargetNav.m_oTargetContainer = oCurNav.m_oTargetContainer;
      oTargetNav.m_iStep = 0;
      oTargetNav.m_sUrl = sAccueil;
      break;
    }
    moStack.pop();
    oTargetNav = moStack[moStack.length-1];
  }
  // si les container sont différents, il suffit de masquer l'actuel
  var oCurCont    = oCurNav.m_oTargetContainer;
  var oTargetCont = oTargetNav.m_oTargetContainer;
  ODYNavigation.record(oTargetNav, true);
  if (oCurCont==oTargetCont) return oTargetNav.display();
  else oCurNav.m_oTargetContainer.hide();
  return oTargetNav;
}

//------------- findContextByContainer ------------------
/** Retrouve la navigation qui a eu lieu dans ce container */
ODYNavigation.findContextByContainer = function(p_oCont) {
  var moStack = ODYNavigation.prototype.stack;
  for (var i=moStack.length-1; i>=0 ; i--) {
    if (moStack[i].m_oTargetContainer==p_oCont) return moStack[i];
  }
}

//------------- back ------------------
/**
 * @param p_iStepBack entier >0 indiquant le nbre d'étapes à franchir en arrière.
 * Si null, 1 est pris par défaut
 */
ODYNavigation.back = function(p_iStepBack) {
  var oCurNav    = ODYNavigation.prototype.history.pop();
  if (!oCurNav) {
    history.back(); return ;
  }
  var iStepBack = p_iStepBack ? p_iStepBack : 1;
  for (var i=0; i<iStepBack;i++) var oTargetNav = ODYNavigation.prototype.history.pop();
  // si les containers sont différents, il suffit de masquer l'actuel
  var oCurCont    = oCurNav.m_oTargetContainer;
  var oTargetCont = oTargetNav.m_oTargetContainer;
  if (oCurCont==oTargetCont) return oTargetNav.display();
  else {
    oCurNav.m_oTargetContainer.hide();
  }
  return oTargetNav;
}

//------------- getHistory ------------------
ODYNavigation.getHistory = function(p_iStepBack) {
  if (!ODYNavigation.prototype.history || ODYNavigation.prototype.history.length<p_iStepBack) return;
  return ODYNavigation.prototype.history[ODYNavigation.prototype.history.length-p_iStepBack];
}

//************************* INSTANCE ********************************
ODYNavigation.prototype= {
   history: []
  ,stack: []
  ,display: function() {
    display(this.m_oTargetContainer, this.m_iStep, this.m_sUrl, this.m_sSubmit);
    return this.m_oTargetContainer;
  }
}

// =========================================================================
// @class
// ODYContPosition - Information de situation et taille d'un container dans Odyssée
//
// @param 
// : p_iLeft  coordonnée x
// : p_iTop  coordonnée y
// : p_iWidth  largeur
// : p_iHeight  hauteur
// =========================================================================

ODYContPosition = function(p_iLeft, p_iTop, p_iWidth, p_iHeight) {
  this.m_iLeft   = p_iLeft   && typeof(p_iLeft)  !="number" ? p_iLeft.replace(/px/,"") : p_iLeft;
  this.m_iTop    = p_iTop    && typeof(p_iTop)   !="number" ? p_iTop.replace(/px/,"") : p_iTop;
  this.m_iWidth  = p_iWidth  && typeof(p_iWidth) !="number" ? p_iWidth.replace(/px/,"") : p_iWidth;
  this.m_iHeight = p_iHeight && typeof(p_iHeight)!="number" ? p_iHeight.replace(/px/,"") : p_iHeight;
}

ODYContPosition.HIDDEN = new ODYContPosition(0,0,0,0);

ODYContPosition.prototype.apply = function(p_oDOMElt) {
  p_oDOMElt.style.width  = this.m_iWidth + "px";
  p_oDOMElt.style.height = this.m_iHeight + "px";
  p_oDOMElt.style.left   = this.m_iLeft + "px";
  p_oDOMElt.style.top    = this.m_iTop + "px";
}

// =========================================================================
// @class
// ODYCont - Container (div) dans le framework Odyssee
// =========================================================================

var ODYCont = new Object();

ODYCont.sATTR_CONT   = "container";
ODYCont.sATTR_LOADED = "bLoaded";

//--------------  create ----------------
/**
 * @return une div ayant l'id indiqué et initialisée comme container 
 */
ODYCont.create = function(p_sId) {
  var oDiv = document.createElement("div");
  ODYCont.makeCont(oDiv);
  oDiv.id   = p_sId;
  oDiv.name = p_sId;
  return oDiv;
}

//--------------  makeCont ----------------
/** marque la div indiquée comme étant un container, c'est à dire l'enrichit de ODYContUtils */
ODYCont.makeCont = function(oDiv) {
  Object.extend(oDiv, ODYContUtils);
  return oDiv;
}

//--------------  isContainer ----------------
ODYCont.isContainer = function(p_oDOMCont) {
  if (p_oDOMCont.m_bContainer) return true;
  if (p_oDOMCont.tagName=="BODY") return true;
  if (p_oDOMCont.getAttribute && p_oDOMCont.getAttribute(ODYCont.sATTR_CONT)=="true") return true;
  return false;
}

//--------------  findContainer ----------------
/**
 * Cherche le premier parent qui est soit un document soit une DIV avec l'attribut "container" à "true"
 * @param p_oNode le node racine de la recherche
 * @return l'objet DOM container de p_oNode qui est soit un BODY soit un DIV
 */
ODYCont.findContainer = ODYCont.findContainerOfElement = function(p_oNode) {
  var oCont = p_oNode;
  // si le node de base est un body, html ou doc, la recherche commence à la window qui les contient, sinon la fonction 
  // retournerait l'élément p_oNode lui-même
  if (oCont.tagName=="HTML" || oCont.tagName=="BODY") oCont = oCont.ownerDocument;
  if (oCont.nodeName=="#document") oCont = (null != oCont.defaultView) ? oCont.defaultView : oCont.parentWindow ;
  // si window, pointe sur le document de la window parente si elle existe, null sinon
  if (oCont.parent) {
    if (oCont.parent!=oCont) return $(oCont.parent.document.body);
    else return null;
  }
  while (oCont) {
    oCont = findParentByTagName(oCont,"DIV|BODY")
    if (!oCont) return;
    if (oCont.documentElement) return $(oCont.body); // si le node est le document, le body est en dessous !
    if (oCont.tagName=="HTML") return $(oCont.lastChild);
    if (ODYCont.isContainer(oCont)) return $(oCont);
  }
  return null;
}


//--------------  findContainerByTs ----------------
/** @return le container qui a le timestamp indiqué  */
ODYCont.findContainerByTs = function(p_iTS) {
  var oTs =  $O2(p_iTS);
  if (!oTs) return null;
  return oTs.getContainer();
}

/**************************************/
/*              ODYContUtils             */
/**************************************/

var ODYContUtils = {
  m_bContainer: true,
//  m_bVisible: true,
  getParentContainer: function() {
    return ODYCont.findContainer(this);
  },

  hide: function() {
    // this.style.visibility = "hidden";
    this.style.display = "none";
    var moConts = this.findChildContainerByPattern(".*");
    for (var i = 0, l = moConts.length; i < l; i++) {
      var oDiv = moConts[i];
      if (oDiv.id && oDiv.isContainer()) oDiv.style.visibility = "";
    }
  },
  showContainer: function(p_sStyle, p_iZIndex) {
    var sStyle = p_sStyle ? p_sStyle : 'block';
    this.style.visibility = "visible";
    this.style.display = sStyle;

    var moConts = this.findChildContainerByPattern(".*");
    for (var i = 0, l = moConts.length; i < l; i++) {
      var oDiv = moConts[i];
      if (oDiv.id && oDiv.isContainer() && oDiv.id!="alerte" && !oDiv.id.match(/calt_.*/)) oDiv.show();
    }
  },
  minimize: function() {
    this.m_oMinimized.apply(this);
    this.m_bMinimized = true;
  },
  /**
   * @param p_oDOMViewport  l'objet DOM qui correspond à la taille que doit avoir le container maximisé 
   */
  initMaxSize: function(p_oDOMViewport) {
    var sPosiStyle = p_oDOMViewport.getStyle("position");
    this.m_oMaximized = new ODYContPosition(
        sPosiStyle=="absolute" ? 0 : p_oDOMViewport.offsetLeft
      , sPosiStyle=="absolute" ? 0 : getElementTop(p_oDOMViewport)
      , p_oDOMViewport.clientWidth-1
      , p_oDOMViewport.clientHeight
    );
  },
  maximize: function() {
    if (!this.m_oMaximized) this.initMaxSize(this.getParentContainer());
    this.m_oMaximized.apply(this);
    this.m_bMinimized = false;
  },
  isContainer: function() {
    return ODYCont.isContainer(this); 
  },

  /** retrun true si le flag "loaded" est true */
  isLoaded: function() {
    if (this.tagName=="DIV") return "true"==this.getAttribute(ODYCont.sATTR_LOADED);
    else return "true"==this.bLoaded;
  },

  setLoading: function(p_sMsg) {
    var oFondDiv = ODYForm.getFondGrisDiv(this);
    if (oFondDiv) oFondDiv.show();
    addCssStyle(this,"loading");
    this.style.cursor = "wait";
    if (this.tagName=="DIV") this.setAttribute(ODYCont.sATTR_LOADED, "false");
    else this.bLoaded = "false";
  },

  unsetLoading: function() {
    removeCssStyle(this,"loading");
    var oFondDiv = ODYForm.getFondGrisDiv(this);
    oFondDiv.hide();
    this.style.cursor = "auto";
    if (this.tagName=="DIV") this.removeAttribute(ODYCont.sATTR_LOADED);
    else delete this.bLoaded;
  },

  setLoaded: function() {
    removeCssStyle(this,"loading");
    this.style.cursor = "auto";
    if (this.tagName=="DIV") this.setAttribute(ODYCont.sATTR_LOADED, "true");
    else this.bLoaded = "true";
  },

  /** retrun true si le chargement a été demandé (c'est à dire que "bLoaded" est défini) mais pas terminé */
  isLoading: function() {
    if (this.tagName=="DIV") return "false"==this.getAttribute(ODYCont.sATTR_LOADED);
    else return "false"==this.bLoaded;
  },
  
  reload: function(p_iDelay) {
    var sCmd = this.getAttribute("reloadCmd");
    if (p_iDelay) window.setTimeout(sCmd, p_iDelay);
    else eval(sCmd);
  },

  //--------------  showLoading ----------------
  /** affiche la mire d'attente "loading" */
  showLoading: function() {
    this.setLoading();
    if ("edi"==this.id && !this.visible()) this.show();
    var sHTML = '<center style="position:relative;top:40%">En cours de chargement<br>'
      + '<img src="/com/img/progressbar_green.gif"></center>';
    // quick & dirty
    if (false) sHTML += '<div id="oClock"></div><script language="javascript">clock("",new Date());</script>';
    this.innerHTML = sHTML;
  },

  //--------------  findChildContainerByPattern ----------------
  findChildContainerByPattern: function(p_sPattern) {
    var reg = new RegExp(p_sPattern, "gi");
    var moDivs = this.getElementsByTagName("div")
    for (var i = 0, matchingDivs = [], length = moDivs.length; i < length; i++) {
      var oDiv = moDivs[i];
      if (oDiv.id && oDiv.m_bContainer && oDiv.id.match(reg)) matchingDivs.push(Element.extend(oDiv));
    }
    return matchingDivs;
  },

  //--------------  createSubContainer ----------------
  /** 
   * @param p_sLoadedState si null ou undefined, l'attribut ODYCont.sATTR_LOADED est supprimé de la div, sinon
   *                       il est positionné à la valeur p_sLoadedState
   * @return l'objet DOM créé */
  createSubContainer: function(p_sId, p_sCSS, p_sLoadedState, p_sStyle) {
    var oDiv = ODYCont.create(p_sId);
    if (p_sCSS) oDiv.className = p_sCSS;
    if (p_sStyle) oDiv.setStyle(p_sStyle);
    var zi = this.style.zIndex;
    if (zi) oDiv.style.zIndex = zi*1.0+5;
    if (p_sLoadedState) oDiv.setAttribute(ODYCont.sATTR_LOADED,p_sLoadedState);
    else oDiv.removeAttribute(ODYCont.sATTR_LOADED);
    if (this.contentDocument) this.contentDocument.body.appendChild(oDiv);
    else this.appendChild(oDiv);
    return oDiv;
  },
  
  //------------------ getDivFiche ------------------
  /** @param p_oCont le container dans lequel on souhaite la fiche  */
  getDivFiche: function() {
    var oDivFiche = this["fiche"];
    if (oDivFiche && oDivFiche.isValid()) return oDivFiche;
  
    oDivFiche = document.createElement("div");
    this["fiche"] = oDivFiche;
    var sTS = this["ts"];
    var sId = "fiche." + sTS;
    oDivFiche.setAttribute("class", "fiche");
    oDivFiche.setAttribute("id", sId);
    this.appendChild(oDivFiche);
    ODYCont.makeCont(oDivFiche);
    return oDivFiche;
  },

  //--------------  showFiche ----------------
  /**
   * @param p_iTS le timestamp de l'appelant
   */
  showFiche: function(p_sUrl, p_iStep) {
    var oFiche = this.getDivFiche();
    oFiche.show();
    oFiche.maximize();
    navigate(p_sUrl, p_iStep, oFiche);
  },
  
  //--------------  hideFiche ----------------
  hideFiche: function(p_bRefreshParent) {
    var oFiche = this.getDivFiche();
    oFiche.hide();
    if (p_bRefreshParent) {
      var sOwner = oFiche.getAttribute("owner");
      if (sOwner) { // si l'attribut est défini, la fiche a été appelée depuis un multi ==> refresh par appel de showList()
        if (oDoc.getElementById("pbShow" + sOwner)) oDoc.getElementById("pbShow" + sOwner).onclick();
  
      } else {
        this.reload();
      }
    }
    var oMainCont = this;
    var oFrmBut = oMainCont.findChildContainerByPattern("(.*)?but(.*)?")[0];
    if (oFrmBut) {
      if (!oFrmBut.visible()) oFrmBut.maximize();
      var oFrmTabs = oMainCont.findChildContainerByPattern("(.*)?tabs(.*)?|edit")[0];
      if (oFrmTabs && !oFrmTabs.visible() ) oFrmTabs.maximize();
    }
  }
}

function doBreak(p_oArg) {
  var a = p_oArg;
}

//--------------  clock ----------------
function clock(p_sClockId, p_iBaseTime) {
  var MyClock = new Date()-new Date(p_iBaseTime);
  var hours = MyClock.getHours();
  var minutes = MyClock.getMinutes();
  var seconds = MyClock.getSeconds();
  var sHour = hours + " h " + minutes +" : "+ seconds;
  document.getElementById(p_sClockId).innerHTML=sHour;
  setTimeout("clock('"+p_sClockId+"','"+p_iBaseTime+"')", 500)
}

// ----------- getScrollerWidth ----------
function getScrollerWidth() {
  if (iSCROLLER_WIDTH) return iSCROLLER_WIDTH;
  // Outer scrolling div
  var scr = document.createElement('div');
  scr.style.position = 'absolute';
  scr.style.top = '-1000px'; scr.style.left = '-1000px';
  scr.style.width = '100px'; scr.style.height = '50px';
  // Start with no scrollbar
  scr.style.overflow = 'hidden';

  // Inner content div
  var inn = document.createElement('div');
  inn.style.width = '100%'; inn.style.height = '200px';

  // Put the inner div in the scrolling div
  scr.appendChild(inn);
  // Append the scrolling div to the doc
  document.body.appendChild(scr);

  // Width of the inner div sans scrollbar
  var wNoScroll = inn.offsetWidth;
  // Add the scrollbar
  scr.style.overflow = 'auto';
  // Width of the inner div width scrollbar
  var wScroll = inn.offsetWidth;

  // Remove the scrolling div from the doc
  document.body.removeChild(document.body.lastChild);

  // Pixel width of the scroller
  iSCROLLER_WIDTH = (wNoScroll - wScroll); 
  return iSCROLLER_WIDTH;
}

// ----------- o2Cal ----------
/** associer un calendrier à un champ de saisie + affiche le calendrier  */
function o2Cal(p_oAnchor, p_sInputName) {
  var oInput = p_oAnchor.findBrotherById(p_sInputName);
  var oCal = getCalendar('cal'+p_sInputName);
  if (oCal.displayType=="date" || oCal.displayType=="week-end") oCal.select(oInput,'imgCal'+p_sInputName,'dd/MM/yyyy'); 
  else if (oCal.displayType=="month") oCal.select(oInput,'imgCal'+p_sInputName,'MM/yyyy'); 
  else oCal.showCalendar('imgCal'+p_sInputName);
}

// ----------- getCal ----------
/** @return l'objet PopupWindow associé au calendrier auquel l'élément p_oElt appartient */
function getCal(p_oElt) {
  return p_oElt.getContainer()['oCal'];
}

