// <select> to stylable HTML
// stephen chai
// version .1
// based on John Long's sweet select (iamjohnlong.com)

;(function($) {

	// jquery extension
	$.fn.StyledSelect = function(config) {
		
		// defaults
		var defaults = {
			// classes below here
			input_class: 'styled_select_input' // class for the hidden <input> that replaces the <select>
			,list_class: 'styled_select_list' // class the <ul> of options
			,list_option_class: 'styled_select_list_option' // class for the <li> option
			,list_option_disabled_class: 'styled_select_list_option_disabled' // class for a disabled <li> option
			,list_option_selected_class: 'styled_select_list_option_selected' // class the the selected <li> option
			,select_error_class: 'select_error' // if the orig <select> has this class, add the config.list_error_class to the styled select <div> status
			,status_class: 'styled_select_status' // class for the <div> status (shows currently selected, click to show list)
			,status_button_class: 'styled_select_status_button' // class for the <span> status button (drop down arrow generally)
			,status_error_class: 'styled_select_status_error' // class for the <ul> if it has an error (transfers through if the <select> has the config.select_error_class)
			,status_button_html: '&darr;' // HTML content for the status button
			,status_text_class: 'styled_select_status_text' // class the the <span> status text (shows currently selected)
			,wrapper_class: 'styled_select_wrapper' // class for the <div> wrapper of the whole thing
		};
		
		// extend config from defaults
		var config = $.extend({}, defaults, config);
		
		// loop through each <select>
		return this.each(function(select_index) {
			// if not a <select> do nothing
			if (this.tagName.toLowerCase() != 'select') return false;
			// vars
			var $select = $(this) // the original select
				,select_width = $select.outerWidth() // the width of the orig <select> (used for sizing later)
				,styled_select_html = [] // array of html pieces (will styled_select.join() later to create full HTML string)
				,styled_select_list_html = [] // array of html pieces for the absolute positioned, body DOM level list
				,styled_select_id = 'styled_select_' + select_index + '_' + (new Date().getTime()); // unique id for the styled select
			// write the styled select html
			styled_select_html.push('<div id="' + styled_select_id + '" class="' + config.wrapper_class + ' ' + ($select.hasClass('input_select_abbr') ? 'styled_select_abbr' : '') + '">');
			styled_select_html.push('	<div class="' + config.status_class + ' ' + ($select.hasClass(config.select_error_class) ? config.status_error_class : '') + '">');
			styled_select_html.push('		<span class="' + config.status_text_class + '">' + $select.find('option:selected').text() + '</span>');
			styled_select_html.push('		<span class="' + config.status_button_class + '">' + config.status_button_html + '</span>');
			styled_select_html.push('	</div>');
			styled_select_html.push('	<input type="hidden" name="' + $select.attr('name') + '" id="' + $select.attr('id') + '" value="' + $select.attr('value') + '" class="' + config.input_class + '">');
			styled_select_html.push('</div>');
			// remove old content, add new
			$select.after(styled_select_html.join('')).remove();
			// write the styled select list htlm
			styled_select_list_html.push('<ul styled_select_id="' + styled_select_id + '" class="' + config.list_class + '">');
			$select.find('option').each(function(option_index) {
				styled_select_list_html.push('	<li styled_select_value="' + $(this).attr('value') + '" title="' + ($(this).attr('title')) + '" class="' + config.list_option_class + ' ' + ($(this).attr('selected') ? config.list_option_selected_class : '') + ' ' + ($(this).attr('disabled') ? config.list_option_disabled_class : '') + '">' + $(this).text() + '</li>');
			});
			styled_select_list_html.push('</ul>');
			// add it in
			$('body').append(styled_select_list_html.join(''));
			// jquery objects for the select, the select list and the select status
			var $styled_select = $('#' + styled_select_id) // the newly created styled select
				,$styled_select_list = $('.' + config.list_class + '[styled_select_id="' + styled_select_id + '"]')
				,$styled_select_status = $styled_select.find('.' + config.status_class); // the styled select status (always viewable)
			// vars needed for sizing
			var list_width = $styled_select_list.outerWidth() // width of the options list
				,wrapper_width = $styled_select.outerWidth(); // width of the styled_select wrapper
			// size the status
			var status_width = select_width > wrapper_width ? wrapper_width : select_width;
			// and et the status sizing (adjust for any padding / border)
			$styled_select_status.css({ width: status_width - ($styled_select_status.outerWidth() - $styled_select_status.width()) + 'px' });
			// if the list should size up to fit the status (only size up, never down)
			if (list_width < status_width) $styled_select_list.css({ width: status_width - ($styled_select_list.outerWidth() - $styled_select_list.width()) + 'px' });
			// hide the list
			$styled_select_list.hide();
			// styled select status <div> on click
			$styled_select_status.click(function() {
				// if any lists are open, close em
				hideList();
				// show the list
				showList($styled_select_list);
				// and stop it
				return false;
			});
			// styled select list <li> on click
			$styled_select_list.find('.' + config.list_option_class).click(function() {
				// if disabled, do nothing
				if ($(this).hasClass(config.list_option_disabled_class)) return false;
				// update list
				updateList($styled_select, $(this));
				// and done
				return false;
			});
			// corresponding <label> on click
			$('label[for=' + $styled_select.find('.' + config.input_class).attr('id') + ']').click(function() {
				// show the list
				showList($styled_select_list);
				// and done
				return false;
			});
		});

		// listener (when list is open)
		function bindOpenListListeners() {
			// document on click
			$(document).bind('click', onOpenListDocumentClick);
			// window resize
			$(window).bind('resize', onOpenListWindowResize);
		}
		
		// hide list
		function hideList() {
			// hide the list
			$('.' + config.list_class + '.styled_select_list_open').removeClass('.styled_select_list_open').hide();
			// unbind listeners
			unbindOpenListListeners();
		}
		
		// open list document click function
		function onOpenListDocumentClick(event) {
			// what was clicked
			var $clicked = $(event.target);
			// if something other than an open list or an open list option was clicked
			if (!$clicked.is('.' +  config.list_class + '.styled_select_list_open') && !$clicked.is('.' + config.list_option_class)) hideList();
		}
		
		// open list window resize function
		function onOpenListWindowResize(event) {
			// get some shit
			var $styled_select_list = $('.' +  config.list_class + '.styled_select_list_open') // the currently open styled select list
				,styled_select_status_offset = $('#' + $styled_select_list.attr('styled_select_id') + ' .' + config.status_class).offset(); // the corresponding styled select status position
			// position the select list
			$styled_select_list.css({ left: styled_select_status_offset.left + 'px', top: styled_select_status_offset.top + 'px' });
		}
		
		// show list
		function showList($styled_select_list) {
			// get the position of the styled_select status
			var offset = $('#' + $styled_select_list.attr('styled_select_id') + ' .' + config.status_class).offset()
			// add class and show
			$styled_select_list.css({ left: offset.left + 'px', top: offset.top + 'px'  }).addClass('styled_select_list_open').show();
			// add listeners
			bindOpenListListeners();
		}
		
		// update list
		function updateList($styled_select, $selected_list_option) {
			// un-select old list option, select new
			$selected_list_option.parent().find('.' + config.list_option_selected_class).not($selected_list_option).removeClass(config.list_option_selected_class);
			// hide the list
			hideList();
			// update status
			$styled_select.find('.' + config.status_text_class).text($styled_select.hasClass('styled_select_abbr') && $selected_list_option.attr('title') ? $selected_list_option.attr('title') : $selected_list_option.text());
			// update hidden input
			$styled_select.find('.' + config.input_class).attr('value', $selected_list_option.attr('styled_select_value'));
			// if validator function
			if (typeof tp.validate == 'function') {
				// validation type
				var validation_type = $styled_select.parents('.validate_date_group').length ? 'date_group' : 'styled_select_input';
				tp.validate($styled_select.find('.' + config.input_class), validation_type);
			}
		}
		
		// unbind open list listeners
		function unbindOpenListListeners() {
			// document on click
			$(document).unbind('click', onOpenListDocumentClick);
			// window resize
			$(window).unbind('resize', onOpenListWindowResize);
		}
		
		// return
		return $(this);
	};

})(jQuery);
