var ac_timeOut = 10000; // Autocomplete Timeout in ms (-1: autocomplete never time out)
var ac_startOut = 1000; // autocomplete start en millisecond (-1; instantanné)
var ac_lim = 5; // Number of elements autocomplete can show (-1: no limit)
var ac_width_minimum = 100; // taille minimum
var ac_width_full = 100; // taille appliquée quand taille minimum dépassée
var ac_url = '/js/autocomplete.php?';
var ac_final_url = ac_url;
var ac_caracteres_minimum = 3; // nombre minimum de caractères requis
var ac_nouvelle_valeur_definie = false; // une valeur a t elle été définie ?

var ac_keywords = []; // valeurs trouvées après recherche AJAX
var ac_display = false; // affiche ou non le tableau
var ac_pos = 0; // position courante
var ac_curr = null; // input courant
var ac_rangeu = 0; // ???
var ac_ranged = 0; // ???
var ac_toid; // id timeout
var ac_soid; // id startout
var mouse_on_list = 0; // souris sur la liste ou pas
var ac_handler_on_penter = null; // function a trigger quand une selection est faite ( penter() )
var ac_current_request = 0; // id de la requete en cours
var ac_last_recherche = null; // dernière texte recherche, la valeur entree

var ac_plugs =
{
 posX:{}, posY:{},
 focus:{}, blur:{},
 enter:{}
};

var _KEY_UP = 38;
var _KEY_DOWN = 40;
var _KEY_ENTER = 13;
var _KEY_RIGHT = 39;
var _KEY_ESC = 27;

function ac_checkkey(evt)
{
//  var a = evt.keyCode;
 var a = (evt || window.event).keyCode;
  if ( a == _KEY_UP )
  {
    ac_goup();
  }
  else if ( a == _KEY_DOWN )
  {
    ac_godown();
  }
  else if ( a == _KEY_ENTER )
  {
    ac_penter(evt);
  }
}

function ac_setval(val, hid)
{
  if ( typeof ac_curr == 'undefined' )
  {
    return false;
  }
  ac_curr.value = val;
  var nom = ac_curr.name.substr(3, 200);
  ac_curr.form.elements[nom].value = hid;
  return true;
}

function ac_penter(evt)
{
  if ( !ac_display )
  {
    return false;
  }
  ac_display = false;
  var K = ac_keywords[ac_pos - 1];
  try
  {
    // on trim en ajoutant des espaces pour éviter les erreurs si le type est numérique
    var val = ' ' + K.txt + ' ';
    var hid = ' ' + K.id + ' ';
    if ( typeof K.txtvisible != 'undefined' ) { val = ' ' + K.txtvisible + ' '; }
    val = val.trim();
    hid = hid.trim();
    ac_setval(val, hid);
    ac_nouvelle_valeur_definie = true;
  }
  catch(ex)
  {
    alert("Erreur lors de l'attribution de la valeur, ac_pos est : (" + (typeof ac_pos) + ") = " + ac_pos + CR + ex);
  }
  if ( ac_handler_on_penter !== null && typeof ac_handler_on_penter == 'function' )
  {
    try { ac_handler_on_penter(evt, val, hid, K); }
    catch(ex) { alert(ex); }
  }
  if ( typeof ac_plugs.enter[ac_curr.name] == 'function' ) { ac_plugs.enter[ac_curr.name].call(ac_curr, K); }
  ac_removedisp(true);

  return true;
}

function ac_movedown()
{
  ac_rangeu++;
  ac_ranged++;
  ac_remake();
}

function ac_moveup()
{
  ac_rangeu--;
  ac_ranged--;
  ac_remake();
}

function ac_goup()
{
  if ( !ac_display )
  {
    return false;
  }
  if ( ac_pos == 1 )
  {
    return false;
  }
  var old = AI.DOM.getById('tat_tr'+ac_pos);
  if ( old ) { old.className = (ac_pos % 2) ? 'odd' : ''; }
  ac_pos--;
  if ( ac_pos < ac_rangeu )
  {
    ac_moveup();
  }
  var nouv = AI.DOM.getById('tat_tr'+ac_pos);
  if ( nouv )
  {
    nouv.className = 'ac_active';
  }
  ac_removedisp_startTimeout();
  return true;
}

function ac_godown()
{
  if ( !ac_display )
  {
    return false;
  }
  if ( ac_pos == ac_keywords.length )
  {
    return false;
  }
  var old = AI.DOM.getById('tat_tr'+ac_pos);
  if ( old )
  {
    old.className = (ac_pos % 2) ? 'odd' : '';
  }
  ac_pos++;
  if ( ac_pos > ac_ranged )
  {
    ac_movedown();
  }
  var nouv = AI.DOM.getById('tat_tr'+ac_pos);
  if ( nouv )
  {
    nouv.className = 'ac_active';
  }
  ac_removedisp_startTimeout();
  return true;
}

function ac_remplir_start()
{
  ac_nouvelle_valeur_definie = false;
  ac_current_request = ac_current_request + 1;
  var local_request = ac_current_request;
  ac_loading_en_cours();
  if ( ac_curr.value === '' )
  {
    return ac_removedisp();
  }
//  AJAX_get(ac_final_url, ac_remplir_start_onok, ac_remplir_start_onerror, ac_handler_requete_en_cours, null, local_request);
  AI.AJAX.send({"url":ac_final_url,"onload":ac_remplir_start_onok,"onerror":ac_remplir_start_onerror,"onprogress":ac_handler_requete_en_cours,"arbitrary":local_request});
  return true;
}

/**
 * reponse AJAX ok
 * @private
 */
function ac_remplir_start_onok(req, local_request)
{
  if ( local_request != ac_current_request )
  {
    return false;
  }
  try
  {
    eval("ac_keywords = " + req.responseText);
  }
  catch (ex)
  {
    alert(req.responseText + ' ' + CR + ' ' + ex);
    // @todo : traduire
    ac_keywords = [{id:'', txt:'erreur de format de la réponse'}];
  }
  ac_removedisp_startTimeout();
  ac_generate();
  return true;
};

/**
 * reponse AJAX error
 * @private
 */
function ac_remplir_start_onerror(req, local_request)
{
  if ( local_request != ac_current_request )
  {
    return false;
  }
  ac_keywords = [{id:'', txt:'erreur de connection, réponse : ' + req.statusText + ' ' + ac_final_url}];
  ac_removedisp_startTimeout();
  ac_generate();
  return true;
};

function ac_remplir(elt, evt, objet, url, handler_penter)
{
 var objet_send = objet;
 if ( typeof objet == 'function' ) { objet_send = objet(); }
  ac_curr = elt;
  if ( ac_soid )
  {
    clearTimeout(ac_soid);
  }
  if ( ac_toid )
  {
    clearTimeout(ac_toid);
  }
  if ( evt.keyCode == _KEY_UP || evt.keyCode == _KEY_DOWN || evt.keyCode == _KEY_ENTER )
  {
    return false;
  }
  if ( evt.keyCode == _KEY_ESC )
  {
    return ac_removedisp();
  }
  if ( elt.value.length < ac_caracteres_minimum && evt.keyCode != _KEY_RIGHT )
  {
    return false;
  }
  if ( elt.value === '' )
  {
    return ac_removedisp();
  }
  ac_handler_on_penter = handler_penter;
  if ( typeof url == 'undefined' || url === null )
  {
    url = ac_url;
  }
  ac_last_recherche = ac_curr.value;
  ac_final_url = url + 'obj=' + escape(objet) + '&v=' + escape(ac_curr.value);
  if ( ac_startOut > 0 && evt.keyCode != _KEY_RIGHT )
  {
    ac_soid = setTimeout(ac_remplir_start, ac_startOut);
  }
  else
  {
    ac_remplir_start();
  }
  return true;
}

function curTop()
{
/*
  var ac_toreturn = 0;
  var obj = ac_curr;
  while ( obj )
  {
    ac_toreturn += obj.offsetTop;
    obj = obj.offsetParent;
  }
  //ac_toreturn -= DOM.getScrollTopSum(ac_curr);
  ac_toreturn = DOM.getPageBoxTop(ac_curr);
  return ac_toreturn;
*/
 var pos = AI.DOM.position.get(ac_curr);
// AI.console.error('CURTOP / pageBoxTop = ' + AI.DOM.getPageBoxTop(ac_curr) + '/pos.top = ' + pos.top);
 return pos.top;
}

function curLeft()
{
/*
  var ac_toreturn = 0;
  var obj = ac_curr;
  while ( obj )
  {
    ac_toreturn += obj.offsetLeft;
    obj = obj.offsetParent;
  }
  //ac_toreturn -= DOM.getScrollLeftSum(ac_curr);
  ac_toreturn = DOM.getPageBoxLeft(ac_curr);
  return ac_toreturn;
*/
 var pos = AI.DOM.position.get(ac_curr);
// AI.console.error('CURLEFT / pageBoxLeft = ' + AI.DOM.getPageBoxLeft(ac_curr) + '/pos.left = ' + pos.left + '/borderLeft = ' + AI.DOM.getBorderLeft(ac_curr));
 return pos.left;
}

function ac_table_focus(evt)
{
//  AI.console.info('focus');
  mouse_on_list = 1;
  return true;
}

function ac_maskWithIframe()
{
  if ( AI.DOM.getById('tat_iframe') )
  {
    document.body.removeChild(AI.DOM.getById('tat_iframe'));
  }
  var iframe = document.createElement('iframe');
  var table = AI.DOM.getById('tat_table');
  iframe.id = 'tat_iframe';
  iframe.className = 'ac_iframe';
  iframe.style.width = table.offsetWidth;
  iframe.style.height = table.offsetHeight;
  iframe.style.top = table.style.top;
  iframe.style.left = table.style.left;
  document.body.appendChild(iframe);
  return table;
}

function ac_loading_en_cours()
{
 var a, hauteur, left, N, r, c, pos;
  if ( AI.DOM.getById('tat_table') )
  {
    document.body.removeChild(AI.DOM.getById('tat_table'));
  }
//  a = document.createElement('table');
  a = AI.DOM.createElement('TABLE', {className:'ac_table', id:'tat_table'});
//  a.className='ac_table';
  a.width = ( ac_curr.offsetWidth < ac_width_minimum ) ? Math.max(ac_width_full, ac_width_minimum) : ac_curr.offsetWidth;
//  hauteur = curTop() + ac_curr.offsetHeight;
//  left = curLeft();
  pos = AI.DOM.position.get(ac_curr);
  hauteur = pos.top + pos.height;
  left = pos.left + AI.DOM.getBorderLeft(a);
  N = ac_curr.name;
  if ( typeof ac_plugs.posY[N] == 'function' ) { hauteur = ac_plugs.posY[N].call(ac_curr, hauteur); }
  if ( typeof ac_plugs.posX[N] == 'function' ) { left = ac_plugs.posX[N].call(ac_curr, left); }
  a.style.top = hauteur + "px";
  a.style.left = left + "px";
//  a.id = 'tat_table';
  r = a.insertRow(-1);
  c = r.insertCell(-1);
  // @todo : traduction
  c.innerHTML = 'Chargement #' + ac_current_request + ' en cours...';
  document.body.appendChild(a);
  /*@cc_on ac_maskWithIframe(); @*/
}

function ac_base_table()
{
 var a, hauteur, left, N, pos;
  if ( AI.DOM.getById('tat_table') )
  {
    document.body.removeChild(AI.DOM.getById('tat_table'));
  }
//  a = document.createElement('table');
  a = AI.DOM.createElement('TABLE', {className:'ac_table', id:'tat_table'});
//  a.className='ac_table';
  a.width = ( ac_curr.offsetWidth < ac_width_minimum ) ? Math.max(ac_width_full, ac_width_minimum) : ac_curr.offsetWidth;
//  hauteur = curTop() + ac_curr.offsetHeight;
//  left = curLeft();
  pos = AI.DOM.position.get(ac_curr);
  hauteur = pos.top + pos.height;
  left = pos.left + AI.DOM.getBorderLeft(a);
  N = ac_curr.name;
  if ( typeof ac_plugs.posY[N] == 'function' ) { hauteur = ac_plugs.posY[N].call(ac_curr, hauteur); }
  if ( typeof ac_plugs.posX[N] == 'function' ) { left = ac_plugs.posX[N].call(ac_curr, left); }
  a.style.top = hauteur + "px";
  a.style.left = left + "px";
//  a.id = 'tat_table';
  a.onmouseover = ac_table_focus;
  a.onmouseout = ac_table_unfocus;
  return a;
}

function ac_table_unfocus(evt)
{
//  AI.console.info('unfocus');
  mouse_on_list = 0;
  ac_removedisp_startTimeout();
  return true;
}

function ac_click_down(evt)
{
  evt = (evt) ? evt : ((window.event) ? window.event : "");
  if ( evt )
  {
    var elem = EVT.getTarget(evt);
    // humm, "this" n'est pas égal à "elem" ici ?
    if ( elem )
    {
      ac_pos = elem.id.substr(6,50);
      ac_movedown();
      ac_removedisp_startTimeout();
    }
  }
  return true;
}

function ac_click_up(evt)
{
  evt = ( evt ) ? evt : ((window.event) ? window.event : "");
  if ( evt )
  {
    var elem = EVT.getTarget(evt);
    // humm, "this" n'est pas égal à "elem" ici ?
    if ( elem )
    {
      ac_pos = elem.id.substr(6,50);
      ac_moveup();
      ac_removedisp_startTimeout();
    }
  }
  return true;
}

function ac_click_table(evt)
{
//  evt = ( evt ) ? evt : ((window.event) ? window.event : "");
  evt = evt || window.event;
  if ( evt )
  {
/*
    var elem = EVT.getTarget(evt);
    if ( elem )
    {
      ac_pos = elem.id.substr(6,50);
      mouse_on_list = 0;
      ac_penter(evt);
    }
*/
    ac_pos = this.id.substr(6,50);
    mouse_on_list = 0;
    ac_penter(evt);
  }
  return true;
}
/*
function ac_flecheup(table, rangeu)
{
  if ( ac_keywords.length > ac_lim )
  {
    r = table.insertRow(-1);
    c = r.insertCell(-1);
    c.colSpan = 2;
//    c.innerHTML = ' ';
    c.appendChild(AI.DOM.createElement('span', {"className":'ac-close',"onclick":function(){ac_removedisp(true); return false;},"title":'Fermer'}));
    if (rangeu > 1)
    {
      c.className='ac_arrow_up_on';
      c.onclick = ac_click_up;
    }
    else
    {
      c.className='ac_arrow_up_off';
    }
  }
}

function ac_flechedown(table, j)
{
  if ( ac_keywords.length > ac_lim )
  {
    r = table.insertRow(-1);
    c = r.insertCell(-1);
    c.colSpan = 2;
//    c.innerHTML = '<img src="/images/pictos/16x16/file-close.png" onclick="ac_removedisp(true);EVT.stop(event); return false;" align="right" alt="fermer" Title="Fermer" width="16" height="16" border="0"> ';
    c.appendChild(AI.DOM.createElement('span', {"className":'ac-close',"onclick":function(){ac_removedisp(true); return false;},"title":'Fermer'}));
    if ( j-1 < ac_keywords.length )
    {
      c.className='ac_arrow_down_on';
      c.onclick = ac_click_down;
    }
    else
    {
      c.className='ac_arrow_down_off';
    }
  }
}
*/
/*
function ac_fleches(table, rangeu, j)
{
 var tblElt, r;
 if ( ac_keywords.length > ac_lim )
 {
  tblElt = table.createTHead();
  r = tblElt.insertRow(-1);
  c = r.insertCell(-1);
  if ( rangeu > 1 )
  {
    c.className='ac_arrow_up_on';
    c.onclick = ac_click_up;
  }
  else { c.className='ac_arrow_up_off'; }

  c = r.insertCell(-1);
  if ( j-1 < ac_keywords.length )
  {
   c.className='ac_arrow_down_on';
   c.onclick = ac_click_down;
  }
  else { c.className='ac_arrow_down_off'; }
 }

 tblElt = table.createTFoot();
 r = tblElt.insertRow(-1);
 c = r.insertCell(-1);
 c.colSpan = 2;
 c.appendChild(AI.DOM.createElement('span', {"className":'ac-close',"onclick":function(){ac_removedisp(true); return false;},"title":'Fermer'}));
}
*/
function ac_fleches(table, rangeu, j)
{
 var
  tblElt, r, c,
  tbl = AI.DOM.createElement('TABLE', {}, {width:'100%'});

 if ( ac_keywords.length > 1 )
 {
  tblElt = tbl.createTFoot();
  r = tblElt.insertRow(-1);
  c = r.insertCell(-1);
  c.style.padding = '0px';
  c.appendChild(AI.DOM.createElement('DIV', {}, {width:'16px'}));
  if ( rangeu > 1 )
  {
   c.className = 'ac_arrow_up_on';
   c.onclick = ac_click_up;
  }
  else { c.className = 'ac_arrow_up_off'; }

  c = r.insertCell(-1);
  c.style.padding = '0px';
  c.appendChild(AI.DOM.createElement('DIV', {}, {width:'16px'}));
  if ( j-1 < ac_keywords.length )
  {
   c.className = 'ac_arrow_down_on';
   c.onclick = ac_click_down;
  }
  else { c.className='ac_arrow_down_off'; }

  c = r.insertCell(-1);
  c.style.padding = '0px';
  c.style.width = '100%';
  c.appendChild(AI.DOM.createElement('SPAN', {className:'ac-close',onclick:function(){ac_removedisp(true); return false;},title:'Fermer'}));

  tblElt = table.createTHead();
  r = tblElt.insertRow(-1);
  r.style.backgroundColor = '#F0F0F0';
  c = r.insertCell(-1);
  c.style.padding = '0px';
  c.style.borderBottom = '1px solid #000000';
  c.colSpan = 2;
  c.appendChild(tbl);
 }
}
function ac_remake()
{
  var a = ac_base_table();
  var first = true;
  var j = 1;
//  ac_flecheup(a, ac_rangeu);
  for ( var i=0; i<ac_keywords.length; i++ )
  {
    var K = ac_keywords[i];
    if ( j >= ac_rangeu && j <= ac_ranged )
    {
      r = a.insertRow(-1);
      if ( j % 2 )
      {
        AI.DOM.CSS.add('odd', r);
      }
      r.id = 'tat_tr'+(j);
      c = r.insertCell(0);
      c.onclick = ac_click_table;
      c.innerHTML = K.txt;
      c.id = 'tat_td'+(j);
      if ( K.title ) { c.title = K.title; }
      if ( typeof K.hid == 'undefined' )
      {
        c.colSpan = 2;
      }
      else
      {
        d = r.insertCell(-1);
        d.id = 'tac_td' + (j);
        d.className = 'complement';
        if ( K.title ) { d.title = K.title; }
        if ( typeof K.hidAlign != 'undefined' )
        {
//          d.vAlign = K.hidAlign;
          d.style.verticalAlign = K.hidAlign;
        }
        d.innerHTML = K.hid;
        d.onclick = ac_click_table;
      }
    }
    j++;
    if ( j > ac_ranged )
    {
      break;
    }
  }
//  ac_flechedown(a, j);
  ac_fleches(a, ac_rangeu, j);
  document.body.appendChild(a);
  /*@cc_on ac_maskWithIframe(); @*/
}

function ac_generate()
{
  var a = ac_base_table();
  var first = true;
  var j = 1;
//  ac_flecheup(a, 0);
  var counter = 0;
  for ( var i=0; i<ac_keywords.length; i++ )
  {
    var K = ac_keywords[i];
    counter++;
    r = a.insertRow(-1);
    if ( first )
    {
      r.className = 'ac_active';
      first = false;
      ac_pos = counter;
    }
    else if ( j % 2 )
    {
      AI.DOM.CSS.add('odd', r);
    }
    r.id = 'tat_tr'+(j);
    c = r.insertCell(-1);
    c.onclick = ac_click_table;
    c.innerHTML = K.txt;
    c.id = 'tat_td'+(j);
    if ( K.title ) { c.title = K.title; }
    if ( typeof K.hid == 'undefined' )
    {
      c.colSpan = 2;
    }
    else
    {
      d = r.insertCell(-1);
      d.className = 'complement';
      d.id = 'tac_td' + (j);
      if ( K.title ) { d.title = K.title; }
      if ( typeof K.hidAlign != 'undefined' )
      {
//        d.vAlign = K.hidAlign;
        d.style.verticalAlign = K.hidAlign;
      }
      d.innerHTML = K.hid;
      d.onclick = ac_click_table;
    }
    j++;
    if ( j - 1 == ac_lim && j < ac_keywords.length )
    {
      break;
    }
  }
//  ac_flechedown(a, j);
  ac_fleches(a, 0, j);
  ac_rangeu = 1;
  ac_ranged = j-1;
  ac_display = true;
  if (ac_pos <= 0) { ac_pos = 1; }
  document.body.appendChild(a);
  /*@cc_on ac_maskWithIframe(); @*/
}

function ac_onFocus(elt)
{
  ac_curr = elt;
  ac_curr.ancienne_valeur = ac_curr.value;
//  alert(ac_curr + '/' + ac_curr.form + '/' + ac_curr.form.elements +' / ' + ac_getHiddenId() + '/' + ac_curr.form.elements[ac_getHiddenId()]);
  var eltHide = ac_curr.form.elements[ac_getHiddenId()];
  eltHide.ancienne_valeur = eltHide.value;
  if ( typeof elt.select == 'function' ) { elt.select(); }
  if ( typeof ac_plugs.focus[ac_curr.name] == 'function' ) { ac_plugs.focus[ac_curr.name].call(ac_curr); }
}

function ac_getHiddenId()
{
  if ( typeof ac_curr == 'undefined' )
  {
    return false;
  }
  return ac_curr.name.substr(3, 200);
}

function ac_removedisp(forced)
{
  var tbl = AI.DOM.getById('tat_table');
  if ( typeof ac_curr == 'undefined' )
  {
    return false;
  }
  forced = forced ? forced : false;
//  if (typeof vider == 'undefined') var vider = false;
  if ( forced || mouse_on_list === 0 || ac_curr.value === '' )
  {
    try
    {
      if ( ac_curr.value === '' )
      {
        ac_setval('', '');
      }
      else
      {
        if ( !ac_nouvelle_valeur_definie )
        {
          ac_setval(ac_curr.ancienne_valeur, ac_curr.form.elements[ac_getHiddenId()].ancienne_valeur);
        }
      }
    } catch(x) {}
    ac_display = false;
    if ( tbl )
    {
      document.body.removeChild(tbl);
    }
    /*@cc_on
    var iframe = AI.DOM.getById('tat_iframe');
    if ( iframe )
    {
      document.body.removeChild(iframe);
    }
    @*/
    if ( ac_toid )
    {
      clearTimeout(ac_toid);
    }
//    if (ac_soid) { clearTimeout(ac_soid); }
  }
  return true;
}

function ac_focusnext(form, i)
{
  i = (1 + parseInt(i, 10)) % form.elements.length;
  if ( form.elements[i].type == 'hidden' )
  {
    return ac_focusnext(form, i);
  }
  form.elements[i].focus();
  return false;
}

function ac_handleEnter(field, event)
{
	var keyCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode;
	if ( keyCode == 13 )
  {
		for (var i=0, imax=field.form.elements.length; i<imax; i++ )
    {
			if ( field == field.form.elements[i] )
      {
        break;
      }
    }
    return ac_focusnext(field.form, i);
	}
	// @todo : ca devrait pas etre return false ici ?
	return true;
}

/* handler généric AJAX pour afficher qu'une transaction est en cours */
function ac_handler_requete_en_cours(req)
{
  AI.DOM.CSS.add('ac_transaction_en_cours', ac_curr);
  if ( req.readyState==4 )
  {
    AI.DOM.CSS.remove('ac_transaction_en_cours', ac_curr);
  }
}

/**
 * Initialise le timer de ac_removedisp
 * @private
 */
function ac_removedisp_startTimeout()
{
  if ( ac_toid )
  {
//    window.clearTimeout(ac_toid);
    clearTimeout(ac_toid);
  }
  if ( ac_timeOut > 0 )
  {
//    ac_toid = window.setTimeout(ac_removedisp, ac_timeOut);
    ac_toid = setTimeout(ac_removedisp, ac_timeOut);
  }
}
/**
 * Désactive l'autocomplete ajouté par les navigateurs
 */
function ac_desactiveBrowsers(e)
{
  var E = AI.DOM.getById(e);
  try { E.setAttribute('autocomplete', "off"); } catch(x) {}
  try { E.autocomplete = 'off'; } catch(x) {}
}

// version de remplacement (non fini, non testé)
AI.autocomplete = function()
{
/*
--------------------------------------------------------------------------------
  AI.autocomplete : PRIVATE
--------------------------------------------------------------------------------
*/
 var
  table = AI.DOM.createElement('table'),
  all = {},
  UID = 0,
  current = null,
  ancienneValeurHidden = '', ancienneValeurInput = '';

 function findUID(elt)
 {
  for ( var i in all )
  {
   if ( all[i].input == elt || all[i].hidden == elt ) { return i; }
  }
  return null;
 }
 
 function focus(uid)
 {
  return function(evt)
  {
   if ( !uid || !all[uid] ) { return true; }
   current = uid;
   ancienneValeurHidden = all[uid].hidden.value;
   ancienneValeurInput = all[uid].input.value;
   if ( typeof all[uid].input.select == 'function' ) { all[uid].input.select(); }
   activatePlug('focus', uid);
   return true;
  }
 }
 
 function activatePlug(type, uid)
 {
  if ( uid && all[uid] && all[uid].options && all[uid].options.plugs && all[uid].options.plugs[type] && typeof all[uid].options.plugs[type] == 'function' )
  {
   all[uid].options.plugs[type].call(all[uid].input);
  }
/*
  var plug;
  if ( uid && ( plug = all[uid] ) && ( plug = plug.options ) && ( plug = plug.plugs ) && ( plug = plug[type] ) && typeof plug == 'function' )
  {
   plug.call(all[uid].input);
  }
*/
 }
 
 function blur(uid)
 {
  return function(evt)
  {
   if ( !uid || !all[uid] ) { return true; }
   return true;
  }
 }
 
 function keyup(uid)
 {
  return function(evt)
  {
   if ( !uid || !all[uid] ) { return true; }
   return true;
  }
 }
 
 function keydown(uid)
 {
  return function(evt)
  {
   if ( !uid || !all[uid] ) { return true; }
   return true;
  }
 }
 
 function keypress(uid)
 {
  return function(evt)
  {
   if ( !uid || !all[uid] ) { return true; }
   return true;
  }
 }
 
 EVT.unloader(function() { for ( var uid in all ) { __dispose__(all[uid]); } delete all; });
/*
--------------------------------------------------------------------------------
  AI.autocomplete : PROTECTED
--------------------------------------------------------------------------------
*/
 function __generate__(input, hidden, options)
 {
  input = AI.DOM.getById(input);
  hidden = AI.DOM.getById(hidden);
  uid = 'AC' + UID++;
  all[uid] = [input, hidden, options];
  AI.HTML.setAutocomplete(input, 'off');
  input.onfocus = focus(uid);
  input.onkeydown = keydown(uid);
  input.onkeypress = keypress(uid);
  input.onkeyup = keyup(uid);
  input.onblur = blur(uid);
 }
 
 // retourne l'élément courant
 function __get__(uid) { uid = uid || current; if ( uid && all[uid] ) { return all[uid].input; } return null; }
 function __getHidden__(uid) { uid = uid || current; if ( uid && all[uid] ) { return all[uid].hidden; } return null; }
 
 // libère un autocomplete
 function __dispose__(input)
 {
  var uid = findUID(input);
  if ( uid && all[uid] )
  {
   all[uid].input.onfocus = null;
   all[uid].input.onkeydown = null;
   all[uid].input.onkeypress = null;
   all[uid].input.onkeyup = null;
   all[uid].input.onblur = null;
   all[uid].input = null;
   all[uid].hidden = null;
   all[uid].options = null;
   delete all[uid];
  }
 }

/*
--------------------------------------------------------------------------------
  AI.autocomplete : PUBLIC
--------------------------------------------------------------------------------
*/
 return {
  "generate":__generate__, "dispose":__dispose__,
  "get":__get__,
  // ceux là, je suis pas sur que ce soit nécessaire qu'ils soient publics
  "getHidden":__getHidden__
 };
}();