nur-sery/nur_public/nur-base/navigate/main.js

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