| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 
 | /*
Toutes les 200 milisecondes, s'il y a un champs à autocompléter dans lequel il y a des lettres,
alors rechercher les possibilités.
*/
 
// retourne un objet xmlHttpRequest.
// méthode compatible entre tous les navigateurs (IE/Firefox/Opera)
function get_Xhr(){
  var xhr=null;
  if(window.XMLHttpRequest) // Firefox et autres
  xhr = new XMLHttpRequest();
  else if(window.ActiveXObject){ // Internet Explorer
    try {
      xhr = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (e1) {
        xhr = null;
      }
    }
  }
  else { // XMLHttpRequest non supporté par le navigateur
    alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
  }
  return xhr;
}
 
var _form = null; // Variable qui permettra (une fois affectée) d'accéder directement au fromulaire (ça donnera: _form au lieu de faire par exemple: document.forms["nouveauContact"] ou document.getElementById('nouveauContact'))
var _tabInputFields = new Array(); // Tableau qui contient des objets qui permettront d'accéder directement aux champs à autocompléter (ça donne: _tabInputFields[0] au lieu de faire par exemple: document.getElementById('prenom'))
var _selectedSugg = -1; // Variable qui retient quel était la dernière suggestion que l'internaute a sélectionnée quand il a touché aux flèches haut ou bas pour choisir une suggestion
var _xmlHttp = null; // L'objet xmlHttpRequest utilisé pour contacter le serveur
var _adresseRecherche = "../AutoCompletion/AC_Fonctions.php5" // L'adresse à interroger pour trouver les suggestions
var _eventKeycode = null;
var _champsCaches = new Array(); // Tableau contenant tous les champs cachés créés automatiquement (par exemple quand je tape le nom d'un pays et que je le sélectionne via autocomplétion, je dois renvoyer son id via un champ caché)
 
function initAutoComplete(form,tabFields){
  _form = form; // Je stocke l'objet form[] dans une variable globale
  _tabInputFields = tabFields;
  for (var i=0; i < _tabInputFields.length; i++) { // J'initialise chaque champ qui attend une auto-complétion
    _tabInputFields[i].oldValue = _tabInputFields[i].value; // Pour ne pas faire de requete AJAX tant que je n'ai rien inscrit dans ce champs
    _tabInputFields[i].stopReq = null; // Cette variable sert à ne plus effectuer de requete si l'internaute continue de taper un mot dont le début ne renvoie déjà pas de suggestions (par exemple: à partir de la 3eme lettre, la requete ne retourne rien. Si l'internaute a un mot de 10 lettres, alors il ne faut plus faire de requete à chaque fois que l'internaute écrit une des 7 lettres restantes)
    _tabInputFields[i].setAttribute("autocomplete", "off"); // Pour éviter l'autocompletion des champs input automatique des navigateurs (quand le navigateur propose les entrées précédentes par défaut)
    _tabInputFields[i].onkeydown = onKeyDownFunction; // Pour détecter certaines actions du clavier qui nous intéressent (flèches haut et bas, tab, enter) quand la touche est ENFONCEE
    _tabInputFields[i].onkeyup = onKeyUpFunction; // Pour détecter certaines actions du clavier qui nous intéressent (flèches haut et bas, tab, enter) quand la touche est RELACHEE
    _tabInputFields[i].onblur = onBlurFunction; // Quand on quitte ce champ qui attend l'auto-completion, je fais tout disparaitre et je réinitialise certaines variables
 }
  window.onresize = onResizeFunction; // Si l'internaute resize la fenetre, je fais tout disparaitre
  // Premier déclenchement de la fonction dans 200 millisecondes
  setTimeout("mainLoop()",200)
}
 
// Fonction qui tourne en permanence et qui vérifie si un des champs à autocompléter a été modifié par l'internaute. Si c'est le cas, on déclenche le processus de recherche
function mainLoop(){
  for (var i=0; i < _tabInputFields.length; i++) { // Je vérifie chaque champ qui attend une auto-complétion
    // Si l'internaute a tapé une nouvelle lettre dans ce champ (ou s'il en a supprimé) depuis la dernière boucle,...
    // ... ET SI la partie présente dans le champ ne contient pas une suite de lettres qui n'ont plus aucune correspondance dans la BDD
    if(_tabInputFields[i].value != _tabInputFields[i].oldValue && (_tabInputFields[i].value.indexOf(_tabInputFields[i].stopReq) == -1)){
      if(_champsCaches[_tabInputFields[i].id]){ // S'il y a un champ caché pour l'ancienne valeur de ce champ,...
        _form.removeChild(_champsCaches[_tabInputFields[i].id]); // ... il faut l'effacer dans le code html ...
        delete _champsCaches[_tabInputFields[i].id]; // ... et effacer la variable qui contient ce champ dans mon code javascript
      }
      var valeur = escapeURI(_tabInputFields[i].value); // Fonction qui échappe les caractères spéciaux avant la requete
//      var valeur = _tabInputFields[i].value;
      callSuggestions(valeur, _tabInputFields[i]); // Fonction qui fait la requete et interprète le résultat obtenu
      _tabInputFields[i].oldValue = _tabInputFields[i].value; // Je réinitialise oldValue pour stopper la recherche lors de la prochaine boucle
    }
  }
  setTimeout("mainLoop()",200); // la fonction se redéclenchera dans 200 ms
  return true
}
 
function callSuggestions(valeur, inputField){
  _xmlHttp = null;
  if(_xmlHttp && _xmlHttp.readyState != 0){
    _xmlHttp.abort()
  }
  _xmlHttp = get_Xhr();
  if(_xmlHttp){
    // Création de la requete
    // Je dois d'abord vérifier s'il y a d'autres infos à envoyer en plus pour la requete (c'est précisé dans le fichier qui initialise l'autocomplete AC_FORM_???.js)
    var champsSupp = '';
    if(inputField.champsSupp){ // S'il y a des valeurs en plus à joindre dans la requete
      for(var i=0; i<inputField.champsSupp.length; i++){ // Pour chaque valeur je complète la requete
        if(document.getElementById(inputField.champsSupp[i])){ // S'il y a déjà un pays mentionné dans le champ pays
  //        champsSupp = '&id'+inputField.champsSupp[i].id+'='+inputField.champsSupp[i].value;
          champsSupp = '&'+inputField.champsSupp[i]+'='+document.getElementById(inputField.champsSupp[i]).value;
        }
      }
    }
    // J'envoie la requete
    _xmlHttp.open("GET",_adresseRecherche+"?debut="+valeur+"&champ="+inputField.champBDD+champsSupp,true);
 
    _xmlHttp.onreadystatechange = function() {
      if(_xmlHttp.readyState==4 && _xmlHttp.status == 200 && _xmlHttp.responseText != 'false') { // Si je reçois des données, autres que 'false'
        inputField.stopReq = null; // Si la requete retourne une réponse, alors il n'y a pas encore de raisons de bloquer les requetes suivantes si l'internaute continue à taper la suite de son mot
        if(document.getElementById("autoComplete_"+inputField.id)){ // Si j'ai déjà un div "autocomplete" pour ce champ
          var _divListe = document.getElementById("autoComplete_"+inputField.id); // j'initialise la var _divListe (plus facile pour la suite)
          if(_divListe.hasChildNodes()){ // Si _divListe a déjà un noeud enfant (s'il y a déjà des suggestions qui sont faites)
            _divListe.removeChild(_divListe.firstChild); // Je supprime les dernières suggestions en vue de les remplacer par celles qui arrivnet
          }
        }else{ // Si je n'ai pas encore de div "autocomplete" pour ce champ, alors je le crée
          // Calcul du décalage en haut
          var parent = inputField.offsetParent;
          var top = inputField.offsetTop;
          while (parent) {
            top += parent.offsetTop;
            parent=parent.offsetParent;
          }
 
          // Calcul du décalage à gauche
          var parent = inputField.offsetParent;
          var left = inputField.offsetLeft;
          while (parent) {
            left += parent.offsetLeft;
            parent=parent.offsetParent;
          }
 
          // Calcul de la largeur          
          var width = (inputField.offsetWidth);
 
          // On crée un nouvel élément          
          var _divListe = document.createElement("DIV"); // création d'un nouvel élément
 
          _divListe.style.top = (top+inputField.offsetHeight)+"px"; // je le positionne par rapport à mon champ de formulaire
          _divListe.style.left = left+"px";
          _divListe.style.width = width+"px";
          _divListe.id = "autoComplete_"+inputField.id; // je lui donne un id
          _divListe.className = "listeAutoCompletion"; // je l'attribue à une classe de mise en page
 
          document.body.appendChild(_divListe); // je l'attache à body, j'en fait un enfant de body
        }
        _divListe.innerHTML = _xmlHttp.responseText; // J'insère la réponse qui m'arrive dans le div créé auparavant
        for(var i = 0; i < _divListe.getElementsByTagName("li").length; i++){
          _divListe.getElementsByTagName("li")[i].numSugg = i; // J'attibue un numéro à chaque suggestion, pour pouvoir identifier laquelle est sélectionnée ou doit être sélectionnée
          _divListe.getElementsByTagName("li")[i].onmouseover = function(){
            // Je désélectionne la sélection active (s'il y en a une)
            if(_selectedSugg >= 0){ // S'il y a déjà une suggestion sélectionnée, alors je la désélectionne
              // Je change l'atribut class="" de cette suggestion (de sugg à selectedSugg)
              document.getElementById("autoComplete_"+inputField.id).getElementsByTagName("li")[_selectedSugg].className = "sugg";
            }
            // La suggestion sélectionnée change de numéro
            _selectedSugg = this.numSugg;
            // Je change l'atribut class="" de cette suggestion (de sugg à selectedSugg)
            document.getElementById("autoComplete_"+inputField.id).getElementsByTagName("li")[_selectedSugg].className = "selectedSugg";
          }
          _divListe.getElementsByTagName("li")[i].onmouseout = function(){
            // Je change l'atribut class="" de cette suggestion (de sugg à selectedSugg)
            document.getElementById("autoComplete_"+inputField.id).getElementsByTagName("li")[_selectedSugg].className = "sugg";
            _selectedSugg = -1;
 
          }
          _divListe.getElementsByTagName("li")[i].onmousedown = function(){
            inputField.value = this.getAttribute("valeur"); // J'attribue la valeur au champ texte
            inputField.oldValue = inputField.value; // J'attribue la valeur que l'internaute vient de sélectionner comme ancienne valeur, pour éviter de faire encore une requete AJAX qui retournera la même suggestion lors de la prochaine boucle
            hiddenUniqueId(inputField); // Je crée un champ caché contenant l'id de la proposition sélectionnée
          }
        }
//        _divListe.appendChild(document.getElementById("liste_"+inputField.id)); // J'attache la liste au div "autocomplete" de ce champ
      }else if(_xmlHttp.readyState == 4 && _xmlHttp.responseText == 'false'){ // Si je n'ai pas de réponse, ou une réponse vide (false renvoyé par autocompletion.php5)
        if(document.getElementById("autoComplete_"+inputField.id)){ // S'il y a un div "autocomplete" pour ce champ
          document.body.removeChild(document.getElementById("autoComplete_"+inputField.id)); // Alors je le supprime
        }
        if(inputField.value != ""){
          inputField.stopReq = inputField.value;
        }else{
          inputField.stopReq = null;
        }
        _selectedSugg = -1; // Pour que le premier element sélectionné avec le clavier soit bien le premier ou le dernier de la liste, et pas le troisième par exemple
      }
    }
    // envoi de la requete
    _xmlHttp.send(null);
  }
}
 
// Fonction pour le keydown du champ texte
var onKeyDownFunction = function(event){
  // accès evenement compatible IE/Firefox
  if(!event&&window.event) {
    event=window.event;
  }
  _eventKeycode=event.keyCode;
//  alert(_eventKeycode);
  // Dans les cas touches touche haute(38) ou touche basse (40)
  if(_eventKeycode == 40 || _eventKeycode == 38) {
    // S'il n'y a pas de suggestion affichée, si on appuye sur fleche haut ou bas, on force une recherche AJAX. C'est utile si l'internaute tape une lettre, puis perd le focus, puis reprend le focus et veut à nouveau avoir la lsite des suggestions pour ce qu'il avait déjà tapé
    if(!document.getElementById("autoComplete_"+this.id)){
      this.oldValue = ""; // Je réinitialise cette vairable qui va alors déclencher la requete AJAX lors de la prochaine boucle
      return;
    }
    var nbSugg = document.getElementById("autoComplete_"+this.id).getElementsByTagName("li").length; // Je calcule le nombre de suggestions renvoyées par le serveur
    if(_selectedSugg >= 0 && _selectedSugg < nbSugg){ // S'il y a déjà une suggestion sélectionnée, alors je la désélectionne
    // Je change l'atribut class="" de cette suggestion (de sugg à selectedSugg)
      document.getElementById("autoComplete_"+this.id).getElementsByTagName("li")[_selectedSugg].className = "sugg";
    }
    if(_eventKeycode == 40) { // Fleche bas
      if(_selectedSugg >= nbSugg-1){ // Si la suggestion sélectionnée avant que l'internaute n'appuye sur une flèche était la dernière suggestion, alors on sélectionne la première suggestion
        _selectedSugg = -1;
      }
      _selectedSugg++; // On sélectionne la suggestion suivante
    }
    if(_eventKeycode == 38) { // Fleche haut
      if(_selectedSugg <= 0){ // Si la suggestion sélectionnée avant que l'internaute n'appuye sur une flèche était la première suggestion, alors on sélectionne la dernière suggestion
        _selectedSugg = nbSugg;
      }
      _selectedSugg--; // On sélectionne la suggestion précédente
    }
    // Je change l'atribut class="" de cette suggestion (de selectedSugg à sugg)
    document.getElementById("autoComplete_"+this.id).getElementsByTagName("li")[_selectedSugg].className = "selectedSugg";
    // J'inscris la valeur sélectionnée dans le champs
    this.value = document.getElementById("autoComplete_"+this.id).getElementsByTagName("li")[_selectedSugg].getAttribute("valeur");
    // Il faut éviter de refaire une requete, pcq sinon on perd les sélections. Il ne faudra le faire que si l'internaute valide son choix
    this.oldValue = this.value;
  }
 
  // Dans le cas de la touche enter (3 ou 13) ou tab (9)
  if(_eventKeycode==13 || _eventKeycode==3 || _eventKeycode == 9){
    if(document.getElementById("autoComplete_"+this.id) && document.getElementById("autoComplete_"+this.id).getElementsByTagName("li")[_selectedSugg].getAttribute("valeur") != ""){ // S'il y a une suggestion sélectionnée,...
 
      hiddenUniqueId(this); // Je crée un champ caché contenant l'id de la proposition sélectionnée
 
      this.value = document.getElementById("autoComplete_"+this.id).getElementsByTagName("li")[_selectedSugg].getAttribute("valeur"); // ... on l'insère dans le champs texte
      this.oldValue = this.value; // Pour éviter de faire une requete lors de la prochaine boucle
      if(_eventKeycode==13 || _eventKeycode==3){ // Si l'internaute a appuyé sur une touche "enter"
        this.blur(); // C'est pour supprimer la listes des suggestions (valable seulement pour les touches enter, car la touche tab fait un blur d'office en passant au champs suivant)
 
        // Ici je vais simuler un appui sur la touche tab pour passer au tabindex suivant
        var tab = parseInt(this.getAttribute("tabindex")); // Je récupère le numéro du tabindex du champ actuel
        tab++; // J'incrémente le tabindex pour passer au suivant
        var allFields = _form.elements; // Je récupère un tableau avec tous les éléments de ce formulaire-ci
        for(var i=0; i!=allFields.length; i++) { // Je vérifie le tabindex de chaque élément de formulaire
          if(parseInt(allFields[i].getAttribute("tabindex")) == tab) { // Si c'est celui-ci le suivant,...
            allFields[i].focus(); // ... alors je lui donne le focus
            break
          }
        }
        return false // Pour que la touche enter ne valide pas le formulaire maintenant
      }
    }
  }
}
 
// Fonction pour le keyup du champ texte
var onKeyUpFunction = function(event){
  // accès evenement compatible IE/Firefox
  if(!event&&window.event) {
    event=window.event;
  }
  _eventKeycode=event.keyCode;
  // Dans le cas de la touche fleche vers le haut (38)
  if(_eventKeycode == 38) {
    // Je dois remettre le curseur en fin de texte dans le champ texte (pcq avec les flèches haut et bas, on fait bouger aussi le curseur dans le champ texte)
    // NOTE: J'aurais préféré mettre cette fonction dans le keyDown, mais le curseur ne bouge que sur le keyUp. Donc si je le met en fin de keyDown, il se met à la fin puis quand on relache la touche, il revient de 1 caractère en arrière
    setCursorToEnd(this); // La fonction setCursorToEnd() est définie plus bas et je ne l'ai pas pondue moi-même
  }
}
 
// Fonction qui fait tout disparaitre et qui réinitialise certaines variables lorsqu'on quitte le champ
var onBlurFunction = function() { // Quand on quitte ce champ qui attend l'auto-completion
  if(document.getElementById("autoComplete_"+this.id)){ // S'il y a un div "autocomplete" pour ce champ avec des suggestions
    document.body.removeChild(document.getElementById("autoComplete_"+this.id)); // Alors je le supprime
  }
  _selectedSugg = -1; // Je réinitialise cette variable
}
 
// Fonction qui supprime toutes les suggestions affichées lors du resize de la fenetre.
// NOTE: plutot que de tout supprimer, je pourrais replcer les suggestions au bon endroit et tout recalculer...
var onResizeFunction = function(){ // Si l'internaute resize la fenetre,...
  for (var i=0; i < _tabInputFields.length; i++) { // ... pour chaque champ à autocompléter...
    _tabInputFields[i].blur(); // ... je lui fais perdre le focus, comme ça je fais disparaitre les suggestions qui étaient éventuellemt affichées.
  }
}
 
// Fonction qui sert à la fonction setCursorToEnd(), et peut-être à autre chose, mais je ne sais pas, ce n'est pas moi qui l'ai écrite
function setSelectionRange(input, selectionStart, selectionEnd) {
  if (input.setSelectionRange) {
    input.focus();
    input.setSelectionRange(selectionStart, selectionEnd);
  }else if (input.createTextRange) {
    var range = input.createTextRange();
    range.collapse(true);
    range.moveEnd('character', selectionEnd);
    range.moveStart('character', selectionStart);
    range.select();
  }
  return false
}
 
// Fonction qui sert à mettre le pointeur en fin de champ texte (je n'ai pas écrit cett fonction moi-même)
function setCursorToEnd (input) {
  setSelectionRange(input, input.value.length, input.value.length);
}
 
// Fonction qui échappe les caractère spéciaux (en fonction des navigateurs)
function escapeURI(valeur){
  if(encodeURIComponent) {
    return encodeURIComponent(valeur);
  }
  if(escape) {
    return escape(valeur)
  }
}
 
// Fonction qui génère un champ hidden avec le uniqueId correspondant au choix de l'internaute pour un champ
function hiddenUniqueId(champInput){
//  alert("toto");
  // S'il y a un uniqueId qui correspond à la proposition sélectionnée, je dois rajouter un champ hidden qui contient ce uniqueId
  if(document.getElementById("autoComplete_"+champInput.id).getElementsByTagName("li")[_selectedSugg].getAttribute("uniqueid")){
    _champsCaches[champInput.id] = document.createElement('input'); // création d'un nouvel élément
    var champ = _champsCaches[champInput.id];
    champ.id = "id"+champInput.id; // je lui donne un id
    champ.setAttribute('type','hidden');
    champ.setAttribute('name','parametres[Champs][id'+champInput.id+']');
    champ.value = document.getElementById("autoComplete_"+champInput.id).getElementsByTagName("li")[_selectedSugg].getAttribute("uniqueid");
    _form.appendChild(champ); // je l'attache au formulaire, j'en fait un enfant du formulaire
    return true
  }
}
 
////////////////////////////////////////////////////////////////////////////////////////////
// TOUT CE QUI SE TROUVE EN DESSOUS DE CETTE LIGNE N EST PAS EMPLOYE
// calcule le décalage à gauche
function calculateOffsetLeft(r){
  return calculateOffset(r,"offsetLeft")
}
 
// calcule le décalage vertical
function calculateOffsetTop(r){
  return calculateOffset(r,"offsetTop")
}
 
function calculateOffset(r,attr){
  var kb=0;
  while(r){
    kb+=r[attr];
    r=r.offsetParent
  }
//  alert(kb);
  return kb
}
// calcule la largeur du champ
function calculateWidth(inputField){
//  alert(inputField.offsetWidth-2*1);
  return inputField.offsetWidth-2*1
}
function setCompleteDivSize(div, inputField){
  div.style.left=calculateOffsetLeft(inputField)+"px";
  div.style.top=calculateOffsetTop(inputField)+inputField.offsetHeight+"px";
  div.style.width=calculateWidth(inputField)+"px"
}
//////////////////////////////////////////////////////////////////////////////////////////
//FindPos(document.getElementById("prenom"));
function FindPos(AObject){
  var posX = 0;
  var posY = 0;
  do{
    posX += AObject.offsetLeft;
    posY += AObject.offsetTop;
    AObject = AObject.offsetParent;
  }
  while( AObject != null );
  var pos = [];
  pos['X'] = posX;
  pos['Y'] = posY;  
 
  alert(posX);
  return pos;
} | 
Partager