Extension pour Autocompleter 1.0rc4
Mise à jour (2007-06-18): Le projet est maintenant hébergé sur googlecode. La révision courante ainsi que la version courante sont maintenant accessibles via SVN. Les fichiers requis (dépendances) ont aussi été ajoutés à la section téléchargements.
Dans mon article annonçant le lancement du plug-in Autocompleter 1.0rc3 pour Mootools, j’avais fait remarquer qu’il était malheureusement impossible de lancer la saisie semi-automatique sur chacune des entrées d’une liste dont les éléments seraient séparés par un espace (étiquettes ala del.icio.us). Puisque ce comportement m’était personnellement indispensable, j’ai décidé de remédié à la situation en créant une extension. Cette extension ajoute ou modifie les comportements suivants du plug-in original :
- Lors de la sélection d’un élément dans la liste de choix proposés, celui-ci sera ajouté à la suite des sélections précédentes plutôt que de les écraser.
- L’option « minLength » sera validée pour chacune des entrées et non plus seulement pour la première.
- La requête en cours sera surlignée dans la liste de choix proposés, et ce, pour chacun des items de la liste.
- Seule l’entrée en cours sera envoyée en paramètre de la requête XHR et non plus tout le contenu du champ texte.
- Lors de la sélection d’un choix dans la liste proposé, lorsque celui-ci contient des espaces, il sera ajouté entre guillemets dans le champ texte.
L’extension réside dans un nouveau namespace soit Autocompleter::MultiSelectable, donc, toutes les fonctionnalités du plug-in demeurent intactes et accessibles.
Dépendances
Cette extension fonctionne avec Autocompleter 1.0rc4 qui requiert Observer 1.0rc1 et mootools 1.11 (Voir la section téléchargement du projet sur googlecode si l’un des sites n’est pas accessible).
L’extension
Autocompleter.MultiSelectable = {};
Autocompleter.MultiSelectable.Base = Autocompleter.Base.extend({
options: {
multiSelect: true,
wrapSelectionsWithSpacesInQuotes: true
},
choiceSelect: function(el) {
//when multiSelect is enabled, append to field value instead of overwriting it.
this.observer.value = this.element.value = this.options.multiSelect? this.element.value.trimLastElement() + el.inputValue + " ":el.inputValue;
this.hideChoices();
this.fireEvent('onSelect', [this.element], 20);
},
prefetch: function() {
//when multiSelect is enabled, min len test on last query element so that min len is tested for every elements.
var elValueToTest = this.options.multiSelect? this.element.value.lastElement(): this.element.value;
if (elValueToTest.length < this.options.minLength) this.hideChoices();
else if (elValueToTest == this.queryValue) this.showChoices();
else this.query();
},
onCommand: function(e, mouse) {
if (mouse && this.focussed) this.prefetch();
if (e.key && !e.shift) switch (e.key) {
case 'enter':
if (this.selected && this.visible) {
this.choiceSelect(this.selected);
e.stop();
} return;
case 'up': case 'down':
//when in multiselect mode, test on last element of element (observer) value or it will not
//listen to key up and down.
var elValueToTest = this.options.multiSelect? this.observer.value.lastElement(): this.observer.value;
if (elValueToTest != (this.value || this.queryValue)) this.prefetch();
else if (this.queryValue === null) break;
else if (!this.visible) this.showChoices();
else {
this.choiceOver((e.key == 'up') ? this.selected.getPrevious() || this.choices.getLast() : this.selected.getNext() || this.choices.getFirst() );
this.setSelection();
}
e.stop(); return;
case 'esc': this.hideChoices(); return;
}
this.value = false;
},
updateChoices: function(choices) {
this.choices.empty();
this.selected = null;
if (!choices || !choices.length) return;
if (this.options.maxChoices < choices.length) choices.length = this.options.maxChoices;
choices.each(this.options.injectChoice || function(choice, i){
var el = new Element('li').setHTML(this.markQueryValue(choice));
//wrapping in quotes if requested/needed.
el.inputValue = this.options.wrapSelectionsWithSpacesInQuotes? choice.wrapInQuotes(): choice;
this.addChoiceEvents(el).injectInside(this.choices);
}, this);
this.showChoices();
}
});
Autocompleter::MultiSelectable::Base (requis) : Ajoute deux options à la classe Autocompleter::Base multiSelect qui sert à activé/désactiver l’extension et wrapSelectionWithSpacesInQuotes qui sert à activé/désactiver l’ajout entre guillemets des items contenant des espaces. Les fonctions overridées ajoutent un test afin de déterminer si le processus doit se faire sur le contenu complet du champ texte ou seulement le dernier élément.
Autocompleter.MultiSelectable.Ajax = {};
Autocompleter.MultiSelectable.Ajax.Base = Autocompleter.MultiSelectable.Base.extend({
options: {
postVar: 'value',
postData: {},
ajaxOptions: {},
onRequest: Class.empty,
onComplete: Class.empty
},
initialize: function(el, url, options) {
this.parent(el, options);
this.ajax = new Ajax(url, $merge({
autoCancel: true
}, this.options.ajaxOptions));
this.ajax.addEvent('onComplete', this.queryResponse.bind(this));
this.ajax.addEvent('onFailure', this.queryResponse.bind(this, [false]));
},
query: function(){
var data = $extend({}, this.options.postData);
//query only on last element if multiSelectable is enabled.
data[this.options.postVar] = this.options.multiSelect? this.element.value.lastElement(): this.element.value;
this.fireEvent('onRequest', [this.element, this.ajax]);
this.ajax.request(data);
},
queryResponse: function(resp) {
//query only on last element if multiSelectable is enabled.
this.value = this.queryValue = this.options.multiSelect? this.element.value.lastElement(): this.element.value;
this.selected = false;
this.hideChoices();
this.fireEvent(resp ? 'onComplete' : 'onFailure', [this.element, this.ajax], 20);
}
});
Autocompleter::MultiSelectable::Ajax::Base (non requis) : Uttiliser seulement si vous désirez faire des requêtes via XHR. Les modifications apportées assurent que seul l’élément en cours soit envoyé en paramètre de la requête.
//ready to use extension for JSON queries Autocompleter.MultiSelectable.Ajax.Json = Autocompleter.MultiSelectable.Ajax.Base.extend({ queryResponse: function(resp) { this.parent(resp); var choices = Json.evaluate(resp || false); if (!choices || !choices.length) return; this.updateChoices(choices); } }); //ready to use extension for Local Array queries Autocompleter.MultiSelectable.Local = Autocompleter.MultiSelectable.Base.extend({ initialize: function(el, tokens, options) { this.parent(el, options); this.tokens = tokens; if (this.options.filterTokens) this.filterTokens = this.options.filterTokens.bind(this); }, query: function() { this.hideChoices(); this.queryValue = this.element.value.lastElement(); this.updateChoices(this.filterTokens()); }, filterTokens: function(token) { var regex = new RegExp('^' + this.queryValue.escapeRegExp(), 'i'); return this.tokens.filter(function(token) { return regex.test(token); }); } });
Autocompleter::MultiSelectable::Ajax::Json et Autocompleter::MultiSelectable::Local (non requis) : Deux classes qui permettent de faire vos requêtes via un objet JSON local ou un objet JSON transmis par une requête XHR.
String.extend({
lastElement: function(separator){
var txt = this.trim();
var index = txt.lastIndexOf(separator || ' ');
return (index == -1)? txt: txt.substr(index + 1, txt.length);
},
trimLastElement: function(separator){
var txt = this.trim();
var index = txt.lastIndexOf(separator || ' ');
return (index == -1)? "": txt.substr(0, index + 1);
},
wrapInQuotes: function(){
var index = this.trim().lastIndexOf(' ');
return (index == -1)? this: '"' + this + '"'
}
});
Ci-dessus, quelques fonctions (extension de l’objet String) tirées de ma librairie personnelle utilisées par cette extension.
Utilisation
Vous n’avez qu’à inclure le fichier contenant le code ci-dessus (compressé) comme vous le feriez avec d’autres scripts. Cependant, celui-ci doit être inclus sous autocompleter.js :
<script type="text/javascript" src="mootools.js"></script> <script type="text/javascript" src="observer.js"></script> <script type="text/javascript" src="autocompleter.js"></script> <script type="text/javascript" src="dks_autocompleter_extension.js"></script>
L’utilisation des nouvelles classes demeure identique au plug-in original à l’exception du paramètre multiSelect qui a été ajouté afin d’activer et de désactiver la saisie semi-automatique de plusieurs entrées :
var searchInput = $('search-field'); var completer = new Autocompleter.MultiSelectable.Ajax.Json(searchInput, { url: '/search', postVar: 'q', multiSelect:true });
Pour minimiser la taille des fichiers…
Afin que l’extension fonctionne aussi en mode locale, j’ai implémenté l’extension à partir d’Autocompleter::Base. Quoique les changements apportés modifient grandement le comportement du plug-in original, la nature de la fonctionnalité ajoutée par l’extension appelle à des modifications plutôt mineures aux classes et fonctions de bases. Ceci a eu pour cause que beaucoup de fonctions qui se trouvent dans les classes de bases ont été copiées afin de changer qu’une seule ligne…
Bref, puisque j’ai ajouté une option à l’extension qui permet d’activer et de désactiver la saisie semi-automatique de plusieurs entrées, il est possible d’utiliser que les classes de base de l’extension. Donc, vous pouvez effacer les classes Autocompleter::Ajax::Base, Autocompleter::Ajax::Json et Autocompleter::Local du plug-in original.
Si vous procédez ainsi, assurez-vous de faire référence à l’une des classes de l’extension soit Autocompleter::MultiSelectable::Ajax, Autocompleter::MultiSelectable::Ajax::Json et Autocompleter::MultiSelectable::Local dans le code que vous avez peut-être déjà implémenté et qui utilise l’une des trois classes originales.
Il y a 0 commentaires pour cet article
La discussion est fermée pour les non-membres. Vous pouvez lire les commentaires. Pour ajouter votre commentaire, vous devrez vous identifier ou vous enregistrer si vous n'êtes pas déjà membre.