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);
|