PageSourceSearch

https://www.bathroomdesigntool.com/js/0002d84d0b76497ce4add8b74ec32adf/bootstrap-select.js

js bathroomdesigntool.com collected 2026-10-07 13:36:27 UTC 88,315 bytes, 1,860 lines download raw bytes

vendor: 4,935 bytes, lines 1-113
1/*!
2 * Bootstrap-select v1.12.13 (https://github.com/heimrichhannot/bootstrap-select)
3 *
4 * Copyright 2013-2017 bootstrap-select
5 * Licensed under MIT (https://github.com/heimrichhannot/bootstrap-select/LICENSE)
6 */
7
8(function(root, factory) {
9    if (typeof define === 'function' && define.amd) {
10        // AMD. Register as an anonymous module unless amdModuleId is set
11        define(['jquery'], function(a0) {
12            return (factory(a0));
13        });
14    } else if (typeof module === 'object' && module.exports) {
15        // Node. Does not work with strict CommonJS, but
16        // only CommonJS-like environments that support module.exports,
17        // like Node.
18        module.exports = factory(require('jquery'));
19    } else {
20        factory(root['jQuery']);
21    }
22}(this, function(jQuery) {
23
24    (function($) {
25        'use strict';
26
27        //<editor-fold desc="Shims">
28        if (!String.prototype.includes) {
29            (function() {
30                'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
31                var toString = {}.toString;
32                var defineProperty = (function() {
33                    // IE 8 only supports `Object.defineProperty` on DOM elements
34                    try {
35                        var object = {};
36                        var $defineProperty = Object.defineProperty;
37                        var result = $defineProperty(object, object, object) && $defineProperty;
38                    } catch (error) {
39                    }
40                    return result;
41                }());
42                var indexOf = ''.indexOf;
43                var includes = function(search) {
44                    if (this == null) {
45                        throw new TypeError();
46                    }
47                    var string = String(this);
48                    if (search && toString.call(search) == '[object RegExp]') {
49                        throw new TypeError();
50                    }
51                    var stringLength = string.length;
52                    var searchString = String(search);
53                    var searchLength = searchString.length;
54                    var position = arguments.length > 1 ? arguments[1] : undefined;
55                    // `ToInteger`
56                    var pos = position ? Number(position) : 0;
57                    if (pos != pos) { // better `isNaN`
58                        pos = 0;
59                    }
60                    var start = Math.min(Math.max(pos, 0), stringLength);
61                    // Avoid the `indexOf` call if no match is possible
62                    if (searchLength + start > stringLength) {
63                        return false;
64                    }
65                    return indexOf.call(string, searchString, pos) != -1;
66                };
67                if (defineProperty) {
68                    defineProperty(String.prototype, 'includes', {
69                        'value': includes,
70                        'configurable': true,
71                        'writable': true,
72                    });
73                } else {
74                    String.prototype.includes = includes;
75                }
76            }());
77        }
78
79        if (!String.prototype.startsWith) {
80            (function() {
81                'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
82                var defineProperty = (function() {
83                    // IE 8 only supports `Object.defineProperty` on DOM elements
84                    try {
85                        var object = {};
86                        var $defineProperty = Object.defineProperty;
87                        var result = $defineProperty(object, object, object) && $defineProperty;
88                    } catch (error) {
89                    }
90                    return result;
91                }());
92                var toString = {}.toString;
93                var startsWith = function(search) {
94                    if (this == null) {
95                        throw new TypeError();
96                    }
97                    var string = String(this);
98                    if (search && toString.call(search) == '[object RegExp]') {
99                        throw new TypeError();
100                    }
101                    var stringLength = string.length;
102                    var searchString = String(search);
103                    var searchLength = searchString.length;
104                    var position = arguments.length > 1 ? arguments[1] : undefined;
105                    // `ToInteger`
106                    var pos = position ? Number(position) : 0;
107                    if (pos != pos) { // better `isNaN`
108                        pos = 0;
109                    }
110                    var start = Math.min(Math.max(pos, 0), stringLength);
111                    // Avoid the `indexOf` call if no match is possible
112                    if (searchLength + start > stringLength) {
113                        return false;
114                    }
115                    var index = -1;
116                    while (++index < searchLength) {
117                        if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {
118                            return false;
119                        }
120                    }
121                    return true;
122                };
123                if (defineProperty) {
124                    defineProperty(String.prototype, 'startsWith', {
125                        'value': startsWith,
126                        'configurable': true,
127                        'writable': true,
128                    });
129                } else {
130                    String.prototype.startsWith = startsWith;
131                }
132            }());
133        }
134
135        if (!Object.keys) {
136            Object.keys = function(
137                o, // object
138                k, // key
139                r  // result array
140            ) {
141                // initialize object and result
142                r = [];
143                // iterate over object keys
144                for (k in o)
145                    // fill result array with non-prototypical keys
146                    r.hasOwnProperty.call(o, k) && r.push(k);
147                // return result
148                return r;
149            };
150        }
151
152        // set data-selected on select element if the value has been programmatically selected
153        // prior to initialization of bootstrap-select
154        // * consider removing or replacing an alternative method *
155        var valHooks = {
156            useDefault: false,
157            _set: $.valHooks.select.set,
158        };
159
160        $.valHooks.select.set = function(elem, value) {
161            if (value && !valHooks.useDefault) $(elem).data('selected', true);
162
163            return valHooks._set.apply(this, arguments);
164        };
165
166        var changed_arguments = null;
167        $.fn.triggerNative = function(eventName) {
168            var el = this[0],
169                event;
170
171            if (el.dispatchEvent) { // for modern browsers & IE9+
172                if (typeof Event === 'function') {
173                    // For modern browsers
174                    event = new Event(eventName, {
175                        bubbles: true,
176                    });
177                } else {
178                    // For IE since it doesn't support Event constructor
179                    event = document.createEvent('Event');
180                    event.initEvent(eventName, true, false);
181                }
182
183                el.dispatchEvent(event);
184            } else if (el.fireEvent) { // for IE8
185                event = document.createEventObject();
186                event.eventType = eventName;
187                el.fireEvent('on' + eventName, event);
188            } else {
189                // fall back to jQuery.trigger
190                this.trigger(eventName);
191            }
192        };
193        //</editor-fold>
194
195        // Case insensitive contains search
196        $.expr.pseudos.icontains = function(obj, index, meta) {
197            var $obj = $(obj).find('span.dropdown-item-inner');
198            var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase();
199            return haystack.includes(meta[3].toUpperCase());
200        };
201
202        // Case insensitive begins search
203        $.expr.pseudos.ibegins = function(obj, index, meta) {
204            var $obj = $(obj).find('span.dropdown-item-inner');
205            var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase();
206            return haystack.startsWith(meta[3].toUpperCase());
207        };
208
209        // Case and accent insensitive contains search
210        $.expr.pseudos.aicontains = function(obj, index, meta) {
211            var $obj = $(obj).find('span.dropdown-item-inner');
212            var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase();
213            return haystack.includes(meta[3].toUpperCase());
214        };
215
216        // Case and accent insensitive begins search
217        $.expr.pseudos.aibegins = function(obj, index, meta) {
218            var $obj = $(obj).find('span.dropdown-item-inner');
219            var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase();
220            return haystack.startsWith(meta[3].toUpperCase());
221        };
222
223        /**
224         * Remove all diatrics from the given text.
225         * @access private
226         * @param {String} text
227         * @returns {String}
228         */
229        function normalizeToBase(text) {
230            var rExps = [
231                {re: /[\xC0-\xC6]/g, ch: 'A'},
232                {re: /[\xE0-\xE6]/g, ch: 'a'},
233                {re: /[\xC8-\xCB]/g, ch: 'E'},
234                {re: /[\xE8-\xEB]/g, ch: 'e'},
235                {re: /[\xCC-\xCF]/g, ch: 'I'},
236                {re: /[\xEC-\xEF]/g, ch: 'i'},
237                {re: /[\xD2-\xD6]/g, ch: 'O'},
238                {re: /[\xF2-\xF6]/g, ch: 'o'},
239                {re: /[\xD9-\xDC]/g, ch: 'U'},
240                {re: /[\xF9-\xFC]/g, ch: 'u'},
241                {re: /[\xC7-\xE7]/g, ch: 'c'},
242                {re: /[\xD1]/g, ch: 'N'},
243                {re: /[\xF1]/g, ch: 'n'},
244            ];
245            $.each(rExps, function() {
246                text = text ? text.replace(this.re, this.ch) : '';
247            });
248            return text;
249        }
250
251        // List of HTML entities for escaping.
252        var escapeMap = {
253            '&': '&amp;',
254            '<': '&lt;',
255            '>': '&gt;',
256            '"': '&quot;',
257            '\'': '&#x27;',
258            '`': '&#x60;',
259        };
260
261        var unescapeMap = {
262            '&amp;': '&',
263            '&lt;': '<',
264            '&gt;': '>',
265            '&quot;': '"',
266            '&#x27;': '\'',
267            '&#x60;': '`',
268        };
269
270        // Functions for escaping and unescaping strings to/from HTML interpolation.
271        var createEscaper = function(map) {
272            var escaper = function(match) {
273                return map[match];
274            };
275            // Regexes for identifying a key that needs to be escaped.
276            var source = '(?:' + Object.keys(map).join('|') + ')';
277            var testRegexp = RegExp(source);
278            var replaceRegexp = RegExp(source, 'g');
279            return function(string) {
280                string = string == null ? '' : '' + string;
281                return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
282            };
283        };
284
285        var htmlEscape = createEscaper(escapeMap);
286        var htmlUnescape = createEscaper(unescapeMap);
287
288        var Selectpicker = function(element, options) {
289            // bootstrap-select has been initialized - revert valHooks.select.set back to its original function
290            if (!valHooks.useDefault) {
291                $.valHooks.select.set = valHooks._set;
292                valHooks.useDefault = true;
293            }
294
295            this.$element = $(element);
296            this.$newElement = null;
297            this.$button = null;
298            this.$menu = null;
299            this.$lis = null;
300            this.options = options;
301
302            // 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
303            // data-attribute)
304            if (this.options.title === null) {
305                this.options.title = this.$element.attr('title');
306            }
307
308            // Format window padding
309            var winPad = this.options.windowPadding;
310            if (typeof winPad === 'number') {
311                this.options.windowPadding = [winPad, winPad, winPad, winPad];
312            }
313
314            //Expose public methods
315            this.val = Selectpicker.prototype.val;
316            this.render = Selectpicker.prototype.render;
317            this.refresh = Selectpicker.prototype.refresh;
318            this.setStyle = Selectpicker.prototype.setStyle;
319            this.selectAll = Selectpicker.prototype.selectAll;
320            this.deselectAll = Selectpicker.prototype.deselectAll;
321            this.destroy = Selectpicker.prototype.destroy;
322            this.remove = Selectpicker.prototype.remove;
323            this.show = Selectpicker.prototype.show;
324            this.hide = Selectpicker.prototype.hide;
325
326            this.init();
327        };
328
329        Selectpicker.VERSION = '1.12.2';
330
331        // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both.
332        Selectpicker.DEFAULTS = {
333            noneSelectedText: 'Nothing selected',
334            noneResultsText: 'No results matched {0}',
335            countSelectedText: function(numSelected, numTotal) {
336                return (numSelected == 1) ? '{0} item selected' : '{0} items selected';
337            },
338            maxOptionsText: function(numAll, numGroup) {
339                return [
340                    (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)',
341                    (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)',
342                ];
343            },
344            selectAllText: 'Select All',
345            deselectAllText: 'Deselect All',
346            doneButton: false,
347            doneButtonText: 'Close',
348            multipleSeparator: ', ',
349            styleBase: 'btn',
350            style: 'btn-default btn-light', // bootstrap4: btn-default replaced by btn-light
351            size: 'auto',
352            title: null,
353            selectedTextFormat: 'values',
354            width: false,
355            container: false,
356            hideDisabled: false,
357            showSubtext: false,
358            showIcon: true,
359            showContent: true,
360            dropupAuto: true,
361            header: false,
362            liveSearch: false,
363            liveSearchPlaceholder: null,
364            liveSearchNormalize: false,
365            liveSearchStyle: 'contains',
366            actionsBox: false,
367            iconBase: 'fa',
368            tickIcon: 'fa-check',
369            showTick: false,
370            template: {
371                caret: '<span class="caret"></span>',
372            },
373            maxOptions: false,
374            mobile: false,
375            selectOnTab: false,
376            dropdownAlignRight: false,
377            windowPadding: 0,
378        };
379
380        Selectpicker.prototype = {
381
382            constructor: Selectpicker,
383
384            init: function() {
385                var that = this,
386                    id = this.$element.attr('id');
387
388                this.$element.addClass('bs-select-hidden');
389
390                // store originalIndex (key) and newIndex (value) in this.liObj for fast accessibility
391                // allows us to do this.$lis.eq(that.liObj[index]) instead of this.$lis.filter('[data-original-index="' + index + '"]')
392                this.liObj = {};
393                this.multiple = this.$element.prop('multiple');
394                this.autofocus = this.$element.prop('autofocus');
395                this.$newElement = this.createView();
396                this.$element.after(this.$newElement).appendTo(this.$newElement);
397                this.$button = this.$newElement.children('button');
398                this.$menu = this.$newElement.children('.dropdown-menu');
399                this.$menuInner = this.$menu.children('.inner');
400                this.$searchbox = this.$menu.find('input');
401
402                this.$element.removeClass('bs-select-hidden');
403
404                if (this.options.dropdownAlignRight === true) this.$menu.addClass('dropdown-menu-right');
405
406                if (typeof id !== 'undefined') {
407                    this.$button.attr('data-id', id);
408                    $('label[for="' + id + '"]').click(function(e) {
409                        e.preventDefault();
410                        that.$button.focus();
411                    });
412                }
413
414                this.checkDisabled();
415                this.clickListener();
416                if (this.options.liveSearch) this.liveSearchListener();
417                this.render();
418                this.setStyle();
419                this.setWidth();
420                if (this.options.container) this.selectPosition();
421                this.$menu.data('this', this);
422                this.$newElement.data('this', this);
423                if (this.options.mobile) this.mobile();
424
425                this.$newElement.on({
426                    'hide.bs.dropdown': function(e) {
427                        that.$menuInner.attr('aria-expanded', false);
428                        that.$element.trigger('hide.bs.select', e);
429                    },
430                    'hidden.bs.dropdown': function(e) {
431                        that.$element.trigger('hidden.bs.select', e);
432                    },
433                    'show.bs.dropdown': function(e) {
434                        that.$menuInner.attr('aria-expanded', true);
435                        that.$element.trigger('show.bs.select', e);
436                    },
437                    'shown.bs.dropdown': function(e) {
438                        that.$element.trigger('shown.bs.select', e);
439                    },
440                });
441
442                if (that.$element[0].hasAttribute('required')) {
443                    this.$element.on('invalid', function() {
444                        that.$button.addClass('bs-invalid').focus();
445
446                        that.$element.on({
447                            'focus.bs.select': function() {
448                                that.$button.focus();
449                                that.$element.off('focus.bs.select');
450                            },
451                            'shown.bs.select': function() {
452                                that.$element.val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened
453                                    .off('shown.bs.select');
454                            },
455                            'rendered.bs.select': function() {
456                                // if select is no longer invalid, remove the bs-invalid class
457                                if (this.validity.valid) that.$button.removeClass('bs-invalid');
458                                that.$element.off('rendered.bs.select');
459                            },
460                        });
461                    });
462                }
463
464                setTimeout(function() {
465                    that.$element.trigger('loaded.bs.select');
466                });
467            },
468
469            createDropdown: function() {
470                // Options
471                // If we are multiple or showTick option is set, then add the show-tick class
472                var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '',
473                    inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '',
474                    autofocus = this.autofocus ? ' autofocus' : '';
475                // Elements
476                var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">&times;</button>' + this.options.header + '</div>' : '';
477                var searchbox = this.options.liveSearch ?
478                    '<div class="bs-searchbox">' +
479                    '<input type="text" class="form-control" autocomplete="off"' +
480                    (null === this.options.liveSearchPlaceholder ? '' : ' placeholder="' + htmlEscape(this.options.liveSearchPlaceholder) + '"') + ' role="textbox" aria-label="Search">' +
481                    '</div>'
482                    : '';
483                var actionsbox = this.multiple && this.options.actionsBox ?
484                    '<div class="bs-actionsbox">' +
485                    '<div class="btn-group btn-group-sm btn-block">' +
486                    '<button type="button" class="actions-btn bs-select-all btn btn-default btn-light">' +
487                    this.options.selectAllText +
488                    '</button>' +
489                    '<button type="button" class="actions-btn bs-deselect-all btn btn-default btn-light">' +
490                    this.options.deselectAllText +
491                    '</button>' +
492                    '</div>' +
493                    '</div>'
494                    : '';
495                var donebutton = this.multiple && this.options.doneButton ?
496                    '<div class="bs-donebutton">' +
497                    '<div class="btn-group btn-block">' +
498                    '<button type="button" class="btn btn-sm btn-default btn-light">' +
499                    this.options.doneButtonText +
500                    '</button>' +
501                    '</div>' +
502                    '</div>'
503                    : '';
504                var drop =
505                    '<div class="btn-group bootstrap-select' + showTick + inputGroup + '">' +
506                    '<button type="button" class="' + this.options.styleBase + ' dropdown-toggle" data-toggle="dropdown"' + autofocus + ' role="button">' +
507                    '<span class="filter-option pull-left"></span>&nbsp;' +
508                    '<span class="bs-caret">' +
509                    this.options.template.caret +
510                    '</span>' +
511                    '</button>' +
512                    '<div class="dropdown-menu open" role="combobox">' +
513                    header +
514                    searchbox +
515                    actionsbox +
516                    '<div class="dropdown-menu inner" role="listbox" aria-expanded="false">' +
517                    '</div>' +
518                    donebutton +
519                    '</div>' +
520                    '</div>';
521
522                return $(drop);
523            },
524
525            createView: function() {
526                var $drop = this.createDropdown(),
527                    li = this.createLi();
528
529                $drop.find('div.inner')[0].innerHTML = li;
530                return $drop;
531            },
532
533            reloadLi: function() {
534                // rebuild
535                var li = this.createLi();
536                this.$menuInner[0].innerHTML = li;
537            },
538
539            createLi: function() {
540                var that = this,
541                    _li = [],
542                    optID = 0,
543                    titleOption = document.createElement('option'),
544                    liIndex = -1; // increment liIndex whenever a new <li> element is created to ensure liObj is correct
545
546                // Helper functions
547                /**
548                 * @param content
549                 * @param [index]
550                 * @param [classes]
551                 * @param [optgroup]
552                 * @returns {string}
553                 */
554                var generateA = function(content, index, classes, optgroup) {
555                    classes = 'dropdown-item ' + (classes || '');
556                    return '<a tabindex="0"' +
557                        ((typeof classes !== 'undefined' && '' !== classes) ? ' class="' + classes + '"' : '') +
558                        ((typeof index !== 'undefined' && null !== index) ? ' data-original-index="' + index + '"' : '') +
559                        ((typeof optgroup !== 'undefined' && null !== optgroup) ? 'data-optgroup="' + optgroup + '"' : '') +
560                        '>' + content + '</a>';
561                };
562
563                var generateDiv = function(content, classes, optgroup) {
564                    return '<div' +
565                        ((typeof classes !== 'undefined' && '' !== classes) ? ' class="' + classes + '"' : '') +
566                        ((typeof optgroup !== 'undefined' && null !== optgroup) ? ' data-optgroup="' + optgroup + '"' : '') +
567                        '>' + content + '</div>';
568                };
569
570                /**
571                 * @param text
572                 * @param [classes]
573                 * @param [inline]
574                 * @param [tokens]
575                 * @returns {string}
576                 */
577                var generateSpan = function(text, classes, inline, tokens) {
578                    // support bootstrap v4: http://v4-alpha.getbootstrap.com/components/dropdowns/#single-button-dropdowns
579                    classes = 'dropdown-item-inner ' + (classes || '');
580                    return '<span' +
581                        (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
582                        (inline ? ' style="' + inline + '"' : '') +
583                        (that.options.liveSearchNormalize ? ' data-normalized-text="' + normalizeToBase(htmlEscape($(text).html())) + '"' : '') +
584                        (typeof tokens !== 'undefined' || tokens !== null ? ' data-tokens="' + tokens + '"' : '') +
585                        ' role="option">
585' + text +
586                        '<span class="' + that.options.iconBase + ' ' + that.options.tickIcon + ' check-mark"></span>' +
587                        '</span>';
588                };
589
590                if (this.options.title && !this.multiple) {
591                    // this option doesn't create a new <li> element, but does add a new option, so liIndex is decreased
592                    // since liObj is recalculated on every refresh, liIndex needs to be decreased even if the titleOption is already appended
593                    liIndex--;
594
595                    if (!this.$element.find('.bs-title-option').length) {
596                        // Use native JS to prepend option (faster)
597                        var element = this.$element[0];
598                        titleOption.className = 'bs-title-option';
599                        titleOption.innerHTML = this.options.title;
600                        titleOption.value = '';
601                        element.insertBefore(titleOption, element.firstChild);
602                        // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option.
603                        // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs,
604                        // if so, the select will have the data-selected attribute
605                        var $opt = $(element.options[element.selectedIndex]);
606                        if ($opt.attr('selected') === undefined && this.$element.data('selected') === undefined) {
607                            titleOption.selected = true;
608                        }
609                    }
610                }
611
612                var $selectOptions = this.$element.find('option');
613
614                $selectOptions.each(function(index) {
615                    var $this = $(this);
616
617                    liIndex++;
618
619                    if ($this.hasClass('bs-title-option')) return;
620
621                    // Get the class and text for the option
622                    var optionClass = this.className || '',
623                        inline = htmlEscape(this.style.cssText),
624                        text = $this.data('content') ? $this.data('content') : $this.html(),
625                        tokens = $this.data('tokens') ? $this.data('tokens') : null,
626                        subtext = typeof $this.data('subtext') !== 'undefined' ? '<small class="text-muted">' + $this.data('subtext') + '</small>' : '',
627                        icon = typeof $this.data('icon') !== 'undefined' ? '<span class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></span> ' : '',
628                        $parent = $this.parent(),
629                        isOptgroup = $parent[0].tagName === 'OPTGROUP',
630                        isOptgroupDisabled = isOptgroup && $parent[0].disabled,
631                        isDisabled = this.disabled || isOptgroupDisabled,
632                        prevHiddenIndex;
633
634                    if (icon !== '' && isDisabled) {
635                        icon = '<span>' + icon + '</span>';
636                    }
637
638                    if (that.options.hideDisabled && (isDisabled && !isOptgroup || isOptgroupDisabled)) {
639                        // set prevHiddenIndex - the index of the first hidden option in a group of hidden options
640                        // used to determine whether or not a divider should be placed after an optgroup if there are
641                        // hidden options between the optgroup and the first visible option
642                        prevHiddenIndex = $this.data('prevHiddenIndex');
643                        $this.next().data('prevHiddenIndex', (prevHiddenIndex !== undefined ? prevHiddenIndex : index));
644
645                        liIndex--;
646                        return;
647                    }
648
649                    if (!$this.data('content')) {
650                        // Prepend any icon and append any subtext to the main text.
651                        text = icon + '<span class="text">' + text + subtext + '</span>';
652                    }
653
654                    if (isOptgroup && $this.data('divider') !== true) {
655                        if (that.options.hideDisabled && isDisabled) {
656                            if ($parent.data('allOptionsDisabled') === undefined) {
657                                var $options = $parent.children();
658                                $parent.data('allOptionsDisabled', $options.filter(':disabled').length === $options.length);
659                            }
660
661                            if ($parent.data('allOptionsDisabled')) {
662                                liIndex--;
663                                return;
664                            }
665                        }
666
667                        var optGroupClass = ' ' + $parent[0].className || '';
668
669                        if ($this.index() === 0) { // Is it the first option of the optgroup?
670                            optID += 1;
671
672                            // Get the opt group label
673                            var label = $parent[0].label,
674                                labelSubtext = typeof $parent.data('subtext') !== 'undefined' ? '<small class="text-muted">' + $parent.data('subtext') + '</small>' : '',
675                                labelIcon = $parent.data('icon') ? '<span class="' + that.options.iconBase + ' ' + $parent.data('icon') + '"></span> ' : '';
676
677                            label = labelIcon + '<span class="text">' + htmlEscape(label) + labelSubtext + '</span>';
678
679                            if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown?
680                                liIndex++;
681                                _li.push(generateDiv('', 'dropdown-divider', optID + 'div'));
682                            }
683                            liIndex++;
684                            _li.push(generateDiv(label, 'dropdown-header' + optGroupClass, optID));
685                        }
686
687                        if (that.options.hideDisabled && isDisabled) {
688                            liIndex--;
689                            return;
690                        }
691
692                        _li.push(generateA(generateSpan(text, 'opt ' + optionClass + optGroupClass, inline, tokens), index, '', optID));
693                    } else if ($this.data('divider') === true) {
694                        _li.push(generateDiv('', 'dropdown-divider', optID + 'div'));
695                    } else if ($this.data('hidden') === true) {
696                        // set prevHiddenIndex - the index of the first hidden option in a group of hidden options
697                        // used to determine whether or not a divider should be placed after an optgroup if there are
698                        // hidden options between the optgroup and the first visible option
699                        prevHiddenIndex = $this.data('prevHiddenIndex');
700                        $this.next().data('prevHiddenIndex', (prevHiddenIndex !== undefined ? prevHiddenIndex : index));
701
702                        _li.push(generateA(generateSpan(text, optionClass, inline, tokens), index, 'hidden is-hidden'));
703                    } else {
704                        var showDivider = this.previousElementSibling && this.previousElementSibling.tagName === 'OPTGROUP';
705
706                        // if previous element is not an optgroup and hideDisabled is true
707                        if (!showDivider && that.options.hideDisabled) {
708                            prevHiddenIndex = $this.data('prevHiddenIndex');
709
710                            if (prevHiddenIndex !== undefined) {
711                                // select the element **before** the first hidden element in the group
712                                var prevHidden = $selectOptions.eq(prevHiddenIndex)[0].previousElementSibling;
713
714                                if (prevHidden && prevHidden.tagName === 'OPTGROUP' && !prevHidden.disabled) {
715                                    showDivider = true;
716                                }
717                            }
718                        }
719
720                        if (showDivider) {
721                            liIndex++;
722                            _li.push(generateDiv('', 'dropdown-divider', optID + 'div'));
723                        }
724                        _li.push(generateA(generateSpan(text, optionClass, inline, tokens), index));
725                    }
726
727                    that.liObj[index] = liIndex;
728                });
729
730                //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
731                if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) {
732                    this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected');
733                }
734
735                return _li.join('');
736            },
737
738            findLis: function() {
739                if (this.$lis == null) this.$lis = this.$menu.find('a, .dropdown-header, .dropdown-divider');
740                return this.$lis;
741            },
742
743            /**
744             * @param [updateLi] defaults to true
745             */
746            render: function(updateLi) {
747                var that = this,
748                    notDisabled,
749                    $selectOptions = this.$element.find('option');
750
751                //Update the LI to match the SELECT
752                if (updateLi !== false) {
753                    $selectOptions.each(function(index) {
754                        var $lis = that.findLis().eq(that.liObj[index]);
755
756                        that.setDisabled(index, this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled, $lis);
757                        that.setSelected(index, this.selected, $lis);
758                    });
759                }
760
761                this.togglePlaceholder();
762
763                this.tabIndex();
764
765                var selectedItems = $selectOptions.map(function() {
766                    if (this.selected) {
767                        if (that.options.hideDisabled && (this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled)) return;
768
769                        var $this = $(this),
770                            icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : '',
771                            subtext;
772
773                        if (that.options.showSubtext && $this.data('subtext') && !that.multiple) {
774                            subtext = ' <small class="text-muted">' + $this.data('subtext') + '</small>';
775                        } else {
776                            subtext = '';
777                        }
778                        if (typeof $this.attr('title') !== 'undefined') {
779                            return $this.attr('title');
780                        } else if ($this.data('content') && that.options.showContent) {
781                            return $this.data('content').toString();
782                        } else {
783                            return icon + $this.html() + subtext;
784                        }
785                    }
786                }).toArray();
787
788                //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled
789                //Convert all the values into a comma delimited string
790                var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator);
791
792                //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc..
793                if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) {
794                    var max = this.options.selectedTextFormat.split('>');
795                    if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) {
796                        notDisabled = this.options.hideDisabled ? ', [disabled]' : '';
797                        var totalCount = $selectOptions.not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length,
798                            tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText;
799                        title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString());
800                    }
801                }
802
803                if (this.options.title == undefined) {
804                    this.options.title = this.$element.attr('title');
805                }
806
807                if (this.options.selectedTextFormat == 'static') {
808                    title = this.options.title;
809                }
810
811                //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text
812                if (!title) {
813                    title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText;
814                }
815
816                //strip all HTML tags and trim the result, then unescape any escaped tags
817                this.$button.attr('title', htmlUnescape($.trim(title.replace(/<[^>]*>?/g, ''))));
818                this.$button.children('.filter-option').html(title);
819
820                this.$element.trigger('rendered.bs.select');
821            },
822
823            /**
824             * @param [style]
825             * @param [status]
826             */
827            setStyle: function(style, status) {
828                if (this.$element.attr('class')) {
829                    this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, ''));
830                }
831
832                var buttonClass = style ? style : this.options.style;
833
834                if (status == 'add') {
835                    this.$button.addClass(buttonClass);
836                } else if (status == 'remove') {
837                    this.$button.removeClass(buttonClass);
838                } else {
839                    this.$button.removeClass(this.options.style);
840                    this.$button.addClass(buttonClass);
841                }
842            },
843            updatePosition: function() {
844                // this fixes https://github.com/heimrichhannot/bootstrap-select/issues/4
845                var event = this.$menu.get(0).ownerDocument.createEvent('HTMLEvents');
846                event.initEvent('resize', true, false);
847                this.$menu.get(0).ownerDocument.dispatchEvent(event);
848            },
849            liHeight: function(refresh) {
850                if (!refresh && (this.options.size === false || this.sizeInfo)) return;
851
852                var newElement = document.createElement('div'),
853                    menu = document.createElement('div'),
854                    menuInner = document.createElement('ul'),
855                    divider = document.createElement('a'),
856                    li = document.createElement('a'),
857                    a = document.createElement('span'),
858                    text = document.createElement('span'),
859                    header = this.options.header && this.$menu.find('.popover-title').length > 0 ? this.$menu.find('.popover-title')[0].cloneNode(true) : null,
860                    search = this.options.liveSearch ? document.createElement('div') : null,
861                    actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null,
862                    doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null;
863
864                text.className = 'text';
865                newElement.className = this.$menu[0].parentNode.className + ' show open'; // bootstrap 4 requires show instead of open
866                menu.className = 'dropdown-menu open show';
867                menuInner.className = 'dropdown-menu inner';
868                divider.className = 'dropdown-divider';
869                a.className = 'dropdown-item-inner';
870
871                text.appendChild(document.createTextNode('Inner text'));
872
873                a.appendChild(text);
874                li.appendChild(a);
875                menuInner.appendChild(li);
876                menuInner.appendChild(divider);
877                if (header) menu.appendChild(header);
878                if (search) {
879                    var input = document.createElement('input');
880                    search.className = 'bs-searchbox';
881                    input.className = 'form-control';
882                    search.appendChild(input);
883                    menu.appendChild(search);
884                }
885                if (actions) menu.appendChild(actions);
886                menu.appendChild(menuInner);
887                if (doneButton) menu.appendChild(doneButton);
888                newElement.appendChild(menu);
889
890                document.body.appendChild(newElement);
891
892                var liHeight = a.offsetHeight,
893                    headerHeight = header ? header.offsetHeight : 0,
894                    searchHeight = search ? search.offsetHeight : 0,
895                    actionsHeight = actions ? actions.offsetHeight : 0,
896                    doneButtonHeight = doneButton ? doneButton.offsetHeight : 0,
897                    dividerHeight = $(divider).outerHeight(true),
898                    // fall back to jQuery if getComputedStyle is not supported
899                    menuStyle = typeof getComputedStyle === 'function' ? getComputedStyle(menu) : false,
900                    $menu = menuStyle ? null : $(menu),
901                    menuPadding = {
902                        vert: parseInt(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) +
903                        parseInt(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) +
904                        parseInt(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) +
905                        parseInt(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')),
906                        horiz: parseInt(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) +
907                        parseInt(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) +
908                        parseInt(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) +
909                        parseInt(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')),
910                    },
911                    menuExtras = {
912                        vert: menuPadding.vert +
913                        parseInt(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) +
914                        parseInt(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2,
915                        horiz: menuPadding.horiz +
916                        parseInt(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) +
917                        parseInt(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2,
918                    };
919
920                document.body.removeChild(newElement);
921
922                this.sizeInfo = {
923                    liHeight: liHeight,
924                    headerHeight: headerHeight,
925                    searchHeight: searchHeight,
926                    actionsHeight: actionsHeight,
927                    doneButtonHeight: doneButtonHeight,
928                    dividerHeight: dividerHeight,
929                    menuPadding: menuPadding,
930                    menuExtras: menuExtras,
931                };
932            },
933
934            setSize: function() {
935                this.findLis();
936                this.liHeight();
937
938                if (this.options.header) this.$menu.css('padding-top', 0);
939                if (this.options.size === false) return;
940
941                var that = this,
942                    $menu = this.$menu,
943                    $menuInner = this.$menuInner,
944                    $window = $(window),
945                    selectHeight = this.$newElement[0].offsetHeight,
946                    selectWidth = this.$newElement[0].offsetWidth,
947                    liHeight = this.sizeInfo['liHeight'],
948                    headerHeight = this.sizeInfo['headerHeight'],
949                    searchHeight = this.sizeInfo['searchHeight'],
950                    actionsHeight = this.sizeInfo['actionsHeight'],
951                    doneButtonHeight = this.sizeInfo['doneButtonHeight'],
952                    divHeight = this.sizeInfo['dividerHeight'],
953                    menuPadding = this.sizeInfo['menuPadding'],
954                    menuExtras = this.sizeInfo['menuExtras'],
955                    notDisabled = this.options.hideDisabled ? '.disabled' : '',
956                    menuHeight,
957                    menuWidth,
958                    getHeight,
959                    getWidth,
960                    selectOffsetTop,
961                    selectOffsetBot,
962                    selectOffsetLeft,
963                    selectOffsetRight,
964                    getPos = function() {
965                        var pos = that.$newElement.offset(),
966                            $container = $(that.options.container),
967                            containerPos;
968
969                        if (that.options.container && !$container.is('body')) {
970                            containerPos = $container.offset();
971                            containerPos.top += parseInt($container.css('borderTopWidth'));
972                            containerPos.left += parseInt($container.css('borderLeftWidth'));
973                        } else {
974                            containerPos = {top: 0, left: 0};
975                        }
976
977                        var winPad = that.options.windowPadding;
978                        selectOffsetTop = pos.top - containerPos.top - $window.scrollTop();
979                        selectOffsetBot = $window.height() - selectOffsetTop - selectHeight - containerPos.top - winPad[2];
980                        selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft();
981                        selectOffsetRight = $window.width() - selectOffsetLeft - selectWidth - containerPos.left - winPad[1];
982                        selectOffsetTop -= winPad[0];
983                        selectOffsetLeft -= winPad[3];
984                    };
985
986                getPos();
987
988                if (this.options.size === 'auto') {
989                    var getSize = function() {
990                        var minHeight,
991                            hasClass = function(className, include) {
992                                return function(element) {
993                                    if (include) {
994                                        return (element.classList ? element.classList.contains(className) : $(element).hasClass(className));
995                                    } else {
996                                        return !(element.classList ? element.classList.contains(className) : $(element).hasClass(className));
997                                    }
998                                };
999                            },
1000                            lis = that.$menuInner[0].getElementsByTagName('a'),
1001                            lisVisible = Array.prototype.filter ? Array.prototype.filter.call(lis, hasClass('d-none', false)) : that.$lis.not('.d-none'),
1002                            optGroup = Array.prototype.filter ? Array.prototype.filter.call(lisVisible, hasClass('dropdown-header', true)) : lisVisible.filter('.dropdown-header');
1003
1004                        getPos();
1005                        menuHeight = selectOffsetBot - menuExtras.vert;
1006                        menuWidth = selectOffsetRight - menuExtras.horiz;
1007
1008                        if (that.options.container) {
1009                            if (!$menu.data('height')) $menu.data('height', $menu.height());
1010                            getHeight = $menu.data('height');
1011
1012                            if (!$menu.data('width')) $menu.data('width', $menu.width());
1013                            getWidth = $menu.data('width');
1014                        } else {
1015                            getHeight = $menu.height();
1016                            getWidth = $menu.width();
1017                        }
1018
1019                        if (that.options.dropupAuto) {
1020                            that.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight);
1021                        }
1022
1023                        if (that.$newElement.hasClass('dropup')) {
1024                            menuHeight = selectOffsetTop - menuExtras.vert;
1025                        }
1026
1027                        if (that.options.dropdownAlignRight === 'auto') {
1028                            $menu.toggleClass('dropdown-menu-right', selectOffsetLeft > selectOffsetRight && (menuWidth - menuExtras.horiz) < (getWidth - selectWidth));
1029                        }
1030
1031                        if ((lisVisible.length + optGroup.length) > 3) {
1032                            minHeight = liHeight * 3 + menuExtras.vert - 2;
1033                        } else {
1034                            minHeight = 0;
1035                        }
1036
1037                        $menu.css({
1038                            'max-height': menuHeight + 'px',
1039                            'overflow': 'hidden',
1040                            'min-height': minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px',
1041                        });
1042                        $menuInner.css({
1043                            'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert + 'px',
1044                            'overflow-y': 'auto',
1045                            'min-height': Math.max(minHeight - menuPadding.vert, 0) + 'px',
1046                        });
1047                    };
1048                    getSize();
1049                    this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize);
1050                    $window.off('resize.getSize scroll.getSize').on('resize.getSize scroll.getSize', getSize);
1051                } else if (this.options.size && this.options.size != 'auto' && this.$lis.not(notDisabled).length > this.options.size) {
1052                    var optIndex = this.$lis.not('.dropdown-divider').not(notDisabled).children().slice(0, this.options.size).last().parent().index(),
1053                        divLength = this.$lis.slice(0, optIndex + 1).filter('.dropdown-divider').length;
1054                    menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert;
1055
1056                    if (that.options.container) {
1057                        if (!$menu.data('height')) $menu.data('height', $menu.height());
1058                        getHeight = $menu.data('height');
1059                    } else {
1060                        getHeight = $menu.height();
1061                    }
1062
1063                    if (that.options.dropupAuto) {
1064                        //noinspection JSUnusedAssignment
1065                        this.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight);
1066                    }
1067                    $menu.css({
1068                        'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px',
1069                        'overflow': 'hidden',
1070                        'min-height': '',
1071                    });
1072                    $menuInner.css({
1073                        'max-height': menuHeight - menuPadding.vert + 'px',
1074                        'overflow-y': 'auto',
1075                        'min-height': '',
1076                    });
1077                }
1078            },
1079
1080            setWidth: function() {
1081                if (this.options.width === 'auto') {
1082                    this.$menu.css('min-width', '0');
1083
1084                    // Get correct width if element is hidden
1085                    var $selectClone = this.$menu.parent().clone().appendTo('body'),
1086                        $selectClone2 = this.options.container ? this.$newElement.clone().appendTo('body') : $selectClone,
1087                        ulWidth = $selectClone.children('.dropdown-menu').outerWidth(),
1088                        btnWidth = $selectClone2.css('width', 'auto').children('button').outerWidth();
1089
1090                    $selectClone.remove();
1091                    $selectClone2.remove();
1092
1093                    // Set width to whatever's larger, button title or longest option
1094                    this.$newElement.css('width', Math.max(ulWidth, btnWidth) + 'px');
1095                } else if (this.options.width === 'fit') {
1096                    // Remove inline min-width so width can be changed from 'auto'
1097                    this.$menu.css('min-width', '');
1098                    this.$newElement.css('width', '').addClass('fit-width');
1099                } else if (this.options.width) {
1100                    // Remove inline min-width so width can be changed from 'auto'
1101                    this.$menu.css('min-width', '');
1102                    this.$newElement.css('width', this.options.width);
1103                } else {
1104                    // Remove inline min-width/width so width can be changed
1105                    this.$menu.css('min-width', '');
1106                    this.$newElement.css('width', '');
1107                }
1108                // Remove fit-width class if width is changed programmatically
1109                if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') {
1110                    this.$newElement.removeClass('fit-width');
1111                }
1112            },
1113
1114            selectPosition: function() {
1115                this.$bsContainer = $('<div class="bs-container" />');
1116
1117                var that = this,
1118                    $container = $(this.options.container),
1119                    pos,
1120                    containerPos,
1121                    actualHeight,
1122                    getPlacement = function($element) {
1123                        that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass('dropup', $element.hasClass('dropup'));
1124                        pos = $element.offset();
1125
1126                        if (!$container.is('body')) {
1127                            containerPos = $container.offset();
1128                            containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop();
1129                            containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft();
1130                        } else {
1131                            containerPos = {top: 0, left: 0};
1132                        }
1133
1134                        actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight;
1135
1136                        that.$bsContainer.css({
1137                            'top': pos.top - containerPos.top + actualHeight,
1138                            'left': pos.left - containerPos.left,
1139                            'width': $element[0].offsetWidth,
1140                        });
1141                    };
1142
1143                this.$button.on('click', function() {
1144                    var $this = $(this);
1145
1146                    if (that.isDisabled()) {
1147                        return;
1148                    }
1149
1150                    getPlacement(that.$newElement);
1151
1152                    that.$bsContainer.appendTo(that.options.container).toggleClass('open', !$this.hasClass('open')).append(that.$menu);
1153                });
1154
1155                $(window).on('resize scroll', function() {
1156                    getPlacement(that.$newElement);
1157                });
1158
1159                this.$element.on('hide.bs.select', function() {
1160                    that.$menu.data('height', that.$menu.height());
1161                    that.$bsContainer.detach();
1162                });
1163            },
1164
1165            /**
1166             * @param {number} index - the index of the option that is being changed
1167             * @param {boolean} selected - true if the option is being selected, false if being deselected
1168             * @param {JQuery} $lis - the 'li' element that is being modified
1169             */
1170            setSelected: function(index, selected, $lis) {
1171                if (!$lis) {
1172                    this.togglePlaceholder(); // check if setSelected is being called by changing the value of the select
1173                    $lis = this.findLis().eq(this.liObj[index]);
1174                }
1175
1176                $lis.toggleClass('selected', selected).find('span.dropdown-item-inner').attr('aria-selected', selected);
1177            },
1178
1179            /**
1180             * @param {number} index - the index of the option that is being disabled
1181             * @param {boolean} disabled - true if the option is being disabled, false if being enabled
1182             * @param {JQuery} $lis - the 'li' element that is being modified
1183             */
1184            setDisabled: function(index, disabled, $lis) {
1185                if (!$lis) {
1186                    $lis = this.findLis().eq(this.liObj[index]);
1187                }
1188
1189                if (disabled) {
1190                    $lis.addClass('disabled').children('span.dropdown-item-inner').attr('href', '#').attr('tabindex', -1).attr('aria-disabled', true);
1191                } else {
1192                    $lis.removeClass('disabled').children('span.dropdown-item-inner').removeAttr('href').attr('tabindex', 0).attr('aria-disabled', false);
1193                }
1194            },
1195
1196            isDisabled: function() {
1197                return this.$element[0].disabled;
1198            },
1199
1200            checkDisabled: function() {
1201                var that = this;
1202
1203                if (this.isDisabled()) {
1204                    this.$newElement.addClass('disabled');
1205                    this.$button.addClass('disabled').attr('tabindex', -1).attr('aria-disabled', true);
1206                } else {
1207                    if (this.$button.hasClass('disabled')) {
1208                        this.$newElement.removeClass('disabled');
1209                        this.$button.removeClass('disabled').attr('aria-disabled', false);
1210                    }
1211
1212                    if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) {
1213                        this.$button.removeAttr('tabindex');
1214                    }
1215                }
1216
1217                this.$button.click(function() {
1218                    return !that.isDisabled();
1219                });
1220            },
1221
1222            togglePlaceholder: function() {
1223                var value = this.$element.val();
1224                this.$button.toggleClass('bs-placeholder', value === null || value === '' || (value.constructor === Array && value.length === 0));
1225            },
1226
1227            tabIndex: function() {
1228                if (this.$element.data('tabindex') !== this.$element.attr('tabindex') &&
1229                    (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) {
1230                    this.$element.data('tabindex', this.$element.attr('tabindex'));
1231                    this.$button.attr('tabindex', this.$element.data('tabindex'));
1232                }
1233
1234                this.$element.attr('tabindex', -98);
1235            },
1236
1237            clickListener: function() {
1238                var that = this,
1239                    $document = $(document);
1240
1241                $document.data('spaceSelect', false);
1242
1243                this.$button.on('keyup', function(e) {
1244                    if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) {
1245                        e.preventDefault();
1246                        $document.data('spaceSelect', false);
1247                    }
1248                });
1249
1250                this.$button.on('click', function() {
1251                    that.setSize();
1252                });
1253
1254                this.$element.on('shown.bs.select', function() {
1255                    if (!that.options.liveSearch && !that.multiple) {
1256                        that.$menuInner.find('a.selected').focus();
1257                    } else if (!that.multiple) {
1258                        var selectedIndex = that.liObj[that.$element[0].selectedIndex];
1259
1260                        if (typeof selectedIndex !== 'number' || that.options.size === false) return;
1261
1262                        // scroll to selected option
1263                        var offset = that.$lis.eq(selectedIndex)[0].offsetTop - that.$menuInner[0].offsetTop;
1264                        offset = offset - that.$menuInner[0].offsetHeight / 2 + that.sizeInfo.liHeight / 2;
1265                        that.$menuInner[0].scrollTop = offset;
1266                    }
1267                });
1268
1269                this.$menuInner.on('click', 'a', function(e) {
1270                    var $this = $(this).find('span.dropdown-item-inner'),
1271                        clickedIndex = $this.parent().data('originalIndex'),
1272                        prevValue = that.$element.val(),
1273                        prevIndex = that.$element.prop('selectedIndex'),
1274                        triggerChange = true;
1275
1276                    // Don't close on multi choice menu
1277                    if (that.multiple && that.options.maxOptions !== 1) {
1278                        e.stopPropagation();
1279                    }
1280
1281                    e.preventDefault();
1282
1283                    //Don't run if we have been disabled
1284                    if (!that.isDisabled() && !$this.parent().hasClass('disabled')) {
1285                        var $options = that.$element.find('option'),
1286                            $option = $options.eq(clickedIndex),
1287                            state = $option.prop('selected'),
1288                            $optgroup = $option.parent('optgroup'),
1289                            maxOptions = that.options.maxOptions,
1290                            maxOptionsGrp = $optgroup.data('maxOptions') || false;
1291
1292                        if (!that.multiple) { // Deselect all others if not multi select box
1293                            $options.prop('selected', false);
1294                            $option.prop('selected', true);
1295                            that.$menuInner.find('.selected').removeClass('selected').find('span.dropdown-item-inner').attr('aria-selected', false);
1296                            that.setSelected(clickedIndex, true);
1297                        } else { // Toggle the one we have chosen if we are multi select.
1298                            $option.prop('selected', !state);
1299                            that.setSelected(clickedIndex, !state);
1300                            $this.blur();
1301
1302                            if (maxOptions !== false || maxOptionsGrp !== false) {
1303                                var maxReached = maxOptions < $options.filter(':selected').length,
1304                                    maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length;
1305
1306                                if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) {
1307                                    if (maxOptions && maxOptions == 1) {
1308                                        $options.prop('selected', false);
1309                                        $option.prop('selected', true);
1310                                        that.$menuInner.find('.selected').removeClass('selected');
1311                                        that.setSelected(clickedIndex, true);
1312                                    } else if (maxOptionsGrp && maxOptionsGrp == 1) {
1313                                        $optgroup.find('option:selected').prop('selected', false);
1314                                        $option.prop('selected', true);
1315                                        var optgroupID = $this.parent().data('optgroup');
1316                                        that.$menuInner.find('[data-optgroup="' + optgroupID + '"]').removeClass('selected');
1317                                        that.setSelected(clickedIndex, true);
1318                                    } else {
1319                                        var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText,
1320                                            maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText,
1321                                            maxTxt = maxOptionsArr[0].replace('{n}', maxOptions),
1322                                            maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp),
1323                                            $notify = $('<div class="notify"></div>');
1324                                        // If {var} is set in array, replace it
1325                                        /** @deprecated */
1326                                        if (maxOptionsArr[2]) {
1327                                            maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]);
1328                                            maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]);
1329                                        }
1330
1331                                        $option.prop('selected', false);
1332
1333                                        that.$menu.append($notify);
1334
1335                                        if (maxOptions && maxReached) {
1336                                            $notify.append($('<div>' + maxTxt + '</div>'));
1337                                            triggerChange = false;
1338                                            that.$element.trigger('maxReached.bs.select');
1339                                        }
1340
1341                                        if (maxOptionsGrp && maxReachedGrp) {
1342                                            $notify.append($('<div>' + maxTxtGrp + '</div>'));
1343                                            triggerChange = false;
1344                                            that.$element.trigger('maxReachedGrp.bs.select');
1345                                        }
1346
1347                                        setTimeout(function() {
1348                                            that.setSelected(clickedIndex, false);
1349                                        }, 10);
1350
1351                                        $notify.delay(750).fadeOut(300, function() {
1352                                            $(this).remove();
1353                                        });
1354                                    }
1355                                }
1356                            }
1357                        }
1358
1359                        if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) {
1360                            that.$button.focus();
1361                        } else if (that.options.liveSearch) {
1362                            that.$searchbox.focus();
1363                        }
1364
1365                        // Trigger select 'change'
1366                        if (triggerChange) {
1367                            if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) {
1368                                // $option.prop('selected') is current option state (selected/unselected). state is previous option state.
1369                                changed_arguments = [clickedIndex, $option.prop('selected'), state];
1370                                that.$element.triggerNative('change');
1371                            }
1372                        }
1373                    }
1374                });
1375
1376                this.$menu.on('click', 'a.disabled span.dropdown-item-inner , .popover-title, .popover-title :not(.close)', function(e) {
1377                    if (e.currentTarget == this) {
1378                        e.preventDefault();
1379                        e.stopPropagation();
1380                        if (that.options.liveSearch && !$(e.target).hasClass('close')) {
1381                            that.$searchbox.focus();
1382                        } else {
1383                            that.$button.focus();
1384                        }
1385                    }
1386                });
1387
1388                this.$menuInner.on('click', '.dropdown-divider, .dropdown-header', function(e) {
1389                    e.preventDefault();
1390                    e.stopPropagation();
1391                    if (that.options.liveSearch) {
1392                        that.$searchbox.focus();
1393                    } else {
1394                        that.$button.focus();
1395                    }
1396                });
1397
1398                this.$menu.on('click', '.popover-title .close', function() {
1399                    that.$button.click();
1400                });
1401
1402                this.$searchbox.on('click', function(e) {
1403                    e.stopPropagation();
1404                });
1405
1406                this.$menu.on('click', '.actions-btn', function(e) {
1407                    if (that.options.liveSearch) {
1408                        that.$searchbox.focus();
1409                    } else {
1410                        that.$button.focus();
1411                    }
1412
1413                    e.preventDefault();
1414                    e.stopPropagation();
1415
1416                    if ($(this).hasClass('bs-select-all')) {
1417                        that.selectAll();
1418                    } else {
1419                        that.deselectAll();
1420                    }
1421                });
1422
1423                this.$element.change(function() {
1424                    that.render(false);
1425                    that.$element.trigger('changed.bs.select', changed_arguments);
1426                    changed_arguments = null;
1427                });
1428            },
1429
1430            liveSearchListener: function() {
1431                var that = this,
1432                    $no_results = $('<li class="no-results"></li>');
1433
1434                this.$button.on('click.dropdown.data-api', function() {
1435                    that.$menuInner.find('.active').removeClass('active');
1436                    if (!!that.$searchbox.val()) {
1437                        that.$searchbox.val('');
1438                        that.$lis.not('.is-hidden').removeClass('d-none');
1439                        if (!!$no_results.parent().length) $no_results.remove();
1440                    }
1441                    if (!that.multiple) that.$menuInner.find('.selected').addClass('active');
1442                    setTimeout(function() {
1443                        that.$searchbox.focus();
1444                    }, 10);
1445                });
1446
1447                this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function(e) {
1448                    e.stopPropagation();
1449                });
1450
1451                this.$searchbox.on('input propertychange', function() {
1452                    that.$lis.not('.is-hidden').removeClass('d-none');
1453                    that.$lis.filter('.active').removeClass('active');
1454                    $no_results.remove();
1455
1456                    if (that.$searchbox.val()) {
1457                        var $searchBase = that.$lis.not('.is-hidden, .dropdown-divider, .dropdown-header'),
1458                            $hideItems;
1459
1460                        if (that.options.liveSearchNormalize) {
1461                            $hideItems = $searchBase.not(':a' + that._searchStyle() + '("' + normalizeToBase(that.$searchbox.val()) + '")');
1462                        } else {
1463                            $hideItems = $searchBase.not(':' + that._searchStyle() + '("' + that.$searchbox.val() + '")');
1464                        }
1465
1466                        if ($hideItems.length === $searchBase.length) {
1467                            $no_results.html(that.options.noneResultsText.replace('{0}', '"' + htmlEscape(that.$searchbox.val()) + '"'));
1468                            that.$menuInner.append($no_results);
1469                            that.$lis.addClass('d-none');
1470                        } else {
1471                            $hideItems.addClass('d-none');
1472
1473                            var $lisVisible = that.$lis.not('.d-none'),
1474                                $foundDiv;
1475
1476                            // hide divider if first or last visible, or if followed by another divider
1477                            $lisVisible.each(function(index) {
1478                                var $this = $(this);
1479
1480                                if ($this.hasClass('dropdown-divider')) {
1481                                    if ($foundDiv === undefined) {
1482                                        $this.addClass('d-none');
1483                                    } else {
1484                                        if ($foundDiv) $foundDiv.addClass('d-none');
1485                                        $foundDiv = $this;
1486                                    }
1487                                } else if ($this.hasClass('dropdown-header') && $lisVisible.eq(index + 1).data('optgroup') !== $this.data('optgroup')) {
1488                                    $this.addClass('d-none');
1489                                } else {
1490                                    $foundDiv = null;
1491                                }
1492                            });
1493                            if ($foundDiv) $foundDiv.addClass('d-none');
1494
1495                            $searchBase.not('.d-none').first().addClass('active');
1496                            that.$menuInner.scrollTop(0);
1497                        }
1498
1499                        that.updatePosition();
1500                    }
1501                });
1502            },
1503
1504            _searchStyle: function() {
1505                var styles = {
1506                    begins: 'ibegins',
1507                    startsWith: 'ibegins',
1508                };
1509
1510                return styles[this.options.liveSearchStyle] || 'icontains';
1511            },
1512
1513            val: function(value) {
1514                if (typeof value !== 'undefined') {
1515                    this.$element.val(value);
1516                    this.render();
1517
1518                    return this.$element;
1519                } else {
1520                    return this.$element.val();
1521                }
1522            },
1523
1524            changeAll: function(status) {
1525                if (!this.multiple) return;
1526                if (typeof status === 'undefined') status = true;
1527
1528                this.findLis();
1529
1530                var $options = this.$element.find('option'),
1531                    $lisVisible = this.$lis.not('.dropdown-divider, .dropdown-header, .disabled, .d-none'),
1532                    lisVisLen = $lisVisible.length,
1533                    selectedOptions = [];
1534
1535                if (status) {
1536                    if ($lisVisible.filter('.selected').length === $lisVisible.length) return;
1537                } else {
1538                    if ($lisVisible.filter('.selected').length === 0) return;
1539                }
1540
1541                $lisVisible.toggleClass('selected', status);
1542
1543                for (var i = 0; i < lisVisLen; i++) {
1544                    var origIndex = $lisVisible[i].getAttribute('data-original-index');
1545                    selectedOptions[selectedOptions.length] = $options.eq(origIndex)[0];
1546                }
1547
1548                $(selectedOptions).prop('selected', status);
1549
1550                this.render(false);
1551
1552                this.togglePlaceholder();
1553
1554                this.$element.triggerNative('change');
1555            },
1556
1557            selectAll: function() {
1558                return this.changeAll(true);
1559            },
1560
1561            deselectAll: function() {
1562                return this.changeAll(false);
1563            },
1564
1565            toggle: function(e) {
1566                e = e || window.event;
1567
1568                if (e) e.stopPropagation();
1569
1570                this.$button.trigger('click');
1571            },
1572
1573            keydown: function(e) {
1574                var $this = $(this),
1575                    $parent = $this.is('input') ? $this.parent().parent() : $this.parent(),
1576                    $items,
1577                    that = $parent.data('this'),
1578                    index,
1579                    prevIndex,
1580                    isActive,
1581                    selector = ':not(.disabled, .d-none, .dropdown-header, .dropdown-divider)',
1582                    keyCodeMap = {
1583                        32: ' ',
1584                        48: '0',
1585                        49: '1',
1586                        50: '2',
1587                        51: '3',
1588                        52: '4',
1589                        53: '5',
1590                        54: '6',
1591                        55: '7',
1592                        56: '8',
1593                        57: '9',
1594                        59: ';',
1595                        65: 'a',
1596                        66: 'b',
1597                        67: 'c',
1598                        68: 'd',
1599                        69: 'e',
1600                        70: 'f',
1601                        71: 'g',
1602                        72: 'h',
1603                        73: 'i',
1604                        74: 'j',
1605                        75: 'k',
1606                        76: 'l',
1607                        77: 'm',
1608                        78: 'n',
1609                        79: 'o',
1610                        80: 'p',
1611                        81: 'q',
1612                        82: 'r',
1613                        83: 's',
1614                        84: 't',
1615                        85: 'u',
1616                        86: 'v',
1617                        87: 'w',
1618                        88: 'x',
1619                        89: 'y',
1620                        90: 'z',
1621                        96: '0',
1622                        97: '1',
1623                        98: '2',
1624                        99: '3',
1625                        100: '4',
1626                        101: '5',
1627                        102: '6',
1628                        103: '7',
1629                        104: '8',
1630                        105: '9',
1631                    };
1632
1633                isActive = that.$newElement.hasClass('open') || that.$newElement.hasClass('show');
1634
1635                if (!isActive && (e.keyCode >= 48 && e.keyCode <= 57 || e.keyCode >= 96 && e.keyCode <= 105 || e.keyCode >= 65 && e.keyCode <= 90)) {
1636                    if (!that.options.container) {
1637                        that.setSize();
1638                        that.$menu.parent().addClass('open show');
1639                        isActive = true;
1640                    } else {
1641                        that.$button.trigger('click');
1642                    }
1643                    that.$searchbox.focus();
1644                    return;
1645                }
1646
1647                if (that.options.liveSearch) {
1648                    if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive) {
1649                        e.preventDefault();
1650                        e.stopPropagation();
1651                        that.$menuInner.click();
1652                        that.$button.focus();
1653                    }
1654                }
1655
1656                if (/(38|40)/.test(e.keyCode.toString(10))) {
1657                    $items = that.$lis.filter(selector);
1658                    if (!$items.length) return;
1659
1660                    if (!that.options.liveSearch) {
1661                        index = $items.index($items.filter(':focus'));
1662                    } else {
1663                        index = $items.index($items.filter('.active'));
1664                    }
1665
1666                    prevIndex = that.$menuInner.data('prevIndex');
1667
1668                    if (e.keyCode == 38) {
1669                        if ((that.options.liveSearch || index == prevIndex) && index != -1) index--;
1670                        if (index < 0) index += $items.length;
1671                    } else if (e.keyCode == 40) {
1672                        if (that.options.liveSearch || index == prevIndex) index++;
1673                        index = index % $items.length;
1674                    }
1675
1676                    that.$menuInner.data('prevIndex', index);
1677
1678                    if (!that.options.liveSearch) {
1679                        $items.eq(index).focus();
1680                    } else {
1681                        e.preventDefault();
1682                        if (!$this.hasClass('dropdown-toggle')) {
1683                            $items.removeClass('active').eq(index).addClass('active').children('span.dropdown-item-inner').focus();
1684                            $this.focus();
1685                        }
1686                    }
1687
1688                } else if (!$this.is('input')) {
1689                    var keyIndex = [],
1690                        count,
1691                        prevKey;
1692
1693                    $items = that.$lis.filter(selector);
1694                    $items.each(function(i) {
1695                        if ($.trim($(this).children('span.dropdown-item-inner').text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) {
1696                            keyIndex.push(i);
1697                        }
1698                    });
1699
1700                    count = $(document).data('keycount');
1701                    count++;
1702                    $(document).data('keycount', count);
1703
1704                    prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1);
1705
1706                    if (prevKey != keyCodeMap[e.keyCode]) {
1707                        count = 1;
1708                        $(document).data('keycount', count);
1709                    } else if (count >= keyIndex.length) {
1710                        $(document).data('keycount', 0);
1711                        if (count > keyIndex.length) count = 1;
1712                    }
1713
1714                    $items.eq(keyIndex[count - 1]).children('span.dropdown-item-inner').focus();
1715                }
1716
1717                // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu.
1718                if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) {
1719                    if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault();
1720                    if (!that.options.liveSearch) {
1721                        var elem = $(':focus');
1722                        elem.click();
1723                        // Bring back focus for multiselects
1724                        elem.focus();
1725                        // Prevent screen from scrolling if the user hit the spacebar
1726                        e.preventDefault();
1727                        // Fixes spacebar selection of dropdown items in FF & IE
1728                        $(document).data('spaceSelect', true);
1729                    } else if (!/(32)/.test(e.keyCode.toString(10))) {
1730                        that.$menuInner.find('a.active').click();
1731                        $this.focus();
1732                    }
1733                    $(document).data('keycount', 0);
1734                }
1735
1736                if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) {
1737                    that.$menu.parent().removeClass('open');
1738                    if (that.options.container) that.$newElement.removeClass('open');
1739                    that.$button.focus();
1740                }
1741            },
1742
1743            mobile: function() {
1744                this.$element.addClass('mobile-device');
1745            },
1746
1747            refresh: function() {
1748                this.$lis = null;
1749                this.liObj = {};
1750                this.reloadLi();
1751                this.render();
1752                this.checkDisabled();
1753                this.liHeight(true);
1754                this.setStyle();
1755                this.setWidth();
1756                if (this.$lis) this.$searchbox.trigger('propertychange');
1757
1758                this.$element.trigger('refreshed.bs.select');
1759            },
1760
1761            hide: function() {
1762                this.$newElement.hide();
1763            },
1764
1765            show: function() {
1766                this.$newElement.show();
1767            },
1768
1769            remove: function() {
1770                this.$newElement.remove();
1771                this.$element.remove();
1772            },
1773
1774            destroy: function() {
1775                this.$newElement.before(this.$element).remove();
1776
1777                if (this.$bsContainer) {
1778                    this.$bsContainer.remove();
1779                } else {
1780                    this.$menu.remove();
1781                }
1782
1783                this.$element.off('.bs.select').removeData('selectpicker').removeClass('bs-select-hidden selectpicker');
1784            },
1785        };
1786
1787        // SELECTPICKER PLUGIN DEFINITION
1788        // ==============================
1789        function Plugin(option) {
1790            // get the args of the outer function..
1791            var args = arguments;
1792            // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them
1793            // to get lost/corrupted in android 2.3 and IE9 #715 #775
1794            var _option = option;
1795
1796            [].shift.apply(args);
1797
1798            var value;
1799            var chain = this.each(function() {
1800                var $this = $(this);
1801                if ($this.is('select')) {
1802                    var data = $this.data('selectpicker'),
1803                        options = typeof _option == 'object' && _option;
1804
1805                    if (!data) {
1806                        var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options);
1807                        config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), $this.data().template, options.template);
1808                        $this.data('selectpicker', (data = new Selectpicker(this, config)));
1809                    } else if (options) {
1810                        for (var i in options) {
1811                            if (options.hasOwnProperty(i)) {
1812                                data.options[i] = options[i];
1813                            }
1814                        }
1815                    }
1816
1817                    if (typeof _option == 'string') {
1818                        if (data[_option] instanceof Function) {
1819                            value = data[_option].apply(data, args);
1820                        } else {
1821                            value = data.options[_option];
1822                        }
1823                    }
1824                }
1825            });
1826
1827            if (typeof value !== 'undefined') {
1828                //noinspection JSUnusedAssignment
1829                return value;
1830            } else {
1831                return chain;
1832            }
1833        }
1834
1835        var old = $.fn.selectpicker;
1836        $.fn.selectpicker = Plugin;
1837        $.fn.selectpicker.Constructor = Selectpicker;
1838
1839        // SELECTPICKER NO CONFLICT
1840        // ========================
1841        $.fn.selectpicker.noConflict = function() {
1842            $.fn.selectpicker = old;
1843            return this;
1844        };
1845
1846        $(document).data('keycount', 0).on('keydown.bs.select', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', Selectpicker.prototype.keydown).on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', function(e) {
1847            e.stopPropagation();
1848        });
1849
1850        // SELECTPICKER DATA-API
1851        // =====================
1852        $(window).on('load.bs.select.data-api', function() {
1853            $('.selectpicker').each(function() {
1854                var $selectpicker = $(this);
1855                Plugin.call($selectpicker, $selectpicker.data());
1856            });
1857        });
1858    })(jQuery);
1859
1860}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.