Conception de contrôles Web JavaScript avancés : Créer une table HTML éditable - HtmlEditTable v2.0


précédentsommairesuivant

III. Navigation au clavier

Pour l'instant les seules interactions que l'on ait avec la table sont :

  • mise en édition d'une cellule par double-clic
  • écriture dans la zone de texte d'une cellule en édition
  • fin de l'édition par perte du focus de la cellule ou pression sur la touche Entrée

Ce qui serait franchement intéressant, c'est de pouvoir saisir les valeurs dans notre table sans avoir recours à la souris pour passer d'une cellule à l'autre. Il faut donc ajouter des événements supplémentaires à ceux déjà mis en place. Pour que l'utilisation de l'HtmlEditTable soit convivial, nous aurons donc besoin de pouvoir :

  • initier l'édition d'une cellule par pression de la touche Entrée
  • annuler l'édition d'une cellule par pression de la touche Echap
  • naviguer d'une cellule à l'autre à l'aide des flèches
  • valider une saisie et mettre la cellule suivante dans la même colonne en édition par pression sur la touche Tab

III-A. Prémisses

Naviguer dans la table c'est bien, mais il faut pouvoir le visualiser. C'est-à-dire que la cellule active doit être mise en évidence! Pour cela il faut être capable de déterminer qu'elle est la cellule active et modifier sa classe CSS.

Modifions le constructeur de la classe HtmlEditTable pour y introduire la propriété activeCell initialisée à null et qui référencera la cellule active.

Constructeur HtmlEditTable - v1.2
Sélectionnez

  HtmlEditTable = function(){
    if (arguments.length > 0 && arguments[0].table){
      this.Transform(arguments);      
    }
    else{
      this.control = document.createElement("table");
      if (arguments.length > 0){
        this.Build(arguments[0]);
      }
    }	
    this.control.cellSpacing = 0;
    this.control.cellPadding = 0;
    this.control.className = "HtmlEditTable";
    this.activeCell = null;
  };

Implémentons la fonction HtmlEditTable.prototype.ActiveCellChange() qui sera appelée lorsque l'on change de cellule active.

HtmlEditTable.prototype.ActiveCellChange
Sélectionnez

ActiveCellChange: function(cell, focus){
  if (this.activeCell){
    this.activeCell.className = null;
  }
  this.activeCell = cell;
    if (cell){
      this.activeCell.className = "activeCell";
      if (focus){
        this.activeCell.focus();
      }
    }
  }
}	

Si une cellule était active avant le changement, alors sa classe CSS avait été modifiée. Donc on réinitialise cette valeur. Ensuite on référence la nouvelle cellule active qui est passée en paramètre, on lui donne la classe CSS activeCell ainsi que le focus suivant le booléen passé en paramètre.

.HtmlEditTable .activeCell
Sélectionnez

.HtmlEditTable .activeCell
{
	background-color: RGB(201,200,181);
}		

Maintenant que nous avons presque tous les outils nécessaires à la reconnaissance et à la mise en évidence de la cellule active, utilisons-les lors du double-clic sur une cellule pour l'édition. Ce qu'il nous manque, c'est une référence sur l'objet HtmlEditTable accessible par les cellules. En effet, lorsque l'on va effectuer une action sur une cellule, il faut pouvoir appeler la fonction HtmlEditTable.prototype.ActiveCellChange(). Pour ce faire, ajoutons à chaque cellule la propriété grid lors de l'appel à la fonction :

HtmlEditTableHelper.CellInitialize v1.1
Sélectionnez
				
CellInitialize: function (grid, cell, value){
  if (cell){
    cell.grid = grid;
    cell.ondblclick = HtmlEditTableHelper.DblClickHandler;
    if (typeof value != "undefined"){
      cell.appendChild(document.createTextNode(value));
    }
  }
}	

Tous les appels à la fonction HtmlEditTableHelper.CellInitialize() dans les fonctions de la classe HtmlEditTable doivent être modifiés en passant this en premier paramètre de cette fonction :

Appels de HtmlEditTableHelper.CellInitialize() dans la classe HtmlEditTable
Sélectionnez

HtmlEditTableHelper.CellInitialize(this, ...);

La fonction loseFocus locale au constructeur de la classe HtmlEdit est modifiée de la façon suivante :

Constructeur HtmlEdit v1.1
Sélectionnez

var loseFocus = function(e){
  var src = Tools.Target(e);
  var cell = Tools.Node(src, "TD");
  var grid = cell.grid;
  Tools.Purge(cell);
  HtmlEditTableHelper.CellInitialize(grid, cell, src.value);
};

Finalement, la fonction HtmlEditTable.prototype.ActiveCellChange() peut être appelée lors du double-clic sur une cellule.

HtmlEditTableHelper.DblClickHandler v1.2
Sélectionnez

DblClickHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  var htmlEdit = new HtmlEdit(src.firstChild.data);
  htmlEdit.AppendTo(src);
  src.ondblclick = null;
  src.grid.ActiveCellChange(src, false);
}	

La nouveauté vient de la dernière instruction de la fonction, l'appel à HtmlEditTable.prototype.ActiveCellChange(). Vous constaterez que maintenant, lorsque vous double-cliquez sur une cellule puis validez par pression sur la touche Entrée, la cellule active à changé de couleur. Si vous éditez une autre cellule, la précédente reprend son aspect normal et la nouvelle cellule active à changé de couleur à la fin de l'édition.

Il faudrait aussi que la cellule active soit désignée comme la cellule sur laquelle on clique (simple-clic). Ce sera le rôle de la fonction HtmlEditTableHelper.ClickHandler().

HtmlEditTableHelper.ClickHandler
Sélectionnez

ClickHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  src.grid.ActiveCellChange(src, true);
}	

Son implémentation est identique à celle de HtmlEditTableHelper.DblClickHandler(), moins la mise en édition de la cellule. Il ne reste qu'à l'assigner au gestionnaire d'événement onclick de chaque cellule via la fonction HtmlEditTableHelper.CellInitialize().

HtmlEditTableHelper.CellInitialize v1.2
Sélectionnez

CellInitialize: function (grid, cell, value){
  if (cell){
    cell.grid = grid;
    cell.onclick = HtmlEditTableHelper.ClickHandler;
    cell.ondblclick = HtmlEditTableHelper.DblClickHandler;
    if (typeof value != "undefined"){
      cell.appendChild(document.createTextNode(value));
    }
  }
}

voir la démo - télécharger les sources

III-B. Initier l'édition d'une cellule

Jusqu'ici on pouvait éditer une cellule en double-cliquant dessus. Maintenant nous sommes prêts à ajouter de nouvelles fonctionnalités de navigation au clavier! Commençons par la touche Entrée. Il serait intéressant de pouvoir éditer la cellule en appuyant sur cette touche.

Affectons un callback HtmlEditTableHelper.KeydownHandler() au gestionnaire d'événement keydown sur les cellules dans la fonction HtmlEditTableHelper.CellInitialize() :

HtmlEditTableHelper.CellInitialize v1.3
Sélectionnez

CellInitialize: function (grid, cell, value){
  if (cell){
    cell.grid = grid;
    cell.onclick = HtmlEditTableHelper.ClickHandler;
    cell.ondblclick = HtmlEditTableHelper.DblClickHandler;
    cell.onkeydown = HtmlEditTableHelper.KeydownHandler;
    if (typeof value != "undefined"){
      cell.appendChild(document.createTextNode(value));
    }
  }
}	
HtmlEditTableHelper.KeydownHandler v1.1
Sélectionnez

KeydownHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  switch(Tools.KeyCode(e)){
    case KeyCodes.RETURN: alert("return"); break;
  }
  
  returnValue = false;
  return false;
}	

Les deux dernières instructions du callback empêchent l'événement de se propager au reste du document.

N'oublions de mettre à jour également la fonction HtmlEditTableHelper.CellNeutralizeEvents() :

HtmlEditTableHelper.CellNeutralizeEvents v1.2
Sélectionnez

CellNeutralizeEvents: function(cell){
  if (cell){
    cell.onclick = null;
    cell.ondblclick = null;
    cell.onkeydown = null;
  }    
}

voir la démo - télécharger les sources

Si vous testez à ce stade vous remarquerez que cela fonctionne sur Internet Explorer (IE6 au moins), mais par exemple ni sur Firefox ni sur Chrome. Kêzako? En fait il existe des éléments capables naturellement d'être accessibles au clavier et d'autres non. Les tables, lignes et cellules font partie de la seconde catégorie d'éléments... Ce qui n'empêche nullement Internet Explorer de gérer les événements claviers sur ces éléments (sacré farceur!). Cependant il en faut plus pour les autres navigateurs.

Pour que la gestion des événements clavier soit possible, il faut que l'élément soit tabulable. C'est le cas des contrôles de formulaire et des liens. Pour les autres éléments, il suffit de donner une valeur supérieure ou égale à zéro à la propriété tabIndex.

Comportement des élément suivant le tabIndex
valeur de tabIndex focus souris ou JavaScript navigable
non renseigné comportement par défaut spécifique à l'élément comportement par défaut spécifique à l'élément
négatif oui non (le focus via JavaScript doit être donné pour que cela soit possible)
zéro oui oui
positif oui oui

Nous allons donner la valeur zéro au tabIndex des cellules du HtmlEditTable afin de les rendre tabulables. Une valeur positive entrainera une modification de l'ordre de tabulation dans la page, les éléments avec un tabIndex supérieur à zéro ayant préséance sur les éléments naturellement tabulables et ceux dont le tabIndex est à zéro. En mettant le tabIndex à zéro, on garde l'ordre de tabulation naturel, celui-ci étant imposé par l'ordre d'apparition des éléments dans le DOM du document.

La valeur du tabIndex est à fixer une fois à l'initialisation de la cellule lors de sa création ou lors de l'extension d'une table HTML en HtmlEditTable. Les fonctions impactées sont donc les suivantes :

  • HtmlEditTable.prototype.Transform()
  • HtmlEditTable.prototype.Populate()
  • HtmlEditTable.prototype.Line()
  • HtmlEditTable.prototype.Column()

Dans chacune de ces fonctions, initialisons le tabIndex des cellules avant l'appel à la fonction HtmlEditTableHelper.CellInitialize() :

Rendre les cellules tabulables
Sélectionnez

[...]
cell.tabIndex = 0;
HtmlEditTableHelper.CellInitialize(this, cell[, ...]);
[...]

Et cette fois ça marche! Pour Firefox comme pour Chrome, lorsqu'une cellule est sélectionnée et que l'on presse sur Entrée, l'alerte s'affiche. Il ne nous reste plus qu'à troquer l'alerte contre le comportement désiré, à savoir l'édition de la cellule. L'action a réalisé est la même que celle effectuée sur le double-clic de la cellule, donc :

HtmlEditTableHelper.KeydownHandler v1.2
Sélectionnez

KeydownHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  switch(Tools.KeyCode(e)){
    case KeyCodes.RETURN: HtmlEditTableHelper.DblClickHandler(e); break;
  }
  
  returnValue = false;
  return false;
}	

voir la démo - télécharger les sources

III-C. Annuler l'édition d'une cellule

Une convention largement répandue est que la touche Echap annule l'action en cours. Dans notre cas il serait intéressant de pouvoir annuler l'édition d'une cellule, c'est-à-dire de revenir en mode visualisation de la cellule sans que sa valeur ne soit modifiée.

L'implémentation de cette fonctionnalité est très simple. Il suffit d'enrichir le constructeur de la classe HtmlEdit. D'abord renommons la fonction loseFocus en returnEvent, sauvegardons la valeur initiale de la zone de texte dans la propriété initialValue de cette dernière et donnons le focus à la cellule à la fin de l'édition. Implémentons la fonction escapeEvent sur le modèle de returnEvent, mais en passant la valeur de la propriété initialValue au lieu de value. Il ne reste plus qu'à appeler cette nouvelle fonction sur le keydown de la zone de texte.

Constructeur HtmlEdit v1.2
Sélectionnez

  var HtmlEdit = function(value){
    var returnEvent = function(e){
      var src = Tools.Target(e);
      var cell = Tools.Node(src, "TD");
	  var grid = cell.grid;
      Tools.Purge(cell);
      HtmlEditTableHelper.CellInitialize(grid, cell, src.value);
      cell.focus();
    };
  
    var escapeEvent = function(e){
      var src = Tools.Target(e);
      var cell = Tools.Node(src, "TD");
	  var grid = cell.grid;
      Tools.Purge(cell);
      HtmlEditTableHelper.CellInitialize(grid, cell, src.initialValue);
      cell.focus();
    };
  
    this.control = document.createElement("input");
    this.control.type = "text";
    this.control.className = "HtmlEdit";
    this.control.onblur = returnEvent;
    this.control.onkeydown = function(e){
      switch(Tools.KeyCode(e)){
        case KeyCodes.RETURN: returnEvent(e); break;
        case KeyCodes.ESCAPE: escapeEvent(e); break;
      }
    };
  
    this.control.value = value;
    this.control.initialValue = value;
  };

voir la démo - télécharger les sources

III-D. Supprimer le contenu d'une cellule

Voilà sûrement la fonctionnalité nécessitant le moins de code :) Pour supprimer le contenu d'une cellule en lecture lorsqu'elle est active, il suffit de gérer la touche DEL dans le gestionnaire de l'événement keydown de la façon suivante :

HtmlEditTableHelper.KeydownHandler v1.3
Sélectionnez

KeydownHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  
  var NavDel = function(cell){
    cell.firstChild.data = "";
  };
      
  switch(Tools.KeyCode(e)){
    case KeyCodes.RETURN: HtmlEditTableHelper.DblClickHandler(e); break;
    case KeyCodes.DELETE: NavDel(src); break;
  }
  
  returnValue = false;
  return false;
}	

Pour ne pas qu'une ligne dont toutes les valeurs ont été supprimées ne prenne une taille nulle, ajoutons la classe CSS suivante au fichier CSS :

.HtmlEditTable tbody tr
Sélectionnez

.HtmlEditTable tbody tr
{
  height: 1em;
}

voir la démo - télécharger les sources

III-E. Déplacement à l'aide des flèches

Je vois que ça vous amuse, vous cliquez partout comme des p'tits fous pour changer la couleur de la cellule cliquée :) Quelle candeur, quelle fraîcheur. Ô joie et inconscience de la jeunesse! (pétage de câble...) Bref, allons plus loin et voyons comment nous passer de la souris.

Le top du top serait de pouvoir passer d'une cellule à l'autre facilement en ne bougeant qu'imperceptiblement nos petites mimines. Économisons nos forces ;). Du coup, l'idéal serait de pouvoir utiliser les flèches du clavier! Très simple une fois encore...

Modifions le callback HtmlEditTableHelper.KeydownHandler() :

HtmlEditTableHelper.KeydownHandler v1.4
Sélectionnez

KeydownHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }
  
  var NavDel = function(cell){
    cell.firstChild.data = "";
  };
      
  switch(Tools.KeyCode(e)){
    case KeyCodes.RETURN: HtmlEditTableHelper.DblClickHandler(e); break;
    case KeyCodes.DELETE: NavDel(src); break;
    case KeyCodes.LEFT: alert("left"); break;
    case KeyCodes.UP: alert("up"); break;
    case KeyCodes.RIGHT: alert("right"); break;
    case KeyCodes.DOWN: alert("down"); break;
  }
  
  returnValue = false;
  return false;
}	

Pour finaliser la navigation par les flèches du clavier, il suffit de remplacer les alertes dans la fonction HtmlEditTableHelper.KeydownHandler() par les fonctions adéquates :

  • NavLeft : active la cellule à gauche de la cellule courante s'il y en a une
  • NavRight : active la cellule à droite de la cellule courante s'il y en a une
  • NavUp : active la cellule au-dessus de la cellule courante s'il y en a une
  • NavDown : active la cellule au-dessous de la cellule courante s'il y en a une

La fonction HtmlEditTableHelper.KeydownHandler() devient donc :

HtmlEditTableHelper.KeydownHandler v1.5
Sélectionnez

KeydownHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }

  var NavDel = function(cell){
    cell.firstChild.data = "";
  };

  var NavLeft = function(cell){
    cell = Tools.Node(cell.previousSibling, "TD");
    if (cell){
      cell.grid.ActiveCellChange(cell, true);
    }
  };

  var NavRight = function(cell){
    cell = Tools.Node(cell.nextSibling, "TD");
    if (cell){
      cell.grid.ActiveCellChange(cell, true);
    }
  };
  
  var NavUp = function(cell){
    var row = cell.parentNode.previousSibling;
    if (row){
      cell = row.cells[cell.cellIndex];
      cell.grid.ActiveCellChange(cell, true);
    }
  };

  var NavDown = function(cell){
    var row = cell.parentNode.nextSibling;
    if (row){
      cell = row.cells[cell.cellIndex];
      cell.grid.ActiveCellChange(cell, true);
    }
  };
  
  switch(Tools.KeyCode(e)){
    case KeyCodes.RETURN: HtmlEditTableHelper.DblClickHandler(e); break;
    case KeyCodes.DELETE: NavDel(src); break;
    case KeyCodes.LEFT: NavLeft(src); break;
    case KeyCodes.UP: NavUp(src); break;
    case KeyCodes.RIGHT: NavRight(src); break;
    case KeyCodes.DOWN: NavDown(src); break;
  }
  
  returnValue = false;
  return false;
}

voir la démo - télécharger les sources

III-F. Déplacement à l'aide de la touche de tabulation

Souvent dans les tableurs, Excel par exemple, on peut passer à la cellule suivante de la même colonne par pression de la touche tabulation. Maintenant que nous avons réalisé la navigation à l'aide des flèches, il est trivial d'implémenter la tabulation. Tabuler correspond à naviguer de la même façon qu'avec la flèche bas jusqu'à la dernière cellule de la colonne, puis à passer ensuite à la première cellule de la colonne suivante. Allons même plus loin en considérant la combinaison de touche SHIFT+TAB pour passer à la cellule précédente!

D'où la nouvelle mouture de la fonction HtmlEditTableHelper.KeydownHandler() :

HtmlEditTableHelper.KeydownHandler v1.6
Sélectionnez

KeydownHandler: function(e){
  var src = Tools.Node(Tools.Target(e), "TD");
  if (!src){
    Tools.Event(e).returnValue = false;
    return false;
  }

  var NavDel = function(cell){
    cell.firstChild.data = "";
  };

  var NavLeft = function(cell){
    cell = Tools.Node(cell.previousSibling, "TD");
    if (cell){
      cell.grid.ActiveCellChange(cell, true);
    }
  };

  var NavRight = function(cell){
    cell = Tools.Node(cell.nextSibling, "TD");
    if (cell){
      cell.grid.ActiveCellChange(cell, true);
    }
  };
  
  var NavUp = function(cell){
    var row = cell.parentNode.previousSibling;
    if (row){
      cell = row.cells[cell.cellIndex];
      cell.grid.ActiveCellChange(cell, true);
    }
  };

  var NavDown = function(cell){
    var row = cell.parentNode.nextSibling;
    if (row){
      cell = row.cells[cell.cellIndex];
      cell.grid.ActiveCellChange(cell, true);
    }
  };
  
  var NavTab = function(cell){
    var shiftKey = Tools.SpecialKeys(e).ShiftKey;
    var row = shiftKey ? cell.parentNode.previousSibling : cell.parentNode.nextSibling;
    if (row){
      cell = row.cells[cell.cellIndex];
      cell.grid.ActiveCellChange(cell, true);
    }
    else{
      row = cell.parentNode;
      tBody = row.parentNode;
      if (shiftKey && cell.cellIndex>0){
        row = tBody.lastChild;
        cell = row.cells[cell.cellIndex-1];
        cell.grid.ActiveCellChange(cell, true);
      }
      if (!shiftKey && row.cells.length-1 > cell.cellIndex){
        row = tBody.firstChild;
        cell = row.cells[cell.cellIndex+1];
        cell.grid.ActiveCellChange(cell, true);
      }
    }
  };
  
  switch(Tools.KeyCode(e)){
    case KeyCodes.RETURN: HtmlEditTableHelper.DblClickHandler(e); break;
    case KeyCodes.DELETE: NavDel(src); break;
    case KeyCodes.LEFT: NavLeft(src); break;    
    case KeyCodes.UP: NavUp(src); break;
    case KeyCodes.RIGHT: NavRight(src); break;
    case KeyCodes.DOWN: NavDown(src); break;
    case KeyCodes.TAB: NavTab(src); break;
  }
  
  returnValue = false;
  return false;
}

Maintenant on peut passer en lecture d'une cellule à l'autre par tabulation. Il nous reste à gérer la touche de tabulation sur la zone de texte pour pouvoir faire de même en édition. Pour se faire, on modifie une fois de plus le constructeur de la classe HtmlEdit et on ajoute un nouveau cas dans le gestionnaire d'événement onkeydown de la zone de texte. La fonction tabEvent() commence par réaliser l'action returnEvent (comme lorsqu'on presse la touche Entrée), puis active la cellule suivante (au sens de la tabulation dans un tableur) ou précédente dans le même style que la navigation par les flèches, et finalement ajoute la zone de texte dans la nouvelle cellule active.

Constructeur HtmlEdit v1.3
Sélectionnez

  var HtmlEdit = function(value){
    var returnEvent = function(e){
      var src = Tools.Target(e);
      var cell = Tools.Node(src, "TD");
      var grid = cell.grid;
      Tools.Purge(cell);
      HtmlEditTableHelper.CellInitialize(grid, cell, src.value);
      cell.focus();
    };
  
    var escapeEvent = function(e){
      var src = Tools.Target(e);
      var cell = Tools.Node(src, "TD");
      var grid = cell.grid;
      Tools.Purge(cell);
      HtmlEditTableHelper.CellInitialize(grid, cell, src.initialValue);
      cell.focus();
    };
  
    var tabEvent = function(e){
      var src = Tools.Target(e);
      var cell = Tools.Node(src, "TD");
      var shiftKey = Tools.SpecialKeys(e).ShiftKey;
      var row = shiftKey ? cell.parentNode.previousSibling : cell.parentNode.nextSibling;
      
      returnEvent(e);
      
      if (row){
        cell = row.cells[cell.cellIndex];
        cell.grid.ActiveCellChange(cell, true);
        
        var htmlEdit = new HtmlEdit(cell.firstChild.data);
        htmlEdit.AppendTo(cell);
      }
      else{
        row = cell.parentNode;
        tBody = row.parentNode;
        if (shiftKey && cell.cellIndex>0){
          row = tBody.lastChild;
          cell = row.cells[cell.cellIndex-1];
          cell.grid.ActiveCellChange(cell, true);
          var htmlEdit = new HtmlEdit(cell.firstChild.data);
          htmlEdit.AppendTo(cell);
        }
        else if (!shiftKey && row.cells.length-1 > cell.cellIndex){
          row = tBody.firstChild;
          cell = row.cells[cell.cellIndex+1];
          cell.grid.ActiveCellChange(cell, true);
          var htmlEdit = new HtmlEdit(cell.firstChild.data);
          htmlEdit.AppendTo(cell);
        }
      }
    };
      
    this.control = document.createElement("input");
    this.control.type = "text";
    this.control.className = "HtmlEdit";
    this.control.onblur = returnEvent;
    this.control.onkeydown = function(e){
      switch(Tools.KeyCode(e)){
        case KeyCodes.RETURN: returnEvent(e); break;
        case KeyCodes.ESCAPE: escapeEvent(e); break;
        case KeyCodes.TAB: tabEvent(e); break;
      }
    };
      
    this.control.value = value;
    this.control.initialValue = value;
  };

voir la démo - télécharger les sources


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Nourdine FALOLA. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.