Created
October 19, 2015 22:09
-
-
Save alexander-daniel/d527e8be5f5a3ec5eb15 to your computer and use it in GitHub Desktop.
Bootstrap select 1.5.4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* bootstrap-select v1.5.4 | |
* http://silviomoreto.github.io/bootstrap-select/ | |
* | |
* Copyright 2013 bootstrap-select | |
* Licensed under the MIT license | |
*/ | |
(function($) { | |
'use strict'; | |
$.expr[':'].icontains = function(obj, index, meta) { | |
return $(obj).text().toUpperCase().indexOf(meta[3].toUpperCase()) >= 0; | |
}; | |
var Selectpicker = function(element, options, e) { | |
if (e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
} | |
this.$element = $(element); | |
this.$newElement = null; | |
this.$button = null; | |
this.$menu = null; | |
this.$lis = 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.setStyle = Selectpicker.prototype.setStyle; | |
this.selectAll = Selectpicker.prototype.selectAll; | |
this.deselectAll = Selectpicker.prototype.deselectAll; | |
this.init(); | |
}; | |
Selectpicker.prototype = { | |
constructor: Selectpicker, | |
init: function() { | |
var that = this, | |
id = this.$element.attr('id'); | |
this.$element.hide(); | |
this.multiple = this.$element.prop('multiple'); | |
this.autofocus = this.$element.prop('autofocus'); | |
this.$newElement = this.createView(); | |
this.$element.after(this.$newElement); | |
this.$menu = this.$newElement.find('> .dropdown-menu'); | |
this.$button = this.$newElement.find('> button'); | |
this.$searchbox = this.$newElement.find('input'); | |
if (id !== undefined) { | |
this.$button.attr('data-id', id); | |
$('label[for="' + id + '"]').click(function(e) { | |
e.preventDefault(); | |
that.$button.focus(); | |
}); | |
} | |
this.checkDisabled(); | |
this.clickListener(); | |
if (this.options.liveSearch) this.liveSearchListener(); | |
this.render(); | |
this.liHeight(); | |
this.setStyle(); | |
this.setWidth(); | |
if (this.options.container) this.selectPosition(); | |
this.$menu.data('this', this); | |
this.$newElement.data('this', this); | |
}, | |
createDropdown: function() { | |
//If we are multiple, then add the show-tick class by default | |
var multiple = this.multiple ? ' show-tick' : ''; | |
var inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : ''; | |
var autofocus = this.autofocus ? ' autofocus' : ''; | |
var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">×</button>' + this.options.header + '</div>' : ''; | |
var searchbox = this.options.liveSearch ? '<div class="bootstrap-select-searchbox"><input type="text" class="input-block-level form-control" autocomplete="off" /></div>' : ''; | |
var actionsbox = this.options.actionsBox ? '<div class="bs-actionsbox">' + | |
'<div class="btn-group btn-block">' + | |
'<button class="actions-btn bs-select-all btn btn-sm btn-default">' + | |
'Select All' + | |
'</button>' + | |
'<button class="actions-btn bs-deselect-all btn btn-sm btn-default">' + | |
'Deselect All' + | |
'</button>' + | |
'</div>' + | |
'</div>' : ''; | |
var drop = | |
'<div class="btn-group bootstrap-select' + multiple + inputGroup + '">' + | |
'<button type="button" class="btn dropdown-toggle selectpicker" data-toggle="dropdown"'+ autofocus +'>' + | |
'<span class="filter-option pull-left"></span> ' + | |
'<span class="caret"></span>' + | |
'</button>' + | |
'<div class="dropdown-menu open">' + | |
header + | |
searchbox + | |
actionsbox + | |
'<ul class="dropdown-menu inner selectpicker" role="menu">' + | |
'</ul>' + | |
'</div>' + | |
'</div>'; | |
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 | |
var $li = this.createLi(); | |
this.$menu.find('ul').append( $li ); | |
}, | |
destroyLi: function() { | |
this.$menu.find('li').remove(); | |
}, | |
createLi: function() { | |
var that = this, | |
_liA = [], | |
_liHtml = ''; | |
this.$element.find('option').each(function() { | |
var $this = $(this); | |
//Get the class and text for the option | |
var optionClass = $this.attr('class') || ''; | |
var inline = $this.attr('style') || ''; | |
var text = $this.data('content') ? $this.data('content') : $this.html(); | |
var subtext = $this.data('subtext') !== undefined ? '<small class="muted text-muted">' + $this.data('subtext') + '</small>' : ''; | |
var icon = $this.data('icon') !== undefined ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : ''; | |
if (icon !== '' && ($this.is(':disabled') || $this.parent().is(':disabled'))) { | |
icon = '<span>'+icon+'</span>'; | |
} | |
if (!$this.data('content')) { | |
//Prepend any icon and append any subtext to the main text. | |
text = icon + '<span class="text">' + text + subtext + '</span>'; | |
} | |
if (that.options.hideDisabled && ($this.is(':disabled') || $this.parent().is(':disabled'))) { | |
_liA.push('<a style="min-height: 0; padding: 0"></a>'); | |
} else 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 ? '<small class="muted text-muted">'+$this.parent().data('subtext')+'</small>' : ''; | |
var labelIcon = $this.parent().data('icon') ? '<i class="'+$this.parent().data('icon')+'"></i> ' : ''; | |
label = labelIcon + '<span class="text">' + label + labelSubtext + '</span>'; | |
if ($this[0].index !== 0) { | |
_liA.push( | |
'<div class="div-contain"><div class="divider"></div></div>'+ | |
'<dt>'+label+'</dt>'+ | |
that.createA(text, 'opt ' + optionClass, inline ) | |
); | |
} else { | |
_liA.push( | |
'<dt>'+label+'</dt>'+ | |
that.createA(text, 'opt ' + optionClass, inline )); | |
} | |
} else { | |
_liA.push(that.createA(text, 'opt ' + optionClass, inline )); | |
} | |
} else if ($this.data('divider') === true) { | |
_liA.push('<div class="div-contain"><div class="divider"></div></div>'); | |
} else if ($(this).data('hidden') === true) { | |
_liA.push('<a></a>'); | |
} else { | |
_liA.push(that.createA(text, optionClass, inline )); | |
} | |
}); | |
$.each(_liA, function(i, item) { | |
var hide = item === '<a></a>' ? 'class="hide is-hidden"' : ''; | |
_liHtml += '<li rel="' + i + '"' + hide + '>' + item + '</li>'; | |
}); | |
//If we are not multiple, and 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.multiple && 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(text, classes, inline) { | |
return '<a tabindex="0" class="'+classes+'" style="'+inline+'">' + | |
text + | |
'<i class="' + this.options.iconBase + ' ' + this.options.tickIcon + ' icon-ok check-mark"></i>' + | |
'</a>'; | |
}, | |
render: function(updateLi) { | |
var that = this; | |
//Update the LI to match the SELECT | |
if (updateLi !== false) { | |
this.$element.find('option').each(function(index) { | |
that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') ); | |
that.setSelected(index, $(this).is(':selected') ); | |
}); | |
} | |
this.tabIndex(); | |
var selectedItems = this.$element.find('option:selected').map(function() { | |
var $this = $(this); | |
var icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : ''; | |
var subtext; | |
if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) { | |
subtext = ' <small class="muted text-muted">'+$this.data('subtext') +'</small>'; | |
} else { | |
subtext = ''; | |
} | |
if ($this.data('content') && that.options.showContent) { | |
return $this.data('content'); | |
} else if ($this.attr('title') !== undefined) { | |
return $this.attr('title'); | |
} else { | |
return icon + $this.html() + subtext; | |
} | |
}).toArray(); | |
//Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled | |
//Convert all the values into a comma delimited string | |
var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); | |
//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('>'); | |
var notDisabled = this.options.hideDisabled ? ':not([disabled])' : ''; | |
if ( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) { | |
title = this.options.countSelectedText.replace('{0}', selectedItems.length).replace('{1}', this.$element.find('option:not([data-divider="true"]):not([data-hidden="true"])'+notDisabled).length); | |
} | |
} | |
this.options.title = this.$element.attr('title'); | |
//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.$button.attr('title', $.trim(title)); | |
this.$newElement.find('.filter-option').html(title); | |
}, | |
setStyle: function(style, status) { | |
if (this.$element.attr('class')) { | |
this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|validate\[.*\]/gi, '')); | |
} | |
var buttonClass = style ? style : this.options.style; | |
if (status == 'add') { | |
this.$button.addClass(buttonClass); | |
} else if (status == 'remove') { | |
this.$button.removeClass(buttonClass); | |
} else { | |
this.$button.removeClass(this.options.style); | |
this.$button.addClass(buttonClass); | |
} | |
}, | |
liHeight: function() { | |
if (this.options.size === false) return; | |
var $selectClone = this.$menu.parent().clone().find('> .dropdown-toggle').prop('autofocus', false).end().appendTo('body'), | |
$menuClone = $selectClone.addClass('open').find('> .dropdown-menu'), | |
liHeight = $menuClone.find('li > a').outerHeight(), | |
headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0, | |
searchHeight = this.options.liveSearch ? $menuClone.find('.bootstrap-select-searchbox').outerHeight() : 0, | |
actionsHeight = this.options.actionsBox ? $menuClone.find('.bs-actionsbox').outerHeight() : 0; | |
$selectClone.remove(); | |
this.$newElement | |
.data('liHeight', liHeight) | |
.data('headerHeight', headerHeight) | |
.data('searchHeight', searchHeight) | |
.data('actionsHeight', actionsHeight); | |
}, | |
setSize: function() { | |
var that = this, | |
menu = this.$menu, | |
menuInner = menu.find('.inner'), | |
selectHeight = this.$newElement.outerHeight(), | |
liHeight = this.$newElement.data('liHeight'), | |
headerHeight = this.$newElement.data('headerHeight'), | |
searchHeight = this.$newElement.data('searchHeight'), | |
actionsHeight = this.$newElement.data('actionsHeight'), | |
divHeight = menu.find('li .divider').outerHeight(true), | |
menuPadding = parseInt(menu.css('padding-top')) + | |
parseInt(menu.css('padding-bottom')) + | |
parseInt(menu.css('border-top-width')) + | |
parseInt(menu.css('border-bottom-width')), | |
notDisabled = this.options.hideDisabled ? ':not(.disabled)' : '', | |
$window = $(window), | |
menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2, | |
menuHeight, | |
selectOffsetTop, | |
selectOffsetBot, | |
posVert = function() { | |
selectOffsetTop = that.$newElement.offset().top - $window.scrollTop(); | |
selectOffsetBot = $window.height() - selectOffsetTop - selectHeight; | |
}; | |
posVert(); | |
if (this.options.header) menu.css('padding-top', 0); | |
if (this.options.size == 'auto') { | |
var getSize = function() { | |
var minHeight, | |
lisVis = that.$lis.not('.hide'); | |
posVert(); | |
menuHeight = selectOffsetBot - menuExtras; | |
if (that.options.dropupAuto) { | |
that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height())); | |
} | |
if (that.$newElement.hasClass('dropup')) { | |
menuHeight = selectOffsetTop - menuExtras; | |
} | |
if ((lisVis.length + lisVis.find('dt').length) > 3) { | |
minHeight = liHeight*3 + menuExtras - 2; | |
} else { | |
minHeight = 0; | |
} | |
menu.css({'max-height' : menuHeight + 'px', 'overflow' : 'hidden', 'min-height' : minHeight + headerHeight + searchHeight + actionsHeight + 'px'}); | |
menuInner.css({'max-height' : menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', 'overflow-y' : 'auto', 'min-height' : Math.max(minHeight - menuPadding, 0) + 'px'}); | |
}; | |
getSize(); | |
this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); | |
$(window).off('resize.getSize').on('resize.getSize', getSize); | |
$(window).off('scroll.getSize').on('scroll.getSize', getSize); | |
} else if (this.options.size && this.options.size != 'auto' && menu.find('li'+notDisabled).length > this.options.size) { | |
var optIndex = menu.find('li'+notDisabled+' > *').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; | |
if (that.options.dropupAuto) { | |
this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height())); | |
} | |
menu.css({'max-height' : menuHeight + headerHeight + searchHeight + actionsHeight + 'px', 'overflow' : 'hidden'}); | |
menuInner.css({'max-height' : menuHeight - menuPadding + 'px', 'overflow-y' : 'auto'}); | |
} | |
}, | |
setWidth: function() { | |
if (this.options.width == 'auto') { | |
this.$menu.css('min-width', '0'); | |
// Get correct width if element hidden | |
var selectClone = this.$newElement.clone().appendTo('body'); | |
var ulWidth = selectClone.find('> .dropdown-menu').css('width'); | |
var btnWidth = selectClone.css('width', 'auto').find('> button').css('width'); | |
selectClone.remove(); | |
// Set width to whatever's larger, button title or longest option | |
this.$newElement.css('width', Math.max(parseInt(ulWidth), parseInt(btnWidth)) + 'px'); | |
} else if (this.options.width == 'fit') { | |
// Remove inline min-width so width can be changed from 'auto' | |
this.$menu.css('min-width', ''); | |
this.$newElement.css('width', '').addClass('fit-width'); | |
} else if (this.options.width) { | |
// Remove inline min-width so width can be changed from 'auto' | |
this.$menu.css('min-width', ''); | |
this.$newElement.css('width', this.options.width); | |
} else { | |
// Remove inline min-width/width so width can be changed | |
this.$menu.css('min-width', ''); | |
this.$newElement.css('width', ''); | |
} | |
// Remove fit-width class if width is changed programmatically | |
if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { | |
this.$newElement.removeClass('fit-width'); | |
} | |
}, | |
selectPosition: function() { | |
var that = this, | |
drop = '<div />', | |
$drop = $(drop), | |
pos, | |
actualHeight, | |
getPlacement = function($element) { | |
$drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); | |
pos = $element.offset(); | |
actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; | |
$drop.css({'top' : pos.top + actualHeight, 'left' : pos.left, 'width' : $element[0].offsetWidth, 'position' : 'absolute'}); | |
}; | |
this.$newElement.on('click', function() { | |
if (that.isDisabled()) { | |
return; | |
} | |
getPlacement($(this)); | |
$drop.appendTo(that.options.container); | |
$drop.toggleClass('open', !$(this).hasClass('open')); | |
$drop.append(that.$menu); | |
}); | |
$(window).resize(function() { | |
getPlacement(that.$newElement); | |
}); | |
$(window).on('scroll', function() { | |
getPlacement(that.$newElement); | |
}); | |
$('html').on('click', function(e) { | |
if ($(e.target).closest(that.$newElement).length < 1) { | |
$drop.removeClass('open'); | |
} | |
}); | |
}, | |
mobile: function() { | |
this.$element.addClass('mobile-device').appendTo(this.$newElement); | |
if (this.options.container) this.$menu.hide(); | |
}, | |
refresh: function() { | |
this.$lis = null; | |
this.reloadLi(); | |
this.render(); | |
this.setWidth(); | |
this.setStyle(); | |
this.checkDisabled(); | |
this.liHeight(); | |
}, | |
update: function() { | |
this.reloadLi(); | |
this.setWidth(); | |
this.setStyle(); | |
this.checkDisabled(); | |
this.liHeight(); | |
}, | |
setSelected: function(index, selected) { | |
if (this.$lis == null) this.$lis = this.$menu.find('li'); | |
$(this.$lis[index]).toggleClass('selected', selected); | |
}, | |
setDisabled: function(index, disabled) { | |
if (this.$lis == null) this.$lis = this.$menu.find('li'); | |
if (disabled) { | |
$(this.$lis[index]).addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1); | |
} else { | |
$(this.$lis[index]).removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0); | |
} | |
}, | |
isDisabled: function() { | |
return this.$element.is(':disabled'); | |
}, | |
checkDisabled: function() { | |
var that = this; | |
if (this.isDisabled()) { | |
this.$button.addClass('disabled').attr('tabindex', -1); | |
} else { | |
if (this.$button.hasClass('disabled')) { | |
this.$button.removeClass('disabled'); | |
} | |
if (this.$button.attr('tabindex') == -1) { | |
if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex'); | |
} | |
} | |
this.$button.click(function() { | |
return !that.isDisabled(); | |
}); | |
}, | |
tabIndex: function() { | |
if (this.$element.is('[tabindex]')) { | |
this.$element.data('tabindex', this.$element.attr('tabindex')); | |
this.$button.attr('tabindex', this.$element.data('tabindex')); | |
} | |
}, | |
clickListener: function() { | |
var that = this; | |
$('body').on('touchstart.dropdown', '.dropdown-menu', function(e) { | |
e.stopPropagation(); | |
}); | |
this.$newElement.on('click', function() { | |
that.setSize(); | |
if (!that.options.liveSearch && !that.multiple) { | |
setTimeout(function() { | |
that.$menu.find('.selected a').focus(); | |
}, 10); | |
} | |
}); | |
this.$menu.on('click', 'li a', function(e) { | |
var clickedIndex = $(this).parent().index(), | |
prevValue = that.$element.val(), | |
prevIndex = that.$element.prop('selectedIndex'); | |
//Dont close on multi choice menu | |
if (that.multiple) { | |
e.stopPropagation(); | |
} | |
e.preventDefault(); | |
//Dont run if we have been disabled | |
if (!that.isDisabled() && !$(this).parent().hasClass('disabled')) { | |
var $options = that.$element.find('option'), | |
$option = $options.eq(clickedIndex), | |
state = $option.prop('selected'), | |
$optgroup = $option.parent('optgroup'), | |
maxOptions = that.options.maxOptions, | |
maxOptionsGrp = $optgroup.data('maxOptions') || false; | |
//Deselect all others if not multi select box | |
if (!that.multiple) { | |
$options.prop('selected', false); | |
$option.prop('selected', true); | |
that.$menu.find('.selected').removeClass('selected'); | |
that.setSelected(clickedIndex, true); | |
} | |
//Else toggle the one we have chosen if we are multi select. | |
else { | |
$option.prop('selected', !state); | |
that.setSelected(clickedIndex, !state); | |
if ((maxOptions !== false) || (maxOptionsGrp !== false)) { | |
var maxReached = maxOptions < $options.filter(':selected').length, | |
maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length, | |
maxOptionsArr = that.options.maxOptionsText, | |
maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), | |
maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), | |
$notify = $('<div class="notify"></div>'); | |
if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { | |
// If {var} is set in array, replace it | |
if (maxOptionsArr[2]) { | |
maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); | |
maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); | |
} | |
$option.prop('selected', false); | |
that.$menu.append($notify); | |
if (maxOptions && maxReached) { | |
$notify.append($('<div>' + maxTxt + '</div>')); | |
that.$element.trigger('maxReached.bs.select'); | |
} | |
if (maxOptionsGrp && maxReachedGrp) { | |
$notify.append($('<div>' + maxTxtGrp + '</div>')); | |
that.$element.trigger('maxReachedGrp.bs.select'); | |
} | |
setTimeout(function() { | |
that.setSelected(clickedIndex, false); | |
}, 10); | |
$notify.delay(750).fadeOut(300, function() { $(this).remove(); }); | |
} | |
} | |
} | |
if (!that.multiple) { | |
that.$button.focus(); | |
} else if (that.options.liveSearch) { | |
that.$searchbox.focus(); | |
} | |
// Trigger select 'change' | |
if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { | |
that.$element.change(); | |
} | |
} | |
}); | |
this.$menu.on('click', 'li.disabled a, li dt, li .div-contain, .popover-title, .popover-title :not(.close)', function(e) { | |
if (e.target == this) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
if (!that.options.liveSearch) { | |
that.$button.focus(); | |
} else { | |
that.$searchbox.focus(); | |
} | |
} | |
}); | |
this.$menu.on('click', '.popover-title .close', function() { | |
that.$button.focus(); | |
}); | |
this.$searchbox.on('click', function(e) { | |
e.stopPropagation(); | |
}); | |
this.$menu.on('click', '.actions-btn', function(e) { | |
if (that.options.liveSearch) { | |
that.$searchbox.focus(); | |
} else { | |
that.$button.focus(); | |
} | |
e.preventDefault(); | |
e.stopPropagation(); | |
if ($(this).is('.bs-select-all')) { | |
that.selectAll(); | |
} else { | |
that.deselectAll(); | |
} | |
that.$element.change(); | |
}); | |
this.$element.change(function() { | |
that.render(false); | |
}); | |
}, | |
liveSearchListener: function() { | |
var that = this, | |
no_results = $('<li class="no-results"></li>'); | |
this.$newElement.on('click.dropdown.data-api', function() { | |
that.$menu.find('.active').removeClass('active'); | |
if (!!that.$searchbox.val()) { | |
that.$searchbox.val(''); | |
that.$lis.not('.is-hidden').removeClass('hide'); | |
if (!!no_results.parent().length) no_results.remove(); | |
} | |
if (!that.multiple) that.$menu.find('.selected').addClass('active'); | |
setTimeout(function() { | |
that.$searchbox.focus(); | |
}, 10); | |
}); | |
this.$searchbox.on('input propertychange', function() { | |
if (that.$searchbox.val()) { | |
that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':icontains(' + that.$searchbox.val() + ')').parent().addClass('hide'); | |
if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) { | |
if (!!no_results.parent().length) no_results.remove(); | |
no_results.html(that.options.noneResultsText + ' "'+ that.$searchbox.val() + '"').show(); | |
that.$menu.find('li').last().after(no_results); | |
} else if (!!no_results.parent().length) { | |
no_results.remove(); | |
} | |
} else { | |
that.$lis.not('.is-hidden').removeClass('hide'); | |
if (!!no_results.parent().length) no_results.remove(); | |
} | |
that.$menu.find('li.active').removeClass('active'); | |
that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus(); | |
$(this).focus(); | |
}); | |
this.$menu.on('mouseenter', 'a', function(e) { | |
that.$menu.find('.active').removeClass('active'); | |
$(e.currentTarget).parent().not('.disabled').addClass('active'); | |
}); | |
this.$menu.on('mouseleave', 'a', function() { | |
that.$menu.find('.active').removeClass('active'); | |
}); | |
}, | |
val: function(value) { | |
if (value !== undefined) { | |
this.$element.val( value ); | |
this.$element.change(); | |
return this.$element; | |
} else { | |
return this.$element.val(); | |
} | |
}, | |
selectAll: function() { | |
if (this.$lis == null) this.$lis = this.$menu.find('li'); | |
this.$element.find('option:enabled').prop('selected', true); | |
$(this.$lis).filter(':not(.disabled)').addClass('selected'); | |
this.render(false); | |
}, | |
deselectAll: function() { | |
if (this.$lis == null) this.$lis = this.$menu.find('li'); | |
this.$element.find('option:enabled').prop('selected', false); | |
$(this.$lis).filter(':not(.disabled)').removeClass('selected'); | |
this.render(false); | |
}, | |
keydown: function(e) { | |
var $this, | |
$items, | |
$parent, | |
index, | |
next, | |
first, | |
last, | |
prev, | |
nextPrev, | |
that, | |
prevIndex, | |
isActive, | |
keyCodeMap = { | |
32:' ', 48:'0', 49:'1', 50:'2', 51:'3', 52:'4', 53:'5', 54:'6', 55:'7', 56:'8', 57:'9', 59:';', | |
65:'a', 66:'b', 67:'c', 68:'d', 69:'e', 70:'f', 71:'g', 72:'h', 73:'i', 74:'j', 75:'k', 76:'l', | |
77:'m', 78:'n', 79:'o', 80:'p', 81:'q', 82:'r', 83:'s', 84:'t', 85:'u', 86:'v', 87:'w', 88:'x', | |
89:'y', 90:'z', 96:'0', 97:'1', 98:'2', 99:'3', 100:'4', 101:'5', 102:'6', 103:'7', 104:'8', 105:'9' | |
}; | |
$this = $(this); | |
$parent = $this.parent(); | |
if ($this.is('input')) $parent = $this.parent().parent(); | |
that = $parent.data('this'); | |
if (that.options.liveSearch) $parent = $this.parent().parent(); | |
if (that.options.container) $parent = that.$menu; | |
$items = $('[role=menu] li:not(.divider) a', $parent); | |
isActive = that.$menu.parent().hasClass('open'); | |
if (!isActive && /([0-9]|[A-z])/.test(String.fromCharCode(e.keyCode))) { | |
if (!that.options.container) { | |
that.setSize(); | |
that.$menu.parent().addClass('open'); | |
isActive = that.$menu.parent().hasClass('open'); | |
} else { | |
that.$newElement.trigger('click'); | |
} | |
that.$searchbox.focus(); | |
} | |
if (that.options.liveSearch) { | |
if (/(^9$|27)/.test(e.keyCode) && isActive && that.$menu.find('.active').length === 0) { | |
e.preventDefault(); | |
that.$menu.parent().removeClass('open'); | |
that.$button.focus(); | |
} | |
$items = $('[role=menu] li:not(.divider):visible', $parent); | |
if (!$this.val() && !/(38|40)/.test(e.keyCode)) { | |
if ($items.filter('.active').length === 0) { | |
$items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')'); | |
} | |
} | |
} | |
if (!$items.length) return; | |
if (/(38|40)/.test(e.keyCode)) { | |
index = $items.index($items.filter(':focus')); | |
first = $items.parent(':not(.disabled):visible').first().index(); | |
last = $items.parent(':not(.disabled):visible').last().index(); | |
next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index(); | |
prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index(); | |
nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index(); | |
if (that.options.liveSearch) { | |
$items.each(function(i) { | |
if ($(this).is(':not(.disabled)')) { | |
$(this).data('index', i); | |
} | |
}); | |
index = $items.index($items.filter('.active')); | |
first = $items.filter(':not(.disabled):visible').first().data('index'); | |
last = $items.filter(':not(.disabled):visible').last().data('index'); | |
next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index'); | |
prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index'); | |
nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index'); | |
} | |
prevIndex = $this.data('prevIndex'); | |
if (e.keyCode == 38) { | |
if (that.options.liveSearch) index -= 1; | |
if (index != nextPrev && index > prev) index = prev; | |
if (index < first) index = first; | |
if (index == prevIndex) index = last; | |
} | |
if (e.keyCode == 40) { | |
if (that.options.liveSearch) index += 1; | |
if (index == -1) index = 0; | |
if (index != nextPrev && index < next) index = next; | |
if (index > last) index = last; | |
if (index == prevIndex) index = first; | |
} | |
$this.data('prevIndex', index); | |
if (!that.options.liveSearch) { | |
$items.eq(index).focus(); | |
} else { | |
e.preventDefault(); | |
if (!$this.is('.dropdown-toggle')) { | |
$items.removeClass('active'); | |
$items.eq(index).addClass('active').find('a').focus(); | |
$this.focus(); | |
} | |
} | |
} else if (!$this.is('input')) { | |
var keyIndex = [], | |
count, | |
prevKey; | |
$items.each(function() { | |
if ($(this).parent().is(':not(.disabled)')) { | |
if ($.trim($(this).text().toLowerCase()).substring(0,1) == keyCodeMap[e.keyCode]) { | |
keyIndex.push($(this).parent().index()); | |
} | |
} | |
}); | |
count = $(document).data('keycount'); | |
count++; | |
$(document).data('keycount',count); | |
prevKey = $.trim($(':focus').text().toLowerCase()).substring(0,1); | |
if (prevKey != keyCodeMap[e.keyCode]) { | |
count = 1; | |
$(document).data('keycount', count); | |
} else if (count >= keyIndex.length) { | |
$(document).data('keycount', 0); | |
if (count > keyIndex.length) count = 1; | |
} | |
$items.eq(keyIndex[count - 1]).focus(); | |
} | |
// Select focused option if "Enter", "Spacebar", "Tab" are pressed inside the menu. | |
if (/(13|32|^9$)/.test(e.keyCode) && isActive) { | |
if (!/(32)/.test(e.keyCode)) e.preventDefault(); | |
if (!that.options.liveSearch) { | |
$(':focus').click(); | |
} else if (!/(32)/.test(e.keyCode)) { | |
that.$menu.find('.active a').click(); | |
$this.focus(); | |
} | |
$(document).data('keycount',0); | |
} | |
if ((/(^9$|27)/.test(e.keyCode) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode) && !isActive)) { | |
that.$menu.parent().removeClass('open'); | |
that.$button.focus(); | |
} | |
}, | |
hide: function() { | |
this.$newElement.hide(); | |
}, | |
show: function() { | |
this.$newElement.show(); | |
}, | |
destroy: function() { | |
this.$newElement.remove(); | |
this.$element.remove(); | |
} | |
}; | |
$.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. | |
var 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: 'btn-default', | |
size: 'auto', | |
title: null, | |
selectedTextFormat : 'values', | |
noneSelectedText : 'Nothing selected', | |
noneResultsText : 'No results match', | |
countSelectedText: '{0} of {1} selected', | |
maxOptionsText: ['Limit reached ({n} {var} max)', 'Group limit reached ({n} {var} max)', ['items','item']], | |
width: false, | |
container: false, | |
hideDisabled: false, | |
showSubtext: false, | |
showIcon: true, | |
showContent: true, | |
dropupAuto: true, | |
header: false, | |
liveSearch: false, | |
actionsBox: false, | |
multipleSeparator: ', ', | |
iconBase: 'glyphicon', | |
tickIcon: 'glyphicon-ok', | |
maxOptions: false | |
}; | |
$(document) | |
.data('keycount', 0) | |
.on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bootstrap-select-searchbox input', Selectpicker.prototype.keydown) | |
.on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bootstrap-select-searchbox input', function (e) { e.stopPropagation(); }); | |
})(window.jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment