/**
*    Json key/value autocomplete for jQuery 
*    Provides a transparent way to have key/value autocomplete
*    Copyright (C) 2008 Ziadin Givan www.CodeAssembly.com  
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Lesser General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public License
*    along with this program.  If not, see http://www.gnu.org/licenses/
*    
*    Examples 
*	 $("input#example").autocomplete("autocomplete.php");//using default parameters
*	 $("input#example").autocomplete("autocomplete.php",{minChars:3,timeout:3000,validSelection:false,parameters:{'myparam':'myvalue'},before : function(input,text) {},after : function(input,text) {}});
*    minChars = Minimum characters the input must have for the ajax request to be made
*	 timeOut = Number of miliseconds passed after user entered text to make the ajax request   
*    validSelection = If set to true then will invalidate (set to empty) the value field if the text is not selected (or modified) from the list of items.
*    parameters = Custom parameters to be passed
*    after, before = a function that will be caled before/after the ajax request
*/
jQuery.fn.autocomplete = function(url, settings ) 
{
	return this.each( function()//do it for each matched element
	{
		//this is the original input
		var textInput = jQuery(this);
		//create a new hidden input that will be used for holding the return value when posting the form, then swap names with the original input
		textInput.after('<input type=hidden name="' + textInput.attr("name") + '"/>').attr("name", textInput.attr("name") + "_text");
		var valueInput = jQuery(this).next();
		valueInput.val( textInput.attr('value') );
		textInput.change(function() { valueInput.val( textInput.val() ); });		
		//create the ul that will hold the text and values
		valueInput.after('<ul class="autocomplete"></ul>');
		//var list = valueInput.next().css({top: textInput.offset().top + textInput.outerHeight(), left: textInput.offset().left, width: textInput.width()});
		var list = valueInput.next().css({width: textInput.width()});
		var oldText = '';
		var typingTimeout;
		var size = 0;
		var selected = 0;

		settings = jQuery.extend(//provide default settings
		{
			minChars : 1,
			timeout: 1000,
			after : null,
			before : null,
			validSelection : true,
			autoSubmit: false,
			parameters : {'inputName' : valueInput.attr('name'), 'inputId' : textInput.attr('id')}
		} , settings);

		function getData(text)
		{
			window.clearInterval(typingTimeout);
			if (text != oldText && (settings.minChars != null && text.length >= settings.minChars))
			{
				clear();
				if (settings.before == "function") 
				{
					settings.before(textInput,text);
				}
				textInput.addClass('autocomplete-loading');
				settings.parameters.text = text;
                                if (settings.parameters.staticData) {
                                    var newdata = new Array();
                                    var j = 0;
                                    for (i = 0; i < settings.parameters.staticData.length; i++)
                                    {
                                        if (stripos(settings.parameters.staticData[i][0], text, 0)===0) {
                                            newdata[j] = new Array (settings.parameters.staticData[i][0]) ;
                                            j++;
                                        }
                                        if (j >= settings.parameters.limitStaticData) break;
                                    }
                                    createList(newdata, text);
                                    textInput.removeClass('autocomplete-loading');
                                }
                                else {
                                    jQuery.getJSON(url,settings.parameters,function(data)
                                    {
                                            if (data)
                                            {
                                                    createList(data, text);
                                            }
                                            textInput.removeClass('autocomplete-loading');
                                    });
                                }
                                if (settings.after == "function")
                                {
                                        settings.after(textInput,text);
                                }
				oldText = text;
			}
		}

                function createList(data, text)
                {
                    var items = '';
                    size = data.length;
                    for (i = 0; i < data.length; i++)//iterate over all options
                    {
                      //for ( key in data[i] )//get key => value
                      //{

                                //if (data[i][key] == undefined) continue;
                                //alert(data[i][key]);
                                items += '<li value="' + data[i][0] + '">' + data[i][0].replace(new RegExp("(" + text + ")","i"),"<strong>$1</strong>") + '</li>';
                      //}
                      list.html(items);
                      //on mouse hover over elements set selected class and on click set the selected value and close list
                      list.show().children().
                      hover(function() { jQuery(this).addClass("selected").siblings().removeClass("selected");}, function() { jQuery(this).removeClass("selected") } ).
                      click(function () { valueInput.val( jQuery(this).text() );textInput.val( jQuery(this).text() ); clear(); textInput.focus(); if(settings.autoSubmit) textInput.parent("form").submit()});
                    }
                }

                function stripos ( f_haystack, f_needle, f_offset ){	// Find position of first occurrence of a case-insensitive string
                        //
                        // +	 original by: Martijn Wieringa

                        var haystack = f_haystack.toLowerCase();
                        var needle = f_needle.toLowerCase();
                        var index = 0;

                        if(f_offset == undefined) {
                                f_offset = 0;
                        }

                        if((index = haystack.indexOf(needle, f_offset)) > -1) {
                                return index;
                        }

                        return false;
                }
		
		function clear()
		{
			list.hide();
			size = 0;
			selected = -1;
		}	
		
		textInput.keydown(function(e) 
		{
			window.clearInterval(typingTimeout);

			valueInput.val( textInput.val() );

			if(e.which == 27 || e.which == 9)//escape and tab
			{
				clear();
			} else if (e.which == 46 || e.which == 8)//delete and backspace
			{
				clear();
				//invalidate previous selection
				if (settings.validSelection) valueInput.val('');
				typingTimeout = window.setTimeout(function() { getData(textInput.val()) },settings.timeout);
			}
			else if(e.which == 13)//enter 
			{
				if ( list.css("display") == "none")//if the list is not visible then make a new request, otherwise hide the list
				{ 
					//getData(textInput.val());
				} else
				{
                                    clear();
                                    e.preventDefault();
									if(settings.autoSubmit) textInput.parents('form').submit();
				    return false;
				}				
			}
			else if(e.which == 40 || e.which == 38)//move up, down 
			{
			  switch(e.which) 
			  {
				case 40: 
				case 9:				  
				  selected = selected >= size - 1 ? 0 : selected + 1; break;
				case 38:
				  selected = selected <= 0 ? size - 1 : selected - 1; break;
				default: break;
			  }
			  //set selected item and input values
			  textInput.val( list.children().removeClass('selected').eq(selected).addClass('selected').text() );	        
			  valueInput.val( list.children().eq(selected).text() );
			} else 
			{ 
				//invalidate previous selection
				if (settings.validSelection) valueInput.val('');
				typingTimeout = window.setTimeout(function() { getData(textInput.val()) },settings.timeout);
			}
		});
	});
};

