
function PseudoSelect (select) {
	var element = null;
	var toggler = null;
	var input = null;
	var list = null;
	var options = [];
	var open = false;

	var init = function (select) {
		replaceSelect(select);
		open = false;
		list.css('display', 'none');
		toggler.click(togglerMouseDownListener);

		// populate the selected value
		var selected = list.find('.selected');
		if (!selected) {
			selected = options[0];
		}
		setSelected(selected);

		$(document.body).mousedown(bodyMouseDownListener);

		// add option events
		for (var i = 0; i < options.length; i++) {
			var option = options[i];
			option.click(optionClickListener);
		}

		// fix the width of the lists
		list.css({
			display: 'block',
			visibility: 'hidden'
		});
		var width = toggler.width();

		var lis = options;
		var w = 0;
		for (var i = 0; i < options.length; i++) {
			var optionWidth = options[i].outerWidth();
			w = (optionWidth > w) ? optionWidth : w;
		}
		if (w > 0) {
			for (var i = 0; i < options.length; i++) {
				options[i].css('width', w);
			}
		}

		list.css({
			display: 'none',
			visibility: ''
		});

//		element.set = set;
		return element;
	}

	var replaceSelect =  function (select) {
		element = $('<div></div>');
		element.attr('class', select.attr('class'));
		element.addClass('pseudo-select');

		var value = select.attr('value');

		input = $('<input />');
		input.attr({
			id: select.attr('id'),
			type: 'hidden',
			name: select.attr('name'),
			value: value
		});

		select.parent().append(element);

		toggler = $('<span>');
		label = $('<em>');
		list = $('<ul class="dropdown">');

		toggler.append(label);
		element.append(toggler).append(list);

		var children = select.children();
		for (var i = 0; i < children.length; i++) {
			var el = $(children[i]);
			if (el.get(0).nodeName == 'OPTGROUP') {
				// add the optgroup
				var ul = $('<ul></ul>');
				var groupLabel = el.attr('label');
				if (groupLabel) {
					var li = $('<li class="head"></li>');
					li.html(groupLabel);
					ul.append(li);
				}
				list.append(ul);

				var optionEls = el.find('option');
				for (var j = 0; j < optionEls.length; j++) {
					var option = $(optionEls[j]);
					var optionValue = option.attr('value');
					var li = $('<li></li>');
					li.html(option.attr('text') + ' <span>' + optionValue + '</span>');
					ul.append(li);
					options.push(li);
					if (optionValue == value) {
						li.addClass('selected');
						label.html(value);
					}
				}
			}
			else {
				var option = el;
				var optionValue = option.attr('value');
				var li = $('<li></li>');
				li.html(option.attr('text') + ' <span>' + optionValue + '</span>');
				list.append(li);
				options.push(li);
				if (optionValue == value) {
					li.addClass('selected');
					label.html(value);
				}
			}
		}

		// do the replacement
		select.replaceWith(element);
	}

	var toggleDisplay = function () {
		open = !open;
		list.css('display', open ? 'block' : 'none');
		toggler.toggleClass('open');
	}

	var togglerMouseDownListener = function (event) {
		toggleDisplay();
		return false;
	}

	var bodyMouseDownListener = function (evt) {
		var target = $(evt.target)[0];
		if (open && !$.contains(element[0], target)) {
			evt.stopPropagation();
			toggleDisplay();
			return false;
		}
	}

	var optionClickListener = function (evt) {
		var target = $(evt.target);
		if (!open || !target) {
			return false;
		}

		setSelected(target);
		toggleDisplay()

		return false;
	}

	var setSelected = function (el) {
		if (!el) {
			return;
		}
		var value = el.find('span').html();

		// get the label
		var e = el.clone();
		e.find('span').remove();
		var text = e.html();

		for (var i = 0; i < options.length; i++) {
			element.removeClass(options[i].attr('class'));
		}
		element.addClass(el.attr('class'));

		element.data('value', value);
		input.attr('value', value);
		label.html(text)
		element.change();
	}

	return init($(select));
}

