(function($){
	var undefined;
	var num = function(el, props) {
		var r = 0;
		$.each(props.split(/\s+/) || [], function(i,v){
			r += parseInt($(el).css(v)) || 0;
		});
		return r;
	}
	
	var innerHSpace = function(el) { return num(el, "paddingLeft paddingRight"); }
	var innerVSpace = function(el) { return num(el, "paddingTop paddingBottom"); }
	var outerHSpace = function(el) { return innerHSpace(el) + num(el, "marginLeft marginRight borderLeftWidth borderRightWidth"); }
	var outerVSpace = function(el) { return innerHSpace(el) + num(el, "marginTop marginBottom borderTopWidth borderBottomWidth"); }
	
	var cssShow = { position: "absolute", visibility: "hidden", display:"block" };
	
	function getWH(elem, func) {
		var ret = 0, props = cssShow, el = $(elem), elem = el[0], p;
		
		function getWH() {
			ret = func.call(elem);
		}
		
		function swap(elem, options, callback) {
			var old = {};

			// Remember the old values, and insert the new ones
			for ( var name in options ) {
				old[ name ] = elem.style[ name ];
				elem.style[ name ] = options[ name ];
			}

			callback.call( elem );

			// Revert the old values
			for ( var name in options ) {
				elem.style[ name ] = old[ name ];
			}
		}
		
		if (elem.offsetWidth !== 0) {
			getWH();
		} else {
			if (el.is(":hidden")) {
				p = el.add(el.parents(":hidden"));
			} else {
				p = el.parents(":hidden");
			}
			
			
			for (var i=p.length-1; i >= 0; i--) {
				swap(p[i], props, getWH);
				if (ret > 0) {
					break;
				}
			}
		}
		
		return Math.max(0, Math.round(ret));
	}
	
	
	
	$.fn.dropdown = function(options){
		var isMethodCall = (typeof options == "string"),
			args = Array.prototype.slice.call(arguments, 1);
			
		if (isMethodCall) {
			var instance = $(this[0]).data("dropdown");
			if (instance && $.isFunction(instance[options])) {
				return instance[options].apply(instance, args);
			} else {
				return;
			}
		}
		
		return this.each(function() {
			var instance = $(this).data("dropdown");
			if (isMethodCall && instance && $.isFunction(instance[options])) {
				instance[options].apply(instance, args);
			} else if (!instance) {
				$(this).data("dropdown", new $.dropdown(this, options));
			}
		});
	};
	
	var saveProps = function (el) {
		var ret = {};
		
		$(["display", "overflow", "width", "height"]).each(function(i,v) {
			ret[v] = el.css(v);
		})
		return ret;
	};
	
	var restoreProps = function(el, props) {
		var css = {};
		$.each(props, function(i, v) {
			css[i] = v;
		});
		el.css(css);
	}
	
	var Dropdown = $.dropdown = function(element, options) {
		this.element = $(element);
		this.options = $.extend(true, {},$.dropdown.defaults, options);
		this.init();
	};
	
	$.extend($.dropdown.prototype, {
		init: function() {
			
			if ($(this.element).is("select")) {
				this.select = this.element;
				$(this.select).wrap("<div class='dropdown'></div>");
				this.control = $(this.select).parent();
			} else {
				this.select = $("select:eq(0)", this.element);
				if (!this.select.length) return;
				this.select = this.select[0];
				$(this.element).addClass('dropdown');
				this.control = $(this.element);
			}
			this.initControl();
		},
		
		initControl: function() {
			var a,w,e,save = {};
			
			this.control.append( 
				$("<div class='dropdown-result-container'><div class='dropdown-result'></div></div><div class='dropdown-arrow-container'><a class='dropdown-arrow'></a></div>"));
			this.resultContainer = $(".dropdown-result-container", this.control);
			this.arrowContainer = $(".dropdown-arrow-container", this.control);
			this.arrow = $(".dropdown-arrow", this.arrowContainer);
			this.result = $(".dropdown-result", this.resultContainer);
			
			this.resize();
			
			this.initList();
			
			if (!this.select_options.length > 0) return;
			this.initEvents();
		},
		
		resize: function(w,h,changed) {
			var arrowWidth;
			
			this.resultContainer.hide();
			this.arrowContainer.hide();
			
			arrowWidth = getWH(this.arrowContainer, function() {
				return $(this).outerWidth(true);
			});
			
			if ("auto" == (this.select[0].style.width || "auto")) {
				this.selectWidth = getWH(this.select, function() {
					return $(this).innerWidth();
				}) + 16;
			}
			
			//this.selectWidth += innerHSpace(this.result);
			
			this.selectMargin = {
				marginLeft: parseInt(this.select.css("marginleft")) || 0,
				marginRight: parseInt(this.select.css("marginRight")) || 0,
				marginTop: parseInt(this.select.css("marginTop")) || 0,
				marginBottom: parseInt(this.select.css("marginBottom")) || 0
			}
			
			//this.select.hide();
			this.select.css({
				visibility:"hidden",
				position:"absolute",
				left:0,
				top:"-99999em"
			});
			
			this.resultContainer.show();
			this.arrowContainer.show();
			
			this.resultContainer.css("width","");
			this.result.css("width","");
			this.control.css({
				width: "",
				margin: 0
			});
			
			w = this.selectWidth - outerHSpace(this.control);
			this.control.css({
				width: w+"px",
				marginLeft: this.selectMargin.marginLeft + "px",
				marginTop: this.selectMargin.marginTop + "px",
				marginRight: this.selectMargin.marginRight + "px",
				marginBottom: this.selectMargin.marginBottom + "px"
			});
			
			w -= arrowWidth;
			
			w -= outerHSpace(this.resultContainer);
			this.resultContainer.css({
				width: w+"px"
			});
			
			w -= outerHSpace(this.result);
			this.result.css({
				width: w+'px'
			});
			
			this.resultContainerHeight = this.resultContainer.outerHeight();
		},
		
		initList: function() {
			var options, html, value, classes, ul, i, n;
			this.select_options = $("option", this.select);
			if (!this.select_options.length > 0) return;
			this.itemsValue = [];
			
			this.listContainer = $("<div class='dropdown-list-container'><div class='dropdown-list-wrapper'><ul class='dropdown-list'></ul></div></div>");
			this.listWrapper = $(".dropdown-list-wrapper", this.listContainer);
			ul = $(".dropdown-list", this.listContainer);
			
			for (i=0,n=this.select_options.length; i < n; i++) {
				option = $(this.select_options[i]);
				html = option.text();
				value = option.attr("value") || "";
				
				//((option.attr("disabled") || false))
				classes = [];
				(this.select_options[i].disabled && classes.push("disabled"));
				(this.select_options[i].selected && classes.push("selected"));
				
				ul.append($("<li class='" + classes.join(" ") + "'/>")
					.append($("<span />")
						.attr({
							title: html
						})
						.text(html))
				);
				this.itemsValue[i] = value;
			}
			
			this.list = ul;
			this.listItems = $("li", this.list);
			
			if (!this.listItems.filter(".selected")) {
				$(this.listItems[0]).addClass("selected");
			}
			
			this.listItems.hover(function(){
				$(this).addClass("hover");
			}, function(){
				$(this).removeClass("hover");
			});
			
			this.current = this.initial = $(this.listItems.filter(".selected")[0]);
			this.initialValue = this.getItemValue(this.initial);
			this.setValue( this.getItemValue(this.current) );
			
			this.listContainer.appendTo("body").hide();
		},
		
		initEvents: function() {
			var self = this;
			this.arrow.bind("click.dropdown", function(event) {
				$.dropdown.showDropdown(self, event);
				event.preventDefault();
				return false;
			});
			
			this.resultContainer.bind("dblclick.dropdown", function(event) {
				try {
					window.getSelection().collapseToStart();
					document.selection.clear();
				} catch(e) { }
				$.dropdown.showDropdown(self, event);
				event.preventDefault();
				return false;
			});
			
			self.bySetValue = false;
			$(this.select).bind("change.dropdown", function(event) {
				if (self.bySetValue) {
					//this.bySetValue = false;
				} else {
					var v = this.value || "";
					self.setValue.call ( self, v );
				}
			});
			
			//bind form reset
			if ($(this.select).parents("form").length) {
				$(this.select).parents("form").bind("reset", function() {
					self.setValue.call ( self, self.initialValue);
				});
			}
		},
		
		setValue: function(val) {
			var idx, curIdx, i, n, r, changed, text;
			
			if (typeof val === "undefined") return;
			
			for (i=0,n=this.itemsValue.length; i < n; i++) {
				if (this.itemsValue[i] == val) {
					idx = i;
					break;
				}
			}
			
			changed = false;
			this.bySetValue = true;
			curIdx = this.listItems.index(this.current);
			$(this.listItems).removeClass("selected");
			if (idx > -1 && idx != curIdx) {
				this.current = $(this.listItems[idx]);
				if (this.select[0].value != val) {
					changed = true;
				}
				this.select[0].value = val;
			}
			
			if (idx > -1) {
				$(this.listItems[idx]).addClass("selected");
				text = $(this.listItems[idx]).text();
				r = $("<span />").text(text).attr("title", text);
				if (val == this.initialValue) {
					r.addClass("initialValue");
				}
				if (val == "" ) {
					r.addClass("emptyValue");
				}
				this.result.html("").append(r);
			}
			
			if (changed) {
				//this is for jQuery.validator();
				$(this.select).trigger("keyup");
				
				if ($.browser.msie) {
					this.select[0].oldOnChange = this.select[0].onchange;
					this.select[0].onchange = null;
				}
				
				this.select.trigger("change");
				
				if ($.browser.msie) {
					this.select[0].onchange = this.select[0].oldOnChange;
					this.select[0].oldOnChange = null;
					var ev=document.createEventObject();
					ev.srcElement = this.select[0];
					ev.target = this.select[0];
					
					this.select[0].fireEvent("onchange", ev);
				}
			}
			
			this.bySetValue = false;
		},
		
		getItemValue: function(item) {
			var idx; 
			if (!item) return;
			
			idx = this.listItems.index(item);
			if ( idx > -1 && idx < this.itemsValue.length ) {
				return this.itemsValue[idx];
			}
			return;
		},
		
		hideItem: function(idx) {
			$("li:eq(" + idx + ")", this.listContainer).hide();
		},
		
		showItem: function(idx) {
			$("li:eq(" + idx + ")", this.listContainer).show();
		}
	});
	
	$.extend($.dropdown, {
		defaults: {
		}
	});
	
	$.extend($.dropdown, {
		getPageSize: function () {
			var xScroll, yScroll;

			if (window.innerHeight && window.scrollMaxY) {	
				xScroll = window.innerWidth + window.scrollMaxX;
				yScroll = window.innerHeight + window.scrollMaxY;
			} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
				xScroll = document.body.scrollWidth;
				yScroll = document.body.scrollHeight;
			} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
				xScroll = document.body.offsetWidth;
				yScroll = document.body.offsetHeight;
			}

			var windowWidth, windowHeight;

			if (self.innerHeight) {	// all except Explorer
				if(document.documentElement.clientWidth){
					windowWidth = document.documentElement.clientWidth; 
				} else {
					windowWidth = self.innerWidth;
				}
				windowHeight = self.innerHeight;
			} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
				windowWidth = document.documentElement.clientWidth;
				windowHeight = document.documentElement.clientHeight;
			} else if (document.body) { // other Explorers
				windowWidth = document.body.clientWidth;
				windowHeight = document.body.clientHeight;
			}	

			// for small pages with total height less then height of the viewport
			if(yScroll < windowHeight){
				pageHeight = windowHeight;
			} else { 
				pageHeight = yScroll;
			}


			// for small pages with total width less then width of the viewport
			if(xScroll < windowWidth){	
				pageWidth = xScroll;		
			} else {
				pageWidth = windowWidth;
			}
			
			//
			var largestWidth;
			var largestHeight;
			var smallestWidth;
			var smallestHeight;
			//
			if ( pageWidth >= windowWidth )
			{	largestWidth = pageWidth; smallestWidth = windowWidth;	}
			else
			{	largestWidth = windowWidth; smallestWidth = pageWidth;	}
			//
			if ( pageHeight >= windowHeight )
			{	largestHeight = pageHeight; smallestHeight = windowHeight;	}
			else
			{	largestHeight = windowHeight; smallestHeight = pageHeight;	}
			
			// Return
			var arrayPageSize = {'pageWidth':pageWidth,'pageHeight':pageHeight,'windowWidth':windowWidth,'windowHeight':windowHeight,'largestWidth':largestWidth,'largestHeight':largestHeight};
			return arrayPageSize;
		},
			
		getPageScroll: function ( ) {
			var xScroll, yScroll;
			if (self.pageYOffset) {
				yScroll = self.pageYOffset;
				xScroll = self.pageXOffset;
			} else if (document.documentElement && document.documentElement.scrollTop) {	 // Explorer 6 Strict
				yScroll = document.documentElement.scrollTop;
				xScroll = document.documentElement.scrollLeft;
			} else if (document.body) {// all other Explorers
				yScroll = document.body.scrollTop;
				xScroll = document.body.scrollLeft;	
			}
			var arrayPageScroll = {'xScroll':xScroll,'yScroll':yScroll};
			return arrayPageScroll;
		},
		
		showDropdown: function(dropdown, e) {
			var w, h, x, y, t, b, maxH, maxW;
			
			if (!dropdown.firstTimeShow) {
				$.dropdown.initListDimension(dropdown);
				dropdown.firstTimeShow = true;
			}
			
			pageSize = $.dropdown.getPageSize();
			pageScroll = $.dropdown.getPageScroll();
			controlOffset = dropdown.control.offset();
			
			controlOffset.top -= parseInt(dropdown.control.css("marginTop")) || 0;
			
			x = controlOffset.left;
			y = controlOffset.top + dropdown.resultContainerHeight;
			t = controlOffset.top - pageScroll.yScroll;
			
			/*
			b = pageSize.windowHeight - (y - pageScroll.yScroll);
			if ( t > b ) {
				y = 0;
				maxH = t - 20;
			} else {
				maxH = b - 20;
			}
			
			h = Math.min(maxH, dropdown.listHeight + dropdown.listOuterVSpace);
			if (y < 1) {
				y = controlOffset.top - h;
			}
			h -= outerHSpace(dropdown.listContainer);
			*/
			dropdown.listContainer.css({
				left: x+"px",
				top: y+"px",
				height: ""
			}).show();
			
			/*
			h -= outerHSpace(dropdown.listWrapper);
			dropdown.listWrapper.css({
				height: h+"px",
				overflow:"auto"
			});
			*/
			/*
			w = dropdown.listContainer.width();
			h = dropdown.listContainer.height();
			
			dropdown.listContainer.css({
				width:0,
				height:0,
				opacity:0
			}).animate({width: w+'px', height: h+'px', opacity:1}, {queue:false, easing:'swing', duration:300, complete:function(){
				dropdown.listContainer.css({
					opacity:"",
					width: w+"px",
					height:""
				});
				dropdown.listContainer.addClass("dropdown-list-container-show");
					$("html")
						.unbind("mousedown.dropdown")
						.bind("mousedown.dropdown", function(e) {
							var idx;
							if ($(e.target).parents(".dropdown-list-container-show").length > 0) {
								idx = dropdown.listItems.index($(e.target).parent());
								if (idx >= 0) {
									//dropdown.setValue(dropdown.getItemValue(dropdown.listItems[idx]));
									dropdown.setValue.call(dropdown, dropdown.getItemValue(dropdown.listItems[idx]));
									$.dropdown.hideDropdown(dropdown);
								}
							} else {
								$.dropdown.hideDropdown(dropdown);
							}
							
							e.preventDefault();
							e.stopPropagation();
							return false;
						});
			}});
			*/
			dropdown.listContainer.addClass("dropdown-list-container-show");
			dropdown.control.addClass("dropdown-show");
			$("html")
				.unbind("mousedown.dropdown")
				.bind("mousedown.dropdown", function(e) {
					var idx;
					if ($(e.target).parents(".dropdown-list-container-show").length > 0) {
						idx = dropdown.listItems.index($(e.target).parent());
						if (idx >= 0) {
							//dropdown.setValue(dropdown.getItemValue(dropdown.listItems[idx]));
							dropdown.setValue.call(dropdown, dropdown.getItemValue(dropdown.listItems[idx]));
							$.dropdown.hideDropdown(dropdown);
						}
					} else {
						$.dropdown.hideDropdown(dropdown);
					}
					
					e.preventDefault();
					e.stopPropagation();
					return false;
				});
		},
		
		hideDropdown: function(dropdown) {
			$(".dropdown-list-container").removeClass("dropdown-list-container-show");//.hide();
			dropdown.control.removeClass("dropdown-show");
			$("html").unbind("mousedown.dropdown");
			dropdown.listContainer.hide();
			/*
			dropdown.listContainer.animate({width: 0+'px', height: 0+'px', opacity:0}, {queue:false, easing:'swing', duration:250, complete:function(){
				dropdown.listContainer.css({
					opacity:"",
					width: dropdown.listContainerWidth+"px",
					height:""
				}).hide();
			}});
			*/
		},
		
		initListDimension: function(dropdown) {
			var w, W, H, controlWidth, listWidth;
			
			dropdown.listContainer.show();
			
			controlWidth = dropdown.control.outerWidth();
			listWidth = dropdown.listContainer.outerWidth();
			
			if (listWidth < controlWidth) {
				w = listWidth = controlWidth;
				w -= outerHSpace(dropdown.listContainer)
				dropdown.listContainer.css({
					width: w+"px",
					overflow: "hidden"
				});
			}
			
			dropdown.listContainerWidth = w;
			dropdown.listWidth = dropdown.list.outerWidth(true);
			dropdown.listHeight = dropdown.list.outerHeight(true);
			dropdown.listOuterHSpace = dropdown.listContainer.outerWidth(true) - dropdown.listWidth;
			dropdown.listOuterVSpace = dropdown.listContainer.outerHeight(true) - dropdown.listHeight;
		}
	});
})(jQuery);;
