1 2 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