/**
 * NOTE: To use this file you need jScrollPane.js, jquery.mousewheel.js (these are required for scrollable dropdowns)
 * and jquery.livequery.js (for styling selects that are dynamically added to the page (or hidden to begin with))
 * 
 * It is recommended that you set selects to display: none in your CSS as correct width values are only picked up
 * properly (from the CSS file) when selects are hidden
 * 
 * When creating styled selects we assume that:
 * - The select element has a width applied in the CSS
 * - The majority of CSS for the look and feel of the styled selects will be done in a CSS file based on the classes
 *   we apply to each element
 */

var styledSelects = new Array();

jQuery(document).click( function(event) {
	for (var i = 0; i < styledSelects.length; i++) {
		if (!styledSelects[i].mouseIn && styledSelects[i].open) {
			styledSelects[i].closeSelect();
		}
	}
});

function StyledSelectField(element, index) {



	this.init = function() {
		this.index = index;
		
		this.selectElement = element;
		
		this.optionElements = this.selectElement.children();
		
		this.selectElementWidth = this.selectElement.css('width').substr(0, this.selectElement.css('width').length-2);
		
		this.styledSelectOptionFocus = 0;
		
		this.open = false;
		this.mouseIn = false;
		this.optionsMouseIn = false;
		this.textInputTimeout = null;
		
		this.buildStyledSelect();
		this.applyStyles();
		this.addEventHandlers();
		this.addScrollbar();

	}
	
	
	
	this.buildStyledSelect = function() {
		var styledSelectWrapper = jQuery('<div></div>').attr({ 'className': 'selectWrapper' });
		var styledSelectHolder = jQuery('<div></div>').attr({ 'className': 'styledSelect' });
		var styledSelectSelected = jQuery('<div></div>').attr({ 'className': 'selectedOption' });
		var styledSelectOptionHolder = jQuery('<div></div>').attr({ 'className': 'optionHolder' }).hide();
		var styledSelectArrow = jQuery('<div></div>').attr({ 'className': 'selectArrow' });
		var styledSelectBottom = jQuery('<div></div>').attr({ 'className': 'selectFooter' }).hide();
		var styledSelectBottomRight = jQuery('<div></div>').addClass('selectFooterRight');
		
		this.selectElement.wrap(styledSelectWrapper);
		
		styledSelectHolder.append(styledSelectArrow);
		styledSelectHolder.append(styledSelectSelected);
		styledSelectHolder.append(styledSelectOptionHolder);
		styledSelectBottom.append(styledSelectBottomRight);
		styledSelectHolder.append(styledSelectBottom);
		
		this.styledSelectWrapper = this.selectElement.parent();
		
		this.styledSelectWrapper.append(styledSelectHolder);

		//This is added to stop the window from moving on keydown
		this.styledSelectWrapper.prepend(jQuery("<input/>").addClass('dummy').css({'opacity': '0','position': 'absolute', 'left': '-9999px'}));
		
		// get all dom elements
		this.styledSelectHolder = jQuery('.styledSelect', this.styledSelectWrapper);
		this.styledSelectSelected = jQuery('.selectedOption', this.styledSelectWrapper);
		this.styledSelectOptionHolder = jQuery('.optionHolder', this.styledSelectWrapper);
		this.styledSelectArrow = jQuery('.selectArrow', this.styledSelectWrapper);
		
		var that = this;
		
		// build options html
		this.optionElements.each(function(i) {
			var optionElement = jQuery(this);
			var value = optionElement.val();
			var text = optionElement.text();
			
			var styledSelectOption = jQuery('<div></div>').addClass('option');
			var styledSelectOptionValue = jQuery('<span></span>').addClass('optionValue').css({ display: 'none' }).text(value);
			var styledSelectOptionText = jQuery('<span></span>').addClass('optionText').text(text);
			styledSelectOption.append(styledSelectOptionValue, styledSelectOptionText);

			that.styledSelectOptionHolder.append(styledSelectOption);
			
			// check if this option is selected
			if (that.selectElement.val() == value) {
				that.styledSelectSelected.html('<span>' + text + '</span>');
			} else if (i == 0) {
				that.styledSelectSelected.html('<span>' + text + '</span>');
			}
		});
		
		this.styledSelectOptions = this.styledSelectOptionHolder.children();
	}



	this.applyStyles = function() {
		var that = this;

		this.styledSelectWrapper.css({
			'z-index': (9999 - this.index),
			'width': this.selectElementWidth + 'px'
		});		

		this.styledSelectHolder.css({
			'width': this.selectElementWidth + 'px'
		});

		this.styledSelectSelected.css({
			'width': parseInt(this.selectElementWidth)-(parseInt(this.styledSelectArrow.css('width').substr(0, this.styledSelectArrow.css('width').length-2))) + 'px'
		});
		
		this.styledSelectOptionHolder.css({
			'width':parseInt(this.selectElementWidth) + 'px'
		});
	}



	this.addEventHandlers = function() {
		var that = this;
		
		jQuery('input.dummy', this.styledSelectWrapper).focus(function(){
			if (!that.open) {
				that.openSelect();
			}
		}).bind('textEntry', function() {
			that.searchSelect();
		});
		
		this.styledSelectHolder.click(function(event) {
			event.stopPropagation();
			if (that.open && !that.optionsMouseIn) {
				that.closeSelect();
			} else {
				that.closeAllSelects();
				jQuery('input.dummy', that.styledSelectWrapper).focus();
			}
	 	});
		
		this.styledSelectHolder.bind('mouseenter', function() {
			that.mouseIn = true;
		}).bind('mouseleave', function() {
			that.mouseIn = false;
		});
		
		this.styledSelectOptions.each(function(i) {
			jQuery(this).hover(function() {
				that.setFocus(jQuery(this));
			}, function() {
				jQuery(this).removeClass('optionHover');
			});
			
			jQuery(this).click(function(event) {
				event.stopPropagation();
				if (that.open) {
					that.setFocus(jQuery(this));
					that.closeSelect();
					that.setSelected(this);
				}
			});
		});
		
	}



	this.addScrollbar = function() {
		if (this.styledSelectOptionHolder.outerHeight() > 150) {
			this.styledSelectOptionHolder.css({ height: '150px' });
			this.styledSelectOptionHolder.jScrollPane(
				{
					showArrows: false,
                    wheelSpeed: 5,
					scrollbarWidth: this.styledSelectArrow.width()
				}
			);
			
			jQuery(this.styledSelectHolder).find('div.jScrollPaneContainer').css({ position: 'absolute', overflow: 'hidden', left: 0, top: this.styledSelectHolder.css('height') }).hide();
			this.styledSelectOptionHolder.css({ paddingRight: '0' });
		}
	}



	this.setFocus = function(element, jumpToSelected) {
		if (this.styledSelectOptions.index(element) != this.styledSelectOptionFocus) {
			this.removeFocus(jQuery(this.styledSelectOptions[this.styledSelectOptionFocus]));
		}
		element.addClass('optionHover');
		
		// if the dropdown has a scrollbar we need to keep the focused element in view
		if (jQuery('div.jScrollPaneTrack2', this.styledSelectHolder).length) {
			var scrollContainerHeight = jQuery('div.jScrollPaneContainer', this.styledSelectHolder).height();
			var optionHeight = element.height();
			var optionOffset = element.position();
			var optionHolderPosition = this.styledSelectOptionHolder.position();
			
			var maxViewablePosition = scrollContainerHeight - optionHeight;
			var currentPosition = optionOffset.top + optionHolderPosition.top;
			if (jumpToSelected) {
				if (currentPosition > maxViewablePosition) {
					jQuery('div.jScrollPaneContainer', this.styledSelectHolder).trigger('mousewheel', -0.75);
					this.setFocus(element, true);
				} else if (currentPosition < 0) {
					jQuery('div.jScrollPaneContainer', this.styledSelectHolder).trigger('mousewheel', 0.75);
					this.setFocus(element, true);
				}
			}
		}
		
		this.styledSelectOptionFocus = this.styledSelectOptions.index(element);
	}



	this.removeFocus = function(element) {
		element.removeClass('optionHover');
	}



	this.attachKeyPressHandler = function() {
		var that = this;
		this.downArrowInterval = null;
		this.upArrowInterval = null;
		if (this.open) {
			this.styledSelectWrapper.bind('keydown', function(event) {
				switch (event.keyCode) {
					case 38:
						// up
						that.setFocusUp();
						if (!jQuery.browser.safari) {
							that.upArrowInterval = setInterval(function() {
								that.setFocusUp();
							}, 150);
						}
						break;
					case 40:
						// down
						that.setFocusDown();
						if (!jQuery.browser.safari) {
							that.downArrowInterval = setInterval(function() {
								that.setFocusDown();
							}, 150);
						}
						break;
					case 13:
						// enter
						event.preventDefault();
						that.setSelected(jQuery(that.styledSelectOptions[that.styledSelectOptionFocus]));
						that.closeSelect();
						break;
					case 9:
						// tab
						that.closeSelect();
						break;
				}
			}).bind('keyup', function(event) {
				switch (event.keyCode) {
					case 38:
						// up
						clearInterval(that.upArrowInterval);
						break;
					case 40:
						// down
						clearInterval(that.downArrowInterval);
						break;
					default:
						that.checkForTextInput(event.keyCode);
						break;
				}
			}).bind('keypress', function(event) {
				switch(event.keyCode) {
					case 9:
						// tab
						that.closeSelect();
						break;
				}
			});
		}
	}



	this.checkForTextInput = function(keyCode) {
		if ((keyCode >= 65 && keyCode <= 90)) {
			clearTimeout(this.textInputTimeout);
			this.textInputTimeout = setTimeout(function() {
				jQuery('input.dummy', this.styledSelectWrapper).val('');
			}, 1000);
			jQuery('input.dummy', this.styledSelectWrapper).trigger('textEntry');
		}
	}



	this.setFocusUp = function() {
		if (this.styledSelectOptionFocus != 0) {
			this.removeFocus(jQuery(this.styledSelectOptions[this.styledSelectOptionFocus]));
			this.setFocus(jQuery(this.styledSelectOptions[this.styledSelectOptionFocus-1]), true);
		}
	}



	this.setFocusDown = function() {
		if ((this.styledSelectOptionFocus + 1) < this.styledSelectOptions.length) {
			this.removeFocus(jQuery(this.styledSelectOptions[this.styledSelectOptionFocus]));
			this.setFocus(jQuery(this.styledSelectOptions[this.styledSelectOptionFocus+1]), true);
		}
	}



	this.detachKeyPressHandler = function() {
		this.styledSelectWrapper.unbind('keydown');
	}



	this.addScrollbarListener = function() {
		var that = this;
		var scrollbar = jQuery('div.jScrollPaneTrack2', this.styledSelectHolder);
		if (jQuery(scrollbar).length) {
			scrollbar.bind('mouseenter', function() {
				that.optionsMouseIn = true;
			}).bind('mouseleave', function() {
				that.optionsMouseIn = false;
			});
		}
	}



	this.removeScrollbarListener = function() {
		var scrollbar = jQuery('div.jScrollPaneTrack2', this.styledSelectHolder);
		if (jQuery(scrollbar).length) {
			scrollbar.unbind('mouseenter').unbind('mouseleave');
		}
	}



	this.closeSelect = function() {
		jQuery('.selectFooter', this.styledSelectHolder).hide();
		this.styledSelectOptionHolder.parent('div.jScrollPaneContainer').hide();
		this.styledSelectWrapper.css({'background-position':'left top'});
		this.styledSelectOptionHolder.hide();
		this.open = false;
		this.detachKeyPressHandler();
		this.removeScrollbarListener();
		clearInterval(this.upArrowInterval);
		clearInterval(this.downArrowInterval);
	}



	this.openSelect = function() {
		this.styledSelectOptionHolder.show();
		this.styledSelectOptionHolder.parent('div.jScrollPaneContainer').show();
		jQuery('.selectFooter', this.styledSelectHolder).show();
		this.open = true;
		this.setFocus(jQuery(this.styledSelectOptions[this.styledSelectOptionFocus]));
		this.attachKeyPressHandler();
		this.addScrollbarListener();
	}



	this.closeAllSelects = function() {
		for (var i = 0; i < styledSelects.length; i++) {
			styledSelects[i].closeSelect();
		}
	}



	this.setSelected = function(optionElement) {
		var value = jQuery('.optionValue', optionElement).text();
		var text  = jQuery('.optionText', optionElement).text();
		
		this.selectElement.val(value);
		
		this.styledSelectSelected.find('span').text(text);
		this.selectElement.change();
	}



	this.searchSelect = function() {
		var that = this;
		var searchVal = jQuery('input.dummy', this.styledSelectWrapper).val();
		var results = jQuery("span.optionText", this.styledSelectOptionHolder);
		var regexp = new RegExp("^" + searchVal);
		if (results.length) {
			results.each(function(i) {
				if (regexp.test(jQuery(this).text().toLowerCase())) {
					that.setFocus(jQuery(this).parent(), true);
					return false;
				}
			});
		}
	}



	this.init();
}



jQuery(window).load(function() {
	var zIndex = 0;
	jQuery("select:not(.notready select)").livequery(function() {
		var element = jQuery(this);
		styledSelects[zIndex] = new StyledSelectField(element, zIndex);
		zIndex++;
	});
});