nutools/lib/ulib/templates/webpyapp/static/bootstrap/jquery-plugins/navigate/jquery.navigate.js

217 lines
9.3 KiB
JavaScript

/* -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
*/
;(function($) {
var DEFAULT_EXPR = ":input:visible";
$.resetTabindex = function(expr) {
// Par défaut, prendre tous les champs de saisie *visibles*
if (expr === undefined) expr = DEFAULT_EXPR;
// Réinitialiser tabindex
$(expr).each(function() {
$(this).attr("tabindex", 0);
});
};
$.initTabindex = function(expr) {
// Par défaut, prendre tous les champs de saisie *visibles*
if (expr === undefined) expr = DEFAULT_EXPR;
// D'abord calculer la plus grande valeur de tabindex
var tabindex = 0;
var $expr = $(expr);
$expr.each(function() {
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 ti = parseInt($(this).attr("tabindex"));
if (!ti) $(this).attr("tabindex", tabindex++);
});
};
$.updateTabindex = function(expr) {
if (expr === undefined) {
$.resetTabindex(":input");
$.initTabindex(DEFAULT_EXPR);
} else {
$.resetTabindex(expr);
$.initTabindex(expr);
}
};
// éléments de formulaires navigables avec la touche Entrée
$.NAVIGATE_INCLUDES = ":input";
// éléments de formulaire qui ne sont pas navigables avec la touche Entrée,
// à exclure de la liste sélectionnée avec $.NAVIGATE_INCLUDES
$.NAVIGATE_EXCLUDES = "textarea, :submit, :reset, :button";
$.navigate = function(expr, selectAfterFocus, initTabindex) {
// Par défaut, prendre tous les champs de saisie *visibles*
if (expr === undefined) expr = DEFAULT_EXPR;
// 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;
// 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($.NAVIGATE_INCLUDES) && !$input.is($.NAVIGATE_EXCLUDES);
}
var noSel = function(input) {
return input.selectionStart == input.selectionEnd;
}
var allSel = function(input) {
var length = $(input).val().length;
return length > 0 && 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 (noSelOrAllSel(input)) return input.selectionStart == 0;
else return false;
}
var atend = function(input) {
var length = $(input).val().length;
if (! hasSelection(input)) return true;
else if (noSelOrAllSel(input)) return input.selectionEnd == length;
else return false;
}
var hideAutocompleter = function(input) {
var select = $(input).data("ac.select");
if (select) select.hide();
}
if (initTabindex) $.initTabindex(expr);
// Installer les handlers pour backspace, entrée, flèches gauche et droite.
var state = {
'autocomplete': false,
'leftKeyThenPrevinput': false,
'rightKeyThenNextinput': false,
};
$(expr).keypress(function (e) {
if (state.autocomplete) {
// Si on est dans l'état auto-complétion, ne pas traiter les
// caractères pour éviter les conflits
state.autocomplete = false;
} else 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);
previnput(this);
return false;
} else if (e.which == 13) {
if (e.ctrlKey) {
if ($.wosubmit) {
$.wosubmit(this);
return false;
}
} else if (isNavigable(this)) {
// entrée sur un champ déplace la sélection sur le champ suivant
nextinput(this, selectAfterFocus);
return false;
}
}
}).keydown(function (e) {
// intercepter flèches gauche et droite pour ne pas avoir d'effet de bord qui
// gênerait le traitement des événements dans keyup
var key = e.which;
if (key == 37 && atbegin(this) && !allSel(this)) {
state.leftKeyThenPrevinput = true;
return false;
} else if (key == 39 && atend(this) && !allSel(this)) {
state.rightKeyThenNextinput = true;
return false;
}
}).keyup(function (e) {
var key = e.which;
if (state.leftKeyThenPrevinput) {
// flèche gauche au début du champ déplace la sélection sur le champ précédent
state.leftKeyThenPrevinput = false;
hideAutocompleter(this);
previnput(this, selectAfterFocus);
} else if (state.rightKeyThenNextinput) {
// flèche droite à la fin du champ déplace la sélection sur le champ suivant
state.rightKeyThenNextinput = false;
hideAutocompleter(this);
nextinput(this, selectAfterFocus);
} else if (key == 40 || (state.autocomplete && key == 38)) {
// flèche basse: on va sur un élément de l'auto-complétion.
// Si on est déjà dans l'état auto-complétion, la flèche haute
// est reconnue aussi
state.autocomplete = true;
} else {
// Toute autre touche désactive le mode auto-complétion
state.autocomplete = false;
}
}).attr("autocomplete", "off");
};
// 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);