

; (function($) {

  $.fn.sexyCombo = function(config) {
    config = config || {};
    var defaults = {
      //CSS class name that will be applied to the elements. See ./sexy-combo.css for details
      css: "combo",

      ///URL of the blank image
      blankImageSrc: "s.gif",

      //the value that will be set to the selectbox if none of its options are selected
      selectboxDefaultValue: "",

      //if true, the default option of selectbox (see previous config option) won't be included to the combobox's options
      ignoreSelectboxDefaultValue: true,

      //width of the combobox
      width: 146,

      //the default value of the combobox
      emptyText: "",


      //if true, combo's value will be autofilled with the first item of the dropdown list
      autoComplete: false,

      //if true, the option with the selected attribute will be the initial value of the combobox
      triggerSelected: false,

      //combo name
      name: ""
    };
    config = $.extend(defaults, config);

    return this.each(function() {
      if ("SELECT" != this.tagName.toUpperCase())
        return;

      //create sexy elements
      var $selectbox = $(this);



      var $wrapper = $selectbox.wrap("<div>").hide().parent().addClass("combo");

      if (!config.name.length) {
        config.name = $selectbox.attr("name") + "__sexyCombo";
      }
      //var $input = $("<input />").appendTo($wrapper).attr("autocomplete", "off").attr("value", "").attr("name", config.name);
      var $input = $("<input readonly />").appendTo($wrapper).attr("autocomplete", "off").attr("value", "").attr("name", config.name);



      var $icon = $("<img />").appendTo($wrapper).attr("src", config.blankImageSrc);

      var $listWrapper = $("<div>").appendTo($wrapper).addClass("invisible");

      var $list = $("<ul />").appendTo($listWrapper);



      $selectbox.children().each(function() {
        var $this = $(this);
        if ((false !== config.selectboxDefaultValue) && (config.ignoreSelectboxDefaultValue) && ($this.val() == config.selectboxDefaultValue)) {
          return;
        }
        $("<li />").appendTo($list).text($this.text()).addClass("visible");
      });

      var $listItems = $list.children();



      if ($.browser.opera) {
        $wrapper.css({ position: "relative", left: "0", top: "0" });
      }

      //apply width
      $wrapper.width(config.width);
      $input.width(config.width - $icon.width());
      $listWrapper.css("width", config.width);
      $icon.css("left", $input.css("width"));

      if ($.browser.opera || $.browser.msie) {
        $icon.one("click", function(e) {
          $wrapper.attr("sexycombo:comboY", e.pageY);
        });
      }


      var setSelectboxValue = function() {
        var set = false;
        var initVal = $selectbox.val();
        $selectbox.children().each(function() {
          if (set) {
            return;
          }
          var $this = $(this);
          if ($this.text() == $input.val()) {
            $selectbox.val($this.attr("value"));
            set = true;
          }


        });

        if (!set) {
          $selectbox.val(config.selectboxDefaultValue);
        }

        if (initVal != $selectbox.val()) {
          $selectbox.trigger("change");
        }
      };


      var getLiHeight = function() {
        var height = 0;
        $listItems.each(function() {
          var $this = $(this);
          if ($this.is(".visible")) {

            height += parseInt($this.css("height"), 10);

          }
        });

        return height;
      };



      var correctListHeight = function() {

        if (getLiHeight() < $listWrapper.height()) {
          $listWrapper.height(getLiHeight());
        }
        else if (getLiHeight() > $listWrapper.height()) {
          $listWrapper.height(Math.min(parseInt($listWrapper.css("maxHeight")), getLiHeight()));
        }
      };

      var correctOverflow = function() {
        if (getLiHeight() > parseInt($listWrapper.css("maxHeight"), 10)) {

          $listWrapper.css($.browser.opera ? "overflow" : "overflowY", "scroll");
        }
        else {
          $listWrapper.css($.browser.opera ? "overflow" : "overflowY", "hidden");
        }
      };

      correctListHeight();
      correctOverflow();

      //borrowed from Autocomplete plugin
      var selection = function(field, start, end) {

        if (field.createTextRange) {
          var selRange = field.createTextRange();
          selRange.collapse(true);
          selRange.moveStart("character", start);
          selRange.moveEnd("character", end);
          selRange.select();
        } else if (field.setSelectionRange) {
          field.setSelectionRange(start, end);
        } else {
          if (field.selectionStart) {
            field.selectionStart = start;
            field.selectionEnd = end;
          }
        }
        field.focus();
      };


      var selectFirst = function() {
        if ($listItems.filter(".visible:eq(0)").is(".active")) {
          return;
        }
        $listItems.removeClass("active").filter(".visible:eq(0)").addClass("active");
        if (config.autoComplete) {
          var curVal = $input.val();
          $input.val($listItems.filter(".active").text());
          selection($input.get(0), curVal.length, $input.val().length);
          $input.get(0).focus();
        }

      };


      var getComboY = function() {
        if ($wrapper.attr("sexycombo:comboY")) {
          return parseFloat($wrapper.attr("sexycombo:comboY"));
        }
        $wrapper.css("position", "absolute");
        var result = parseFloat($wrapper.css("top"));
        $wrapper.css({ position: "relative", left: "0", top: "0" });
        $wrapper.attr("sexycombo:comboY", result);

        return result;
      };


      var showList = function() {
        $listWrapper.removeClass("invisible").addClass("visible");
        
        //fix opera bug
        $listWrapper.css("display", "block");
        if ($.browser.msie) {
//          $("div.combo").not($wrapper).css("zIndex", "-1");
          $wrapper.css("zIndex", "99999");
        }

        var h1 = $listWrapper.css("pixelHeight");
        var h2 = getLiHeight();
        var h = (h1 < h2 ? h1 : h2);

        var comboEdge = getComboY() + h - $(window).scrollTop();
        var screenHeight = $(window).height();

        if (comboEdge > screenHeight) {
          $listWrapper.css({ top: "auto", bottom: "21" });
        }
        else {
          $listWrapper.css({ top: "23", bottom: "auto" });
        }

        //selectFirst();
      };

      var hideList = function() {
        $listWrapper.removeClass("visible").addClass("invisible");
        //opera bugfix
        $listWrapper.css("display", "none");
        //$("div.combo").not($wrapper).css("zIndex", "99999");
      };


      $icon.hover(function() {
        $icon.css({ backgroundPosition: "-" + $icon.width().toString() + "px 0" });
      }, function() {
        $icon.css({ backgroundPosition: "0 0" });
      });



      $icon.bind("click", function() {
        if ($listWrapper.is(".invisible")) {
          showList();
        }
        else if ($listWrapper.is(".visible")) {
          hideList();
        }

        $input.focus();
      });


      $listItems.bind("mouseover", function() {
        $listItems.removeClass("active");
        $(this).addClass("active");
      });



      $(document).bind("click", function(e) {
        if (($icon.get(0) == e.target) || ($input.get(0) == e.target)) {
          return;
        }

        hideList();
      });


      var select = function(val) {
        $input.removeClass("gray").val(val);
        setSelectboxValue();
        hideList();
      };



      if (config.triggerSelected) {
        $selectbox.children().each(function() {
          var $this = $(this);
          if (true == $this.attr("selected")) {
            select($this.text());
          }
        });
      }

      $listItems.bind("click", function(e) {
        select($(this).text());
        //filterList();
      });


      var filterList = function() {
        var val = $.trim($input.val().toLowerCase());

        $listItems.each(function() {
          var $this = $(this);

          if ($this.text().toLowerCase().search(val) != 0) {
            $this.removeClass("visible").addClass("invisible");
          }
          else {
            $this.removeClass("invisible").addClass("visible");
          }
        });


        correctListHeight();
        correctOverflow();
      };



      var getSelected = function() {
        return $listItems.filter(".active");
      };

      var selectNext = function() {
        if (!$listItems.filter(".active").next().is("li.visible")) {
          return;
        }

        $listItems.filter(".active").removeClass("active").next().filter("li").addClass("active");


        var activeReached = false;
        var num = 0;
        $listItems.filter(".visible").each(function() {
          if (activeReached) {
            return;
          }
          var $this = $(this);
          ++num;
          if ($this.is(".active")) {
            activeReached = true;
          }
        });

        if ($.browser.opera) {
          ++num;
        }
        var scrollNeed = $listItems.filter(".active").height() * num - parseInt($listWrapper.height(), 10);
        if ($.browser.msie) {
          scrollNeed += num;
        }

        if ($listWrapper.scrollTop() < scrollNeed) {
          $listWrapper.scrollTop(scrollNeed);
        }

      };

      var selectPrev = function() {
        if (!$listItems.filter(".active").prev().is("li.visible")) {
          return;
        }
        $listItems.filter(".active").removeClass("active").prev().addClass("active");

        var activeReached = false;
        var num = 0;
        $listItems.each(function() {
          if (activeReached) {
            return;
          }

          if ($(this).is(".active")) {
            activeReached = true;
          }
          ++num;
        });
        --num;
        var maxScroll = num * $listItems.filter(".active").height();

        if ($listWrapper.scrollTop() > maxScroll) {
          $listWrapper.scrollTop(maxScroll);
        }
      };

      var KEY = {
        UP: 38,
        DOWN: 40,
        DEL: 46,
        TAB: 9,
        RETURN: 13,
        ESC: 27,
        COMMA: 188,
        PAGEUP: 33,
        PAGEDOWN: 34,
        BACKSPACE: 8
      };

      $input.bind("keypress", function(e) {
        if (KEY.RETURN == e.keyCode) {
          e.preventDefault();
        }
      });

      $input.bind("keyup", function(e) {


        switch (e.keyCode) {
          case KEY.RETURN:
            select(getSelected().text());
            filterList();
            hideList();
            e.preventDefault();
            selection($input.get(0), $input.val().length - 1, 0);
            $input.get(0).blur();
            break;
          case KEY.DOWN:
            selectNext();
            break;
          case KEY.UP:
            selectPrev();
            break;
          case KEY.TAB:
            hideList();
            break;
          default:
            filterList();

            if ($listItems.filter(".visible").get().length) {
              showList();

            }
            else {
              hideList();
            }
            setSelectboxValue();
        }

      });


      if (config.emptyText.length) {
        if ("" == $input.val()) {
          $input.addClass("gray").val(config.emptyText);
        }
        $input.bind("focus", function() {
          if ($input.is(".gray")) {
            $input.removeClass("gray").val("");
          }
        }).bind("blur", function() {
          if ("" == $input.val()) {
            $input.addClass("gray").val(config.emptyText);
          }
        });
      }


    });


  };

})(jQuery);