(function ($) {
'use strict';
//
if (!string.prototype.includes) {
(function () {
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
var tostring = {}.tostring;
var defineproperty = (function () {
// ie 8 only supports `object.defineproperty` on dom elements
try {
var object = {};
var $defineproperty = object.defineproperty;
var result = $defineproperty(object, object, object) && $defineproperty;
} catch (error) {
}
return result;
}());
var indexof = ''.indexof;
var includes = function (search) {
if (this == null) {
throw new typeerror();
}
var string = string(this);
if (search && tostring.call(search) == '[object regexp]') {
throw new typeerror();
}
var stringlength = string.length;
var searchstring = string(search);
var searchlength = searchstring.length;
var position = arguments.length > 1 ? arguments[1] : undefined;
// `tointeger`
var pos = position ? number(position) : 0;
if (pos != pos) { // better `isnan`
pos = 0;
}
var start = math.min(math.max(pos, 0), stringlength);
// avoid the `indexof` call if no match is possible
if (searchlength + start > stringlength) {
return false;
}
return indexof.call(string, searchstring, pos) != -1;
};
if (defineproperty) {
defineproperty(string.prototype, 'includes', {
'value': includes,
'configurable': true,
'writable': true
});
} else {
string.prototype.includes = includes;
}
}());
}
if (!string.prototype.startswith) {
(function () {
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
var defineproperty = (function () {
// ie 8 only supports `object.defineproperty` on dom elements
try {
var object = {};
var $defineproperty = object.defineproperty;
var result = $defineproperty(object, object, object) && $defineproperty;
} catch (error) {
}
return result;
}());
var tostring = {}.tostring;
var startswith = function (search) {
if (this == null) {
throw new typeerror();
}
var string = string(this);
if (search && tostring.call(search) == '[object regexp]') {
throw new typeerror();
}
var stringlength = string.length;
var searchstring = string(search);
var searchlength = searchstring.length;
var position = arguments.length > 1 ? arguments[1] : undefined;
// `tointeger`
var pos = position ? number(position) : 0;
if (pos != pos) { // better `isnan`
pos = 0;
}
var start = math.min(math.max(pos, 0), stringlength);
// avoid the `indexof` call if no match is possible
if (searchlength + start > stringlength) {
return false;
}
var index = -1;
while (++index < searchlength) {
if (string.charcodeat(start + index) != searchstring.charcodeat(index)) {
return false;
}
}
return true;
};
if (defineproperty) {
defineproperty(string.prototype, 'startswith', {
'value': startswith,
'configurable': true,
'writable': true
});
} else {
string.prototype.startswith = startswith;
}
}());
}
if (!object.keys) {
object.keys = function (
o, // object
k, // key
r // result array
){
// initialize object and result
r=[];
// iterate over object keys
for (k in o)
// fill result array with non-prototypical keys
r.hasownproperty.call(o, k) && r.push(k);
// return result
return r;
};
}
$.fn.triggernative = function (eventname) {
var el = this[0],
event;
if (el.dispatchevent) {
if (typeof event === 'function') {
// for modern browsers
event = new event(eventname, {
bubbles: true
});
} else {
// for ie since it doesn't support event constructor
event = document.createevent('event');
event.initevent(eventname, true, false);
}
el.dispatchevent(event);
} else {
if (el.fireevent) {
event = document.createeventobject();
event.eventtype = eventname;
el.fireevent('on' + eventname, event);
}
this.trigger(eventname);
}
};
//
// case insensitive contains search
$.expr[':'].icontains = function (obj, index, meta) {
var $obj = $(obj);
var haystack = ($obj.data('tokens') || $obj.text()).touppercase();
return haystack.includes(meta[3].touppercase());
};
// case insensitive begins search
$.expr[':'].ibegins = function (obj, index, meta) {
var $obj = $(obj);
var haystack = ($obj.data('tokens') || $obj.text()).touppercase();
return haystack.startswith(meta[3].touppercase());
};
// case and accent insensitive contains search
$.expr[':'].aicontains = function (obj, index, meta) {
var $obj = $(obj);
var haystack = ($obj.data('tokens') || $obj.data('normalizedtext') || $obj.text()).touppercase();
return haystack.includes(meta[3].touppercase());
};
// case and accent insensitive begins search
$.expr[':'].aibegins = function (obj, index, meta) {
var $obj = $(obj);
var haystack = ($obj.data('tokens') || $obj.data('normalizedtext') || $obj.text()).touppercase();
return haystack.startswith(meta[3].touppercase());
};
/**
* remove all diatrics from the given text.
* @access private
* @param {string} text
* @returns {string}
*/
function normalizetobase(text) {
var rexps = [
{re: /[\xc0-\xc6]/g, ch: "a"},
{re: /[\xe0-\xe6]/g, ch: "a"},
{re: /[\xc8-\xcb]/g, ch: "e"},
{re: /[\xe8-\xeb]/g, ch: "e"},
{re: /[\xcc-\xcf]/g, ch: "i"},
{re: /[\xec-\xef]/g, ch: "i"},
{re: /[\xd2-\xd6]/g, ch: "o"},
{re: /[\xf2-\xf6]/g, ch: "o"},
{re: /[\xd9-\xdc]/g, ch: "u"},
{re: /[\xf9-\xfc]/g, ch: "u"},
{re: /[\xc7-\xe7]/g, ch: "c"},
{re: /[\xd1]/g, ch: "n"},
{re: /[\xf1]/g, ch: "n"}
];
$.each(rexps, function () {
text = text.replace(this.re, this.ch);
});
return text;
}
function htmlescape(html) {
var escapemap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'`': '`'
};
var source = '(?:' + object.keys(escapemap).join('|') + ')',
testregexp = new regexp(source),
replaceregexp = new regexp(source, 'g'),
string = html == null ? '' : '' + html;
return testregexp.test(string) ? string.replace(replaceregexp, function (match) {
return escapemap[match];
}) : string;
}
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;
this.options = options;
// if we have no title yet, try to pull it from the html title attribute (jquery doesnt' pick it up as it's 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.destroy = selectpicker.prototype.destroy;
this.remove = selectpicker.prototype.remove;
this.show = selectpicker.prototype.show;
this.hide = selectpicker.prototype.hide;
this.init();
};
selectpicker.version = '1.10.0';
// part of this is duplicated in i18n/defaults-en_us.js. make sure to update both.
selectpicker.defaults = {
noneselectedtext: 'nothing selected',
noneresultstext: 'no results matched {0}',
countselectedtext: function (numselected, numtotal) {
return (numselected == 1) ? "{0} item selected" : "{0} items selected";
},
maxoptionstext: function (numall, numgroup) {
return [
(numall == 1) ? 'limit reached ({n} item max)' : 'limit reached ({n} items max)',
(numgroup == 1) ? 'group limit reached ({n} item max)' : 'group limit reached ({n} items max)'
];
},
selectalltext: 'select all',
deselectalltext: 'deselect all',
donebutton: false,
donebuttontext: 'close',
multipleseparator: ', ',
stylebase: 'btn',
style: 'btn-default',
size: 'auto',
title: null,
selectedtextformat: 'values',
width: false,
container: false,
hidedisabled: false,
showsubtext: false,
showicon: true,
showcontent: true,
dropupauto: true,
header: false,
livesearch: false,
livesearchplaceholder: null,
livesearchnormalize: false,
livesearchstyle: 'contains',
actionsbox: false,
iconbase: 'glyphicon',
tickicon: 'glyphicon-ok',
showtick: false,
template: {
caret: ''
},
maxoptions: false,
mobile: false,
selectontab: false,
dropdownalignright: false
};
selectpicker.prototype = {
constructor: selectpicker,
init: function () {
var that = this,
id = this.$element.attr('id');
this.$element.addclass('bs-select-hidden');
// store originalindex (key) and newindex (value) in this.liobj for fast accessibility
// allows us to do this.$lis.eq(that.liobj[index]) instead of this.$lis.filter('[data-original-index="' + index + '"]')
this.liobj = {};
this.multiple = this.$element.prop('multiple');
this.autofocus = this.$element.prop('autofocus');
this.$newelement = this.createview();
this.$element
.after(this.$newelement)
.appendto(this.$newelement);
this.$button = this.$newelement.children('button');
this.$menu = this.$newelement.children('.dropdown-menu');
this.$menuinner = this.$menu.children('.inner');
this.$searchbox = this.$menu.find('input');
this.$element.removeclass('bs-select-hidden');
if (this.options.dropdownalignright)
this.$menu.addclass('dropdown-menu-right');
if (typeof 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.setstyle();
this.setwidth();
if (this.options.container) this.selectposition();
this.$menu.data('this', this);
this.$newelement.data('this', this);
if (this.options.mobile) this.mobile();
this.$newelement.on({
'hide.bs.dropdown': function (e) {
that.$element.trigger('hide.bs.select', e);
},
'hidden.bs.dropdown': function (e) {
that.$element.trigger('hidden.bs.select', e);
},
'show.bs.dropdown': function (e) {
that.$element.trigger('show.bs.select', e);
},
'shown.bs.dropdown': function (e) {
that.$element.trigger('shown.bs.select', e);
}
});
if (that.$element[0].hasattribute('required')) {
this.$element.on('invalid', function () {
that.$button
.addclass('bs-invalid')
.focus();
that.$element.on({
'focus.bs.select': function () {
that.$button.focus();
that.$element.off('focus.bs.select');
},
'shown.bs.select': function () {
that.$element
.val(that.$element.val()) // set the value to hide the validation message in chrome when menu is opened
.off('shown.bs.select');
},
'rendered.bs.select': function () {
// if select is no longer invalid, remove the bs-invalid class
if (this.validity.valid) that.$button.removeclass('bs-invalid');
that.$element.off('rendered.bs.select');
}
});
});
}
settimeout(function () {
that.$element.trigger('loaded.bs.select');
});
},
createdropdown: function () {
// options
// if we are multiple or showtick option is set, then add the show-tick class
var showtick = (this.multiple || this.options.showtick) ? ' show-tick' : '',
inputgroup = this.$element.parent().hasclass('input-group') ? ' input-group-btn' : '',
autofocus = this.autofocus ? ' autofocus' : '';
// elements
var header = this.options.header ? '
' + this.options.header + '
' : '';
var searchbox = this.options.livesearch ?
'' +
'' +
'
'
: '';
var actionsbox = this.multiple && this.options.actionsbox ?
'' +
'
' +
'' +
'' +
'
' +
'
'
: '';
var donebutton = this.multiple && this.options.donebutton ?
''
: '';
var drop =
'' +
'' +
'' +
'
';
return $(drop);
},
createview: function () {
var $drop = this.createdropdown(),
li = this.createli();
$drop.find('ul')[0].innerhtml = li;
return $drop;
},
reloadli: function () {
//remove all children.
this.destroyli();
//re build
var li = this.createli();
this.$menuinner[0].innerhtml = li;
},
destroyli: function () {
this.$menu.find('li').remove();
},
createli: function () {
var that = this,
_li = [],
optid = 0,
titleoption = document.createelement('option'),
liindex = -1; // increment liindex whenever a new element is created to ensure liobj is correct
// helper functions
/**
* @param content
* @param [index]
* @param [classes]
* @param [optgroup]
* @returns {string}
*/
var generateli = function (content, index, classes, optgroup) {
return '' + content + '';
};
/**
* @param text
* @param [classes]
* @param [inline]
* @param [tokens]
* @returns {string}
*/
var generatea = function (text, classes, inline, tokens) {
return '' + text +
'' +
'';
};
if (this.options.title && !this.multiple) {
// this option doesn't create a new element, but does add a new option, so liindex is decreased
// since liobj is recalculated on every refresh, liindex needs to be decreased even if the titleoption is already appended
liindex--;
if (!this.$element.find('.bs-title-option').length) {
// use native js to prepend option (faster)
var element = this.$element[0];
titleoption.classname = 'bs-title-option';
titleoption.appendchild(document.createtextnode(this.options.title));
titleoption.value = '';
element.insertbefore(titleoption, element.firstchild);
// check if selected attribute is already set on an option. if not, select the titleoption option.
if ($(element.options[element.selectedindex]).attr('selected') === undefined) titleoption.selected = true;
}
}
this.$element.find('option').each(function (index) {
var $this = $(this);
liindex++;
if ($this.hasclass('bs-title-option')) return;
// get the class and text for the option
var optionclass = this.classname || '',
inline = this.style.csstext,
text = $this.data('content') ? $this.data('content') : $this.html(),
tokens = $this.data('tokens') ? $this.data('tokens') : null,
subtext = typeof $this.data('subtext') !== 'undefined' ? '' + $this.data('subtext') + '' : '',
icon = typeof $this.data('icon') !== 'undefined' ? ' ' : '',
isoptgroup = this.parentnode.tagname === 'optgroup',
isdisabled = this.disabled || (isoptgroup && this.parentnode.disabled);
if (icon !== '' && isdisabled) {
icon = '' + icon + '';
}
if (that.options.hidedisabled && isdisabled && !isoptgroup) {
liindex--;
return;
}
if (!$this.data('content')) {
// prepend any icon and append any subtext to the main text.
text = icon + '' + text + subtext + '';
}
if (isoptgroup && $this.data('divider') !== true) {
var optgroupclass = ' ' + this.parentnode.classname || '';
if ($this.index() === 0) { // is it the first option of the optgroup?
optid += 1;
// get the opt group label
var label = this.parentnode.label,
labelsubtext = typeof $this.parent().data('subtext') !== 'undefined' ? '' + $this.parent().data('subtext') + '' : '',
labelicon = $this.parent().data('icon') ? ' ' : '';
label = labelicon + '' + label + labelsubtext + '';
if (index !== 0 && _li.length > 0) { // is it not the first option of the select && are there elements in the dropdown?
liindex++;
_li.push(generateli('', null, 'divider', optid + 'div'));
}
liindex++;
_li.push(generateli(label, null, 'dropdown-header' + optgroupclass, optid));
}
if (that.options.hidedisabled && isdisabled) {
liindex--;
return;
}
_li.push(generateli(generatea(text, 'opt ' + optionclass + optgroupclass, inline, tokens), index, '', optid));
} else if ($this.data('divider') === true) {
_li.push(generateli('', index, 'divider'));
} else if ($this.data('hidden') === true) {
_li.push(generateli(generatea(text, optionclass, inline, tokens), index, 'hidden is-hidden'));
} else {
if (this.previouselementsibling && this.previouselementsibling.tagname === 'optgroup') {
liindex++;
_li.push(generateli('', null, 'divider', optid + 'div'));
}
_li.push(generateli(generatea(text, optionclass, inline, tokens), index));
}
that.liobj[index] = liindex;
});
//if we are not multiple, we don't have a selected item, and we don't 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 _li.join('');
},
findlis: function () {
if (this.$lis == null) this.$lis = this.$menu.find('li');
return this.$lis;
},
/**
* @param [updateli] defaults to true
*/
render: function (updateli) {
var that = this,
notdisabled;
//update the li to match the select
if (updateli !== false) {
this.$element.find('option').each(function (index) {
var $lis = that.findlis().eq(that.liobj[index]);
that.setdisabled(index, this.disabled || this.parentnode.tagname === 'optgroup' && this.parentnode.disabled, $lis);
that.setselected(index, this.selected, $lis);
});
}
this.tabindex();
var selecteditems = this.$element.find('option').map(function () {
if (this.selected) {
if (that.options.hidedisabled && (this.disabled || this.parentnode.tagname === 'optgroup' && this.parentnode.disabled)) return;
var $this = $(this),
icon = $this.data('icon') && that.options.showicon ? ' ' : '',
subtext;
if (that.options.showsubtext && $this.data('subtext') && !that.multiple) {
subtext = ' ' + $this.data('subtext') + '';
} else {
subtext = '';
}
if (typeof $this.attr('title') !== 'undefined') {
return $this.attr('title');
} else if ($this.data('content') && that.options.showcontent) {
return $this.data('content');
} 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('>');
if ((max.length > 1 && selecteditems.length > max[1]) || (max.length == 1 && selecteditems.length >= 2)) {
notdisabled = this.options.hidedisabled ? ', [disabled]' : '';
var totalcount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notdisabled).length,
tr8ntext = (typeof this.options.countselectedtext === 'function') ? this.options.countselectedtext(selecteditems.length, totalcount) : this.options.countselectedtext;
title = tr8ntext.replace('{0}', selecteditems.length.tostring()).replace('{1}', totalcount.tostring());
}
}
if (this.options.title == undefined) {
this.options.title = this.$element.attr('title');
}
if (this.options.selectedtextformat == 'static') {
title = this.options.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 = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneselectedtext;
}
//strip all html-tags and trim the result
this.$button.attr('title', $.trim(title.replace(/<[^>]*>?/g, '')));
this.$button.children('.filter-option').html(title);
this.$element.trigger('rendered.bs.select');
},
/**
* @param [style]
* @param [status]
*/
setstyle: function (style, status) {
if (this.$element.attr('class')) {
this.$newelement.addclass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|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 (refresh) {
if (!refresh && (this.options.size === false || this.sizeinfo)) return;
var newelement = document.createelement('div'),
menu = document.createelement('div'),
menuinner = document.createelement('ul'),
divider = document.createelement('li'),
li = document.createelement('li'),
a = document.createelement('a'),
text = document.createelement('span'),
header = this.options.header && this.$menu.find('.popover-title').length > 0 ? this.$menu.find('.popover-title')[0].clonenode(true) : null,
search = this.options.livesearch ? document.createelement('div') : null,
actions = this.options.actionsbox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].clonenode(true) : null,
donebutton = this.options.donebutton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].clonenode(true) : null;
text.classname = 'text';
newelement.classname = this.$menu[0].parentnode.classname + ' open';
menu.classname = 'dropdown-menu open';
menuinner.classname = 'dropdown-menu inner';
divider.classname = 'divider';
text.appendchild(document.createtextnode('inner text'));
a.appendchild(text);
li.appendchild(a);
menuinner.appendchild(li);
menuinner.appendchild(divider);
if (header) menu.appendchild(header);
if (search) {
// create a span instead of input as creating an input element is slower
var input = document.createelement('span');
search.classname = 'bs-searchbox';
input.classname = 'form-control';
search.appendchild(input);
menu.appendchild(search);
}
if (actions) menu.appendchild(actions);
menu.appendchild(menuinner);
if (donebutton) menu.appendchild(donebutton);
newelement.appendchild(menu);
document.body.appendchild(newelement);
var liheight = a.offsetheight,
headerheight = header ? header.offsetheight : 0,
searchheight = search ? search.offsetheight : 0,
actionsheight = actions ? actions.offsetheight : 0,
donebuttonheight = donebutton ? donebutton.offsetheight : 0,
dividerheight = $(divider).outerheight(true),
// fall back to jquery if getcomputedstyle is not supported
menustyle = typeof getcomputedstyle === 'function' ? getcomputedstyle(menu) : false,
$menu = menustyle ? null : $(menu),
menupadding = parseint(menustyle ? menustyle.paddingtop : $menu.css('paddingtop')) +
parseint(menustyle ? menustyle.paddingbottom : $menu.css('paddingbottom')) +
parseint(menustyle ? menustyle.bordertopwidth : $menu.css('bordertopwidth')) +
parseint(menustyle ? menustyle.borderbottomwidth : $menu.css('borderbottomwidth')),
menuextras = menupadding +
parseint(menustyle ? menustyle.margintop : $menu.css('margintop')) +
parseint(menustyle ? menustyle.marginbottom : $menu.css('marginbottom')) + 2;
document.body.removechild(newelement);
this.sizeinfo = {
liheight: liheight,
headerheight: headerheight,
searchheight: searchheight,
actionsheight: actionsheight,
donebuttonheight: donebuttonheight,
dividerheight: dividerheight,
menupadding: menupadding,
menuextras: menuextras
};
},
setsize: function () {
this.findlis();
this.liheight();
if (this.options.header) this.$menu.css('padding-top', 0);
if (this.options.size === false) return;
var that = this,
$menu = this.$menu,
$menuinner = this.$menuinner,
$window = $(window),
selectheight = this.$newelement[0].offsetheight,
liheight = this.sizeinfo['liheight'],
headerheight = this.sizeinfo['headerheight'],
searchheight = this.sizeinfo['searchheight'],
actionsheight = this.sizeinfo['actionsheight'],
donebuttonheight = this.sizeinfo['donebuttonheight'],
divheight = this.sizeinfo['dividerheight'],
menupadding = this.sizeinfo['menupadding'],
menuextras = this.sizeinfo['menuextras'],
notdisabled = this.options.hidedisabled ? '.disabled' : '',
menuheight,
getheight,
selectoffsettop,
selectoffsetbot,
posvert = function () {
selectoffsettop = that.$newelement.offset().top - $window.scrolltop();
selectoffsetbot = $window.height() - selectoffsettop - selectheight;
};
posvert();
if (this.options.size === 'auto') {
var getsize = function () {
var minheight,
hasclass = function (classname, include) {
return function (element) {
if (include) {
return (element.classlist ? element.classlist.contains(classname) : $(element).hasclass(classname));
} else {
return !(element.classlist ? element.classlist.contains(classname) : $(element).hasclass(classname));
}
};
},
lis = that.$menuinner[0].getelementsbytagname('li'),
lisvisible = array.prototype.filter ? array.prototype.filter.call(lis, hasclass('hidden', false)) : that.$lis.not('.hidden'),
optgroup = array.prototype.filter ? array.prototype.filter.call(lisvisible, hasclass('dropdown-header', true)) : lisvisible.filter('.dropdown-header');
posvert();
menuheight = selectoffsetbot - menuextras;
if (that.options.container) {
if (!$menu.data('height')) $menu.data('height', $menu.height());
getheight = $menu.data('height');
} else {
getheight = $menu.height();
}
if (that.options.dropupauto) {
that.$newelement.toggleclass('dropup', selectoffsettop > selectoffsetbot && (menuheight - menuextras) < getheight);
}
if (that.$newelement.hasclass('dropup')) {
menuheight = selectoffsettop - menuextras;
}
if ((lisvisible.length + optgroup.length) > 3) {
minheight = liheight * 3 + menuextras - 2;
} else {
minheight = 0;
}
$menu.css({
'max-height': menuheight + 'px',
'overflow': 'hidden',
'min-height': minheight + headerheight + searchheight + actionsheight + donebuttonheight + 'px'
});
$menuinner.css({
'max-height': menuheight - headerheight - searchheight - actionsheight - donebuttonheight - 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 scroll.getsize').on('resize.getsize scroll.getsize', getsize);
} else if (this.options.size && this.options.size != 'auto' && this.$lis.not(notdisabled).length > this.options.size) {
var optindex = this.$lis.not('.divider').not(notdisabled).children().slice(0, this.options.size).last().parent().index(),
divlength = this.$lis.slice(0, optindex + 1).filter('.divider').length;
menuheight = liheight * this.options.size + divlength * divheight + menupadding;
if (that.options.container) {
if (!$menu.data('height')) $menu.data('height', $menu.height());
getheight = $menu.data('height');
} else {
getheight = $menu.height();
}
if (that.options.dropupauto) {
//noinspection jsunusedassignment
this.$newelement.toggleclass('dropup', selectoffsettop > selectoffsetbot && (menuheight - menuextras) < getheight);
}
$menu.css({
'max-height': menuheight + headerheight + searchheight + actionsheight + donebuttonheight + 'px',
'overflow': 'hidden',
'min-height': ''
});
$menuinner.css({
'max-height': menuheight - menupadding + 'px',
'overflow-y': 'auto',
'min-height': ''
});
}
},
setwidth: function () {
if (this.options.width === 'auto') {
this.$menu.css('min-width', '0');
// get correct width if element is hidden
var $selectclone = this.$menu.parent().clone().appendto('body'),
$selectclone2 = this.options.container ? this.$newelement.clone().appendto('body') : $selectclone,
ulwidth = $selectclone.children('.dropdown-menu').outerwidth(),
btnwidth = $selectclone2.css('width', 'auto').children('button').outerwidth();
$selectclone.remove();
$selectclone2.remove();
// set width to whatever's larger, button title or longest option
this.$newelement.css('width', math.max(ulwidth, 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 () {
this.$bscontainer = $('');
var that = this,
pos,
actualheight,
getplacement = function ($element) {
that.$bscontainer.addclass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleclass('dropup', $element.hasclass('dropup'));
pos = $element.offset();
actualheight = $element.hasclass('dropup') ? 0 : $element[0].offsetheight;
that.$bscontainer.css({
'top': pos.top + actualheight,
'left': pos.left,
'width': $element[0].offsetwidth
});
};
this.$button.on('click', function () {
var $this = $(this);
if (that.isdisabled()) {
return;
}
getplacement(that.$newelement);
that.$bscontainer
.appendto(that.options.container)
.toggleclass('open', !$this.hasclass('open'))
.append(that.$menu);
});
$(window).on('resize scroll', function () {
getplacement(that.$newelement);
});
this.$element.on('hide.bs.select', function () {
that.$menu.data('height', that.$menu.height());
that.$bscontainer.detach();
});
},
setselected: function (index, selected, $lis) {
if (!$lis) {
$lis = this.findlis().eq(this.liobj[index]);
}
$lis.toggleclass('selected', selected);
},
setdisabled: function (index, disabled, $lis) {
if (!$lis) {
$lis = this.findlis().eq(this.liobj[index]);
}
if (disabled) {
$lis.addclass('disabled').children('a').attr('href', '#').attr('tabindex', -1);
} else {
$lis.removeclass('disabled').children('a').removeattr('href').attr('tabindex', 0);
}
},
isdisabled: function () {
return this.$element[0].disabled;
},
checkdisabled: function () {
var that = this;
if (this.isdisabled()) {
this.$newelement.addclass('disabled');
this.$button.addclass('disabled').attr('tabindex', -1);
} else {
if (this.$button.hasclass('disabled')) {
this.$newelement.removeclass('disabled');
this.$button.removeclass('disabled');
}
if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) {
this.$button.removeattr('tabindex');
}
}
this.$button.click(function () {
return !that.isdisabled();
});
},
tabindex: function () {
if (this.$element.data('tabindex') !== this.$element.attr('tabindex') &&
(this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) {
this.$element.data('tabindex', this.$element.attr('tabindex'));
this.$button.attr('tabindex', this.$element.data('tabindex'));
}
this.$element.attr('tabindex', -98);
},
clicklistener: function () {
var that = this,
$document = $(document);
this.$newelement.on('touchstart.dropdown', '.dropdown-menu', function (e) {
e.stoppropagation();
});
$document.data('spaceselect', false);
this.$button.on('keyup', function (e) {
if (/(32)/.test(e.keycode.tostring(10)) && $document.data('spaceselect')) {
e.preventdefault();
$document.data('spaceselect', false);
}
});
this.$button.on('click', function () {
that.setsize();
});
this.$element.on('shown.bs.select', function () {
if (!that.options.livesearch && !that.multiple) {
that.$menuinner.find('.selected a').focus();
} else if (!that.multiple) {
var selectedindex = that.liobj[that.$element[0].selectedindex];
if (typeof selectedindex !== 'number' || that.options.size === false) return;
// scroll to selected option
var offset = that.$lis.eq(selectedindex)[0].offsettop - that.$menuinner[0].offsettop;
offset = offset - that.$menuinner[0].offsetheight/2 + that.sizeinfo.liheight/2;
that.$menuinner[0].scrolltop = offset;
}
});
this.$menuinner.on('click', 'li a', function (e) {
var $this = $(this),
clickedindex = $this.parent().data('originalindex'),
prevvalue = that.$element.val(),
previndex = that.$element.prop('selectedindex');
// don't close on multi choice menu
if (that.multiple) {
e.stoppropagation();
}
e.preventdefault();
//don't 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;
if (!that.multiple) { // deselect all others if not multi select box
$options.prop('selected', false);
$option.prop('selected', true);
that.$menuinner.find('.selected').removeclass('selected');
that.setselected(clickedindex, true);
} else { // toggle the one we have chosen if we are multi select.
$option.prop('selected', !state);
that.setselected(clickedindex, !state);
$this.blur();
if (maxoptions !== false || maxoptionsgrp !== false) {
var maxreached = maxoptions < $options.filter(':selected').length,
maxreachedgrp = maxoptionsgrp < $optgroup.find('option:selected').length;
if ((maxoptions && maxreached) || (maxoptionsgrp && maxreachedgrp)) {
if (maxoptions && maxoptions == 1) {
$options.prop('selected', false);
$option.prop('selected', true);
that.$menuinner.find('.selected').removeclass('selected');
that.setselected(clickedindex, true);
} else if (maxoptionsgrp && maxoptionsgrp == 1) {
$optgroup.find('option:selected').prop('selected', false);
$option.prop('selected', true);
var optgroupid = $this.parent().data('optgroup');
that.$menuinner.find('[data-optgroup="' + optgroupid + '"]').removeclass('selected');
that.setselected(clickedindex, true);
} else {
var maxoptionsarr = (typeof that.options.maxoptionstext === 'function') ?
that.options.maxoptionstext(maxoptions, maxoptionsgrp) : that.options.maxoptionstext,
maxtxt = maxoptionsarr[0].replace('{n}', maxoptions),
maxtxtgrp = maxoptionsarr[1].replace('{n}', maxoptionsgrp),
$notify = $('');
// if {var} is set in array, replace it
/** @deprecated */
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($('' + maxtxt + '
'));
that.$element.trigger('maxreached.bs.select');
}
if (maxoptionsgrp && maxreachedgrp) {
$notify.append($('' + maxtxtgrp + '
'));
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)) {
// $option.prop('selected') is current option state (selected/unselected). state is previous option state.
that.$element
.trigger('changed.bs.select', [clickedindex, $option.prop('selected'), state])
.triggernative('change');
}
}
});
this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) {
if (e.currenttarget == this) {
e.preventdefault();
e.stoppropagation();
if (that.options.livesearch && !$(e.target).hasclass('close')) {
that.$searchbox.focus();
} else {
that.$button.focus();
}
}
});
this.$menuinner.on('click', '.divider, .dropdown-header', function (e) {
e.preventdefault();
e.stoppropagation();
if (that.options.livesearch) {
that.$searchbox.focus();
} else {
that.$button.focus();
}
});
this.$menu.on('click', '.popover-title .close', function () {
that.$button.click();
});
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).hasclass('bs-select-all')) {
that.selectall();
} else {
that.deselectall();
}
});
this.$element.change(function () {
that.render(false);
});
},
livesearchlistener: function () {
var that = this,
$no_results = $('');
this.$button.on('click.dropdown.data-api touchstart.dropdown.data-api', function () {
that.$menuinner.find('.active').removeclass('active');
if (!!that.$searchbox.val()) {
that.$searchbox.val('');
that.$lis.not('.is-hidden').removeclass('hidden');
if (!!$no_results.parent().length) $no_results.remove();
}
if (!that.multiple) that.$menuinner.find('.selected').addclass('active');
settimeout(function () {
that.$searchbox.focus();
}, 10);
});
this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function (e) {
e.stoppropagation();
});
this.$searchbox.on('input propertychange', function () {
if (that.$searchbox.val()) {
var $searchbase = that.$lis.not('.is-hidden').removeclass('hidden').children('a');
if (that.options.livesearchnormalize) {
$searchbase = $searchbase.not(':a' + that._searchstyle() + '("' + normalizetobase(that.$searchbox.val()) + '")');
} else {
$searchbase = $searchbase.not(':' + that._searchstyle() + '("' + that.$searchbox.val() + '")');
}
$searchbase.parent().addclass('hidden');
that.$lis.filter('.dropdown-header').each(function () {
var $this = $(this),
optgroup = $this.data('optgroup');
if (that.$lis.filter('[data-optgroup=' + optgroup + ']').not($this).not('.hidden').length === 0) {
$this.addclass('hidden');
that.$lis.filter('[data-optgroup=' + optgroup + 'div]').addclass('hidden');
}
});
var $lisvisible = that.$lis.not('.hidden');
// hide divider if first or last visible, or if followed by another divider
$lisvisible.each(function (index) {
var $this = $(this);
if ($this.hasclass('divider') && (
$this.index() === $lisvisible.first().index() ||
$this.index() === $lisvisible.last().index() ||
$lisvisible.eq(index + 1).hasclass('divider'))) {
$this.addclass('hidden');
}
});
if (!that.$lis.not('.hidden, .no-results').length) {
if (!!$no_results.parent().length) {
$no_results.remove();
}
$no_results.html(that.options.noneresultstext.replace('{0}', '"' + htmlescape(that.$searchbox.val()) + '"')).show();
that.$menuinner.append($no_results);
} else if (!!$no_results.parent().length) {
$no_results.remove();
}
} else {
that.$lis.not('.is-hidden').removeclass('hidden');
if (!!$no_results.parent().length) {
$no_results.remove();
}
}
that.$lis.filter('.active').removeclass('active');
if (that.$searchbox.val()) that.$lis.not('.hidden, .divider, .dropdown-header').eq(0).addclass('active').children('a').focus();
$(this).focus();
});
},
_searchstyle: function () {
var styles = {
begins: 'ibegins',
startswith: 'ibegins'
};
return styles[this.options.livesearchstyle] || 'icontains';
},
val: function (value) {
if (typeof value !== 'undefined') {
this.$element.val(value);
this.render();
return this.$element;
} else {
return this.$element.val();
}
},
changeall: function (status) {
if (typeof status === 'undefined') status = true;
this.findlis();
var $options = this.$element.find('option'),
$lisvisible = this.$lis.not('.divider, .dropdown-header, .disabled, .hidden').toggleclass('selected', status),
lisvislen = $lisvisible.length,
selectedoptions = [];
for (var i = 0; i < lisvislen; i++) {
var origindex = $lisvisible[i].getattribute('data-original-index');
selectedoptions[selectedoptions.length] = $options.eq(origindex)[0];
}
$(selectedoptions).prop('selected', status);
this.render(false);
this.$element
.trigger('changed.bs.select')
.triggernative('change');
},
selectall: function () {
return this.changeall(true);
},
deselectall: function () {
return this.changeall(false);
},
toggle: function (e) {
e = e || window.event;
if (e) e.stoppropagation();
this.$button.trigger('click');
},
keydown: function (e) {
var $this = $(this),
$parent = $this.is('input') ? $this.parent().parent() : $this.parent(),
$items,
that = $parent.data('this'),
index,
next,
first,
last,
prev,
nextprev,
previndex,
isactive,
selector = ':not(.disabled, .hidden, .dropdown-header, .divider)',
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'
};
if (that.options.livesearch) $parent = $this.parent().parent();
if (that.options.container) $parent = that.$menu;
$items = $('[role=menu] li', $parent);
isactive = that.$newelement.hasclass('open');
if (!isactive && (e.keycode >= 48 && e.keycode <= 57 || e.keycode >= 96 && e.keycode <= 105 || e.keycode >= 65 && e.keycode <= 90)) {
if (!that.options.container) {
that.setsize();
that.$menu.parent().addclass('open');
isactive = true;
} else {
that.$button.trigger('click');
}
that.$searchbox.focus();
}
if (that.options.livesearch) {
if (/(^9$|27)/.test(e.keycode.tostring(10)) && isactive && that.$menu.find('.active').length === 0) {
e.preventdefault();
that.$menu.parent().removeclass('open');
if (that.options.container) that.$newelement.removeclass('open');
that.$button.focus();
}
// $items contains li elements when livesearch is enabled
$items = $('[role=menu] li' + selector, $parent);
if (!$this.val() && !/(38|40)/.test(e.keycode.tostring(10))) {
if ($items.filter('.active').length === 0) {
$items = that.$menuinner.find('li');
if (that.options.livesearchnormalize) {
$items = $items.filter(':a' + that._searchstyle() + '(' + normalizetobase(keycodemap[e.keycode]) + ')');
} else {
$items = $items.filter(':' + that._searchstyle() + '(' + keycodemap[e.keycode] + ')');
}
}
}
}
if (!$items.length) return;
if (/(38|40)/.test(e.keycode.tostring(10))) {
index = $items.index($items.find('a').filter(':focus').parent());
first = $items.filter(selector).first().index();
last = $items.filter(selector).last().index();
next = $items.eq(index).nextall(selector).eq(0).index();
prev = $items.eq(index).prevall(selector).eq(0).index();
nextprev = $items.eq(next).prevall(selector).eq(0).index();
if (that.options.livesearch) {
$items.each(function (i) {
if (!$(this).hasclass('disabled')) {
$(this).data('index', i);
}
});
index = $items.index($items.filter('.active'));
first = $items.first().data('index');
last = $items.last().data('index');
next = $items.eq(index).nextall().eq(0).data('index');
prev = $items.eq(index).prevall().eq(0).data('index');
nextprev = $items.eq(next).prevall().eq(0).data('index');
}
previndex = $this.data('previndex');
if (e.keycode == 38) {
if (that.options.livesearch) index--;
if (index != nextprev && index > prev) index = prev;
if (index < first) index = first;
if (index == previndex) index = last;
} else if (e.keycode == 40) {
if (that.options.livesearch) index++;
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).children('a').focus();
} else {
e.preventdefault();
if (!$this.hasclass('dropdown-toggle')) {
$items.removeclass('active').eq(index).addclass('active').children('a').focus();
$this.focus();
}
}
} else if (!$this.is('input')) {
var keyindex = [],
count,
prevkey;
$items.each(function () {
if (!$(this).hasclass('disabled')) {
if ($.trim($(this).children('a').text().tolowercase()).substring(0, 1) == keycodemap[e.keycode]) {
keyindex.push($(this).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]).children('a').focus();
}
// select focused option if "enter", "spacebar" or "tab" (when selectontab is true) are pressed inside the menu.
if ((/(13|32)/.test(e.keycode.tostring(10)) || (/(^9$)/.test(e.keycode.tostring(10)) && that.options.selectontab)) && isactive) {
if (!/(32)/.test(e.keycode.tostring(10))) e.preventdefault();
if (!that.options.livesearch) {
var elem = $(':focus');
elem.click();
// bring back focus for multiselects
elem.focus();
// prevent screen from scrolling if the user hit the spacebar
e.preventdefault();
// fixes spacebar selection of dropdown items in ff & ie
$(document).data('spaceselect', true);
} else if (!/(32)/.test(e.keycode.tostring(10))) {
that.$menuinner.find('.active a').click();
$this.focus();
}
$(document).data('keycount', 0);
}
if ((/(^9$|27)/.test(e.keycode.tostring(10)) && isactive && (that.multiple || that.options.livesearch)) || (/(27)/.test(e.keycode.tostring(10)) && !isactive)) {
that.$menu.parent().removeclass('open');
if (that.options.container) that.$newelement.removeclass('open');
that.$button.focus();
}
},
mobile: function () {
this.$element.addclass('mobile-device');
},
refresh: function () {
this.$lis = null;
this.liobj = {};
this.reloadli();
this.render();
this.checkdisabled();
this.liheight(true);
this.setstyle();
this.setwidth();
if (this.$lis) this.$searchbox.trigger('propertychange');
this.$element.trigger('refreshed.bs.select');
},
hide: function () {
this.$newelement.hide();
},
show: function () {
this.$newelement.show();
},
remove: function () {
this.$newelement.remove();
this.$element.remove();
},
destroy: function () {
this.$newelement.before(this.$element).remove();
if (this.$bscontainer) {
this.$bscontainer.remove();
} else {
this.$menu.remove();
}
this.$element
.off('.bs.select')
.removedata('selectpicker')
.removeclass('bs-select-hidden selectpicker');
}
};
// selectpicker plugin definition
// ==============================
function plugin(option, event) {
// get the args of the outer function..
var args = arguments;
// the arguments of the function are explicitly re-defined from the argument list, because the shift causes them
// to get lost/corrupted in android 2.3 and ie9 #715 #775
var _option = option,
_event = event;
[].shift.apply(args);
var value;
var chain = this.each(function () {
var $this = $(this);
if ($this.is('select')) {
var data = $this.data('selectpicker'),
options = typeof _option == 'object' && _option;
if (!data) {
var config = $.extend({}, selectpicker.defaults, $.fn.selectpicker.defaults || {}, $this.data(), options);
config.template = $.extend({}, selectpicker.defaults.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), $this.data().template, options.template);
$this.data('selectpicker', (data = new selectpicker(this, config, _event)));
} else if (options) {
for (var i in options) {
if (options.hasownproperty(i)) {
data.options[i] = options[i];
}
}
}
if (typeof _option == 'string') {
if (data[_option] instanceof function) {
value = data[_option].apply(data, args);
} else {
value = data.options[_option];
}
}
}
});
if (typeof value !== 'undefined') {
//noinspection jsunusedassignment
return value;
} else {
return chain;
}
}
var old = $.fn.selectpicker;
$.fn.selectpicker = plugin;
$.fn.selectpicker.constructor = selectpicker;
// selectpicker no conflict
// ========================
$.fn.selectpicker.noconflict = function () {
$.fn.selectpicker = old;
return this;
};
$(document)
.data('keycount', 0)
.on('keydown.bs.select', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="menu"], .bs-searchbox input', selectpicker.prototype.keydown)
.on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="menu"], .bs-searchbox input', function (e) {
e.stoppropagation();
});
// selectpicker data-api
// =====================
$(window).on('load.bs.select.data-api', function () {
$('.selectpicker').each(function () {
var $selectpicker = $(this);
plugin.call($selectpicker, $selectpicker.data());
})
});
})(jquery);