/**
 * 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.outerWidth();

        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,
                scrollbarWidth: this.styledSelectArrow.width()
            }
            );

            jQuery(this.styledSelectHolder).find('div.jScrollPaneContainer').css({
                position: 'relative',
                overflow: 'hidden'
            }).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.styledSelectSelected.css('background-position','left top');
        this.styledSelectArrow.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();

        this.styledSelectSelected.css('background-position','left -20px');
        this.styledSelectArrow.css('background-position','left -20px');
        
        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);
        jQuery(this).hide();
        zIndex++;
    });
});
