234 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- coding: utf-8 mode: javascript -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
 | |
| */
 | |
| (function($) {
 | |
|   $.resetTabindex = function(expr) {
 | |
|     // Par défaut, prendre tous les champs de saisie *visibles*
 | |
|     if (expr === undefined) expr = ":input:visible";
 | |
|     $(expr).attr("tabindex", 0);
 | |
|   };
 | |
|   $.initTabindex = function(expr) {
 | |
|     // Par défaut, prendre tous les champs de saisie *visibles*
 | |
|     if (expr === undefined) expr = ":input:visible";
 | |
|     // D'abord calculer la plus grande valeur de tabindex
 | |
|     var tabindex = 0;
 | |
|     var $expr = $(expr);
 | |
|     $expr.each(function() {
 | |
|       var $this = $(this);
 | |
|       var ti = parseInt($this.attr("tabindex"));
 | |
|       if (ti > tabindex) tabindex = ti;
 | |
|     });
 | |
|     // Puis initialiser les éléments restants à partir de tabindex + 1
 | |
|     tabindex += 1;
 | |
|     $expr.each(function() {
 | |
|       var $this = $(this);
 | |
|       var ti = parseInt($this.attr("tabindex"));
 | |
|       if (!ti) $this.attr("tabindex", tabindex++);
 | |
|     });
 | |
|   };
 | |
|   $.updateTabindex = function(expr) {
 | |
|     if (expr === undefined) {
 | |
|       $.resetTabindex(":input");
 | |
|       $.initTabindex(":input:visible");
 | |
|     } else {
 | |
|       $.resetTabindex(expr);
 | |
|       $.initTabindex(expr);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // Dans les fonctions suivantes, input est un objet non wrappé par
 | |
|   // jQuery. $input est le même objet wrappé par jQuery.
 | |
|   var isdefined = function(value) { return value !== null && value !== undefined; }
 | |
|   var isempty = function(input) { return $(input).val().length === 0; }
 | |
|   var hasSelection = function(input) {
 | |
|     // Tester si l'élément peut contenir une sélection. Typiquement,
 | |
|     // il s'agit d'un champ de saisie de texte.
 | |
|     try {
 | |
|       return isdefined(input.selectionStart)
 | |
|     } catch (e) {
 | |
|     }
 | |
|   };
 | |
|   var isNavigable = function(input) {
 | |
|     // Tester si l'élément est navigable avec la touche Entrée
 | |
|     // Par défaut, on prend tous les éléments de type :input sauf textarea et submit
 | |
|     var $input = $(input);
 | |
|     return $input.is(":input") && !$input.is("textarea, :submit, :reset, :button")
 | |
|   };
 | |
|   var noSel = function(input) {
 | |
|     if (! hasSelection(input)) return true;
 | |
|     return input.selectionStart === input.selectionEnd;
 | |
|   };
 | |
|   var allSel = function(input) {
 | |
|     if (! hasSelection(input)) return false;
 | |
|     var length = $(input).val().length;
 | |
|     return input.selectionStart === 0 && input.selectionEnd === length;
 | |
|   };
 | |
|   var noSelOrAllSel = function(input) {
 | |
|     return noSel(input) || allSel(input);
 | |
|   };
 | |
|   var focusinput = function(tabindex, select) {
 | |
|     if (tabindex > 0) {
 | |
|       var $input = $("[tabindex='" + tabindex + "']");
 | |
|       $input.focus();
 | |
|       if (select) $input.select();
 | |
|     }
 | |
|   };
 | |
|   var previnput = function(input, select) {
 | |
|     var tabindex = $(input).attr("tabindex");
 | |
|     if (tabindex) focusinput(parseInt(tabindex) - 1, select);
 | |
|   };
 | |
|   var nextinput = function(input, select) {
 | |
|     var tabindex = $(input).attr("tabindex");
 | |
|     if (tabindex) focusinput(parseInt(tabindex) + 1, select);
 | |
|   };
 | |
|   var atbegin = function(input) {
 | |
|     if (! hasSelection(input)) return true;
 | |
|     else if (noSel(input)) return input.selectionStart === 0;
 | |
|     else return false;
 | |
|   };
 | |
|   var atend = function(input) {
 | |
|     if (! hasSelection(input)) return true;
 | |
|     else if (noSel(input)) return input.selectionEnd === $(input).val().length;
 | |
|     else return false;
 | |
|   };
 | |
|   var hideAutocompleter = function(input) {
 | |
|     var select = $(input).data("ac.select");
 | |
|     if (select) select.hide();
 | |
|   };
 | |
|   var submitForm = function(input) {
 | |
|     var $form = $(input).closest("form");
 | |
|     var found = false;
 | |
|     if ($form.length) {
 | |
|       var phase = 0;
 | |
|       $form.find(":input").each(function() {
 | |
|         if (phase === 0 && this === input) {
 | |
|           phase = 1;
 | |
|         } else if (phase === 1 && $(this).is("input[type='submit'], button[type='submit']")) {
 | |
|           found = true;
 | |
|           $(this).click();
 | |
|           return false;
 | |
|         }
 | |
|       });
 | |
|       if (!found) {
 | |
|         // si on ne trouve pas de bouton après l'élément, prendre le premier
 | |
|         var $submit = $form.find("input[type='submit'], button[type='submit']").first();
 | |
|         if ($submit.length) {
 | |
|           found = true;
 | |
|           $submit.click();
 | |
|         }
 | |
|       }
 | |
|       if (!found) {
 | |
|         // s'il n'y a aucun bouton, demander au formulaire de se soumettre lui-même
 | |
|         $form.submit();
 | |
|       }
 | |
|     }
 | |
|     return found;
 | |
|   };
 | |
| 
 | |
|   $.navigate = function(onEnter, selectAfterFocus, initTabindex) {
 | |
|     // Action par défaut: next
 | |
|     if (onEnter === undefined) onEnter = "next";
 | |
|     // Faut-il sélectionner le champ après avoir changé le focus?
 | |
|     if (selectAfterFocus === undefined) selectAfterFocus = true;
 | |
|     // Faut-il initialiser tabindex pour les éléments sélectionnés? Sinon,
 | |
|     // il FAUT spécifier les valeurs de tabindex pour tous les champs
 | |
|     // concernés par la navigation
 | |
|     if (initTabindex === undefined) initTabindex = true;
 | |
| 
 | |
|     if (onEnter === "submit") {
 | |
|       $(":input").off(".navigate").on("keypress.navigate", function(e) {
 | |
|         if (e.which === 13 && isNavigable(this)) {
 | |
|           e.preventDefault();
 | |
|           submitForm(this);
 | |
|         }
 | |
|       });
 | |
|     } else if (onEnter === "next") {
 | |
|       var willMove = false;
 | |
|       if (initTabindex) $.initTabindex(":input");
 | |
|       $(":input").off(".navigate").on("keypress.navigate", function(e) {
 | |
|         if (e.which === 8 && hasSelection(this) && isempty(this)) {
 | |
|           // backspace sur un champ vide déplace la sélection sur le champ précédent
 | |
|           //hideAutocompleter(this);
 | |
|           e.preventDefault();
 | |
|           previnput(this);
 | |
|         } else if (e.which === 13) {
 | |
|           if (e.ctrlKey) {
 | |
|             // Ctrl-Entrée soumet le formulaire
 | |
|             e.preventDefault();
 | |
|             submitForm(this);
 | |
|           } else if (isNavigable(this)) {
 | |
|             // Entrée sur un champ déplace la sélection sur le champ suivant
 | |
|             e.preventDefault();
 | |
|             nextinput(this, selectAfterFocus);
 | |
|           }
 | |
|         }
 | |
|       }).on("keydown.navigate", function(e) {
 | |
|         willMove = false;
 | |
|         if (e.which === 37 && atbegin(this)) {
 | |
|           // flèche gauche au début du champ déplace la sélection sur le champ précédent
 | |
|           if (!hasSelection(this) || isempty(this) || !allSel(this)) {
 | |
|             willMove = true;
 | |
|             e.preventDefault();
 | |
|           }
 | |
|         } else if (e.which === 39 && atend(this)) {
 | |
|           // flèche droite à la fin du champ déplace la sélection sur le champ suivant
 | |
|           if (!hasSelection(this) || isempty(this) || !allSel(this)) {
 | |
|             willMove = true;
 | |
|             e.preventDefault();
 | |
|           }
 | |
|         }
 | |
|       }).on("keyup.navigate", function(e) {
 | |
|         if (e.which === 37 && willMove) {
 | |
|           // flèche gauche au début du champ déplace la sélection sur le champ précédent
 | |
|           //hideAutocompleter(this);
 | |
|           previnput(this, selectAfterFocus);
 | |
|         } else if (e.which === 39 && willMove) {
 | |
|           // flèche droite à la fin du champ déplace la sélection sur le champ suivant
 | |
|           //hideAutocompleter(this);
 | |
|           nextinput(this, selectAfterFocus);
 | |
|         }
 | |
|       });
 | |
|     } else if (onEnter === "disable" || onEnter === "off") {
 | |
|       $(":input").off(".navigate");
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // Activer le focus et placer le curseur à la fin de la ligne spécifiée.
 | |
|   // Par défaut, il s'agit de la première ligne (soit line==0)
 | |
|   // Si setSelectionRange n'est pas supporté, c'est un NOP
 | |
|   $.fn.cursorAtEOFL = function(line) {
 | |
|     if (line === undefined) line = 0;
 | |
|     return this.each(function() {
 | |
|       var $this = $(this);
 | |
|       if (this.setSelectionRange) {
 | |
|         var text = $this.val();
 | |
|         var pos = 0;
 | |
|         while (line-- >= 0) {
 | |
|           var index = text.search('\r?\n');
 | |
|           if (index !== -1) {
 | |
|             // index est la position avant [CR]LF, indexnl la position après [CR]LF
 | |
|             var indexnl = index;
 | |
|             if (text.charAt(indexnl) === '\r') indexnl++;
 | |
|             indexnl++;
 | |
| 
 | |
|             if (line === -1) {
 | |
|               // à la dernière ligne, considérer index, parce
 | |
|               // qu'on veut se placer avant [CR]LF
 | |
|               pos += index;
 | |
|             } else {
 | |
|               // avant la dernière ligne, considérer indexnl, parce
 | |
|               // qu'on veut avoir un compte exact
 | |
|               pos += indexnl;
 | |
|             }
 | |
|             text = text.substr(indexnl);
 | |
|           } else {
 | |
|             pos += text.length;
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         this.setSelectionRange(pos, pos);
 | |
|       }
 | |
|       $this.focus();
 | |
|     });
 | |
|   };
 | |
| })(jQuery);
 |