/*
Script: dks_autocompleter_extension.js
Mootools Autocompleter plugin extension to enable autocomplete of multiple items.
License:
MIT license.
Copyright:
copyright (c) 2007 Philip Allard, .
Credits:
- Mootools Autocompleter plugin by digitarald .
Note:
Autocompleter.MultiSelectable requires Mootools .
Autocompleter.MultiSelectable requires Autocompleter .
Autocompleter.MultiSelectable requires Observer .
*/
Autocompleter.MultiSelectable = {};
/*
Class: Autocompleter.MultiSelectable.Base
Base class (extends Autocompleter.Base) for multiselect enabled autocompleter.
Arguments:
el - required, element that holds the autocompleter (a textbox).
options - optional, see options below.
Options:
multiSelect - boolean, enable/disable multiselect. defaults to true.
wrapSelectionsWithSpacesInQuotes - boolean, enable/disable automatic quoting of selections that contain spaces. defaults to true.
*/
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();
}
});
/*
Class: Autocompleter.MultiSelectable.Ajax.Base
Base class for multiselect enabled XHR autocompleter.
Arguments:
el - required, element that holds the autocompleter (a textbox).
url - required, url the xhr request will be sent to.
options - optional, see options below.
Options:
postVar - boolean, enable/disable multiselect. defaults to true.
postData - boolean, enable/disable automatic quoting of selections that contain spaces. defaults to true.
ajaxOptions -
onRequest -
onComplete -
*/
Autocompleter.MultiSelectable.Ajax = {};
Autocompleter.MultiSelectable.Ajax.Base = Autocompleter.MultiSelectable.Base.extend({
// duplicated code (same as Autocompleter.Ajax.Base)
options: {
postVar: 'value',
postData: {},
ajaxOptions: {},
onRequest: Class.empty,
onComplete: Class.empty
},
// duplicated code (same as Autocompleter.Ajax.Base)
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);
}
});
/*
Class: Autocompleter.MultiSelectable.Ajax.Json
Ready to use extension for JSON queries.
Arguments:
el - required, element that holds the autocompleter (a textbox).
url - required, url the xhr request will be sent to.
options - optional, see Autocompleter.MultiSelectable.Ajax options.
*/
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);
}
});
/*
Class: Autocompleter.MultiSelectable.Local
ready to use extension for Local Array queries.
Arguments:
el - required, element that holds the autocompleter (a textbox).
tokens - required, list of available elements.
options - optional, see Autocompleter.Base options.
*/
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);
});
}
});
String.extend({
lastElement: function(separator){
var txt = this.trim();
var index = txt.lastIndexOf(separator || ' ');
return (index == -1)? txt: txt.substr(index + 1, txt.length);
},//end lastElement
trimLastElement: function(separator){
var txt = this.trim();
var index = txt.lastIndexOf(separator || ' ');
return (index == -1)? "": txt.substr(0, index + 1);
},//end trimLastElement
wrapInQuotes: function(){
var index = this.trim().lastIndexOf(' ');
return (index == -1)? this: '"' + this + '"'
}//end wrapInQuotes
}); //end String.extend