(function($) {
	$.fn.vSelect = function(options, config)
	{
		// Initialisation du plugin - wrapping
		settings = $.extend($.fn.vSelect.defaults, config);
		if (!options) {
			options = [];
		}

		// Pour chaque élément, on le wrappe par un select
		return this.each(function() {
			var select = $(this);
			// Initialisation du conteneur du fake select
			if ( settings.width ) {
				select.css('width', settings.width );
			}
			if ( settings.height ) {
				select.css('height', settings.height );
			}
			if ( settings.disabled ) {
				select.addClass( 'disabled' );
			}else
			{
				select.removeClass( 'disabled' );
			}

			// Création du menu déroulant si besoin est
			var dropDownDiv = $(this).get(0).dropdown;
			if (dropDownDiv) {
				dropDownDiv.empty();
			}
			else {
				dropDownDiv = $("<div class='" + settings.css.dropdown + "' />");
				$(/*settings.dropdownscontainer*/"#sns_mainpagecontent").append(dropDownDiv);
				$(this).get(0).dropdown = dropDownDiv;
			}

			// Initialisation du menu déroulant à partir des options
			_addOptions( $(this), dropDownDiv, options, settings );
			
			// Handler d'ouverture de la liste déroulante au click sur le select
			var selectClickHandler = function()
			{
				dropDownDiv.unbind('mouseenter');
				dropDownDiv.unbind('mouseleave');
				if (dropDownDiv.is(":visible"))
				{
					$(document).unbind('keydown');
					dropDownDiv.slideUp('fast');
				}
				else if ($("." + settings.css.dropdown + ":visible").length > 0)
				{
					$("." + settings.css.dropdown + ":visible")
						.slideUp('fast', function() { dropDownDiv.slideDown('fast'); });
				}
				else
				{
					_refreshPosition( select, settings );
					$("." + settings.css.dropdown + " ." + settings.css.hovered)
						.removeClass(settings.css.hovered);
					dropDownDiv.slideDown('fast');
				}				
			};

			// Handler d'entré de la souris au dessu de la liste déroulante
			var dropDownEnterHandler = function()
			{
				window.clearTimeout(autoCloseTimeOut);
			};
			// Handler de sortie de la souris au dessus de la liste déroulante
			var dropDownLeaveHandler = function()
			{
				// Rétraction menu déroulant
				select.get(0).dropdown.slideUp('fast');
			};

			// Handler d'entrée de la souris au dessus du bouton du select
			var mouseEnterHandler = function()
			{
				dropDownDiv.unbind('mouseenter');
				dropDownDiv.unbind('mouseleave');
				dropDownDiv.mouseenter(dropDownEnterHandler);
				dropDownDiv.mouseleave(dropDownLeaveHandler);
				if (dropDownDiv.is(":visible")) {
					$(document).unbind('keydown');
					dropDownDiv.slideUp('fast');
				}
				else if ($("." + settings.css.dropdown + ":visible").length > 0)
				{
					$("." + settings.css.dropdown + ":visible")
						.slideUp('fast', function() { dropDownDiv.slideDown('fast'); });
				}
				else
				{
					_refreshPosition( select, settings );
					$("." + settings.css.dropdown + " ." + settings.css.hovered)
						.removeClass(settings.css.hovered);
					dropDownDiv.slideDown('fast');
				}				
			};

			// Handler de sortie de la souris au dessus du bouton du select
			var mouseLeaveHandler = function()
			{
				autoCloseTimeOut = window.setTimeout(
						function() { select.get(0).dropdown.slideUp('fast'); },
						100);
				$(document).unbind('keydown');
			};
						
			// Branchement des gestionnaires
//			select.find('.button').unbind('mouseenter');
//			select.find('.button').unbind('mouseleave');
			select.unbind('click');
			if ( !select.hasClass('disabled') )
			{
//				select.find('.button').mouseenter(mouseEnterHandler);
				var autoCloseTimeOut = 0;
//				select.find('.button').mouseleave(mouseLeaveHandler);
				select.click(function(){
					var links = select.get(0).dropdown.find('a');
					if( this.dropdown.css('display') == 'none')
						lastIndex = links.index(select.get(0).selItem);
					$(this).focus();
					$(document).unbind('keydown');
					$(document).keydown(function(event){
						var idx = 0;
						switch (event.keyCode)
						{
							case 37 :
							case 38 :
								$("." + settings.css.dropdown + " ." + settings.css.hovered)
									.removeClass(settings.css.hovered);
								idx = --lastIndex <= 0 ? lastIndex = 0 : lastIndex;
								links.eq(idx).addClass(settings.css.hovered);
								links.eq(idx).get(0).scrollIntoView(false);
								break;
							case 39 :
							case 40 :
								$("." + settings.css.dropdown + " ." + settings.css.hovered)
									.removeClass(settings.css.hovered);
								idx = ++lastIndex >= links.length ? lastIndex =links.length - 1 : lastIndex;
								links.eq(idx).addClass(settings.css.hovered);
								links.eq(idx).get(0).scrollIntoView(false);
								break;
							case 13 :
								var selected = select.get(0).dropdown.find('a.' + settings.css.hovered);
								if ( selected.length > 0)
								{
									$.vSelect.select(select, selected.attr('value'));
									select.change();
								}
								select.get(0).dropdown.slideUp('fast');
								$(document).unbind('keydown');
							case 27 :
								select.get(0).dropdown.slideUp('fast');
								$(document).unbind('keydown');
								break;
							default:
								// Ne fait rien
						}
						return false;
					});
					selectClickHandler();
					return false;
				});

				dropDownDiv.unbind('mouseenter');
				dropDownDiv.unbind('mouseleave');
				dropDownDiv.mouseenter(dropDownEnterHandler);
				dropDownDiv.mouseleave(dropDownLeaveHandler);
			}


			// Positionnement / taille
			_refreshPosition(select, settings);
		});
	};

	// -- private functions
	
	function _refreshPosition( select, settings )
	{
		dropDownDiv = select.get(0).dropdown;
		if (dropDownDiv.width() < select.width()) {
			dropDownDiv.css("width", select.width() + "px");
		}
		var contentOffset = $(/*settings.dropdownscontainer*/"#sns_mainpagecontent").offset();
		dropDownDiv.css({
			left : Math.floor(select.offset().left - contentOffset.left + select.width() - dropDownDiv.width()) + "px",
			top  : Math.floor(select.offset().top - contentOffset.top + select.outerHeight()) + "px" });
	}
	
	function _refreshSelected( select, selItem, settings )
	{
		var domSelect = select.get(0);
		if (selItem.length == 0) {
			domSelect.selItem = null;
			domSelect.selText = '';
			domSelect.selValue = null;
		}
		else {
			domSelect.selItem = selItem.get(0);
			domSelect.selText = selItem.text();
			domSelect.selValue = domSelect.selItem.value;
		}
		select.get(0).dropdown.find("li." + settings.css.selected).removeClass(settings.css.selected);
		selItem.parent().addClass(settings.css.selected);
		select.find("." + settings.css.value).text(selItem.text());
	}
	
	// Ajoute récursivement des options à la liste
	// parent = élément jquery conteneur (div, li, ...)
	// options = un tableau d'options, ou une option (structure ou object standard Option) 
	function _addOptions( select, parent, options, settings ) {
		var clickHandler = function() {
			// Changement état interne
			_refreshSelected(select, $(this), settings);

			// Rétraction menu déroulant
			select.get(0).dropdown.slideUp('fast', function() { select.change(); });
		};
		var ul = $("<ul/>");
		parent.append(ul);
		if (options.length) {
			for (var i = 0; i < options.length; ++i ) {
				var li = $("<li/>");
				if ( options[i].cssClass && options[i].cssClass != '' )
				{
					li.addClass( options[i].cssClass );
				}
				ul.append(li);
				var a = $("<a/>");
				li.append(a);
				a.get(0).value = options[i].value;
				a.html(options[i].text);
				if (options[i].children && options[i].children.length) {
					_addOptions(select, li, options[i].children, settings);
				}
				a
					.mouseup(clickHandler)
					.hover(
						function() {
							$(this).parent().addClass(settings.css.hovered);
						},
						function() {
							$(this).parent().removeClass(settings.css.hovered);
						});
				if ( options[i].selected ) {
					_refreshSelected( select, a, settings );
				}
			}
		}
		else {
			var li = $("<li/>");
			ul.append(li);
			var a = $("<a/>");
			li.append(a);
			a.get(0).value = options.value;
			a.html(options.text);
			if (options.children && options.children.length) {
				_addOptions(select, li, options.children, settings);
			}
			a
				.mouseup(clickHandler)
				.hover(
						function() {
							li.addClass(settings.css.hovered);
						},
						function() {
							li.removeClass(settings.css.hovered);
						});
			if ( options.selected ) {
				_refreshSelected( select, a, settings );
			}
		}
	}
	
	// Configuration par défaut
	$.fn.vSelect.defaults = {
		css : {
			value :    'value',    // class CSS désignant le div recevant le texte actuellement sélectionné
			selected : 'selected', // class CSS désignant un élément du menu sélectionné
			hovered : 'hovered',    // class CSS désignant un élément du menu survolé
			dropdown : 'dropdown', // class CSS désignant le div du menu déroulant
			disabled : 'disabled' // class CSS état désactivé de la combobox
		},
		dropdownscontainer : 'body' // sélecteur jQuery désignant le conteneur général des menus déroulants
	};
	
	$.vSelect = {
		select : function(select, value, config) {
			settings = $.extend($.fn.vSelect.defaults, config);
			switch ( value )
			{
				case ':first' :
					_refreshSelected(
						select,
						select.get(0).dropdown.find('a:first'),
						settings );
					break;
				case ':last' :
					_refreshSelected(
						select,
						select.get(0).dropdown.find('a:last'),
						settings );
					break;
				case ':prev' :
					var links = select.get(0).dropdown.find('a');
				    var index = links.index(select.get(0).selItem);
				    if (index > 0) {
				    	_refreshSelected(
								select,
								links.eq(index - 1),
								settings );
				    }
					break;
				case ':next' :
					var links = select.get(0).dropdown.find('a');
				    var index = links.index(select.get(0).selItem);
				    if (index < links.length - 1) {
				    	_refreshSelected(
								select,
								links.eq(index + 1),
								settings );
				    }
					break;
				default :
					_refreshSelected(
						select,
						select.get(0).dropdown.find('a[value="' + value + '"]'),
						settings );
			}
		},
		syncDropDown : function(select, config) {
			settings = $.extend($.fn.vSelect.defaults, config);
			_refreshPosition( select, settings );
		}
	};

	$('*').click(function(){
		$(document).unbind('keydown');
		$('.select').each(function(){
			if( this.dropdown && this.dropdown.css('display') == 'block')
			{
				this.dropdown.slideUp('fast');
			}
		});
	});
	var lastIndex = 0;
})(jQuery);

