| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 | // Author: David Qin// E-mail: david@hereapp.cn// Date: 2014-11-05(function($){  // a case insensitive jQuery :contains selector  $.expr[":"].searchableSelectContains = $.expr.createPseudo(function(arg) {    return function( elem ) {      return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;    };  });  $.searchableSelect = function(element, options) {    this.element = element;    this.options = options || {};    this.init();    var _this = this;    this.searchableElement.click(function(event){      // event.stopPropagation();      _this.show();    }).on('keydown', function(event){      if (event.which === 13 || event.which === 40 || event.which == 38){        event.preventDefault();        _this.show();      }    });    $(document).on('click', null, function(event){      if(_this.searchableElement.has($(event.target)).length === 0)        _this.hide();    });    this.input.on('keydown', function(event){      event.stopPropagation();      if(event.which === 13){         //enter        event.preventDefault();        _this.selectCurrentHoverItem();        _this.hide();      } else if (event.which == 27) { //ese        _this.hide();      } else if (event.which == 40) { //down        _this.hoverNextItem();      } else if (event.which == 38) { //up        _this.hoverPreviousItem();      }    }).on('keyup', function(event){      if(event.which != 13 && event.which != 27 && event.which != 38 && event.which != 40)        _this.filter();    })  }  var $sS = $.searchableSelect;  $sS.fn = $sS.prototype = {    version: '0.0.1'  };  $sS.fn.extend = $sS.extend = $.extend;  $sS.fn.extend({    init: function(){      var _this = this;      this.element.hide();      this.searchableElement = $('<div tabindex="0" class="searchable-select"></div>');      this.holder = $('<div class="searchable-select-holder"></div>');      this.dropdown = $('<div class="searchable-select-dropdown searchable-select-hide"></div>');      this.input = $('<input type="text" class="searchable-select-input" />');      this.items = $('<div class="searchable-select-items"></div>');      this.caret = $('<span class="searchable-select-caret"></span>');      this.scrollPart = $('<div class="searchable-scroll"></div>');      this.hasPrivious = $('<div class="searchable-has-privious">...</div>');      this.hasNext = $('<div class="searchable-has-next">...</div>');      this.hasNext.on('mouseenter', function(){        _this.hasNextTimer = null;        var f = function(){          var scrollTop = _this.items.scrollTop();          _this.items.scrollTop(scrollTop + 20);          _this.hasNextTimer = setTimeout(f, 50);        }        f();      }).on('mouseleave', function(event) {        clearTimeout(_this.hasNextTimer);      });      this.hasPrivious.on('mouseenter', function(){        _this.hasPriviousTimer = null;        var f = function(){          var scrollTop = _this.items.scrollTop();          _this.items.scrollTop(scrollTop - 20);          _this.hasPriviousTimer = setTimeout(f, 50);        }        f();      }).on('mouseleave', function(event) {        clearTimeout(_this.hasPriviousTimer);      });      this.dropdown.append(this.input);      this.dropdown.append(this.scrollPart);      this.scrollPart.append(this.hasPrivious);      this.scrollPart.append(this.items);      this.scrollPart.append(this.hasNext);      this.searchableElement.append(this.caret);      this.searchableElement.append(this.holder);      this.searchableElement.append(this.dropdown);      this.element.after(this.searchableElement);      this.buildItems();      this.setPriviousAndNextVisibility();    },    filter: function(){      var text = this.input.val();      this.items.find('.searchable-select-item').addClass('searchable-select-hide');      this.items.find('.searchable-select-item:searchableSelectContains('+text+')').removeClass('searchable-select-hide');      if(this.currentSelectedItem.hasClass('searchable-select-hide') && this.items.find('.searchable-select-item:not(.searchable-select-hide)').length > 0){        this.hoverFirstNotHideItem();      }      this.setPriviousAndNextVisibility();    },    hoverFirstNotHideItem: function(){      this.hoverItem(this.items.find('.searchable-select-item:not(.searchable-select-hide)').first());    },    selectCurrentHoverItem: function(){      if(!this.currentHoverItem.hasClass('searchable-select-hide'))        this.selectItem(this.currentHoverItem);    },    hoverPreviousItem: function(){      if(!this.hasCurrentHoverItem())        this.hoverFirstNotHideItem();      else{        var prevItem = this.currentHoverItem.prevAll('.searchable-select-item:not(.searchable-select-hide):first')        if(prevItem.length > 0)          this.hoverItem(prevItem);      }    },    hoverNextItem: function(){      if(!this.hasCurrentHoverItem())        this.hoverFirstNotHideItem();      else{        var nextItem = this.currentHoverItem.nextAll('.searchable-select-item:not(.searchable-select-hide):first')        if(nextItem.length > 0)          this.hoverItem(nextItem);      }    },    buildItems: function(){      var _this = this;      this.element.find('option').each(function(){        var item = $('<div class="searchable-select-item" data-value="'+$(this).attr('value')+'">'+$(this).text()+'</div>');        if(this.selected){          _this.selectItem(item);          _this.hoverItem(item);        }        item.on('mouseenter', function(){          $(this).addClass('hover');        }).on('mouseleave', function(){          $(this).removeClass('hover');        }).click(function(event){          event.stopPropagation();          _this.selectItem($(this));          _this.hide();        });        _this.items.append(item);      });      this.items.on('scroll', function(){        _this.setPriviousAndNextVisibility();      })    },    show: function(){      this.dropdown.removeClass('searchable-select-hide');      this.input.focus();      this.status = 'show';      this.setPriviousAndNextVisibility();    },    hide: function(){      if(!(this.status === 'show'))        return;      if(this.items.find(':not(.searchable-select-hide)').length === 0)          this.input.val('');      this.dropdown.addClass('searchable-select-hide');      this.searchableElement.trigger('focus');      this.status = 'hide';    },    hasCurrentSelectedItem: function(){      return this.currentSelectedItem && this.currentSelectedItem.length > 0;    },    selectItem: function(item){      if(this.hasCurrentSelectedItem())        this.currentSelectedItem.removeClass('selected');      this.currentSelectedItem = item;      item.addClass('selected');      this.hoverItem(item);      this.holder.text(item.text());      var value = item.data('value');      this.holder.data('value', value);      this.element.val(value);      if(this.options.afterSelectItem){        this.options.afterSelectItem.apply(this);      }    },    hasCurrentHoverItem: function(){      return this.currentHoverItem && this.currentHoverItem.length > 0;    },    hoverItem: function(item){      if(this.hasCurrentHoverItem())        this.currentHoverItem.removeClass('hover');      if(item.outerHeight() + item.position().top > this.items.height())        this.items.scrollTop(this.items.scrollTop() + item.outerHeight() + item.position().top - this.items.height());      else if(item.position().top < 0)        this.items.scrollTop(this.items.scrollTop() + item.position().top);      this.currentHoverItem = item;      item.addClass('hover');    },    setPriviousAndNextVisibility: function(){      if(this.items.scrollTop() === 0){        this.hasPrivious.addClass('searchable-select-hide');        this.scrollPart.removeClass('has-privious');      } else {        this.hasPrivious.removeClass('searchable-select-hide');        this.scrollPart.addClass('has-privious');      }      if(this.items.scrollTop() + this.items.innerHeight() >= this.items[0].scrollHeight){        this.hasNext.addClass('searchable-select-hide');        this.scrollPart.removeClass('has-next');      } else {        this.hasNext.removeClass('searchable-select-hide');        this.scrollPart.addClass('has-next');      }    }  });  $.fn.searchableSelect = function(options){    this.each(function(){      var sS = new $sS($(this), options);    });    return this;  };})(jQuery);
 |