(function ($) {
	$.fn.extend({
		"customSelect": function (o){
			var focussed_element, // div.customSelect
				default_ops = {
					hover_class: "hover",
					selected_class: "selected"
				},
				change_replacer = function (ev){ // select 
					return select_li($(this));
				},
				focus_replacer = function (ev){ // bound to div.customSelect
					ev.stopPropagation();
					if (this !== focussed_element){
					//	$(focussed_element).blur(); Does not seem to work properly
						$("div.customSelect").blur(); // UPDATE JvdB 20091216: Worse performance but actually works
						focussed_element = this.className === "customSelect" ? this : $(this).parent(".customSelect").get(0);
						$(focussed_element)
							.css({"zIndex": '1000'})
							.addClass("open")
							.find("ul")
							.show()
							.end()
							.prev()
							.focus();
					}
					return false;
				},
				blur_replacer = function (ev){ // bound to div.customSelect
					$(this)
						.css({"zIndex": ''})
						.removeClass("open")
						.find("ul")
						.hide()
						.end()
						.prev()
						.blur();
					focussed_element = undefined;
				},
				// Iverse of select_option: called select.onchange (not nec. onclick)
				select_li = function (target) { // NOT an event handler, target = select
					var sel_opt = target.find("option:selected");
					target
						.next()	// div.customSelect
						.find("div.value strong") // div.customSelect > div.value	
						.text(sel_opt.text())
						.attr("title", sel_opt.text()).parent().next()	// set the visible selected option and move to ul
						.find("li."+default_ops.selected_class).removeClass(default_ops.selected_class).end()	// remove the selected
						.find("li[rel=" + sel_opt.val() + "]").addClass(default_ops.selected_class);	// set the new selected
				},
				// Iverse of select_li: called customSelect.onclick (not nec. onchange)
				select_option = function (target){ // NOT an event handler, target = li (to be selected)
					var sel = target.parent().parent().prev(),	// select
						opt_sel  = sel.find("option[value=" + target.attr("rel") + "]")
						changed = false;
						if (opt_sel.is(":selected") === false){ // only fire change when changed
							opt_sel.attr("selected", "selected");
							changed = true;
						} // will update select replacer
						return changed;
				}; // end populating local scope
			
			$.extend(default_ops, o);
			(this.is("select")? this:this.find("select:visible"))			 
				.hide()	// hide'm
				.each(function (){// create div replacing select
					var li = "";
					$(this).after(
						'<div tabindex="0" class="customSelect"><div class="value"><strong></strong></div><ul class="options" style="display: none;"></ul></div>'
					).find('option').each(function (){
						li += '<li rel="' + this.value + '">' + this.innerHTML + '</li>';
					}).end().next().find("ul").html(li);
				})
				.change(change_replacer) // add change on select
//				.click(change_replacer)
				.change() // and change the replacers (through the selects)
				.next() // move to div.customSelect-s
				.click(function (ev){ // add a click event to div.customSelect
					var sel, 
						$this = $(this),
						$target = $(ev.target);
					if ($target.is("div.value") ||
						$target.is("div.value strong") ||
						$target.is("div.customSelect")){
						/* focus event doesn't (rather: shouldn't) bubble.
						 * focus does show clicked select-replacer
						 * clicking triggers (saf4/OSX) (in order):
						 * 1) focus event > also bubbles to this > fires focus_replacer()
						 * 2) click event > also bubbles to this
						 * > focus_replacer() called twice
						 * clicking (div.value) triggers (ie6/WinXP):
						 * 1) focus event > doesnot bubble to this > hence this case
						 * 2) click event > also bubbles to this > call focus_replacer()
						 * > focus_replacer() called once
						 * clicking (div.customSelect) triggers (ie6/WinXP):
						 * 1) focus event > originated on this > fires focus_replacer()
						 * 2) click event > originated on this
						 * > focus_replacer() called once
						 */
					 	$this.focus(); 
					 	ev.stopPropagation(); // will be hidden by body.onclick
					}
					if ($target.is("li") && $target.attr("rel") != ""){ // Select the clicked option (rel="" = selecteer)
						sel = $target.parent().parent().prev();	// select
						if (select_option($target)){
							sel.change();
							$this.blur();
						}
						//sel.click(); // does not appear to work: resets the select
					}
				})
				.blur(blur_replacer)
				.focus(focus_replacer)
				.mouseover(function (ev){
					var target = $(ev.target);
					if (target.is("li")){
						target.addClass(default_ops.hover_class);
					}
				}).mouseout(function (ev){
					var target = $(ev.target);
					if (target.is("li")){
						target.removeClass(default_ops.hover_class);
					}
				});
			$("body")
				.keydown(function (ev){
					if (focussed_element){
						var $this = $(focussed_element), 
							kc = ev.keyCode ? ev.keyCode : ev.which,
							n;
						switch (kc){
							case 13: // enter: select current option
								$this.blur();
								return false;
							case 38: // arrow up
							case 40: // arrow down
								n = $this
									.find("li." + default_ops.selected_class);
								n = kc === 40 ? n.next() : n.prev();
								if (n.length > 0){
									if (select_option(n)){
										$this.prev().change();
									}
								} // there is no next/prev
						}
					}
				})
				.click(function (ev){
					if (focussed_element)
						$(focussed_element).blur();
				});
			return this;
		}
	});
} (jQuery));
