/**
 * frmCheckRequired(formulaire, field_name, no_msg, field_label)
 *
 * frmSelect(formName)
 * frmUnselect(formName)
 * frmSubmit(formName, altAction)
 * frmReset(formName)
 * frmCheckSelectionAndSubmit(formName, altAction)
 *
 * frmShowHelp(formName, action, p0, context, p1, subject)
 * frmShowIndexAssist(formName, action, table, index, crit, dest)
 * frmSetIndexTerm(formName, frameName, inputName, value, separator)
 * frmAddFieldValue(formName, frameName, inputName, value, separator )
 * frmSetFieldValue(formName, frameName, inputName, value )
 *
 * frmActionConfirm(formName, altAction, p0, actionName, pn, vn)
 * frmActionCancel(formName, altAction, p0, actionName)
 * frmActionExecute(formName, altAction, p0, actionName)
 * frmActionExecuteOnly(formName, altAction, p0, actionName, pn, vn)
 * frmActionReturn(formName, altAction, p0, actionName)
 */

var MULTI_VALUE_SEP = "\037";
var isNetscape4x = ((navigator.appName=="Netscape")&&(parseFloat(navigator.appVersion)< 5.0));

var form_version = "1.2 (RC8)";


//Ouverture d'une nouvelle fenêtre pour l'export iso
function frmExportIso(formName, action, p0, recordId, p1)
{
   var finalAction = action + "?recordId=" + recordId;
   var h        = 300;
   var w        = 300;
   var top_pos  = 0;
   var left_pos = 0;

   // Ouvre la fenêtre
   var appName = getAppName();
   var win = window.open(finalAction, "win_export_iso"+appName,
   "toolbar=0,location=0,directories=0,menuBar=0,scrollbars=yes,resizable=yes,status=yes" +
   ",width=" + w + ",height=" + h +
   ",top=" + top_pos + ",left=" + left_pos);
   win.focus();   
}


/**
 * Vérifie qu'un champs est renseigné
 */
function frmCheckRequired(formulaire, field_name, no_msg, field_label)
{
   var fld_msg = field_name;
   if ( field_label != null )
   {
      fld_msg = field_label;
   }
   // On parcourt tous les champs et on teste tous les
   // champs portant le même nom
   for (var i=0;i<formulaire.elements.length;i++)
   {
      var elt = formulaire.elements[i];
      var elt_type = elt.type;
      if (elt.name == field_name)
      {
         if ( (elt_type == 'text') || (elt_type == 'hidden') || (elt_type == 'password'))
         {
            if ((elt.value == null) || (elt.value == ""))
            {
               if ( ! no_msg )
               {
                  alert(mess_oblig + " " + fld_msg);
                  // sinon message d'erreur
                  if ( elt_type != 'hidden' ) { elt.focus(); }
               }
               return false;
            }
         }
         else if ( (elt_type == 'checkbox') || (elt_type == 'radio') )
         {
            if ( ! elt.checked)
            {
               if ( ! no_msg)
               {
                  alert(mess_oblig + " " + fld_msg);
                  elt.focus();
               }
               return false;
            }
         }
         else if ( (elt_type == 'select-one') || (elt_type == 'select-multiple') )
         {
            var has_one_selected = false;
            for (var j=0; (j< elt.options.length) && (!has_one_selected) ; j++)
            {
               has_one_selected = elt.options[j].selected;
            }
            if (! has_one_selected )
            {
               if ( ! no_msg)
               {
                  alert(mess_oblig + " " + fld_msg);
                  elt.focus();
               }
               return false;
            }
         }
      }
   }
   return true;
}
/**
 * Si tous les champs oblig sont OK, soumet le formulaire ou appelle une
 * - obligList = liste des champs à contrôler séparé par une virgule,
 *   un champ pouvant être de la forme a|b|c , qui signifie que un
 *   des champs cités doit être renseigné.
 * - labelList = libellés des champs séparés par une virgule.
 * - eval_call = éventuellement la fonction à appeler en cas de succès. Si non
 *   présent, on fait un submit du formulaire.
 */
function frmValidateForm(frm, obligList, labelList, eval_call, doNotSubmit)
{ 
   var flds = obligList.split(",");
   var labels = labelList.split(",");
   for (var i=0; i<flds.length; i++)
   {
      var or_fields = flds[i].split("|");
      if ( or_fields.length > 1 )
      {
         var one_is_ok = false;
         for (var j=0; ((j<or_fields.length) && ! one_is_ok); j++ )
         {
            if ( frmCheckRequired(frm, or_fields[j], 1 ) )
            {
               one_is_ok = true;
            }
         }
         if ( ! one_is_ok )
         {
            alert(mess_oblig + " " + labels[i]);
            frm.elements[or_fields[0]].focus();
            return false;
         }
      }
      else if ( ! frmCheckRequired(frm, flds[i], 0, labels[i] )  )
      {
            frm.elements[flds[i]].focus();
            return false;
      }
   }
   if ( eval_call )
      eval(eval_call);
   else if ( doNotSubmit == true )
   {
      return true;
   }
   mySubmit(frm);
}

/**
 * Fonctions de sélection des checkbox.
 * Par défaut, on recherche ceux qui se nommenet "record".
 * Il est possible de spécifier un autre nom.
 */
function frmSelect(formName, unused, p0, checkName)
{
   var frm = document.forms[formName];
   if ( ! checkName || (checkName == '' ))
      checkName = "record";
   if ( frm != null )
   {
      for (var i=0;i<frm.elements.length; i++)
      {
         var elem = frm.elements[i];
         if ( (elem.type=="checkbox") && ( elem.name==checkName))
         {
            elem.checked = true;
         }
      }
   }
   else
      alert(mess_form_not_found + formName);
}
function frmUnselect(formName, unused, p0, checkName)
{
   var frm = document.forms[formName];
   if ( ! checkName || (checkName == '' ))
      checkName = "record";
   if ( frm != null )
   {
      for (var i=0;i<frm.elements.length; i++)
      {
         var elem = frm.elements[i];
         if ( (elem.type=="checkbox") && ( elem.name==checkName))
         {
            elem.checked = false;
         }
      }
   }
   else
      alert(mess_form_not_found + formName);
}

function frmSubmit(formName, action, pn, vn)
{
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // Complète les paramètres de l'action
      var moreParams = frmGetMoreParams(frmSubmit.arguments);
      action = frmCompleteAction(frm, action, moreParams);
      // Change la cible du formulaire si le paramètre 'target' existe
      var target = frmGetParam(moreParams, "target");

      mySubmit(frm, action, target);
   }
   else
      alert(mess_form_not_found + formName);
}

function frmReset(formName)
{
   var frm = document.forms[formName];
   if ( frm != null )
      frm.reset();
   else
      alert(mess_form_not_found + formName);
}

function countSelection(frm)
{
   var count = 0;
   if ( frm != null )
   {
      for (var i=0;i<frm.elements.length; i++)
      {
         var elem = frm.elements[i];
         if ( elem.type=='checkbox')
         {
            if ( elem.checked )
               count ++;
         }
      }
   }
   return count;
}

// Fonction de contrôle, doit renvoyer true ou false
function frmCheckSelection(formName)
{
   var frm = document.forms[formName];
   if ( frm != null )
   {
      var count = countSelection(frm);
      if ( count < 1 )
         alert(mess_selection_empty);
      else
         return true;
   }
   else
      alert(mess_form_not_found + formName);
   return false;
}

// action : action alternative si spécifié
function frmCheckSelectionAndSubmit(formName, action, pn, vn)
{
   var frm = document.forms[formName];
   if ( frm != null )
   {
      var count = countSelection(frm);
      if ( count < 1 )
         alert(mess_selection_empty);
      else
      {
         // Complète les paramètres de l'action
         var moreParams = frmGetMoreParams(frmCheckSelectionAndSubmit.arguments);
         action = frmCompleteAction(frm, action, moreParams);
         // Change la cible du formulaire si le paramètre 'target' existe
         var target = frmGetParam(moreParams, "target");
         mySubmit(frm, action, target);
      }
   }
   else
      alert(mess_form_not_found + formName);
}
// Les fonctions de soumission qui modifient les attributs par défaut des
// formulaires doivent les remettre sinon s'il y a plusieurs boutons dans une
// même page, celui utilisant les attributs par défaut ne fonctionnera plus
// correctement une fois que le second utilisant les attributs alternatifs a
// été activé
function frmSaveAttributes(frm)
{
   var attr = new Array();
   attr[attr.length] = frm.target;
   attr[attr.length] = frm.action;
   return attr;
}
function frmRestoreAttributes(frm, attr)
{
   frm.target = attr[0];
   frm.action = attr[1];
}

// Privé
// Renvoie un tableau des paramètres (nom,valeur) si args en contient plus de
// 2 valeurs, sinon renvoie null
// - args : paramètres d'une fonction frm(formName, altAction)
// - startIndex : index dans le tableau du premier nom. Par défaut, 2
function frmGetMoreParams(args, startIndex)
{
   if ( ! args ) return null;
   var moreParams = new Array();
   if ( ! startIndex || (startIndex <= 0) )
      startIndex = 2;
   for (var i=startIndex; i<args.length; i+=2)
   {
      if ( args[i] && (args[i] != ""))
      {
         moreParams[moreParams.length] = args[i];
         moreParams[moreParams.length] = args[i+1];
      }
   }
   if ( moreParams.length == 0)
      moreParams = null;
   return moreParams;
}
// Ajoute des paramètres cgi à l'action
// Renvoie l'action telle quelle si moreParams est vide
function frmCompleteAction(frm, action, moreParams)
{
   if ( !action || (action == "") )
   {
      if ( frm ) action = frm.action;
   }
   if ( action && (action !="") )
   {
      if ( moreParams )
      {
         if ( action.indexOf("?") == -1 )
            action += "?";
         else
            action += "&";
         for (var i=0; i<moreParams.length; i+=2)
         {
            if ( i > 0 )
               action += "&";
            action += moreParams[i] + "=" + myEscape(moreParams[i+1]);
         }
      }
   }
   return action;
}
// Renvoie la valeur du paramètre s'il existe dans le tableau, null sinon
function frmGetParam(moreParams, paramName)
{
   if ( ! moreParams ) return null;
   for (var i=0; i<moreParams.length; i+=2)
   {
      if (moreParams[i] == paramName )
      {
         return moreParams[i+1];
      }
   }
   return null;
}
/**
 * Appel de la fenêtre d'aide
 */
function frmShowHelp(formName, action, p0, context, p1, subject)
{
   // Paramètre la fenêtre
   // TODO : Lire dans la config taille et position
   var cgi = action;
   if ( cgi.lastIndexOf("?") < 0)
      cgi += "?";
   else
      cgi += "&";
   cgi += "context=" + myEscape(context)
       + "&subject=" + myEscape(subject);
   var h        = 400;
   var w        = 500;
   var top_pos  = 400;
   var left_pos = 400;

   // Ouvre la fenêtre
   var appName = getAppName();
   var win = window.open(cgi, "win_help"+appName,
  "toolbar=0,location=0,directories=0,menuBar=0,scrollbars=yes,resizable=yes,status=yes" +
  ",width=" + w + ",height=" + h +
  ",top=" + top_pos + ",left=" + left_pos);
   win.focus();
}

/**
 * Appel de l'assistant sur index
 * table : nom de la table ou nom du champ contenant la valeur
 * index : nom de l'index  ou nom du champ contenant la valeur
 * crit  : critère ou nom du champ contenant la valeur
 * dest  : nom du champ qui contient la valeur rapatriée (nb :la frame destination
 *    est la frame qui contient ce script).
 */
function frmShowIndexAssist(formName, action, p0, table, p1, index, p2, crit, p3, dest)
{
   var frame = this.name;

   var the_form = document.forms[formName];
   if ( the_form == null )
   {
      alert(mess_form_not_found + formName);
      return;
   }

   var tableName = getFieldValue(the_form, table, table);
   var indexName = getFieldValue(the_form, index, index);
   var critere   = getFieldValue(the_form, crit, '');

   // on teste que les paramètres sont correctement spécifiés
   if (critere == '')
   {
      alert (mess_field_empty + crit);
      if ( the_form.elements[crit] != null )
      {
         the_form.elements[crit].focus();
      }
      return;
   }
   if (tableName == ''){alert (mess_param_empty + table) ; return;}
   if (indexName == ''){alert (mess_param_empty + index) ; return;}

   // Paramètre la fenêtre
   // TODO : Lire de la config taille et position
   var cgi = action;
   if ( cgi.lastIndexOf("?") < 0)
      cgi += "?";
   else
      cgi += "&";
   cgi += "tableName=" + myEscape(tableName)
       + "&indexName=" + myEscape(indexName)
       + "&criteria=" + myEscape(critere.getLastWordToken())
       + "&targetFrame=" + myEscape(frame)
       + "&targetInput=" + myEscape(dest);
   var h        = 450;
   var w        = 350;
   var top_pos  = 400;
   var left_pos = 400;

   // Ouvre la fenêtre avec un nom unique par application
   var appName = getAppName();
   var win = window.open(cgi, "win_index" + appName,
  "toolbar=0,location=0,directories=0,menuBar=0,scrollbars=yes,resizable=yes" +
  ",width=" + w + ",height=" + h +
  ",top=" + top_pos + ",left=" + left_pos);
   win.focus();
}
/**
 * Appel de l'assistant thesaurus
 * table = nom de la table source ou nom du champ contenant la valeur
 * thes  = nom de la table THESAURUS ou nom du champ contenant la valeur
 * crit = critère ou nom du champ contenant le critère
 * crit_dest = nom du champ où on rapatrie les termes (nb :la frame destination
 *    est la frame qui contient ce script).
 * index_dest = nom du champ où on rapatrie le nom de l'index thesaurus qui est
 * de la forme "table->thes"
 */
function frmShowThesaurusAssist(formName, action, p0, table, p1, thes, p2, crit, p3, crit_dest, p4, index_dest)
{
   var frame = this.name;

   var the_form = document.forms[formName];
   if ( the_form == null )
   {
      alert(mess_form_not_found + formName);
      return;
   }

   var tableName = getFieldValue(the_form, table, table);
   var critere   = getFieldValue(the_form, crit, '');
   var thesName  = getFieldValue(the_form, thes, thes);

   // on teste que les paramètres sont correctement spécifiés
   if (critere == '')
   {
      alert (mess_field_empty + crit);
      if ( the_form.elements[crit] != null )
      {
         the_form.elements[crit].focus();
      }
      return;
   }
   if (tableName == ''){alert (mess_param_empty + table) ; return;}
   if (thesName == ''){alert (mess_param_empty + thes) ; return;}

   // Paramètre la fenêtre
   // TODO : Lire de la config taille et position
   var cgi = action;
   if ( cgi.lastIndexOf("?") < 0)
      cgi += "?";
   else
      cgi += "&";
   // BUG JS : pourquoi le signe plus n'est pas encodé par escape ?
   cgi += "tableName=" + myEscape(tableName)
       + "&thesaurusName=" + myEscape(thesName)
       + "&criteria=" + myEscape(critere.getLastWordToken())
       + "&targetFrame=" + myEscape(frame)
       + "&targetInput=" + myEscape(crit_dest)
       + "&targetIndex=" + myEscape(index_dest);
   var h        = 450;
   var w        = 350;
   var top_pos  = 400;
   var left_pos = 400;

   // Ouvre la fenêtre avec un nom unique par application
   var appName = getAppName();
   var win = window.open(cgi, "win_thesaurus" + appName,
  "toolbar=0,location=0,directories=0,menuBar=0,scrollbars=yes,resizable=yes" +
  ",width=" + w + ",height=" + h +
  ",top=" + top_pos + ",left=" + left_pos);
   win.focus();
}
/**
 * Change le terme courant du thesaurus
 *
 * inputName : nom du champ contenant la clé du terme courant
 * termName : nom du champ recevant la clé du terme courant
 */
function frmThesaurusSetTerm(formName, action, p0, inputName, p1, termName)
{
   var the_form = document.forms[formName];
   var new_term = getFieldValue(the_form, inputName, '');
   var old_term = getFieldValue(the_form, termName, '');
   if ( (new_term != '') && ( new_term != old_term ))
   {
      setFieldValue(the_form, termName, new_term);
      mySubmit(the_form);
   }
}
/**
 * Recopie le terme courant de l'assistant dans le formulaire de recherche.
 *
 * termName : nom du champ contenant la valeur chaîne du terme
 * termValue : valeur du terme, si vide, on cherche dans termName
 * optionName : nom du champ contenant la valeur de l'option qui peut être
 * optionValue : valeur de l'option, si vide, on cherche dans optionName
 * indexName : nom du champ contenant la valeur de l'index
 * closeWindow : si non vide, on ferme la fenêtre après la recopie
 * vide, ">" ou "+>"
 */
function frmThesaurusRetrieveTerm(formName, action, termName, termValue, p1, frameName,
   p2, inputName, optionName, optionValue, p4, indexName, p5, indexValue,
   p6, closeWindow)
{
   // separator : séparateur à ajouter avant dans le champ destinataire
   var separator = " ";
   var targetInput = findField(frameName, inputName);
   if ( targetInput == null )
   {
      alert(mess_field_not_found + inputName);
      return;
   }
   if ( targetInput.type != "text" )
   {
      alert(mess_wrong_field_type + inputName);
      return;
   }
   // Cherche la valeur des champs
   var the_form = document.forms[formName];
   if ( (termValue == null) || (termValue == '') || (termValue == "empty"))
      termValue = getFieldValue(the_form, termName, '');

   if ( (optionValue == null) || (optionValue == '') || (optionValue == "empty"))
      optionValue = getFieldValue(the_form, optionName, '');
   termValue += optionValue;

   // Ajoute le terme ou remplace le terme courant du critère
   targetInput.value = targetInput.value.setOrAddCriteria(termValue, ' ');

   // positionne l'index thesaurus
   if ( indexName != '' )
   {
      var indexInput = findField(frameName, indexName);
      if ( indexInput == null )
      {
         alert(mess_field_not_found + indexName);
         return;
      }
      setFieldValue(indexInput.form, indexName, indexValue);
   }

   if ( closeWindow != '')
      window.close();
}
/**
 * Complète le champ nommé avec la valeur précédée éventuellement du séparateur
 * Ne fonctionne que si inputName désigne un champ de type texte
 * Sert au rapatriement d'un terme d'index de l'assistant
 */
function frmSetIndexTerm(formName, altAction, p0, frameName, p1, inputName, p2, value, p3, separator)
{
   var targetInput = findField(frameName, inputName);
   if ( targetInput == null )
   {
      alert(mess_field_not_found + inputName);
      return;
   }
   if ( targetInput.type != "text" )
   {
      alert(mess_wrong_field_type + inputName);
      return;
   }
   targetInput.value = targetInput.value.setOrAddCriteria(value, separator);
}
 
// Ajoute ou remplace un terme à la fin d'une chaîne de critère
// Renvoie la nouvelle valeur   
String.prototype.setOrAddCriteria = function(value, separator)
{   
   var endsWithSep = (this.charAt(this.length-1) == separator);
   // Masque les cotes internes
   value = value.mask('"');
   // Masque les * pour ne pas les remplacer par %
   value = value.mask('*');
   // Ajoute des cotes au début et à la fin
   value = value.quotes();
   // Remplace _quote_ par \"
   value = value.unmaskQuoteKeyWord();
   
   var newValue = value;
   // On remplace le dernier 'mot' de la chaîne par la valeur passée
   var tokens = this.getWordTokens(true /* ne pas ajouter le dernier " manquant */);
   if ( tokens != null )
   {
      newValue = "";
      for(var i=0; i<tokens.length-1; i++)
         newValue += tokens[i].quotes() + separator;
      var lastToken = tokens[tokens.length-1];
      // Test pour savoir si c'est un remplacement ou un ajout
      // abc_   : ajoute
      // "abc"_ : ajoute
      // "abc   : remplace
      // "abc"  : remplace
      // "abc_  : remplace
      // abc    : remplace
      var doReplace = true;
      if ( endsWithSep )
      {
         doReplace = ( (lastToken.charAt(0) == '"') &&  
              (lastToken.charAt(lastToken.length-1) != '"') );
      }
      // Ne pas remplacer la dernière ?
      if ( doReplace == false )
         newValue += lastToken.quotes() + separator;
      // Ajoute nouvelle valeur
      newValue += value + separator;
   }
   return newValue;
}

/**
 * Ajoute une valeur à un champ. Si le champ était vide le séparateur ne
 * sert à rien. Sinon, si c'est un champ texte, ajoute le séparateur d'abord.
 * Dans le cas d'un champ non texte, le séparateur ne sert à rien.
 */
function frmAddFieldValue(formName, action, p0, frameName, p1, inputName, p2, value, p3, separator )
{
   var targetInput = findField(frameName, inputName);
   if ( targetInput == null )
   {
      alert(mess_field_not_found + inputName);
      return;
   }
   setOrAddFieldValue(targetInput.form, inputName, value, separator);

}
/**
 * Positionne la valeur d'un champ se trouvant dans une frame.
 * On parcourt tous les formulaires de la frame pour trouver le champ.
 */
function frmSetFieldValue(formName, action, p0, frameName, p1, inputName, p2, value )
{
   var targetInput = findField(frameName, inputName);
   if ( targetInput == null )
   {
      alert(mess_field_not_found + inputName);
      return;
   }
   setOrAddFieldValue(targetInput.form, inputName, value, "");
}

/**
 * Les fonction frmActionXXX gère l'enchaînement des actions nécessitant un 
 * message de confirmation, avec possibilité d'annulation ou non.
 * Si le nom du formulaire n'est pas spécifié, utilise le formulaire 
 * sysActionForm inclus automatiquement par tagext:init.
 */
function frmActionConfirm(formName, action, p0, actionName, pn, vn)
{
   // appelle l'action manager lui passant la href de la fenêtre
   // servlet/ActionFlowManager?do=confirm&referer=...&
   //    action=hold_cancel&...
   if ( formName == "" )
      formName = "sysActionForm";
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // Complète les paramètres de l'action
      var moreParams = frmGetMoreParams(frmActionConfirm.arguments, 4);
      action = frmCompleteAction(frm, action, moreParams);
      // Paramètre du manager
      var referer = window.location.href;
      action += "&do=confirm";
      action += "&referer=" + myEscape(referer);
      action += "&action=" + myEscape(actionName);

      // Appel de la fonction onSubmit s'il y en a une
      mySubmit(frm, action);
   }
   else
   {
      alert(mess_form_not_found + formName);
   }
}
/**
 * Exécute l'action sans confirmation
 */
function frmActionExecuteOnly(formName, action, p0, actionName, pn, vn)
{
   if ( formName == "" )
      formName = "sysActionForm";
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // Complète les paramètres de l'action
      var moreParams = frmGetMoreParams(frmActionExecuteOnly.arguments, 4);
      action = frmCompleteAction(frm, action, moreParams);
      // Paramètre du manager
      var referer = window.location.href;
      action += "&do=executeOnly";
      action += "&referer=" + myEscape(referer);
      action += "&action=" + myEscape(actionName);

      // Appel de la fonction onSubmit s'il y en a une
      mySubmit(frm, action);
   }
   else
   {
      alert(mess_form_not_found + formName);
   }
}

/**
 * Réinitialise l'action en cours et revient au point initial
 */
function frmActionCancel(formName, altAction, p0, actionName)
{
   if ( formName == "" )
      formName = "sysActionForm";
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // servlet/ActionFlowManager?do=cancel&action=hold_cancel
      var frmAction = ( altAction != "" ) ? altAction : frm.action;
      if ( frmAction.lastIndexOf("?") < 0)
         frmAction += "?";
      else
         frmAction += "&";
      frmAction += "do=cancel";
      frmAction += "&action=" + myEscape(actionName);

      mySubmit(frm, frmAction);
   }
   else
   {
      alert(mess_form_not_found + formName);
   }
}
function frmActionExecute(formName, altAction, p0, actionName)
{
   if ( formName == "" )
      formName = "sysActionForm";
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // servlet/ActionFlowManager?do=execute&action=hold_cancel
      var frmAction = ( altAction != "" ) ? altAction : frm.action;
      if ( frmAction.lastIndexOf("?") < 0)
         frmAction += "?";
      else
         frmAction += "&";
      frmAction += "do=execute";
      frmAction += "&action=" + myEscape(actionName);

      mySubmit(frm, frmAction);
   }
   else
   {
      alert(mess_form_not_found + formName);
   }
}
/**
 * Revient au point initial
 */
function frmActionReturn(formName, altAction, p0, actionName)
{
   if ( formName == "" )
      formName = "sysActionForm";
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // servlet/ActionFlowManager?do=return&action=hold_cancel
      var frmAction = ( altAction != "" ) ? altAction : frm.action;
      if ( frmAction.lastIndexOf("?") < 0)
         frmAction += "?";
      else
         frmAction += "&";
      frmAction += "do=return";
      frmAction += "&action=" + myEscape(actionName);

      mySubmit(frm, frmAction);
   }
   else
   {
      alert(mess_form_not_found + formName);
   }
}
/**
 * Exécute l'action et revient au point initial. Permet d'exécuter une
 * action sans passer par un formulaire.
 * Cette fonction est conservée pour compatibilité avec les versions précédentes
 * Il vaut mieux la remplacer maintenant par frmActionExecuteOnly qui utilise
 * le formulaire système lorqu'aucun n'est spécifié.
 */
function frmActionDirectExecute(empty, action, p0, actionName, pn, vn)
{
   // Complète les paramètres de l'action
   var moreParams = frmGetMoreParams(frmActionDirectExecute.arguments, 4);
   action = frmCompleteAction(null, action, moreParams);
   // Paramètre du manager
   var referer = window.location.href;
   action += "&do=executeOnly";
   action += "&referer=" + myEscape(referer);
   action += "&action=" + myEscape(actionName);

   // La target est forcément la window courante
   // TODO : est-ce suffisant dans tous les cas ?
   window.location.href = action;
}
/**
 * Forward les paramètres déjà mémorisées pour une action à une autre action.
 */
function frmActionForward(formName, action, p0, actionName, p1, toAction, pn, vn)
{
   if ( formName == "" )
      formName = "sysActionForm";
   var frm = document.forms[formName];
   if ( frm != null )
   {
      // Complète les paramètres de l'action
      var moreParams = frmGetMoreParams(frmActionForward.arguments, 6);
      action = frmCompleteAction(frm, action, moreParams);
      // Paramètre du manager
      var referer = window.location.href;
      action += "&do=forward";
      action += "&referer=" + myEscape(referer);
      action += "&action=" + myEscape(actionName);
      action += "&to=" + myEscape(toAction);

      // Appel de la fonction onSubmit s'il y en a une
      mySubmit(frm, action);
   }
   else
   {
      alert(mess_form_not_found + formName);
   }
}
/*
* Soumet un formulaire Z3950.
* S'il n'y a qu'une source, fait juste un submit
* S'il y a plusieurs sources sélectionnées, construit l'URL pour chaque
* source et redirige vers la page qui va créer les frames
*/
function frmSubmitZ3950(frm, sourceInputName, dispatchAction)
{
   var srce = getFieldValue(frm, sourceInputName, "");
   if ( srce == "" )
   {
      alert(mess_select_z_source);
      return false;
   }
   var sources = srce.split("\037");
   // Si une seule source, submit normal
   if ( sources.length == 1 ) return true;
   // Sinon sauve l'ancienne action et la passe dans un paramètre
   var oldAction = frm.action;
   if ( dispatchAction.indexOf("?") == -1 )
      dispatchAction += "?";
   else
      dispatchAction += "&";
   dispatchAction += "queryAction=" + myEscape(oldAction);
   dispatchAction += "&sourceInput=" + sourceInputName;
   //alert(dispatchAction);
   frm.action = dispatchAction;
    
   return true;
   
}

// Privé
// Teste si le formulaire a une fonction nommée frmName_onSubmit().
// Si oui, l'appelle d'abord, sinon appelle directemenr frm.submit().
function mySubmit(frm, action, target)
{
   var formName = frm.name;
   var onSubmitFunc = "open('','"+ window.name + "')." + formName + "_onSubmit";
   var onSubmitCall = onSubmitFunc + "(frm)";
   var doSubmit = true;
   if ( eval(onSubmitFunc + " != null") )
   {
      if ( ! eval(onSubmitCall) )
      {
         doSubmit = false;
      }
   }
   if ( doSubmit )
   {
      var attributes = frmSaveAttributes(frm);
      if ( action && (action != null) )
         frm.action = action;
      if ( target && (target != null) )
         frm.target = target;
      frm.submit();
      // Sous Netscape 4.x, la fonction frmRestoreAttributes est exécutée trop
      // tôt, avant le submit réel, ce qui fait que l'action du formulaire est
      // celui par défaut au lieu d'être celui spécifié par le paramètre action.
      // Solution : ne pas restaurer les attributs du formulaire pour Netscape 4.x
      if ( ! isNetscape4x ) 
         frmRestoreAttributes(frm, attributes);
   }

}

// Privé
// Retourne la valeur du champs de nom field_name du formulaire spécifié
// Retourne defValue si aucune valeur trouvée
// Si plusieurs valeurs correspondent, elles sont retournées sous la forme
// val1\037val2\037...
function getFieldValue(form, fieldName, defValue)
{
   var fieldValue = "";
   for (var i=0;i<form.elements.length;i++)
   {
      var elt = form.elements[i];
      var elt_type = elt.type;
      var newValue = "";
      if (elt.name == fieldName)
      {
         // pour les champs text ou hidden, on retourne la valeur
         if ( (elt_type == 'text') || (elt_type == 'hidden') )
         {
            newValue = elt.value;
         }
         // pour les radioboutons ou les cases à cocher
         else if ( (elt_type == 'checkbox') || (elt_type == 'radio') )
         {
            if (elt.checked)
            {
               newValue = elt.value;
            }
         }
         // Pour les listes simple ou multiple
         else if ( (elt_type == 'select-one') || (elt_type == 'select-multiple') )
         {
            if ( elt.options != null ) if ( elt.options.length > 0 )
            {
               for (var j=0; j< elt.options.length; j++)
               {
                  if (elt.options[j].selected)
                  {
                     if ( newValue != "" )
                        newValue += MULTI_VALUE_SEP;
                     newValue += elt.options[j].value;
                  }
               }
            }
         }
         // Ajoute newValue
         if ( newValue != "" )
         {
            if (fieldValue != "") { fieldValue += MULTI_VALUE_SEP;}
            fieldValue += newValue;
         }
      }
   }
   return (fieldValue == "")? defValue : fieldValue;
}
// Privé
// Recherche la frame de nom spécifié ET faisant partie de la même application
// On se base sur la racine de l'URL pour le décecter
// La recherche se fait en partant de la fenêtre courante : on remonte à opener 
// s'il y en a un, à parent sinon, et ainsi de suite de manière récursive
function findFrameWithinApp(frameName)
{
   if ( ! isNetscape4x )
   {
      return sysFindFrameWithinApp(frameName);
   }
   else
   {
      return window.open("", frameName);
   }
}
function _isAlreadyTested(testedArray, obj)
{
   for (var i=0; i<testedArray.length; i++)
      if ( testedArray[i] == obj ) return true;
   return false;
}
function _findFrameWithinAppRecurse(frameName, appBaseUrl, frame, testedArray)
{
   var ret = null;
   if ( _isAlreadyTested(testedArray, frame) ) return null;
   testedArray.push(frame);
   // Try/Catch nécessaire si on ouvre à partir l'appli à partir d'une autre
   // fenêtre, sinon message d'erreur "Accès refusé"
   if ( isNetscape4x )
   {
      if ( frame.name==frameName)
      {
         if ( getAppBaseUrl(frame.location.href)==appBaseUrl )
         return frame;
      }
   }
   else
   {
      var hasError = false;
      eval("try {if ( frame.name==frameName){if ( getAppBaseUrl(frame.location.href)==appBaseUrl )ret=frame;}}catch (e){hasError=true}");
      if ( ret != null ) return ret;
      if ( hasError ) return null;
   }
   if ( frame.opener != null )
   {
      ret = _findFrameWithinAppRecurse(frameName, appBaseUrl, frame.opener, testedArray);
      if ( ret == null )
         ret = _findFrameWithinAppRecurse(frameName, appBaseUrl, frame.opener.parent, testedArray);
   }
   if ( ret != null ) return ret;
   if ( frame.parent != null )
       ret = _findFrameWithinAppRecurse(frameName, appBaseUrl, frame.parent, testedArray);
   if ( ret != null ) return ret;
   var frames = frame.frames;
   if ( (frames != null) && (frames.length > 0) )
   {
      for (var i=0; (i<frames.length) && (ret==null); i++)
      {
         ret = _findFrameWithinAppRecurse(frameName, appBaseUrl, frames[i], testedArray);
      }
   }
   return ret;
}

// Privé
// Teste si la frame possède au moins un formulaire contenant un champ du nom
// spécifié : si oui, le renvoie, sinon renvoie null.
function findField(frameName, inputName)
{
   // Rechercher le premier formulaire de frameName qui contient un
   // champ inputName
   // DEI 6064 : limite la recherche aux fenêtres issues de la même application
   // var targetFrame = open( "", frameName); 
   var targetFrame = findFrameWithinApp(frameName);
   if ( targetFrame == null )
   {
      alert(mess_frame_not_found + frameName);
      return;
   }
   else
   {
      //alert("frame trouvée : " + targetFrame.name + " " + targetFrame.location.href);
   }
   var targetForm = null;
   var targetInput = null;

   for (var i=0; (i<targetFrame.document.forms.length) && (targetInput==null); i++)
   {
      // Recherche le champ input_name dans frame_dest
      targetForm = targetFrame.document.forms[i];
      for(var j=0; j < targetForm.elements.length; j++)
      {
         if ( targetForm.elements[j].name == inputName )
         {
            targetInput = targetForm.elements[j];
            break;
         }
      }
   }
   return targetInput;
}

function setFieldValue(form, fieldName, value)
{
   return setOrAddFieldValue(form, fieldName, value, '');
}

// Renvoie le champ spécifié par le nom du formulaire et le nom du champ
function frmGetField(frmName, fieldName)
{
   var frm = document.forms[frmName];
   if ( frm )
   {
      var field = frm.elements[fieldName];
      if ( field )
         return field;
   }
   return null;
}
// Idem frmGetField en affichant un message détaillé
// si le champ n'est pas trouvé
function frmCheckField(frmName, fieldName)
{
   var frm = document.forms[frmName];
   if ( frm )
   {
      var field = frm.elements[fieldName];
      if ( field )
         return field;
      else
         alert(mess_field_not_found + fieldName);   
   }
   alert(mess_form_not_found + frmName);   
}

// Privé
// Met à jour le champ de nom field_name du formulaire spécifié
// val est de la forme "val1:val2:..."
// Si separator est non vide, alors il faut ajouter la nouvelle valeur à la
// valeur courante, sinon, c'est un remplacement.
// Renvoie true si un champ a été positionné, false sinon
function setOrAddFieldValue(form, fieldName, value, separator)
{
   var ret = false;
   for (var i=0;i<form.elements.length;i++)
   {
      var elt = form.elements[i];
      if (elt.name == fieldName)
      {
         if ( ! ret ) ret = HTMLUtil_setElementValue( elt, value, separator );
      }
   }
   return ret;
}
// Renvoie true si le champ a été positionné
function HTMLUtil_setElementValue(elt, value, separator)
{
   var elt_type = elt.type;
   var valueArray = value.split(MULTI_VALUE_SEP);
   var ret = false;
   // pour les champs text ou hidden, on met à jour la valeur
   if ( (elt_type == 'text') || (elt_type == 'hidden') )
   {
      var newValue = value;
      if( separator && separator != "" )
      {
         if (elt.value && elt.value != "")
            newValue = elt.value + separator;
         newValue += value;
      }
      elt.value = newValue;
      ret = true;
   }
   // pour les radioboutons et les cases à cocher, on coche la valeur
   else if ( (elt_type == 'checkbox') || (elt_type == 'radio') )
   {
      if ( contains(valueArray, elt.value) )
      {
         elt.checked = 1;
         ret = true;
      }
      else
      {
         // On ne remet à zéro que si c'est un remplacement
         if ( (separator == null) || (separator == "") )
            elt.checked = 0;
      }
   }
   else if ( (elt_type == 'select-one') || (elt_type == 'select-multiple') )
   {
      for (var j=0; j< elt.options.length; j++)
      {
         if ( contains(valueArray, elt.options[j].value) )
         {
            elt.options[j].selected = 1;
            ret = true;
         }
         else
         {
            // On ne remet à zéro que si c'est un remplacement
            if ( (separator == null) || (separator == "") )
               elt.options[j].selected = 0;
         }
      }
   }
   return ret;
}
// Privé. Teste qu'un tableau contient une valeur
function contains(array, val)
{
   for (var i=0; i< array.length; i++)
   {
      if ( array[i] == val ) { return true; }
   }
   return false;
}
// 
String.prototype.trim = function()
{
   var str = this;
   var idx = 0;
   while ( str.charAt(idx) == ' ' )
   {
      idx ++;
   }
   if ( idx > 0 )
      str = str.substring(idx);
   idx = str.length - 1;
   var sp = 0;
   while ( str.charAt(idx-sp) == ' ' )
   {
      sp ++;
   }
   if ( sp > 0 )
      str = str.substring(0, str.length-sp);
   return str;
}
// Renvoie la valeur de str : si débute ou finit par un ", l'enlève
String.prototype.getTokenValue = function()
{
   var str = this;
   if ( str == null ) return null;
   if (str.charAt(0) == '"' )
      str = str.substring(1);
   if (str.charAt(str.length-1) == '"' )
      str = str.substring(0, str.length-1);
   return str;
}

// Renvoie le dernier 'mot' détecté dans une chaîne
String.prototype.getLastWordToken = function()
{
   var tokens = this.trim().getWordTokens();
   if ( tokens != null )
      return tokens[tokens.length-1].getTokenValue();
   return null;
}
// privé
// En unicode, la fonction escape() sous IE n'encode pas correctement les
// caractères car il le fait sous la forme %uXXXX, alors que le format attendu
// par le serveur HTTP est %XX%XX. A partir de IE 5.5, on peut utiliser à la
// place encodeURI qui le fait correctement
function myEscape(s)
{
   if ( sysIsUTF8 )
   {
      if ( window.encodeURIComponent )
         return encodeURIComponent(s);
   }
   return escape(s);
}
function myUnescape(s)
{
   if ( sysIsUTF8 )
   {
      if ( window.decodeURIComponent )
         return decodeURIComponent(s);
   }
   return unescape(s);
}
/**
 * Découpe une chaîne en éléments. Un élément est un mot simple ne contenant
 * pas d'espace ni de point-virgule, ou un ensemble de mots entre double
 * quotes ("a b").
 * NB : les quotes sont renvoyées
 * doNotAddFinalQuote : si true, n'ajoute pas la quote finale si elle manque 
 */
String.prototype.getWordTokens = function (doNotAddFinalQuote)
{
   var s = this;
   if ( s == null )
      return null;

   var v = new Array();
   var cur = null;
   var curOpenQuotes = false;
   var cbefore = '';
   for (var i=0; i< s.length; i++)
   {
      var c = s.charAt(i);
      // Potentiellement une fin d'élément ou un espace à ignorer
      if ( (c==' ') || (c=='\t') || (c=='\n') || (c=='\r') || ( c == ';') )
      {
         if ( curOpenQuotes )
         {
            // Elément quoté en cours
            cur += c;
         }
         else if ( cur != null )
         {
            // Fin de l'élément simple courant
            v[v.length] = cur;
            cur = null;
         }
         else
         {
            // Espace à ignorer
         }
      }
      // Potentiellement début ou fin d'élément quoté
      else if ( (c == '"') && (cbefore != '\\') )
      {
         if ( curOpenQuotes )
         {
            // Fin
            cur += c;
            v[v.length] = cur;
            cur = null;
            curOpenQuotes = false;
         }
         else
         {
            // Début
            cur = c ;
            curOpenQuotes = true;
         }
      }
      // Potentiellement début élément, ou élément en cours
      else
      {
         if ( cur == null)
            cur = "";;
         cur += c;
      }
      cbefore = c;
   }
   // Il se peut qu'un élément quoté ou non ne soit pas fini
   if ( cur != null )
   {
      if ( curOpenQuotes )
      {
         // Ajoute la quote finale éventuellement
         if ( doNotAddFinalQuote == true )
            ;
         else
            cur += '"';
         v[v.length] = cur;
         cur = null;
      }
      else
      {
         v[v.length] = cur;
      }
   }
   return v;
}
/**
 * @return la chaîne avec les cotes initiale et finale si elle n'en a pas
 * déjà. Ajoute celle qui manque éventuellement.
 */
String.prototype.quotes = function ()
{
   var s = this;
   if (s == null ) return null;
   if ( s.charAt(0) !=  '"' )
      s = '"' + s;
   if ( s.length > 1 )
   {
      if ( s.charAt(s.length-1) != '"' )
         s += '"';
      else if (s.charAt(s.length-2) == '\\' )
         s += '"';
   }
   else
   {
      // chaine vide
      s += '"';
   }
   return s;
}
/*
Dans les appels à un javascript à partir du code HTML, la double-quote est encodé
par tagext:button en _quote_. unmaskQuoteKeyWord renvoie la chaîne où _quote_
est remplacé par \"
*/
String.prototype.unmaskQuoteKeyWord = function()
{
   return this.replace(/_quote_/g, "\\\"");
}
/*
Masque le caractère car par la chaîne by. Tous les caractères de la chaîne
sont masquées sauf ceux précédés par \. Renvoie la nouvelle chaîne.
Si by n'est pas spécifié, il vaut \car
*/
String.prototype.mask = function mask(car, by)
{
   var ret = "";
   var cbefore = "";
   var s = this;
   if ( by == null ) by = "\\" + car;
   for (var i=0; i<s.length; i++)
   {
      var c = s.charAt(i);
      if (  (c == car ) && ( cbefore != '\\' ) )
         ret += by;
      else
         ret += c;
      cbefore = c;
   }
   return ret;
}

/*
Prive
Renvoie l'URL de base de l'application en détectant 'jsp' ou 'servlet' dans
l'URL du document en cours.
Si l'URL est http://serveur:port/loris/jsp/view/view.jsp, renvoie
http://serveur:port/loris/.
Si jsp ou servlet non détecté, renvoie "/"
ref est optionnel (URL à tester) : dans ce cas, vaut alors l'URL du document
en cours.
*/
function getAppBaseUrl(ref)
{
   if ( ref == null )
      ref = document.URL;
   var i = ref.indexOf("/jsp/");
   if ( i < 0 )
      i = ref.indexOf("/servlet/");
   if ( i > - 1 )
   {
      return ref.substring(0, i+1);
   }
   return "/";
}
/*
Renvoie le nom du contexte en se basant sur l'URL du document.
Si l'URL est http://serveur:port/loris/jsp/view/view.jsp, renvoie "loris".
Renvoie vide si non trouvé
*/
function getAppName()
{
   var appBase = getAppBaseUrl();
   if ( appBase != "/" )
   {
      var i = appBase.lastIndexOf("/", appBase.length-2); // saute dernier /
      if ( i > -1 )
      {
         return appBase.substring(i+1, appBase.length-1);
      }
   }
   return "";
}
