(function($) {
	$.fn.CustList = $.CustList = function(params) {
		params = params || {};
		form_config={
			class_name:'list',
			next_element:null,
			prev_element:null,
			selected_index:0,
			selected_value:null,
			elements:[],
			parent:null,
			change:function(){},
			select:function(){},
			changed:true,
			autoselect:true
		}
		params = params || {};
		var attr ={};
		this.parent=$(this);
		form_config.id=this.parent.attr('id');
		form_config.name=this.parent.attr('name');
		form_config.class_name+=' '+this.parent.attr('class');
		form_config.style=' '+this.parent.attr('style');
		attr = $.extend({},form_config, params);
		new_el=$('<div></div>').attr('id',attr.id).attr('class',attr.class_name).attr('style',attr.style);
		this.parent.after(new_el);
		attr.parent=new_el;

		if(attr.elements.length>0){
			attr.parent.empty();
			loadList(attr.elements);
		}
		else{
			if(this.parent.is('select')){
				elements=[];
				this.parent.find('option').each(function(){
					opt=$(this);
					data={
						label:opt.text(),
						value:opt.val(),
						data:opt.data()
					};
					class_name=$.trim(opt.attr('class'));
					if(class_name.length>0)
						data.class_name=class_name;
					elements.push(data);
				});
				attr.elements=elements;
			}
			loadList(attr.elements);
		}
		//
		//	new_el.bind('click.CustLink',function(){
		//	    moveTo(attr.selected_index);
		//	});
		if(attr.autoselect){
			moveTo(attr.selected_index);
		}
		this.parent.remove();
		return attr.parent;

		function moveLast(){
			var $a = $("a", attr.parent);
			moveTo($a.length - 1);
		}
		function moveFirst(){
			moveTo(0);
		}
		function moveSelect(step){
			$curent=$('a.selected',attr.parent);
			var pos = $('a',attr.parent).index($curent);
			moveTo(pos + step);
		}
		function moveTo(pos){
			var $a = $("a", attr.parent), $next;
			// if the options are empty, cancel request
			if( !$a || $a.length == 0 ) return false;

			// don't go lower than first item
			if( isNaN(pos) || pos < 0 ){
				$next = $a.eq(0);
				pos=0;
			// if greater than last position
			} else if( pos > $a.length - 1 ){
				$next = $a.eq($a.length - 1);
				pos=0;
			// get the first item
			} else {		
				$next = $a.eq(pos);
			}
			attr.changed=(pos!=attr.selected_index);
			$next.focus();
			if($.isFunction(attr.change))
				attr.change(attr.selected_index,$next,attr.changed);
		}
		function loadList(data){
			attr.elements=data;
			for (i = 0; i < attr.elements.length; i++) {
				val=attr.elements[i];
				if(typeof(val.value)=='string' && typeof(val.label)=='string'){
					label=val.label?val.label:'&nbsp;';
					child=$('<a href="#"  >'+label+'</a>')
					.data('value',val.value)
					.data('info',val.data)
					.bind("keydown.CustList" , function(e, e2){
						// if we've passed a copy of an event object, use that instead
						if( !!e2 ) var e = e2;
						var key = e.keyCode || e.charCode,
						cur_char = String.fromCharCode(key).toLowerCase();
						switch(key) {
							case 38: // up
							case 40: // down
								e.preventDefault();
								moveSelect((key == 38) ? -1 : 1);
								lastCurrentEvent = e;
								return false;
								break;
							case 37: // left
								e.preventDefault();
								if($(attr.prev_element).length==1){
									el=$(attr.prev_element).find('a.selected');
									if(el.length==0){
										$(attr.prev_element).find('a:first').focus();
									}
									else{
										el.focus();
									}
								}
								break;
							case 13: // return
							case 39: // right
							case 9: // tab
								e.preventDefault();
								el=$(attr.next_element).find('a.selected');
								$(this).click();
								if($.isFunction(attr.select))										
									attr.select(attr.selected_index,attr.selected_item,attr.changed);
								if(el.length==0)
									$(attr.next_element).find('a:first').focus();
								else
									el.focus();
								break;
							case 27: // escape
								e.preventDefault();
								
								if($.isFunction(attr.close))
									attr.close(attr.selected_index,attr.selected_item,attr.changed);
								break;
							case 35: // end
								e.preventDefault();
								moveLast();
								lastCurrentEvent = e;
								break;
							case 36: // home
								e.preventDefault();
								moveFirst();
								lastCurrentEvent = e;
								break;
							case 33: // page up
							case 34: // page down
								e.preventDefault();
								//var iItemsPerPage = parseInt($scrollable.height()/$ul.find("li:first").outerHeight(), 10);
								// move up/down the total number of items visible
								//moveSelect((key == 33) ? iItemsPerPage * -1 : iItemsPerPage);
								break;
						}
					}).bind('keypress.CustList',function(e){
						e.preventDefault();
					}).bind('click.CustList', function(e){
						e.preventDefault();
						var pos = $('a',attr.parent).index(this);
						moveTo(pos);
						if($.isFunction(attr.click))
							attr.click(attr.selected_index,attr.selected_item,true);
						return false;
					}).bind('focus.CustList', function(e, e2){
						$("a", attr.parent).removeClass('selected');
						$(this).addClass('selected').addClass('focus');
						new_pos=$("a", attr.parent).index(this);
						//						attr.changed=(new_pos!=attr.selected_index);
						attr.selected_item=$(this);
						attr.selected_index=new_pos;
					}).bind('blur.CustList focusout.CustList', function(e){
						$(this).removeClass('focus');
					}).appendTo(attr.parent);
					if(typeof(val.class_name)=='string')
						child.addClass(val.class_name);
				}
			}
		}
	};
})(jQuery);
