// ----------------------------------------------------------------------------
// crossSelect - A jQuery Plugin to make multiple select boxes more intuitive
// v 0.1, requires jQuery 1.3.2 or later (may work with jQuery 1.3, but untested)
//
// Dual licensed under the MIT and GPL licenses.
// ----------------------------------------------------------------------------
// Copyright (C) 2009 Rhys Evans
// http://wheresrhys.co.uk/resources/
// ----------------------------------------------------------------------------
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// ----------------------------------------------------------------------------
// DOCUMENTATION
// To apply the plug-in to a <select> tag/some <select> tags add the line, eg 
//
//           $('select[multiple="multiple"]').crossSelect(); 
//  
// The plugin takes 5 optional parameters in JSON form:
// rows (integer, default 8) - the height in rows of new select box
// font (integer, default 12) - font-size in pixels
// listWidth - (integer, default 150) - minimum width of the select box
// vertical (string, default 'scroll') - 'scroll' adds a scroll bar if the number of options is greater than the rows parameter
//                                       'expand' ignores the rows parameter and expands to show all rows
// horizontal (string, default 'hide') - 'scroll' adds a scroll bar if the width of any items is wider than listWidth
//                                       'expand' expands the width to the width of the longest item
//                                       'hide' clips the horizontal display if items are longer than the listWidth
//  
//  eg $('select').crossSelect({horizontal:'expand', font: '17', rows: '20'});
// ----------------------------------------------------------------------------

jQuery.fn.crossSelect = function(options) {
	var defaults = {
		vertical: 'scroll', //scroll or expand
		horizontal: 'hide', //hide, scroll or expand
		listWidth: '150',
		font: '12',
		rows: '8'
	};
	var pars = $.extend(defaults, options);

	return this.each(function() {
		if($(this).attr('multiple') === true)
		{	
			var currentFocus;
			var context = createCrossSelecter(this);
			var longestOpt = setDimensions(this);
			populateLists(this, longestOpt);
			add_listeners(context);
		}
	});
	
	function createCrossSelecter(select) {		
		$(select).wrap('<div class="jqxs"></div>');
		var div = $(select).closest('div');
		$(select).hide();
		var optionsList = $('<ul>').addClass('jqxs_optionsList');
		var buttons = $('<div>').addClass('jqxs_buttons')
		var selectButton = $('<input type="button">').val('->').addClass('jqxs_selectButton').attr('disabled', 'disabled');
		var removeButton = $('<input type="button">').val('<-').addClass('jqxs_removeButton').attr('disabled', 'disabled');
		var selectAllButton = $('<input type="button">').val('Select All').addClass('jqxs_selectAllButton').addClass('jqxs_active').attr('disabled', '');
		var removeAllButton = $('<input type="button">').val('Remove All').addClass('jqxs_removeAllButton').attr('disabled', 'disabled'); 
		$(buttons).append(selectButton).append(selectAllButton).append(removeAllButton).append(removeButton);
		var chosenList = $('<ul>').addClass('jqxs_chosenList');
		return $(div).append(optionsList).append(buttons).append(chosenList).append('<div style="clear:left;">');
	}
	
	function setDimensions(select) {	
		var optionsList = $(select).parent().find('.jqxs_optionsList');
		var chosenList = $(select).parent().find('.jqxs_chosenList');
		switch (pars.vertical)
		{
			case 'expand':
				$(optionsList).css('height', $(select).children('option').size() * (parseFloat(pars.font) *1.25));
				$(chosenList).css('height', $(select).children('option').size() * (parseFloat(pars.font) *1.25));
				break;
			case 'scroll':
				$(optionsList).css({'height': (parseFloat(pars.font) *1.25) * Math.min($(select).children('option').size(),pars.rows), 'overflow-y':'auto'});
				$(chosenList).css({'height': (parseFloat(pars.font) *1.25) * Math.min($(select).children('option').size(),pars.rows), 'overflow-y':'auto'});
		}
		
		var longestOpt = getLongestOpt(select);
		
		switch (pars.horizontal)
		{
			case 'expand':
				$(optionsList).css({width: Math.max(longestOpt + 10, pars.listWidth)});
				$(chosenList).css({width: Math.max(longestOpt + 10, pars.listWidth)});
				return longestOpt;
			case 'scroll':
				$(optionsList).css({width: pars.listWidth + 'px', 'overflow-x': 'auto'});
				$(chosenList).css({width: pars.listWidth + 'px', 'overflow-x': 'auto'});
				if(longestOpt > pars.listWidth)
				{
					$(optionsList).css({height:parseFloat($(optionsList).css('height'))+24});
					$(chosenList).css({height:parseFloat($(chosenList).css('height'))+24});
				}
				return longestOpt;
			case 'hide':
				$(optionsList).css({width: pars.listWidth + 'px', 'overflow-x': 'hidden'});
				$(chosenList).css({width: pars.listWidth + 'px', 'overflow-x': 'hidden'});
				return longestOpt;
		}
	}
		
	function populateLists(select, longestOpt) {
		var optionsList = $(select).parent().find('.jqxs_optionsList');
		var chosenList = $(select).parent().find('.jqxs_chosenList');
		var optOrder = 0;
		var selectedCount = 0;
		$(select).children('option').each(function() {
			var newItem = $(this).text() + '<span>' + optOrder +'</span>';
			//newItem = $('<li>').html(newItem).css({'line-height': (parseFloat(pars.font) *1.25) + 'px', 'height':parseFloat(pars.font) *1.25,'font-size': pars.font +'px', 'overflow-x':'hidden'});
            newItem = $('<li>').html(newItem);
			$(optionsList).append(newItem);
            /*
			if (longestOpt > pars.listWidth && pars.horizontal != 'expand') 
			{
				var ruler = $('<span>').css('font-size',pars.font +'px').addClass('jqxs_ruler');
				$(select).parent().append(ruler);
				$(optionsList).children('li').each(function(){
					$(ruler).html($(this).text());
					$(this).css({
							'overflow-x':'auto',
							'width': Math.max($(ruler).width(), pars.listWidth) - 4
							});
				});
				$(ruler).remove();
			}	
            */
			if($(this).attr('selected') === true)
			{
				var chosenItem = $(newItem).clone()
				$(chosenList).append(chosenItem);
				$(newItem).addClass('jqxs_selected');
				if (selectedCount === 0)
				{
					$(select).parent().find('.jqxs_removeAllButton').addClass('jqxs_active').attr('disabled', '');
				}
				selectedCount++;
			}
			optOrder++;
		});
		if (selectedCount === $(select).children().size())
		{
			$(select).parent().find('.jqxs_selectAllButton').removeClass('jqxs_active').attr('disabled', 'disabled');
		}
	}
	
	function getLongestOpt(select) {
			var ruler = $('<span>').css('font-size',pars.font +'px').addClass('jqxs_ruler');
			$(select).parent().append(ruler);
			var longestOpt = 0;
			for (i=0;i<$(select).children('option').size();i++)
			{	
				var option = $(select).children('option')[i]
				$(ruler).html($(option).text());
				longestOpt = Math.max(longestOpt, $(ruler).width());
			}
			$(ruler).remove();
			return longestOpt = longestOpt;
	}
	
	function add_listeners(context) {
		$(context).children('.jqxs_optionsList').children().click(itemClick);
		$(context).children('.jqxs_chosenList').children().click(itemClick);
		$(context).find('.jqxs_selectButton').click(select);
		$(context).find('.jqxs_removeButton').click(remove);
		$(context).find('.jqxs_selectAllButton').click(selectAll);
		$(context).find('.jqxs_removeAllButton').click(removeAll);
	}
	
	function itemClick() {
		if($(this).hasClass('jqxs_focused'))
		{
			$(this).removeClass('jqxs_focused');
			currentFocus = null;
			$(this).closest('.jqxs').find('.jqxs_selectButton').removeClass('jqxs_active').attr('disabled', 'disabled');
			$(this).closest('.jqxs').find('.jqxs_removeButton').removeClass('jqxs_active').attr('disabled', 'disabled');
			return;
		}
		$(this).closest('.jqxs').find('.jqxs_optionsList').children().removeClass('jqxs_focused');
		$(this).closest('.jqxs').find('.jqxs_chosenList').children().removeClass('jqxs_focused');
		$(this).addClass('jqxs_focused');
		currentFocus = $(this).children('span').text();
		if($(this).parent().hasClass('jqxs_optionsList'))
		{
			$(this).closest('.jqxs').find('.jqxs_selectButton').addClass('jqxs_active').attr('disabled', '');
		} else if ($(this).parent().hasClass('jqxs_chosenList')) {
			$(this).closest('.jqxs').find('.jqxs_removeButton').addClass('jqxs_active').attr('disabled', '');
		}
	}
	
	function select(){
		if($(this +':not([disabled="disabled"])'))
		{
			var option = $(this).closest('.jqxs').find('select').children('option')[currentFocus];	
			if($(option).attr('selected') === false)
			{
				$(option).attr('selected','selected');
				var movedItem = $(this).closest('.jqxs').find('.jqxs_optionsList').children('li')[currentFocus];
				$(movedItem).removeClass('jqxs_focused');
				var chosenList = $(this).closest('.jqxs').find('.jqxs_chosenList')
				$(movedItem).clone().appendTo(chosenList).click(itemClick);
				$(movedItem).addClass('jqxs_selected');
			}
			adjustButtons(this);
		}
	}
	
	function remove(){
		if($(this +':not([disabled="disabled"])'))
		{
			var option = $(this).closest('.jqxs').find('select').children('option')[currentFocus];
			if($(option).attr('selected') === true)
			{
				$(option).attr('selected','');
				$(this).closest('.jqxs').find('.jqxs_chosenList').children().remove('.jqxs_focused');
				var selected = $(this).closest('.jqxs').find('.jqxs_optionsList').children('li')[currentFocus];
				$(selected).removeClass('jqxs_selected');
			}
			adjustButtons(this);
		}
	}
	
	function selectAll(){
		if($(this +':not([disabled="disabled"])'))
		{
			$(this).closest('.jqxs').find('select').children('option').each(function() {
				$(this).attr('selected', 'selected');
			});
			$(this).closest('.jqxs').find('.jqxs_optionsList').children('li').each(function() {
				$(this).removeClass('jqxs_focused');
				if($(this).not('.jqxs_selected'))
				{
					$(this).clone().appendTo($(this).closest('.jqxs').find('.jqxs_chosenList')).click(itemClick);
					$(this).addClass('jqxs_selected');
				}	
			});
			$(this).removeClass('jqxs_active')
			currentFocus = null;
			adjustButtons(this);
		}
	}
	
	function removeAll(){
		if($(this +':not([disabled="disabled"])'))
		{
			$(this).closest('.jqxs').find('select').children('option').each(function() {
				$(this).attr('selected', '');
			});
			$(this).closest('.jqxs').find('.jqxs_optionsList').children('li').each(function() {
				$(this).removeClass('jqxs_focused');
				if($(this).hasClass('jqxs_selected'))
				{
					$(this).removeClass('jqxs_selected');
					$(this).closest('.jqxs').find('.jqxs_chosenList').children().remove();
				}	
			});
			$(this).removeClass('jqxs_active')
			currentFocus = null;
			adjustButtons(this);
		}
	}
		
	function adjustButtons(button) {
		context = $(button).closest('.jqxs');
		if($(button).hasClass('jqxs_selectButton')){
			$(button).removeClass('jqxs_active').attr('disabled', 'disabled');
			$(context).find('.jqxs_removeAllButton').addClass('jqxs_active').attr('disabled', '');
			var optionsList = $(context).find('.jqxs_optionsList');
			if( $(optionsList).children(':not(.jqxs_selected)').size() === 0){$(context).find('.jqxs_selectAllButton').removeClass('jqxs_active').attr('disabled', 'disabled');}		
		} else if($(button).hasClass('jqxs_removeButton')){
			$(button).removeClass('jqxs_active').attr('disabled', 'disabled');
			$(context).find('.jqxs_selectAllButton').addClass('jqxs_active').attr('disabled', '');
			var chosenList = $(context).find('.jqxs_chosenList');
			if($(chosenList).children().size() === 0){$(context).find('.jqxs_removeAllButton').removeClass('jqxs_active').attr('disabled', 'disabled');}
		} else if($(button).hasClass('jqxs_selectAllButton')){
			$(context).find('.jqxs_removeButton').removeClass('jqxs_active').attr('disabled', 'disabled');
			$(context).find('.jqxs_selectButton').removeClass('jqxs_active').attr('disabled', 'disabled');
			$(button).removeClass('jqxs_active').attr('disabled', 'disabled');
			$(context).find('.jqxs_removeAllButton').addClass('jqxs_active').attr('disabled', '');		
		} else if($(button).hasClass('jqxs_removeAllButton')){
			$(context).find('.jqxs_removeButton').removeClass('jqxs_active').attr('disabled', 'disabled');
			$(context).find('.jqxs_selectButton').removeClass('jqxs_active').attr('disabled', 'disabled');
			$(button).removeClass('jqxs_active').attr('disabled', 'disabled');
			$(context).find('.jqxs_selectAllButton').addClass('jqxs_active').attr('disabled', '');		
		} 
	}
};