 /*
 * AutoSuggest
 * Copyright 2009-2010 Drew Wilson
 * www.drewwilson.com
 * code.drewwilson.com/entry/autosuggest-jquery-plugin
 *
 * Version 1.4   -   Updated: Mar. 23, 2010
 *
 * This Plug-In will auto-complete or auto-suggest completed search queries
 * for you as you type. You can add multiple selections and remove them on
 * the fly. It supports keybord navigation (UP + DOWN + RETURN), as well
 * as multiple AutoSuggest fields on the same page.
 *
 * Inspied by the Autocomplete plugin by: J�rn Zaefferer
 * and the Facelist plugin by: Ian Tearle (iantearle.com)
 *
 * This AutoSuggest jQuery plug-in is dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function($){
	
	jQuery('.closer a').live('click', function(){
    
      jQuery(this).closest('fieldset').find(':text').each(function(){
        jQuery(this).removeAttr('readonly');
        jQuery(this).val("");
      });
      
      jQuery(this).closest('fieldset').find('[type="hidden"]').each(function(){
        jQuery(this).val("");
      });
      
      jQuery(this).remove();
      
      updateSpeciesSelect();
      
      return false;
      
    });
	
	// Add a function using the fn function (prototype)
	$.fn.autosuggest = function(data, options) {
		
		// Setup a map of default values for the options
		var defaults = { 
			asHtmlID: false,
			startText: "Enter Name Here",
			emptyText: "No Results Found",
			preFill: {},
			limitText: "No More Selections Are Allowed",
			selectedItemProp: "value", //name of object property
			selectedValuesProp: "value", //name of object property
			searchObjProps: "value", //comma separated list of object property names
			queryParam: "q",
			retrieveLimit: false, //number for 'limit' param on ajax request
			extraParams: "",
			matchCase: false,
			minChars: 1,
			keyDelay: 400,
			resultsHighlight: true,
			neverSubmit: false,
			selectionLimit: false,
			showResultList: true,
		  	start: function(){},
		  	selectionClick: function(elem){},
		  	selectionAdded: function(elem){},
		  	selectionRemoved: function(elem){ elem.remove(); },
		  	formatList: false, //callback function
		  	beforeRetrieve: function(string){ return string; },
		  	retrieveComplete: function(data){ return data; },
		  	resultClick: function(data){},
		  	resultsComplete: function(){}
	  	};
	  	
	  	// Create a options variable by combining the defaults and user options
	 	var opts = $.extend(defaults, options);	 	
		
		// Setup the default data type as 'object'
		//var d_type = "object"; // DONT NEED THIS AS THE DATA WILL ALWAYS BE A STRING
		var d_type = "string";
		
		// Initialise the data count to 0
		var d_count = 0;
		
		// Check if the data is in string format
		if(typeof data == "string") {
			
			// Update the data type variable
			d_type = "string";
			var req_string = data;
			
		}/* else { // ALWAYS STRING THIS NOT NEEDED
			
			// Assign the original data to a new variable
			var org_data = data;
			
			// Loop through all properties of the object incrementing the counter
			for (k in data) if (data.hasOwnProperty(k)) d_count++;
		
		}*/
		
		// Check we have some data to deal with
		if((d_type == "object" && d_count > 0) || d_type == "string"){
			
			// 
			return this.each(function(x){
				
				
				/*if(!opts.asHtmlID){
					x = x+""+Math.floor(Math.random()*100); //this ensures there will be unique IDs on the page if autoSuggest() is called multiple times
					var x_id = "as-input-"+x;
				}/* else {
					x = opts.asHtmlID;
					var x_id = x;
				}*/
				
				// Call the user defined on-load function
				opts.start.call(this);
				
				// Assign the input field user's will type into to a variable
				var input = $(this);
				
				// Turn off autocomplete, add an input class, update the ID and set the value to the start text
				//input.attr("autocomplete","off").addClass("as-input").attr("id",x_id).val(opts.startText);
				input.attr("autocomplete","off");//.attr("id", x_id);
				
				// Define the input focus variable as FALSE
				var input_focus = false;
				
				// Setup basic elements and render them to the DOM
				
				// Wrap the input field with HTML
				//input.wrap('<ul class="as-selections" id="as-selections-'+x+'"></ul>').wrap('<li class="as-original" id="as-original-'+x+'"></li>');
				
				// Assign the new DOM object to a variable
				//var selections_holder = $("#as-selections-"+x);
				//var org_li = $("#as-original-"+x);
				
				// Create a results holder DIV and set display to none
				var results_holder = $('<div class="as-results" id="as-results-'+x+'"></div>').hide();
				
				// Create an empty list to hold the results
				var results_ul =  $('<ul class="as-list"></ul>');
				
				// Create a hidden field to contain the values
				//var values_input = $('<input type="hidden" class="as-values" name="as_values_'+x+'" id="as-values-'+x+'" />');
				
				// Setup a variable to hold the prefill value
				//var prefill_value = "";
				
				// Check if a single prefill string has been specified in the options
				/*if(typeof opts.preFill == "string"){ // WE WONT BE USING THE PREFILL OPTION ANYWAY
					
					// Create an array of prefill values by splitting based on the comma
					var vals = opts.preFill.split(",");					
					
					// Loop through the array of prefill values
					for(var i=0; i < vals.length; i++){
						
						// Create a hash object
						var v_data = {};
						
						// Set a key and value to the hash
						v_data[opts.selectedValuesProp] = vals[i];
						
						// Check the prefill value is not empty
						if(vals[i] != ""){
							
							// Add the selected item
							add_selected_item(v_data, "000"+i);	
						}		
					}
					
					// Assign the prefill value to a variable
					prefill_value = opts.preFill;
					
				} else {
					
					// Initialise a prefill value variable
					prefill_value = "";
					
					// Initialise a prefill counter variable
					var prefill_count = 0;
					
					// Loop through all the prefill objects
					for (k in opts.preFill) if (opts.preFill.hasOwnProperty(k)) prefill_count++;
					
					// Check some prefill values actually exist
					if(prefill_count > 0){
						
						// Loop through all of the prefill values
						for(var i=0; i < prefill_count; i++){
							var new_v = opts.preFill[i][opts.selectedValuesProp];
							if(new_v == undefined){ new_v = ""; }
							prefill_value = prefill_value+new_v+",";
							if(new_v != ""){
								add_selected_item(opts.preFill[i], "000"+i);	
							}		
						}
					}
				} */
				
				// Check if a prefill value is present
				/*if(prefill_value != ""){
					
					// Empty the value of the input
					input.val("");
					
					// Get the last character of the prefill value
					var lastChar = prefill_value.substring(prefill_value.length-1);
					
					// Check if the last character is a comma if not add one
					if(lastChar != ","){ prefill_value = prefill_value+","; }
					
					// Add the prefill values to the values input hidden field
					values_input.val(","+prefill_value);
					$("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected");
				}*/
				
				// Insert the values input after the input field
				//input.after(values_input);
				
				// Set a click event for the selections holder
				/*selections_holder.click(function(){
					
					// Set the input focus variable to TRUE
					input_focus = true;
					
					// Set the focus on the input field
					input.focus();
				
				// On mousedown set focus to FALSE and add the results holder after
				}).mousedown(function(){ input_focus = false; }).after(results_holder);	*/
				input.parents('fieldset').append(results_holder);
				
				// Create a series of variables
				var timeout = null;
				var prev = "";
				var totalSelections = 0;
				var tab_press = false;
				
				// Handle input field events
				input.focus(function(){			
					
					// If there's only the start text or an empty field
					//if($(this).val() == opts.startText && values_input.val() == ""){
					if($(this).val() == opts.startText){
						
						// Ensure there's only an empty string
						$(this).val("");
					
					// Else if input focus is set to true	
					} else if(input_focus){
						
						// Find selection item LI inside the selections holder
						//$("li.as-selection-item", selections_holder).removeClass("blur");
						
						// Check if theres a string
						if($(this).val() != ""){
							
							// Set the results list width to the same as the selections holder 
							results_ul.css("width", input.outerWidth());
							
							// Show the results holder
							results_holder.show();
							
						}
					}
					
					// Set the focus var to TRUE
					input_focus = true;
					
					// Return TRUE
					return true;
				
				// Set event for when the input field loses focus	
				}).blur(function(){
					
					// Check if the value is empty
					/*if($(this).val() == "" && values_input.val() == "" && prefill_value == ""){
						
						// Add the start text back into the input field
						$(this).val(opts.startText);
					
					// If we still have focus e.g. in the results list	
					} else*/ 
					if(input_focus){
						
						// Add the blur class and remove the selected class
						//$("li.as-selection-item", selections_holder).addClass("blur").removeClass("selected");
						
						// Hide the results holder
						results_holder.hide();
					}
					
				// On each key pressed inside the input field					
				}).keydown(function(e) {
					
					// Track the last key pressed
					lastKeyPressCode = e.keyCode;
					
					// Set first focus to FALSE
					first_focus = false;
					
					// Run the pressed key through a switch statement
					switch(e.keyCode) {
						case 38: // up
							e.preventDefault();
							moveSelection("up");
							break;
						case 40: // down
							e.preventDefault();
							moveSelection("down");
							break;
						case 8:  // delete
							if(input.val() == ""){							
								var last = values_input.val().split(",");
								last = last[last.length - 2];
								selections_holder.children().not(org_li.prev()).removeClass("selected");
								if(org_li.prev().hasClass("selected")){
									values_input.val(values_input.val().replace(","+last+",",","));
									opts.selectionRemoved.call(this, org_li.prev());
								} else {
									opts.selectionClick.call(this, org_li.prev());
									org_li.prev().addClass("selected");		
								}
							}
							if(input.val().length == 1){
								results_holder.hide();
								 prev = "";
							}
							if($(":visible",results_holder).length > 0){
								if (timeout){ clearTimeout(timeout); }
								timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay);
							}
							break;
						case 9: case 188:  // tab or comma
							/*tab_press = true;
							var i_input = input.val().replace(/(,)/g, "");
							if(i_input != "" && values_input.val().search(","+i_input+",") < 0 && i_input.length >= opts.minChars){	
								e.preventDefault();
								var n_data = {};
								n_data[opts.selectedItemProp] = i_input;
								n_data[opts.selectedValuesProp] = i_input;																				
								var lis = $("li", selections_holder).length;
								add_selected_item(n_data, "00"+(lis+1));
								input.val("");
							}*/
						case 13: // return
							tab_press = false;
							var active = $("li.active:first", results_holder);
							if(active.length > 0){
								active.click();
								results_holder.hide();
							}
							if(opts.neverSubmit || active.length > 0){
								e.preventDefault();
							}
							break;
						default:
							if(opts.showResultList){
								/*if(opts.selectionLimit && $("li.as-selection-item", selections_holder).length >= opts.selectionLimit){
									results_ul.html('<li class="as-message">'+opts.limitText+'</li>');
									results_holder.show();
								} else {*/
									if (timeout){ clearTimeout(timeout); }
									timeout = setTimeout(function(){ keyChange(); }, opts.keyDelay);
								//}
							}
							break;
					}
				});
				
				// Function to check if a key was pressed
				function keyChange() {
					
					// Ignore if the following keys are pressed: [del] [shift] [capslock]
					if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ){ return results_holder.hide(); }
					
					// Remove any forward or back slashes
					var string = input.val().replace(/[\\]+|[\/]+/g,"");
					
					// If the string hasn't changed don't do anything
					if (string == prev) return;
					prev = string;
					
					// Make sure the string is longer than the minimum length
					if (string.length >= opts.minChars) {
						
						// Add the loading class
						//selections_holder.addClass("loading");
						
						// If the data type was a string
						if(d_type == "string"){
							
							// Initialise a variable
							var limit = "";
							
							// Check if a retrieval limit has been set
							if(opts.retrieveLimit){
								
								// Add the limit clause to the query string
								limit = "&limit="+encodeURIComponent(opts.retrieveLimit);
								
							}
							
							// Check if the user has defined a pre-retrieval function
							if(opts.beforeRetrieve){
								string = opts.beforeRetrieve.call(this, string);
							}
							
							// Make the AJAX request
							$.getJSON(req_string+"?"+opts.queryParam+"="+encodeURIComponent(string)+limit+opts.extraParams, function(data){ 
								
								// Setup a counter variable
								d_count = 0;
								
								// Run the returned data through the retrieve complete function
								var new_data = opts.retrieveComplete.call(this, data);
								
								// Count the number of objects
								for (k in new_data) if (new_data.hasOwnProperty(k)) d_count++;
								
								// Process the returned data
								processData(new_data, string);
								 
							});
						
						// Else the data was in object format
						} /*else {
							
							// Check if a before retrieve function has been defined
							if(opts.beforeRetrieve){
								
								// Call the function of the data
								string = opts.beforeRetrieve.call(this, string);
							}
							
							// Process the data
							processData(org_data, string);
						}*/
					
					// Inputted term not longer than the min chars param
					} else {
						
						// Remove the loading class and hide the results container
						//selections_holder.removeClass("loading");
						results_holder.hide();
					}
				}
				
				// Setup a counter variable
				var num_count = 0;
				
				// Define the process data function
				function processData(data, query){
					
					// If we dont have to match case force the string to lower case
					if (!opts.matchCase){ query = query.toLowerCase(); }
					
					// Initialise a counter variable
					var matchCount = 0;
					
					// Clear out the results holder values and hide it
					results_holder.html(results_ul.html("")).hide();
					
					// Loop through the returned data
					for(var i=0;i<d_count;i++){				
						
						// Assign the iteration to num var
						var num = i;
						num_count++;
						
						// Define forward as FALSE
						var forward = false;
						
						// Check if the value property is using the value handle
						if(opts.searchObjProps == "value") {
							
							// Grab the value and assign to str
							var str = data[num].value;
							
						} else {
							
							// Initialise the str variable	
							var str = "";
							
							// Split the names string into an array on commas
							var names = opts.searchObjProps.split(",");
							
							// Loop through the components of the array
							for(var y=0;y<names.length;y++){
								
								// Trim whitespace from the name var
								var name = $.trim(names[y]);
								
								// Concatinate the new value onto the string
								str = str+data[num][name]+" ";
							}
						}
						
						// If a string has been defined
						if(str){
							
							// Check that we dont need to match case and force to lower case
							if (!opts.matchCase){ str = str.toLowerCase(); }				
							
							// /Check if the queried value exists in the string
							//if(str.search(query) != -1 && values_input.val().search(","+data[num][opts.selectedValuesProp]+",") == -1){
							if(str.search(query) != -1){
								
								// Define forward as TRUE
								forward = true;
							}	
						}
						
						// Check forward is TRUE
						if(forward){
							
							// Create the list element to add the result
							var formatted = $('<li class="as-result-item" id="as-result-item-'+num+'"></li>').click(function(){
									
									// Extract the raw data from the element when clicked
									var raw_data = $(this).data("data");
									
									// Extract the number property
									var number = raw_data.num;
									
									// Look for ID within the selections holder
									if(!tab_press){
										var data = raw_data.attributes;
										input.val("").focus();
										prev = "";
										add_selected_item(data, number);
										opts.resultClick.call(this, raw_data);
										results_holder.hide();
									}
									tab_press = false;
									
								// On mouse down over result remove focus
								}).mousedown(function(){ input_focus = false; }).mouseover(function(){
									
									// Remove active class from the LI
									$("li", results_ul).removeClass("active");
									$(this).addClass("active");
								
									
								}).data("data",{attributes: data[num], num: num_count});
							var this_data = $.extend({},data[num]);
							
							// Check if we need to match the case and define the regex patterns
							if (!opts.matchCase){ 
								var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "gi");
							} else {
								var regx = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + query + ")(?![^<>]*>)(?![^&;]+;)", "g");
							}
							
							// Check if we need to highlight the search terms
							if(opts.resultsHighlight){
								
								// Wrap matched terms in <em> tags
								this_data[opts.selectedItemProp] = this_data[opts.selectedItemProp].replace(regx,"<em>$1</em>");
							}
							
							// Check the user hasn't defined a formatting function
							if(!opts.formatList){
								formatted = formatted.html(this_data[opts.selectedItemProp]);
							} else {
								
								// Applt the formatting function if required
								formatted = opts.formatList.call(this, this_data, formatted);	
							}
							
							// Append the new result to the results holder
							results_ul.append(formatted);
							
							// Delete the data
							delete this_data;
							
							// Increase the match count
							matchCount++;
							
							// Check if we've reached the retrieval limit and break if we have
							if(opts.retrieveLimit && opts.retrieveLimit == matchCount ){ break; }
						}
					}
					
					// Remove the loading class
					//selections_holder.removeClass("loading");
					
					// If there are no matches
					if(matchCount <= 0){
						
						// Add a sinle element with the empty text
						results_ul.html('<li class="as-message">'+opts.emptyText+'</li>');
					}
					
					// Set the results to the width of the selections holder
					//results_ul.css("width", selections_holder.outerWidth());
					
					var pos = input.offset();
					var left = pos.left;
					var top = pos.top + input.height() + 5;
					results_ul.css({
						'width': input.outerWidth(),
						'top': top,
						'left': left
					});
					
					// Show the results
					results_holder.show();
					
					// Call the complete function
					opts.resultsComplete.call(this);
				}
				
				// Function to add the newly selected item
				function add_selected_item(data, num){
					
					/*
					// Concatinate the value onto the values_input var
					//values_input.val(values_input.val()+data[opts.selectedValuesProp]+",");
					
					// Create the item and add click handlers
					var item = $('<li class="as-selection-item" id="as-selection-'+num+'"></li>').click(function(){
							opts.selectionClick.call(this, $(this));
							//selections_holder.children().removeClass("selected");
							$(this).addClass("selected");
						}).mousedown(function(){ input_focus = false; });
						
					// Add a closer link with click handler
					var close = $('<a class="as-close">&times;</a>').click(function(){
							//values_input.val(values_input.val().replace(","+data[opts.selectedValuesProp]+",",","));
							opts.selectionRemoved.call(this, item);
							input_focus = true;
							input.focus();
							return false;
						});
					
					// Add it
					org_li.before(item.html(data[opts.selectedItemProp]).prepend(close));
					
					// Make selection added call
					opts.selectionAdded.call(this, org_li.prev());	
					*/
					
					var ele = input.parents('fieldset')[0];
					
					// Loop through the attributes of the data object
					for (k in data){
			            
			            // Assign the element
			            var closer = jQuery(ele).find('.closer');
			            var element = jQuery(ele).find('[name^="' + k + '"]');
			            
			            // Update the values accordingly
			            jQuery(element).val(data[k]);
			            
			            if (jQuery(element).is(':text')){
			              
			              	var item = jQuery('<a href="#" class="disabled"><img src="/wp-content/plugins/britishbirds/images/cancel.png" width="20" height="26" /></a>');
			              
			             	 jQuery(closer).html(item);
			             	 jQuery(element).attr('readonly', 'readonly');
			              
			            }
			            
					}
					
					updateSpeciesSelect();
					
				}
				
				// Function to move the current selection
				function moveSelection(direction){
					
					// Check for any visible elements in the results
					if($(":visible",results_holder).length > 0){
						
						// Grab all the list elements
						var lis = $("li", results_holder);
						
						// Check the direction
						if(direction == "down"){
							var start = lis.eq(0);
						} else {
							var start = lis.filter(":last");
						}
						
						// Grab the active one					
						var active = $("li.active:first", results_holder);
						if(active.length > 0){
							if(direction == "down"){
								start = active.next();
							} else {
								start = active.prev();
							}	
						}
						lis.removeClass("active");
						start.addClass("active");
					}
				}
									
			});
		}
	}
})(jQuery);  	
