(function($){
	
	$.fn.selBox = function(options){
		
		var opts = $.extend({}, $.fn.selBox.defaults, options);
		
		return this.each(function(){
			
			var sBox  = this;
			var $sBox = $(this);
			
			var o = $.meta ? $.extend({}, opts, $sBox.data()) : opts;
			
			var hasFocus     = 0;
			var hasScroll    = 0;
			var layerHeight  = 0;
			var hoveredIndex = 0;
			
			var $container;
			var $input;
			var $layer;
			var $options;
			var $selectedOpt;
			var $hoveredOpt;
			var $ifrm;
			
			sBox.init = function(){
				$container = sBox.buildContainer();
				$input = sBox.buildInput();
				$layer = sBox.buildLayer();
				
				$input.width((($sBox.width() > o.maxInputWidth) ? o.maxInputWidth : $sBox.width()) - o.arrowWidth);
				$container.width(($sBox.width() > o.maxInputWidth) ? o.maxInputWidth : $sBox.width());
				
				$container.append($input).append($layer);
				
				$sBox.hide().before($container);
				
				$layer.width($sBox.width() - 2).css("top", o.selectHeight + 1);
				if ($layer.height() > o.maxHeight) {
					hasScroll = 1;
					$layer.height(o.maxHeight);
				}
				layerHeight = $layer.height();
				
				if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
					$ifrm = $('<iframe class="bgiframe" frameborder="0" tabindex="-1" src="javascript:false;" style="display:block; position:absolute; z-index:-1;' +
				               'filter:Alpha(Opacity=\'0\');' +
					       'top:' + (o.selectHeight + 1) + 'px;' +
					       'left: 0px;' +
					       'width:' + $sBox.width() + 'px;' +
					       'height:' + layerHeight +'px;' +
					'"/>');
					
					$ifrm.insertBefore($($layer, $container)).hide();
				}
				
				$options = $("li", $layer);
				$selectedOpt = $options.filter("." + o.optOnClass);
				$hoveredOpt = $options.filter("." + o.optHoverClass);
				
				sBox.assignActions();
			}
			
			sBox.buildContainer = function(){
				return $("<div id=\"" + $sBox.attr("id") + "_sBox\" class=\"" + o.containerClass + "\"></div>");
			}
			
			sBox.buildInput = function(){
				return $("<input type=\"text\" id=\"" + $sBox.attr("id") + "_sBoxInput\" class=\"" + o.inputClass + "\" tabIndex=\"" + $sBox.attr("tabindex") + "\" autocomplete=\"off\" readonly=\"readonly\" style=\"-moz-user-select: none;\" />");
			}
			
			sBox.buildLayer = function(){
				var _opt = "<ul id=\"" + $sBox.attr("id") + "_sBoxOptions\" class=\"" + o.layerClass + "\">";
				
				$("option", sBox).each(function(i, v){
					var $v = $(v);
					var _class = "";
					
					if ($v.is(":selected")) {
						_class = " class=\"" + o.optOnClass + " " + o.optHoverClass + "\"";
						$input.val($v.text());
						hoveredIndex = i;
					}
					
					_opt += "<li" + _class + " rel=\"" + $v.val() + "\">" + $v.html() + "</li>";
				});
				
				_opt += "</ul>";
				
				return $(_opt);
			}
			
			sBox.assignActions = function(){
				
				$container.click(function(e){
					$input.click().get(0).focus();
					e.preventDefault(); e.stopPropagation();
				}).hover(function(e){
					hasFocus = 1;
				}, function(e){
					hasFocus = 0;
				});
				
				$layer.hover(function(){}, function(){
					hasFocus = 0;
				})
				
				$input.focus(function(e){
					e.preventDefault(); e.stopPropagation();
				}).blur(function(e){
					if (hasFocus == 0) {
						$layer.hide();
						if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
							$ifrm.hide();
						}
					}
					e.preventDefault(); e.stopPropagation();
				}).keydown(function(e){
					sBox.inputKey(e.keyCode);
					e.preventDefault(); e.stopPropagation();
				}).click(function(e){
					sBox.toggleLayer();
					e.preventDefault(); e.stopPropagation();
				});
				
				$options.click(function(e){
					$input.get(0).focus();
					sBox.optionChange($(this));
					e.preventDefault(); e.stopPropagation();
				}).mouseover(function(e){
					sBox.hoverChange($(this));
					e.preventDefault(); e.stopPropagation();
				});
			}
			
			sBox.toggleLayer = function(){
				$layer.toggle();
				if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
					$ifrm.toggle();
				}
				if ($layer.is(":visible")) {
					hasFocus = 1;
					sBox.scrollTo($selectedOpt);
				}
			}
			
			sBox.scrollTo = function($obj){
				if (hasScroll > 0) {
					var optionOffset = $obj.get(0).offsetTop;
					var layerOffset = $layer.get(0).scrollTop;
					
					if (optionOffset < layerOffset) {
						$layer.scrollTop(optionOffset);
					}
					else if ((optionOffset - layerHeight) >= layerOffset) {
						$layer.scrollTop(optionOffset - layerHeight + $obj.height() + 2);
					}
				}
			}
			
			sBox.inputKey = function(key){
				
				switch(key) {
					case 33: // pgUp
						sBox.moveKey(-10);
						break;
					case 34: // pgDown
						sBox.moveKey(10);
						break;
					case 38: // up
						sBox.moveKey(-1);
						break;
					case 40: // down
						sBox.moveKey(1);
						break;
					case 36: // home
						sBox.moveKey(0);
						break;
					case 35: // end
						sBox.moveKey($options.length - 1);
						break;
					case 13: // return
						if ($layer.is(":visible")) {
							sBox.optionChange($hoveredOpt);
						}
						else {
							$layer.show();
							if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
								$ifrm.show();
							}
							sBox.scrollTo($selectedOpt);
						}
						break;
					case 27: // escape
						$layer.hide();
						if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
							$ifrm.hide();
						}
						break;
						
					default:
						if ((key >= 48 && key <= 57) || (key >= 65 && key <= 90) || (key >= 97 && key <= 122)) {
						
							var patt = new RegExp("^" + String.fromCharCode(key), "i");
							var hasFirst = 0;
							
							$newOpt = $options.filter(function(i){
								if (!hasFirst && (patt.test($options[i].innerHTML) == true)) {
									hasFirst = 1;
									return true;
								}
								else if ((i > hoveredIndex) && (patt.test($options[i].innerHTML) == true)) {
									return true;
								}
								else {
									return false;
								}
							});
							
							if ($newOpt.length > 0) {
								
								if ($newOpt.length > 1) {
									newSel = 1;
								}
								else {
									newSel = 0
								}
								
								if ($layer.is(":visible")) {
									sBox.scrollTo($($newOpt.get(newSel)));
									sBox.hoverChange($($newOpt.get(newSel)));
								}
								else {
									sBox.optionChange($($newOpt.get(newSel)));
								}
							}
						}
				}
			}
			
			sBox.optionChange = function($obj){
				$input.val($obj.text());
				
				$("option", sBox).removeAttr("selected").filter("[value='" + $obj.attr("rel") + "']").attr("selected", "selected");
				
				$layer.hide();
				if ( $.browser.msie && /6.0/.test(navigator.userAgent) ) {
					$ifrm.hide();
				}
				$selectedOpt.removeClass(o.optOnClass);
				$obj.addClass(o.optOnClass);
				$selectedOpt = $obj;
				sBox.hoverChange($obj);
				
				o.afterChange.call(this, $obj);
			}
			
			sBox.hoverChange = function($obj){
				$hoveredOpt.removeClass(o.optHoverClass);
				$obj.addClass(o.optHoverClass);
				$hoveredOpt = $obj;
				hasFocus = 1;
				hoveredIndex = $options.index($obj);
			}
			
			sBox.moveKey = function(offset){
				if ($layer.is(":visible")) {
					var _index = $options.index($hoveredOpt);
					var _new_index;
					
					if ((offset == 0) || (offset == ($options.length - 1))) {
						_new_index = offset;
					}
					else {
						_new_index = ((_index + offset) < 0) ? 0 : (((_index + offset) > ($options.length - 1)) ? ($options.length - 1) : (_index + offset));
					}
					
					sBox.hoverChange($($options[_new_index]));
					sBox.scrollTo($($options[_new_index]));
				}
				else {
					var _index = $options.index($selectedOpt);
					var _new_index
					
					if ((offset == 0) || (offset == ($options.length - 1))) {
						_new_index = offset;
					}
					else {
						_new_index = ((_index + offset) < 0) ? 0 : (((_index + offset) > ($options.length - 1)) ? ($options.length - 1) : (_index + offset));
					}
					
					sBox.optionChange($($options[_new_index]));
				}
			}
			
			sBox.init();
			
		});
	};
	
	$.fn.selBox.defaults = {
		maxHeight      : 250,
		maxInputWidth  : 220,
		arrowWidth     : 31,
		selectHeight   : 22,
		containerClass : "sBox_container",
		inputClass     : "sBox_input",
		layerClass     : "sBox_layer",
		optOnClass     : "sBox_optOn",
		optHoverClass  : "sBox_optHover",
		afterChange    : function(obj){}
	};
	
})(jQuery);
