Skip to content

Commit

Permalink
fix forceSelect==false to allow any value, bind esc key to close the …
Browse files Browse the repository at this point in the history
…ul, allow filter to return false to cancel
  • Loading branch information
pehrlich committed Feb 4, 2012
1 parent 9f6c129 commit ff4be9b
Showing 1 changed file with 66 additions and 61 deletions.
127 changes: 66 additions & 61 deletions jquery.smart_autocomplete.js
@@ -1,9 +1,9 @@
/**
* Smart Auto Complete plugin
*
* Smart Auto Complete plugin
*
* Copyright (c) 2011 Lakshan Perera (laktek.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
*
*
*/

/*
Expand All @@ -21,18 +21,18 @@
Better to enable this option, if you want autocomplete field to behave similar to a HTML select field. (Check Example 2 in the demo)
(default: false)
typeAhead: (boolean) If set to true, it will offer the best matching result in grey within the field; that can be auto-completed by pressing the right arrow-key or enter.
This is similar to behaviour in Google Instant Search's query field (Check Example 3 in the demo)
This is similar to behaviour in Google Instant Search's query field (Check Example 3 in the demo)
(default: false)
source: (string/array) you can supply an array with items or a string containing a URL to fetch items for the source
this is optional if you prefer to have your own filter method
this is optional if you prefer to have your own filter method
filter: (function) define a custom function that would return matching items to the entered text (this will override the default filtering algorithm)
should return an array or a Deferred object (ajax call)
parameters available: term, source
parameters available: term, source
resultFormatter: (function) the function you supply here will be called to format the output of an individual result.
should return a string
parameters available: result
parameters available: result
resultsContainer: (selector) to which element(s) the result should be appended.
resultElement: (selector) references to the result elements collection (e.g. li, div.result)
resultElement: (selector) references to the result elements collection (e.g. li, div.result)
Events:
keyIn: fires when user types into the field (parameters: query)
Expand All @@ -48,10 +48,10 @@
*/

(function($){
$.fn.smartAutoComplete = function(){
$.fn.smartAutoComplete = function(){

if(arguments.length < 1){
// get the smart autocomplete object of the first element and return
// get the smart autocomplete object of the first element and return
var first_element = this[0];
return $(first_element).data("smart-autocomplete")
}
Expand All @@ -66,7 +66,7 @@
}

var default_options = {
minCharLimit: 1,
minCharLimit: 1,
maxResults: null,
delay: 0,
disabled: false,
Expand All @@ -76,32 +76,32 @@

resultFormatter: function(r){ return ("<li>" + r + "</li>"); },

filter: function(term, source){
filter: function(term, source){
var context = this;
var options = $(context).data('smart-autocomplete');


//when source is an array
if($.type(source) === "array") {
// directly map
var results = default_filter_matcher(term, source, context);
return results;
return results;
}
//when source is a string
else if($.type(source) === "string"){
// treat the string as a URL endpoint
// pass the query as 'term'
return $.Deferred(function(dfd){

return $.Deferred(function(dfd){
$.ajax({
url: source,
data: {"term": term},
dataType: "json"
}).success( function(data){
dfd.resolve( default_filter_matcher(term, data, context) );
});
dfd.resolve( default_filter_matcher(term, data, context) );
});
}).promise();

}

},
Expand All @@ -113,7 +113,7 @@
var type_ahead_field = $(this.context).prev(".smart_autocomplete_type_ahead_field");
$(this.context).css({ background: type_ahead_field.css("background") });
type_ahead_field.remove();

//clear results div
$(this.resultsContainer).html("");
},
Expand All @@ -131,7 +131,7 @@

//define the default events
$.event.special.keyIn = {
setup: function(){ return false; },
setup: function(){ return false; },

_default: function(ev){
var context = ev.target;
Expand All @@ -151,6 +151,9 @@
//call the filter function with delay
setTimeout(function(){
$.when( filter.apply(options, [query, options.source]) ).done(function( results ){
if(results == false){
return $(context);
}
//do the trimming
var trimmed_results = (options.maxResults > 0 ? results.splice(0, options.maxResults) : results);

Expand All @@ -174,7 +177,7 @@
if(options.disabled)
return false;

//clear all previous results
//clear all previous results
$(context).smartAutoComplete().clearResults();

//save the raw results
Expand All @@ -199,24 +202,24 @@

//trigger results ready event
$(context).trigger('showResults', [results]);
}
}
};

$.event.special.showResults = {
setup: function(){ return false },

_default: function(ev){
_default: function(ev){
var context = ev.target;
var options = $(context).data("smart-autocomplete");
var results_container = $(options.resultsContainer);

//event specific data
var raw_results = ev.smartAutocompleteData.results;
var raw_results = ev.smartAutocompleteData.results;

//type ahead
if(options.typeAhead && (raw_results[0].substr(0, $(context).val().length) == $(context).val()) ){
var suggestion = raw_results[0]; //options.typeAheadExtractor($(context).val(), raw_results[0]);
var suggestion = raw_results[0]; //options.typeAheadExtractor($(context).val(), raw_results[0]);

//add new typeAhead field
$(context).before("<input class='smart_autocomplete_type_ahead_field' type='text' autocomplete='off' disabled='disabled' value='" + suggestion + "'/>");

Expand All @@ -243,17 +246,17 @@
$(context).trigger('itemFocus', results_container.children()[options.currentSelection]);
}

//show the results container after aligning it with the field
//show the results container after aligning it with the field
if(results_container){
if(options.alignResultsContainer){
results_container.css({
results_container.css({
position: "absolute",
top: function(){ return $(context).offset().top + $(context).height(); },
left: function(){ return $(context).offset().left; },
width: function(){ return $(context).width(); },
top: function(){ return $(context).offset().top + $(context).height(); },
left: function(){ return $(context).offset().left; },
width: function(){ return $(context).width(); },
zIndex: 1000
})
}
}
results_container.show();
}

Expand All @@ -263,23 +266,23 @@
$.event.special.noResults = {
setup: function(){ return false },

_default: function(ev){
_default: function(ev){
var context = ev.target;
var options = $(context).data("smart-autocomplete");
var result_container = $(options.resultsContainer);

if(result_container){
//clear previous results
options.clearResults();
options.clearResults();
}

}
};

$.event.special.itemSelect = {
$.event.special.itemSelect = {
setup: function(){ return false },

_default: function(ev){
_default: function(ev){
var context = ev.target;
var options = $(context).data("smart-autocomplete");

Expand All @@ -289,14 +292,14 @@
//get the text from selected item
var selected_value = $(selected_item).text() || $(selected_item).val();
//set it as the value of the autocomplete field
$(context).val(selected_value);

// if selected_value is undefined, forceSelect will blank the field
if (options.forceSelect || selected_value) {
$(context).val(selected_value);
}
//set item selected property
options.setItemSelected(true);

//set number of current chars in field
options.originalCharCount = $(context).val().length;

//trigger lost focus
$(context).trigger('lostFocus');
}
Expand All @@ -305,7 +308,7 @@
$.event.special.itemFocus = {
setup: function(){ return false },

_default: function(ev){
_default: function(ev){

//event specific data
var item = ev.smartAutocompleteData.item;
Expand All @@ -314,10 +317,10 @@
}
};

$.event.special.itemUnfocus = {
$.event.special.itemUnfocus = {
setup: function(){ return false },

_default: function(ev){
_default: function(ev){

//event specific data
var item = ev.smartAutocompleteData.item;
Expand All @@ -329,16 +332,16 @@
$.event.special.lostFocus = {
setup: function(){ return false },

_default: function(ev){
_default: function(ev){
var context = ev.target;
var options = $(context).data("smart-autocomplete");

//if force select is selected and no item is selected, clear currently entered text
//if force select is selected and no item is selected, clear currently entered text
if(options.forceSelect && !options.itemSelected)
$(options.context).val("");

//clear results
options.clearResults();
options.clearResults();

//hide the results container
if(options.resultsContainer)
Expand All @@ -351,7 +354,7 @@

var passed_options = arguments[0];

return this.each(function(i) {
return this.each(function(i) {
//set the options
var options = $.extend(default_options, $(this).data("smart-autocomplete"), passed_options);
//set the context
Expand Down Expand Up @@ -410,11 +413,11 @@

$(options.context).trigger('itemFocus', [ result_suggestions[current_selection] ] );
}

}

//right arrow & enter key
else if(ev.keyCode == '39' || ev.keyCode == '13'){
else if(ev.keyCode == '39' || ev.keyCode == '13' || ev.keyCode == '27'){
var type_ahead_field = $(options.context).prev('.smart_autocomplete_type_ahead_field');
if(options.resultsContainer && $(options.resultsContainer).is(':visible')){
var current_selection = options.currentSelection;
Expand All @@ -431,15 +434,18 @@
else {
var current_char_count = $(options.context).val().length;
//check whether the string has modified
if(options.originalCharCount == current_char_count)
if(options.originalCharCount == current_char_count){
return;
}else{
options.originalCharCount = current_char_count;
}

//check minimum number of characters are typed
if(current_char_count >= options.minCharLimit){
$(options.context).trigger('keyIn', [$(this).val()]);
$(options.context).trigger('keyIn', [$(this).val()]);
}
else{
if($(options.resultsContainer).is(':visible')){
if($(options.resultsContainer).is(':visible')){
options.currentSelection = null;
$(options.context).trigger('lostFocus');
}
Expand All @@ -449,14 +455,13 @@
});

$(this).focus(function(){
//if the field is in a form capture the return key event
//if the field is in a form capture the return key event
$(this).closest("form").bind("keydown.block_for_smart_autocomplete", function(ev){
var type_ahead_field = $(options.context).prev('.smart_autocomplete_type_ahead_field');
if(ev.keyCode == '13'){
if(ev.keyCode == '13' || ev.keyCode == '27'){ //enter, esc keys
if(options.resultsContainer && $(options.resultsContainer).is(':visible')){
var current_selection = options.currentSelection;
var result_suggestions = $(options.resultsContainer).children();

$(options.context).trigger('itemSelect', [ result_suggestions[current_selection] ] );
return false;
}
Expand All @@ -468,7 +473,7 @@
});

if(options.forceSelect){
$(this).select();
$(this).select();
}
});

Expand All @@ -491,7 +496,7 @@
options['currentSelection'] = $(this).prevAll().length;

$(options.context).trigger('itemFocus', [this] );

});

$(options.resultsContainer).delegate(options.resultElement, 'mouseleave.smart_autocomplete', function(){
Expand All @@ -506,7 +511,7 @@
//bind plugin specific events
$(this).bind({
keyIn: function(ev, query){ ev.smartAutocompleteData = {'query': query }; },
resultsReady: function(ev, results){ ev.smartAutocompleteData = {'results': results }; },
resultsReady: function(ev, results){ ev.smartAutocompleteData = {'results': results }; },
showResults: function(ev, results){ ev.smartAutocompleteData = {'results': results } },
noResults: function(){},
lostFocus: function(){},
Expand All @@ -515,6 +520,6 @@
itemUnfocus: function(ev, item){ ev.smartAutocompleteData = {'item': item }; }
});
});

}
})(jQuery);
})(jQuery);

0 comments on commit ff4be9b

Please sign in to comment.