(function($){

	var menu_objects = {};
	var menu_obj_index = 0;

	$.fn.menu = function(settings) {
		// item types and their params:
		//   link:    name, url
		//   title:   name
		//   divider: -
		// remote content params:
		//   remote_url       - url from which to pull content for the menu
		//   reload_frequency - number of seconds between content reload
		//     0 means no reloading
		//     minimum reload frequency is 15 seconds
		var defaults = {
			'item_data'          : [],
			'current_item_data'  : [],

			'item_template'      : '',		// template used when creating the items
			'filter_fields'      : [],	// the fields in the item data to filter
			'last_filter_text'   : '',    // the last filter text
			'filtered_items'     : [],
			'item_select_func'   : null,
			'empty_list_text'    : 'There are not items',

			'highlight_items'    : true,	// highlights matches when filtering
			'highlighted_item'   : null,
			'highlighted_index'  : null,

			'remote_url'         : '',
			'reload_frequency'   : 0,

			'last_reload_time'   : 0,
			'opacity'            : 1,
			'flush'              : 'left',
			'loading_bar_url'    : ''
		};

		settings = $.extend(defaults, settings);

		return this.each(function() {
			this.settings = settings;

			menu_obj_index++;
			this.menu_id    = 'menu_obj_'+menu_obj_index;

			this.is_open    = false;

			menu_objects[this.menu_id] = this;

			// reload on input
			if(this.settings.filter_fields.length > 0)
			{
				$(this).bind('keydown',     filter_input);
				$(this).bind('keyup',       filter_input);
				$(this).bind('select_item', select_item_event);
			}
			else
			{
				$(this).bind('click', open);
			}
			$(this).bind('close', close);

			if(this.settings.highlight_items)
			{
				$(this).bind('highlight_item', highlight_item_event);
			}

			$(this).bind('create', create);
			$(this).bind('build',  build);

			return this;
		});

		function filter_input(e)
		{
			if(IE) { // ie
				keycode = event.keyCode;
				escapeKey = 27;
				deleteKey = 46;
			} else { // mozilla
				keycode = e.keyCode;
				if(typeof e.DOM_VK_ESCAPE != 'undefined')
				{
					escapeKey = e.DOM_VK_ESCAPE;
					deleteKey = e.DOM_VK_DELETE;
				}
				else
				{
					escapeKey = 27;
					deleteKey = 46;
				}
			}

			enterKey = 13;
			upKey    = 38;
			downKey  = 40;
			spaceKey = 32;

			if(keycode == escapeKey)
			{
				close.call(this);
				return false;
			}

			// do nothing for keyup enter or down key
			if((keycode == enterKey || keycode == downKey) && e.type == 'keyup')
			{
				return;
			}

			// don't update for spaces
			if(keycode == spaceKey)
			{
				return;
			}

			// get rid of extra spaces
			var val = $('#'+this.id).val();
			val = val.replace(/^\s*/,'');
			val = val.replace(/\s*$/,'');
			val = val.replace(/\s+/,' ');

			// if new input, or if empty and down key is pressed
			if((val != this.settings.last_filter_text) || (val == '' && keycode == downKey && !this.is_open))
			{
				this.settings.last_filter_text = val;
				build.call(this);
				if(!this.is_open)
				{
					open.call(this);
				}
				return;
			}

			if(keycode == downKey && e.type != 'keyup')
			{
				highlight_item.call(this, 'down');
				return;
			}
			if(keycode == upKey)
			{
				highlight_item.call(this, 'up');
				return;
			}
			if(keycode == enterKey)
			{
				select_item.call(this);
				return false;
			}
		}

		function select_item_event(e, id)
		{
			select_item.call(this, id);
		}

		function select_item(id)
		{
			if(this.settings.current_item_data.length > 0)
			{
				if(typeof id == 'undefined')
				{
					id = this.settings.highlighted_index;
				}
				this.settings.item_select_func.call(this, this.settings.current_item_data[id]);
			}
		}

		function highlight_item_event(e, id)
		{
			highlight_item.call(this, id);
		}

		function highlight_item(id)
		{
			if(!this.settings.highlight_items)
			{
				return;
			}
			
			var item_data = this.settings.current_item_data;

			if(this.settings.highlighted_index != null)
			{
				$('#'+this.id+'_menu_item_'+this.settings.highlighted_index).removeClass('highlighted');
			}

			if(id == 'first')
			{
				this.settings.highlighted_index = 0;
			}
			else if(id == 'up')
			{
				if(this.settings.highlighted_index > 0)
				{
					this.settings.highlighted_index--;
				}
			}
			else if(id == 'down')
			{
				if(this.settings.highlighted_index < item_data.length-1)
				{
					this.settings.highlighted_index++;
				}
			}
			else
			{
				this.settings.highlighted_index = id;
			}
			this.settings.highlighted_item = item_data[this.settings.highlighted_index];
			
			$('#'+this.id+'_menu_item_'+this.settings.highlighted_index).addClass('highlighted');
		}

		function open()
		{
			if(this.is_open)
			{
				close.call(this);
				return;
			}

			var date = new Date();
			var time = date.getTime()/1000;

			if(this.settings.remote_url != '')
			{
				if(this.settings.last_reload_time == 0 || time > (this.settings.last_reload_time + this.settings.reload_frequency))
				{
					update.call(this);
					return;
				}
			}

			resize.call(this);
		}

		// add the menu container to the DOM
		function create()
		{
			if(this.is_created) return;

			var menu_obj = document.createElement('div');
			menu_obj.setAttribute('class', 'menu_obj');
			menu_obj.setAttribute('className', 'menu_obj');
			menu_obj.id = this.menu_id;
			menu_obj.style.display = 'none';

			$(document.body).append(menu_obj);

			this.is_created = true;
		}

		// build and add menu to menu container
		function build()
		{
			var item_data = [];

			// if filter is enabled, create new list
			if(this.settings.last_filter_text != '' && this.settings.filter_fields.length > 0)
			{
				var item_data_length     = this.settings.item_data.length;

				var filters              = this.settings.last_filter_text.split(' ');
				var filters_length       = filters.length;

				var filter_fields        = this.settings.filter_fields;
				var filter_fields_length = filter_fields.length;

				// for each item
				for(var item_i=0; item_i<item_data_length; item_i++)
				{
					var item = {};
					for(key in this.settings.item_data[item_i])
					{
						item[key] = this.settings.item_data[item_i][key];
					}

					var filters_matched = 0;
					// for each filter
					for(var filter_i=0; filter_i<filters_length; filter_i++)
					{
						var filter = filters[filter_i];

						// for each field
						var field_matched = false;
						for(var field_i=0; field_i<filter_fields_length; field_i++)
						{
							var field = filter_fields[field_i];

							// if the filter is found in the item field
							var regexp = new RegExp("("+filter.reg_quote()+")","i");
							if(item[field].match(regexp))
							{
								item[field] = item[field].replace(regexp,"<b>$1</b>");

								field_matched = true;
							}
						}
						
						if(field_matched)
						{
							filters_matched++;
						}
					}

					if(filters_matched == filters_length)
					{
						item_data.push(item);
					}
				}

				this.settings.current_item_data = item_data;
			}
			else
			{
				item_data = this.settings.item_data;
				this.settings.current_item_data = item_data;
			}

			$('#'+this.menu_id).empty();
			
			if(item_data.length == 0)
			{
				this.settings.highlighted_item = null;
				$('#'+this.menu_id).html(this.settings.empty_list_text);
				return;
			}

			$('#'+this.menu_id).append('<ul></ul>');
			var item_length = item_data.length;
			var divider_found = false;

			for(var i=0; i<item_length; i++)
			{
				var options = item_data[i];

				if(options.type == 'divider')
				{
					var divider_found = true;
				}
				else
				{
					var item_class = options.type;
					if(divider_found)
					{
						item_class += ' divider';
						divider_found = false;
					}
					
					var item_html = '<li id="'+this.id+'_menu_item_'+i+'" class="'+item_class+'"></li>';
					$('#'+this.menu_id+' ul').append(item_html);

					if(this.settings.item_template != '')
					{
						var html = this.settings.item_template;
						for(key in options)
						{
							html = html.replace('{'+key+'}', options[key]);
						}
						$('#'+this.id+'_menu_item_'+i).html(html);
					}
					else
					{
						if(options.type == 'title')
						{
							$('#'+this.id+'_menu_item_'+i).html(options.name);
						}
						else if(options.type == 'link')
						{
							var link = '<a href="'+options.url+'">'+options.name+'</a>';
							$('#'+this.id+'_menu_item_'+i).html(link);
						}
					}

					if(this.settings.item_select_func != null)
					{
						$('#'+this.id+'_menu_item_'+i).bind('click', function(){
							var data = this.id.split('_menu_item_');
							var menu_id = data[0];
							var item_id = data[1];
							$('#'+menu_id).trigger('select_item', [item_id]);
							$('#'+menu_id).trigger('close');
						});
						$('#'+this.id+'_menu_item_'+i).bind('mouseover', function(){
							var data = this.id.split('_menu_item_');
							var menu_id = data[0];
							var item_id = data[1];
							$('#'+menu_id).trigger('highlight_item', [item_id]);
						});
					}
				}
			}

			if(this.settings.filter_fields.length > 0)
			{
				highlight_item.call(this, 'first');
			}
		}

		function close()
		{
			if(!this.is_open) return;

			this.settings.highlighted_item  = null;
			this.settings.highlighted_index = null;
			
			if(this.settings.filter_fields.length > 0)
			{
				$('#'+this.id).val('');
			}

			$('#'+this.menu_id).hide();
			this.is_open = false;
		}
		
		function resize()
		{
			// Set location
			var data = {x: 0, y: 0};
			if(this.settings.flush == 'mouse')
			{
				data.x = mX;
				data.y = mY;
			}
			else
			{
				if(this.id != null)
				{
					// Get the offset of the source
					data = find_pos(document.getElementById(this.id));
					// Add the height of the source
					var source_height =
						($('#'+this.id).height()|0) +
						(parseInt($('#'+this.id).css('paddingTop'))|0) +
						(parseInt($('#'+this.id).css('paddingBottom'))|0) +
						(parseInt($('#'+this.id).css('borderTopWidth'))|0) +
						(parseInt($('#'+this.id).css('borderBottomWidth'))|0);

					data.y += source_height;
				}
				else
				{
					data.x = mX;
					data.y = mY;
				}

				if(this.settings.flush == 'right')
				{
					var menu_width =
						($('#'+this.menu_id).width()|0) +
						(parseInt($('#'+this.menu_id).css('paddingLeft'))|0) +
						(parseInt($('#'+this.menu_id).css('paddingRight'))|0) +
						(parseInt($('#'+this.menu_id).css('borderRightWidth'))|0) +
						(parseInt($('#'+this.menu_id).css('borderLeftWidth'))|0);

					// Subtract the width of the menu
					data.x -= menu_width;
					if(this.id != null)
					{
						// Add the width of the source
						var source_width =
							($('#'+this.id).width()|0) +
							(parseInt($('#'+this.id).css('paddingLeft'))|0) +
							(parseInt($('#'+this.id).css('paddingRight'))|0) +
							(parseInt($('#'+this.id).css('borderRightWidth'))|0) +
							(parseInt($('#'+this.id).css('borderLeftWidth'))|0);

						data.x += source_width;
					}
				}
			}

			var arrayPageSize = getPageSize();
			if(data.x < 5)
			{
				data.x = 5;
			}
			else if(data.x + $('#'+this.id).width() > arrayPageSize[0]-5)
			{
				data.x = arrayPageSize[0] - $('#'+this.id).width()-5;
			}

			$('#'+this.menu_id).css('top',  data.y);
			$('#'+this.menu_id).css('left', data.x);

			$('#'+this.menu_id).css('opacity', this.settings.opacity);
			$('#'+this.menu_id).show();

			this.is_open = true;
		}

		function update(data)
		{
			if(typeof data == 'undefined')
			{
				data = '';
			}

			var date = new Date();
			var time = date.getTime()/1000;
		
			var id   = this.menu_id;
			var menu = this;
			
			if(typeof url == 'undefined')
			{
				url = this.settings.remote_url;
			}

			if(this.settings.loading_bar_url != '')
			{
				$('#'+id).html('<img class="loading_bar" src="'+this.settings.loading_bar_url+'" />');
			}
			else
			{
				$('#'+id).html('loading...');
			}
			resize.call(this);
		
			ajax_request(data,{'url':url, 'success':function(data){
				$('#'+id).html(data.content);
				resize.call(menu);
			}});
		
			this.settings.last_reload_time = time;
		}
	};

	$(document).ready(function(){
		// preload and build menus
		for(key in menu_objects)
		{
			var obj = menu_objects[key];
			
			if(obj.settings.loading_bar_url != '')
			{
				var url = obj.settings.loading_bar_url;
				var img = new Image(1,1);
				img.src = url;
			}
	
			$(obj).trigger('create');
			if(obj.settings.remote_url == '')
			{
				$(obj).trigger('build');
			}
		}
	});

	$(document.body).bind('click', function(e) {
		e = e || window.event;
	
		var target = (e.target) ? e.target : e.srcElement;
		var first_id = '';
		var found = false;
		var abort = false;
	
		if(target.nodeName != 'HTML')
		{
			while(abort == false && found == false && target.nodeName != 'BODY')
			{
				if(target.id != '' && first_id == '')
				{
					first_id = target.id;
				}
				
				if(typeof target.id == 'string' && 
					 (target.id.indexOf('menu_obj_') != -1 ||
					target.id.indexOf('prompt_obj_') != -1))
				{
					found = true;
				}
				else
				{
					target = target.parentNode;
					if(target == null)
					{
						abort = true;
					}
				}
			}
		}
	
		// if an object is found, don't close anything
		if(found)
		{
			return;
		}
		
		var click_type = '';
		if(e.which)
		{
			if(e.which == 1) { click_type = 'left'; }
			else if(e.which == 3) { click_type = 'right'; }
		}
		else
		{
			if(e.button == 1) { click_type = 'left'; }
			else if(e.button == 2) { click_type = 'right'; }
		}
	
		if(click_type == 'right')
		{
			return;
		}
		
		// close all menus that are open
		for(key in menu_objects)
		{
			if(menu_objects[key].is_open && first_id != menu_objects[key].id)
			{
				$('#'+menu_objects[key].id).trigger('close');
			}
		}
	});
})(jQuery);