!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.refresh = Selectpicker.prototype.refresh; this.selectAll = Selectpicker.prototype.selectAll; this.deselectAll = Selectpicker.prototype.deselectAll; this.init(); }; Selectpicker.prototype = { constructor: Selectpicker, init: function (e) { if (!this.options.container) { this.$element.hide(); } else { this.$element.css('visibility','hidden'); }; 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('.bootstrap-select'); if (this.options.container) { this.selectPosition(); } this.button = this.$newElement.find('> button'); if (id !== undefined) { this.button.attr('id', id); $('label[for="' + id + '"]').click($.proxy(this, function(){ this.$newElement.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('show-tick'); } this.button.addClass(this.options.style); this.checkDisabled(); this.checkTabIndex(); this.clickListener(); //Listen for updates to the DOM and re render... this.$element.bind('DOMNodeInserted DOMNodeRemoved', $.proxy(this.refresh, this)); this.render(); this.setSize(); }, 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 ); }, 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 { _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; //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.$newElement.find('.filter-option').html( title ); }, setSize:function() { var _this = this; var menu = this.$newElement.find('.dropdown-menu'); var menuA = menu.find('li > a'); var liHeight = this.$newElement.addClass('open').find('.dropdown-menu li > a').outerHeight(); this.$newElement.removeClass('open'); var divHeight = menu.find('li .divider').outerHeight(true); var selectOffset_top = this.$newElement.offset().top; var selectHeight = this.$newElement.outerHeight(); 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 (_this.$newElement.hasClass('dropup')) { menuHeight = selectOffset_top_scroll - menuExtras; } if ((menu.find('li').length + menu.find('dt').length) > 3) { minHeight = liHeight*3 + menuExtras - 2; } else { minHeight = 0; } menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'auto', 'min-height' : minHeight + 'px'}); } getSize(); $(window).resize(getSize); $(window).scroll(getSize); } else if (this.options.size && this.options.size != 'auto' && menu.find('li').length > this.options.size) { var optIndex = menu.find("li > *").filter(':not(.div-contain)').slice(0,this.options.size).last().parent().index(); var divLength = menu.find("li").slice(0,optIndex + 1).find('.div-contain').length; menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding; menu.css({'max-height' : menuHeight + 'px', 'overflow-y' : 'auto'}); } //Set width of select if (this.options.width == 'auto') { this.$newElement.find('.dropdown-menu').css('min-width','0'); var ulWidth = this.$newElement.find('.dropdown-menu').css('width'); this.$newElement.css('width',ulWidth); if (this.options.container) { this.$element.css('width',ulWidth); } } else if (this.options.width && this.options.width != 'auto') { this.$newElement.css('width',this.options.width); if (this.options.container) { this.$element.css('width',this.options.width); } } }, selectPosition:function() { var selectElementTop = this.$element.offset().top; var selectElementLeft = this.$element.offset().left; this.$newElement.appendTo(this.options.container); this.$newElement.css({'position':'absolute', 'top':selectElementTop+'px', 'left':selectElementLeft+'px'}); }, refresh:function() { this.reloadLi(); this.render(); this.setSize(); this.checkDisabled(); if (this.options.container) { this.selectPosition(); } }, 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(); }); this.button.on('focusin', function() { $(this).blur(); }); } else if (!this.$element.is(':disabled') && this.button.hasClass('disabled')) { this.button.removeClass('disabled'); this.button.click(function() { return true; }); } }, 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('.bootstrap-select'), prevIndex = _this.$element[0].selectedIndex; //Dont close on multi choice menu if (_this.multiple) { e.stopPropagation(); } e.preventDefault(); //Dont run if we have been disabled if (_this.$element.not(':disabled') && !$(this).parent().hasClass('disabled')) { //Deselect all others if not multi select box if (!_this.multiple) { _this.$element.find('option').removeAttr('selected'); _this.$element.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 = _this.$element.find('option').eq(clickedIndex).prop('selected'); if (selected) { _this.$element.find('option').eq(clickedIndex).removeAttr('selected'); } else { _this.$element.find('option').eq(clickedIndex).prop('selected', true).attr('selected', 'selected'); } } $select.find('.filter-option').html($this.text()); $select.find('button').focus(); // Trigger select 'change' if (prevIndex != clickedIndex) { _this.$element.trigger('change'); } _this.render(); } }); this.$newElement.on('click', 'li.disabled a, li dt, li .div-contain', function(e) { e.preventDefault(); e.stopPropagation(); $select = $(this).parent().parents('.bootstrap-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(); } }, selectAll:function() { this.$element.find('option').prop('selected', true).attr('selected', 'selected'); this.render(); }, deselectAll:function() { this.$element.find('option').prop('selected', false).removeAttr('selected'); this.render(); }, }; $.fn.selectpicker = function(option, event) { //get the args of the outer function.. var args = arguments; var value; var chain = this.each(function () { if ($(this).is('select')) { var $this = $(this), data = $this.data('selectpicker'), options = typeof option == 'object' && option; if (!data) { $this.data('selectpicker', (data = new Selectpicker(this, options, event))); } else if (options) { for(var i in options) { data.options[i]=options[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.options[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, container: false } }(window.jQuery); $('.selectpicker').selectpicker();