$.fn.JellyfishCustomSelect = function() {
    // CustomSelect jQuery plug-in
    // based on Select Customizer by Dean Collins http://www.dblog.com.au
    // modified by Steven Anderson + Rich Reeves @ Jellyfish http://www.jellyfishcreative.co.uk
    return this.each(function() {
        var obj = $(this);
        var name = obj.attr('id');
        var id_slc_options = name + '_options';
        var id_icn_select = name + '_iconselect';
        var id_holder = name + '_holder';
        var custom_select = name + '_customselect';
        var listheight = 150;
        var scrollBarWidth = 17;

        $.fn.addItems = function(data) {
            return this.each(function() {
                var list = this;

                $.each(data, function(index, itemData) {

                    var text = $(this).attr('Text');
                    var value = $(this).attr('Value');
                    var css = $(this).attr('Class');
                    var selected = $(this).attr('Selected');

                    if (text == undefined) text = itemData.text;
                    if (value == undefined) value = itemData.value;
                    if (css == undefined) css = $(this).attr('class');
                    if (selected == undefined) selected = $(this).attr('selected');

                    var $selectitem = $('<a></a>').addClass('option-item')
                        .attr('title', text)
                        .attr('rel', value)
                        .attr('href', '')
                        .html('<span>' + text + '</span>');

                    // Does the option have any classes?
                    $selectitem.addClass(css);
                    if (selected) $selectitem.addClass('selected');

                    // Is this the selected option?
                    //if ($(this).is(':selected')) $selectitem.addClass('selected');

                    // A custom option added by the user
                    if ($selectitem.hasClass('user')) {
                        var $removebtn = $('<a></a>').addClass('remove-btn')
                            .attr({ 'rel': itemData.value, 'title': 'Remove from quicklinks' })
                            .css({ 'position': 'absolute' })
                            .html('x');
                        $(list).append($removebtn);
                    }
                    $(list).append($selectitem);
                });

                $(list).find('.option-item').bind('click', bind_optionitem_click);
            });
        };

        var bind_optionitem_click = function(e) {
            e.preventDefault();
            update_select_value($(this));
            var customselect_container = $(this).closest('.custom-select-container');

            if ($(customselect_container).is('.autosubmit')) {
                $(this).closest('form').trigger('submit');
            }

            if ($(customselect_container).is('.cascading')) {
                process_cascading_changes($(customselect_container).find('input:hidden').first().attr('id'), $(this).attr("rel"));
            };

            hide_options_wrapper();
        };

        var update_select_value = function(e) {
            $(e).parent('.options-wrapper').find('.selected').removeClass("selected");
            $(e).addClass("selected");
            $(e).closest('.custom-select-container').find('input:hidden').val($(e).attr("rel"));
            $(e).closest('.custom-select-container').find('.selectbg span').html($(e).text());
        };

        var hide_options_wrapper = function() {
            $('.select-wrapper').slideUp(200, function(e) { $("div", this).closest('.custom-select-container').find('.selectbg').removeClass('active'); });
            $(document).unbind('keyup');
            $(document).unbind('keypress');
            $('body').unbind('click');
        };

        var show_options_wrapper = function(e) {
            var sender = e;
            hide_options_wrapper();
            if ($($selectitemswrapper).css('display') == 'none') {
                $($selectitemswrapper).slideDown(200);
                //$($selectitemswrapper).focus();
                $(this).addClass('active');
                e.stopPropagation();
                $(document).keypress(function(e) {
                    if (!e) e = window.event;
                    e.cancelBubble = true;
                    e.returnValue = false;
                    if (e.stopPropagation) {
                        e.stopPropagation();
                        e.preventDefault();
                    }
                });
                $(document).keyup(function(e) {
                    if (e.which == 40) {
                        var lastSelected = $($selectitemswrapper).find(".selected");
                        if (lastSelected.size() == 0) {
                            var nextSelected = $($optionswrapper).find("a:first:");
                        } else {
                            var nextSelected = lastSelected.next(".option-item");
                        }
                        if (nextSelected.size() == 1) {
                            update_select_value(nextSelected);
                            var rowOffset = (nextSelected.offset().top - $($selectitemswrapper).offset().top);
                            if (rowOffset > listheight) {
                                var value = parseInt($($optionswrapper).offset().top) - 19;
                                $($optionswrapper)[0].scrollTo(value);
                            }
                        }
                    } else if (e.which == 38) {
                        var lastSelected = $($selectitemswrapper).find(".selected");
                        var nextSelected = lastSelected.prev(".option-item");
                        if (nextSelected.size() == 1) {
                            update_select_value(nextSelected);
                            var rowOffset = (nextSelected.offset().top - $($selectitemswrapper).offset().top);
                            if (rowOffset > 0) {
                                $($optionswrapper)[0].scrollTo(($($optionswrapper).scrollTop() - 19) + "px");
                            }
                        }
                    } else if (e.which == 13 || e.which == 27) {
                        hide_options_wrapper();
                    }
                });
                $('.jScrollPaneTrack').click(function() { return false; });
                $('body').click(function() {
                    hide_options_wrapper();
                });
            } else {
                hide_options_wrapper();
            }
        };

        $.fn.clearItems = function() {
            var list = this;
            list.html("");
        };

        setdropdowntext = function(dropdown, text) {
            if (text == "") {
                var $option = $("#" + dropdown + "_options").find('.option-item').first();
                update_select_value($option);
                //$option.remove();
            }
            else {
                $("#" + dropdown + "_iconselect div.middlebg").html("<span>" + text + "</span>");
            }
        };

        getdropdownvalue = function(dropdown) {
            var id = $("#" + dropdown + "_options .selected").attr("rel");
            if (typeof (id) == "undefined") {
                id = -1;
            }
            return id;
        };

        cleardropdown = function(dropdown) {
            $("#" + dropdown + "_options").clearItems();
            $("#" + dropdown + "_customselect").val(0);
            setdropdowntext(dropdown, "");
        };

        loadcustomselectdata = function(target, data, text) {
            $("#" + target + "_options").clearItems();
            $("#" + target + "_options").addItems(data);
            setdropdowntext(target, text);
            reinitialiseScrollPane("#" + target + "_options", true);
        }

        reinitialiseScrollPane = function(e, hideafterinit) {
            $(e).closest('.select-wrapper').show().addClass('active');

            // Reset the heights
            $(e).height('auto');
            var newheight = $(e).height();
            if (newheight > listheight) newheight = listheight;
            $(e).height(newheight);
            $(e).parent('.jScrollPaneContainer').height(newheight);
            $(e).closest('.select-wrapper').find('.leftbg').first().height(newheight);
            $(e).closest('.select-wrapper').find('.rightbg').first().height(newheight);

            // Init scrollbar
            $(e).jScrollPane({ scrollbarMargin: 0, scrollbarWidth: scrollBarWidth, showArrows: true });

            var selectWidth = $(e).closest('.select-wrapper').width();
            $(e).parent('.jScrollPaneContainer').width(selectWidth);

            var scrollPosFromRight = 0;
            var optionsPaddingRight = 0;
            var optionsMarginRight = parseInt($(e).css('marginRight').replace('px', ''), 10);
            var optionsMarginLeft = parseInt($(e).css('marginLeft').replace('px', ''), 10);
            var optionsTotalMargin = optionsMarginRight + optionsMarginLeft;
            var rightPosDiff = 0;
            var optionsWidth = selectWidth - optionsTotalMargin;

            // Is the scoll bar visible?
            if ($(e).next('.jScrollPaneTrack').length > 0) {
                scrollPosFromRight = parseInt($(e).next('.jScrollPaneTrack').css('right').replace('px', ''), 10);
                optionsPaddingRight = scrollBarWidth;
                scrollPosFromRight - optionsMarginRight;
                optionsWidth -= scrollBarWidth - rightPosDiff;
            }

            if (optionsTotalMargin == 0) optionsPaddingRight += scrollPosFromRight;
            else optionsPaddingRight += rightPosDiff;

            $(e).css({ 'padding-right': optionsPaddingRight + 'px', 'width': optionsWidth + 'px' });

            if (hideafterinit) {
                $(e).closest('.select-wrapper').hide().removeClass('active');
            }
        };

        // Wrap everthing up into this container
        var $container = $('<div></div>').addClass('custom-select-container').addClass(obj.attr('class'));
        obj.wrap($container);

        // Find the selected option
        var $selectedoption = obj.find('option:selected');

        // Append the input to the container
        var $hidden = $('<input />').attr({ 'type': 'hidden', 'value': $selectedoption.val(), 'name': this.name, 'id': custom_select });
        obj.before($hidden);

        // Add the select wrapper
        //var objborderwidth = parseInt(obj.css("borderLeftWidth"), 10) + parseInt(obj.css("borderRightWidth"), 10);
        //if (isNaN(objborderwidth)) objborderwidth = 0;        
        var $selectwrapper = $('<div></div>')
            .attr('id', id_icn_select).addClass('selectbg').append(
                $('<div></div>').addClass('leftbg')).append(
                $('<div></div>').addClass('rightbg')).append(
                $('<div></div>').addClass('middlebg').append(
                    $('<span></span>').html($selectedoption.html())
                 )
             );

        // Add select items wrapper  
        var $selectitemswrapper = $('<div></div>')
            .attr('id', id_holder).addClass('select-wrapper').append(
                $('<div></div>').addClass('leftbg')).append(
                $('<div></div>').addClass('rightbg')
            );

        // Add the options wrapper
        var $optionswrapper = $('<div></div>').attr('id', id_slc_options).addClass('options-wrapper');
        $($optionswrapper).addItems(obj.find('option'));

        // Add footer  
        var $selectfooter = $('<div></div>')
            .addClass('select-footer').append(
                $('<div></div>').addClass('leftbg')).append(
                $('<div></div>').addClass('rightbg')).append(
                $('<div></div>').addClass('middlebg')
            );

        // Display custom select and remove select input
        obj.after($selectitemswrapper.append($optionswrapper).append($selectfooter)).after($selectwrapper).remove();

        // Make the select wrapper same width as icn wrapper
        var borderwidth = parseInt($($selectitemswrapper).css("borderLeftWidth"), 10) + parseInt($($selectitemswrapper).css("borderRightWidth"), 10);
        if (isNaN(borderwidth)) borderwidth = 0;
        $($selectitemswrapper).css('width', $($selectwrapper).outerWidth() - borderwidth);

        // Set options wrapper width
        var selectWrapperWidth = $selectitemswrapper.width();
        var selectWrapperLeftWidth = $selectitemswrapper.find('.leftbg').width();
        var selectWrapperRightWidth = $selectitemswrapper.find('.rightbg').width();
        $optionswrapper.width(selectWrapperWidth - (selectWrapperLeftWidth + selectWrapperRightWidth));

        // Footer Widths
        var selectWrapperWidth = $selectitemswrapper.width();
        var footerLeftWidth = $selectfooter.find('.leftbg').width();
        var footerRightWidth = $selectfooter.find('.rightbg').width();
        $selectfooter.width(selectWrapperWidth);
        $selectfooter.find('.middlebg').width(selectWrapperWidth - (footerLeftWidth + footerRightWidth)).css({ 'position': 'absolute', 'left': footerLeftWidth });

        // Add custom scroll bar
        reinitialiseScrollPane($optionswrapper, true);

        // Bind event handlers
        $selectwrapper.bind('click', show_options_wrapper);
        $selectwrapper.bind('mouseenter mouseleave', function(event) {
            $(this).toggleClass('hover');
        });

        $selectitemswrapper.find('.option-item').bind('click', bind_optionitem_click);
    });
}
