!function($) { var selectpicker = function(element, options, e) { if (e ) { e.stoppropagation(); e.preventdefault(); } this.$element = $(element); this.$newelement = null; this.button = null; //merge defaults, options and data-attributes to make our options this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options); //if we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute if(this.options.title==null) this.options.title = this.$element.attr('title'); //expose public methods this.val = selectpicker.prototype.val; this.render = selectpicker.prototype.render; this.init(); }; selectpicker.prototype = { constructor: selectpicker, init: function (e) { var _this = this; this.$element.hide(); this.multiple = this.$element.prop('multiple'); var classlist = this.$element.attr('class') !== undefined ? this.$element.attr('class').split(/\s+/) : ''; var id = this.$element.attr('id'); this.$element.after( this.createview() ); this.$newelement = this.$element.next('.select'); var select = this.$newelement; var menu = this.$newelement.find('.dropdown-menu'); var menuarrow = this.$newelement.find('.dropdown-arrow'); var menua = menu.find('li > a'); var liheight = select.addclass('open').find('.dropdown-menu li > a').outerheight(); select.removeclass('open'); var divheight = menu.find('li .divider').outerheight(true); var selectoffset_top = this.$newelement.offset().top; var size = 0; var menuheight = 0; var selectheight = this.$newelement.outerheight(); this.button = this.$newelement.find('> button'); if (id !== undefined) { this.button.attr('id', id); $('label[for="' + id + '"]').click(function(){ select.find('button#'+id).focus(); }) } for (var i = 0; i < classlist.length; i++) { if(classlist[i] != 'selectpicker') { this.$newelement.addclass(classlist[i]); } } //if we are multiple, then add the show-tick class by default if(this.multiple) { this.$newelement.addclass('select-multiple'); } this.button.addclass(this.options.style); menu.addclass(this.options.menustyle); menuarrow.addclass(function() { if (_this.options.menustyle) { return _this.options.menustyle.replace('dropdown-', 'dropdown-arrow-'); } }); this.checkdisabled(); this.checktabindex(); this.clicklistener(); var menupadding = parseint(menu.css('padding-top')) + parseint(menu.css('padding-bottom')) + parseint(menu.css('border-top-width')) + parseint(menu.css('border-bottom-width')); if (this.options.size == 'auto') { function getsize() { var selectoffset_top_scroll = selectoffset_top - $(window).scrolltop(); var windowheight = $(window).innerheight(); var menuextras = menupadding + parseint(menu.css('margin-top')) + parseint(menu.css('margin-bottom')) + 2; var selectoffset_bot = windowheight - selectoffset_top_scroll - selectheight - menuextras; menuheight = selectoffset_bot; if (select.hasclass('dropup')) { menuheight = selectoffset_top_scroll - menuextras; } menu.css({'max-height' : menuheight + 'px', 'overflow-y' : 'auto', 'min-height' : liheight*3 + 'px'}); } getsize(); $(window).resize(getsize); $(window).scroll(getsize); if (window.mutationobserver) { new mutationobserver(getsize).observe(this.$element.get(0), { childlist: true }); } else { this.$element.bind('domnodeinserted', getsize); } } else if (this.options.size && this.options.size != 'auto' && menu.find('li').length > this.options.size) { var optindex = menu.find("li > *").filter(':not(.divider)').slice(0,this.options.size).last().parent().index(); var divlength = menu.find("li").slice(0,optindex + 1).find('.divider').length; menuheight = liheight*this.options.size + divlength*divheight + menupadding; menu.css({'max-height' : menuheight + 'px', 'overflow-y' : 'scroll'}); } // listen for updates to the dom and re render... (use mutation observer when availiable) if (window.mutationobserver) { new mutationobserver($.proxy(this.reloadli, this)).observe(this.$element.get(0), { childlist: true }); } else { this.$element.bind('domnodeinserted', $.proxy(this.reloadli, this)); } this.render(); }, createdropdown: function() { var drop = "
" + "" + "" + "" + "
"; return $(drop); }, createview: function() { var $drop = this.createdropdown(); var $li = this.createli(); $drop.find('ul').append($li); return $drop; }, reloadli: function() { //remove all children. this.destroyli(); //re build $li = this.createli(); this.$newelement.find('ul').append( $li ); //render view this.render(); }, destroyli:function() { this.$newelement.find('li').remove(); }, createli: function() { var _this = this; var _li = []; var _lia = []; var _lihtml = ''; this.$element.find('option').each(function(){ _li.push($(this).text()); }); this.$element.find('option').each(function(index) { //get the class and text for the option var optionclass = $(this).attr("class") !== undefined ? $(this).attr("class") : ''; var text = $(this).text(); var subtext = $(this).data('subtext') !== undefined ? ''+$(this).data('subtext')+'' : ''; //append any subtext to the main text. text+=subtext; if ($(this).parent().is('optgroup') && $(this).data('divider') != true) { if ($(this).index() == 0) { //get the opt group label var label = $(this).parent().attr('label'); var labelsubtext = $(this).parent().data('subtext') !== undefined ? ''+$(this).parent().data('subtext')+'' : ''; label += labelsubtext; if ($(this)[0].index != 0) { _lia.push( '
'+ '
'+label+'
'+ _this.createa(text, "opt " + optionclass ) ); } else { _lia.push( '
'+label+'
'+ _this.createa(text, "opt " + optionclass )); } } else { _lia.push( _this.createa(text, "opt " + optionclass ) ); } } else if ($(this).data('divider') == true) { _lia.push('
'); } else if ($(this).data('hidden') == true) { _lia.push(''); } else { _lia.push( _this.createa(text, optionclass ) ); } }); if (_li.length > 0) { for (var i = 0; i < _li.length; i++) { var $option = this.$element.find('option').eq(i); _lihtml += "
  • " + _lia[i] + "
  • "; } } //if we dont have a selected item, and we dont have a title, select the first element so something is set in the button if(this.$element.find('option:selected').length==0 && !_this.options.title) { this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); } return $(_lihtml); }, createa:function(test, classes) { return '' + '' + test + '' + ''; }, render:function() { var _this = this; //set width of select if (this.options.width == 'auto') { var ulwidth = this.$newelement.find('.dropdown-menu').css('width'); this.$newelement.css('width',ulwidth); } else if (this.options.width && this.options.width != 'auto') { this.$newelement.css('width',this.options.width); } //update the li to match the select this.$element.find('option').each(function(index) { _this.setdisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') ); _this.setselected(index, $(this).is(':selected') ); }); var selecteditems = this.$element.find('option:selected').map(function(index,value) { if($(this).attr('title')!=undefined) { return $(this).attr('title'); } else { return $(this).text(); } }).toarray(); //convert all the values into a comma delimited string var title = selecteditems.join(", "); //if this is multi select, and the selecttext type is count, the show 1 of 2 selected etc.. if(_this.multiple && _this.options.selectedtextformat.indexof('count') > -1) { var max = _this.options.selectedtextformat.split(">"); if( (max.length>1 && selecteditems.length > max[1]) || (max.length==1 && selecteditems.length>=2)) { title = selecteditems.length +' of ' + this.$element.find('option').length + ' selected'; } } //if we dont have a title, then use the default, or if nothing is set at all, use the not selected text if(!title) { title = _this.options.title != undefined ? _this.options.title : _this.options.noneselectedtext; } this.$element.next('.select').find('.filter-option').html( title ); }, setselected:function(index, selected) { if(selected) { this.$newelement.find('li').eq(index).addclass('selected'); } else { this.$newelement.find('li').eq(index).removeclass('selected'); } }, setdisabled:function(index, disabled) { if(disabled) { this.$newelement.find('li').eq(index).addclass('disabled'); } else { this.$newelement.find('li').eq(index).removeclass('disabled'); } }, checkdisabled: function() { if (this.$element.is(':disabled')) { this.button.addclass('disabled'); this.button.click(function(e) { e.preventdefault(); }); } }, checktabindex: function() { if (this.$element.is('[tabindex]')) { var tabindex = this.$element.attr("tabindex"); this.button.attr('tabindex', tabindex); } }, clicklistener: function() { var _this = this; $('body').on('touchstart.dropdown', '.dropdown-menu', function (e) { e.stoppropagation(); }); this.$newelement.on('click', 'li a', function(e){ var clickedindex = $(this).parent().index(), $this = $(this).parent(), $select = $this.parents('.select'); //dont close on multi choice menu if(_this.multiple) { e.stoppropagation(); } e.preventdefault(); //dont run if we have been disabled if ($select.prev('select').not(':disabled') && !$(this).parent().hasclass('disabled')){ //deselect all others if not multi select box if (!_this.multiple) { $select.prev('select').find('option').removeattr('selected'); $select.prev('select').find('option').eq(clickedindex).prop('selected', true).attr('selected', 'selected'); } //else toggle the one we have chosen if we are multi selet. else { var selected = $select.prev('select').find('option').eq(clickedindex).prop('selected'); if(selected) { $select.prev('select').find('option').eq(clickedindex).removeattr('selected'); } else { $select.prev('select').find('option').eq(clickedindex).prop('selected', true).attr('selected', 'selected'); } } $select.find('.filter-option').html($this.text()); $select.find('button').focus(); // trigger select 'change' $select.prev('select').trigger('change'); } }); this.$newelement.on('click', 'li.disabled a, li dt, li .divider', function(e) { e.preventdefault(); e.stoppropagation(); $select = $(this).parent().parents('.select'); $select.find('button').focus(); }); this.$element.on('change', function(e) { _this.render(); }); }, val:function(value) { if(value!=undefined) { this.$element.val( value ); this.$element.trigger('change'); return this.$element; } else { return this.$element.val(); } } }; $.fn.selectpicker = function(option, event) { //get the args of the outer function.. var args = arguments; var value; var chain = this.each(function () { var $this = $(this), data = $this.data('selectpicker'), options = typeof option == 'object' && option; if (!data) { $this.data('selectpicker', (data = new selectpicker(this, options, event))); } else { for(var i in option) { data[i]=option[i]; } } if (typeof option == 'string') { //copy the value of option, as once we shift the arguments //it also shifts the value of option. property = option; if(data[property] instanceof function) { [].shift.apply(args); value = data[property].apply(data, args); } else { value = data[property]; } } }); if(value!=undefined) { return value; } else { return chain; } }; $.fn.selectpicker.defaults = { style: null, size: 'auto', title: null, selectedtextformat : 'values', noneselectedtext : 'nothing selected', width: null, menustyle: null, togglesize: null } }(window.jquery);