PageSourceSearch

https://saban.caraworld.vn/assets/js/bootstrap-select.js

js caraworld.vn collected 2026-10-05 04:55:58 UTC 143,553 bytes, 3,314 lines download raw bytes

vendor: 15,764 bytes, lines 1-427
1/*!
2 * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select)
3 *
4 * Copyright 2012-2020 SnapAppointments, LLC
5 * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
6 */
7
8(function(root, factory) {
9    if (root === undefined && window !== undefined) root = window;
10    if (typeof define === 'function' && define.amd) {
11        // AMD. Register as an anonymous module unless amdModuleId is set
12        define(["jquery"], function(a0) {
13            return (factory(a0));
14        });
15    } else if (typeof module === 'object' && module.exports) {
16        // Node. Does not work with strict CommonJS, but
17        // only CommonJS-like environments that support module.exports,
18        // like Node.
19        module.exports = factory(require("jquery"));
20    } else {
21        factory(root["jQuery"]);
22    }
23}(this, function(jQuery) {
24
25    (function($) {
26        'use strict';
27
28        var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];
29
30        var uriAttrs = [
31            'background',
32            'cite',
33            'href',
34            'itemtype',
35            'longdesc',
36            'poster',
37            'src',
38            'xlink:href'
39        ];
40
41        var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
42
43        var DefaultWhitelist = {
44            // Global attributes allowed on any supplied element below.
45            '*': ['class', 'dir', 'id', 'lang', 'role', 'tabindex', 'style', ARIA_ATTRIBUTE_PATTERN],
46            a: ['target', 'href', 'title', 'rel'],
47            area: [],
48            b: [],
49            br: [],
50            col: [],
51            code: [],
52            div: [],
53            em: [],
54            hr: [],
55            h1: [],
56            h2: [],
57            h3: [],
58            h4: [],
59            h5: [],
60            h6: [],
61            i: [],
62            img: ['src', 'alt', 'title', 'width', 'height'],
63            li: [],
64            ol: [],
65            p: [],
66            pre: [],
67            s: [],
68            small: [],
69            span: [],
70            sub: [],
71            sup: [],
72            strong: [],
73            u: [],
74            ul: []
75        }
76
77        /**
78         * A pattern that recognizes a commonly useful subset of URLs that are safe.
79         *
80         * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
81         */
82        var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi;
83
84        /**
85         * A pattern that matches safe data URLs. Only matches image, video and audio types.
86         *
87         * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
88         */
89        var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;
90
91        function allowedAttribute(attr, allowedAttributeList) {
92            var attrName = attr.nodeName.toLowerCase()
93
94            if ($.inArray(attrName, allowedAttributeList) !== -1) {
95                if ($.inArray(attrName, uriAttrs) !== -1) {
96                    return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))
97                }
98
99                return true
100            }
101
102            var regExp = $(allowedAttributeList).filter(function(index, value) {
103                return value instanceof RegExp
104            })
105
106            // Check if a regular expression validates the attribute.
107            for (var i = 0, l = regExp.length; i < l; i++) {
108                if (attrName.match(regExp[i])) {
109                    return true
110                }
111            }
112
113            return false
114        }
115
116        function sanitizeHtml(unsafeElements, whiteList, sanitizeFn) {
117            if (sanitizeFn && typeof sanitizeFn === 'function') {
118                return sanitizeFn(unsafeElements);
119            }
120
121            var whitelistKeys = Object.keys(whiteList);
122
123            for (var i = 0, len = unsafeElements.length; i < len; i++) {
124                var elements = unsafeElements[i].querySelectorAll('*');
125
126                for (var j = 0, len2 = elements.length; j < len2; j++) {
127                    var el = elements[j];
128                    var elName = el.nodeName.toLowerCase();
129
130                    if (whitelistKeys.indexOf(elName) === -1) {
131                        el.parentNode.removeChild(el);
132
133                        continue;
134                    }
135
136                    var attributeList = [].slice.call(el.attributes);
137                    var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);
138
139                    for (var k = 0, len3 = attributeList.length; k < len3; k++) {
140                        var attr = attributeList[k];
141
142                        if (!allowedAttribute(attr, whitelistedAttributes)) {
143                            el.removeAttribute(attr.nodeName);
144                        }
145                    }
146                }
147            }
148        }
149
150        // Polyfill for browsers with no classList support
151        // Remove in v2
152        if (!('classList' in document.createElement('_'))) {
153            (function(view) {
154                if (!('Element' in view)) return;
155
156                var classListProp = 'classList',
157                    protoProp = 'prototype',
158                    elemCtrProto = view.Element[protoProp],
159                    objCtr = Object,
160                    classListGetter = function() {
161                        var $elem = $(this);
162
163                        return {
164                            add: function(classes) {
165                                classes = Array.prototype.slice.call(arguments).join(' ');
166                                return $elem.addClass(classes);
167                            },
168                            remove: function(classes) {
169                                classes = Array.prototype.slice.call(arguments).join(' ');
170                                return $elem.removeClass(classes);
171                            },
172                            toggle: function(classes, force) {
173                                return $elem.toggleClass(classes, force);
174                            },
175                            contains: function(classes) {
176                                return $elem.hasClass(classes);
177                            }
178                        }
179                    };
180
181                if (objCtr.defineProperty) {
182                    var classListPropDesc = {
183                        get: classListGetter,
184                        enumerable: true,
185                        configurable: true
186                    };
187                    try {
188                        objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
189                    } catch (ex) { // IE 8 doesn't support enumerable:true
190                        // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36
191                        // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected
192                        if (ex.number === undefined || ex.number === -0x7FF5EC54) {
193                            classListPropDesc.enumerable = false;
194                            objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
195                        }
196                    }
197                } else if (objCtr[protoProp].__defineGetter__) {
198                    elemCtrProto.__defineGetter__(classListProp, classListGetter);
199                }
200            }(window));
201        }
202
203        var testElement = document.createElement('_');
204
205        testElement.classList.add('c1', 'c2');
206
207        if (!testElement.classList.contains('c2')) {
208            var _add = DOMTokenList.prototype.add,
209                _remove = DOMTokenList.prototype.remove;
210
211            DOMTokenList.prototype.add = function() {
212                Array.prototype.forEach.call(arguments, _add.bind(this));
213            }
214
215            DOMTokenList.prototype.remove = function() {
216                Array.prototype.forEach.call(arguments, _remove.bind(this));
217            }
218        }
219
220        testElement.classList.toggle('c3', false);
221
222        // Polyfill for IE 10 and Firefox <24, where classList.toggle does not
223        // support the second argument.
224        if (testElement.classList.contains('c3')) {
225            var _toggle = DOMTokenList.prototype.toggle;
226
227            DOMTokenList.prototype.toggle = function(token, force) {
228                if (1 in arguments && !this.contains(token) === !force) {
229                    return force;
230                } else {
231                    return _toggle.call(this, token);
232                }
233            };
234        }
235
236        testElement = null;
237
238        // shallow array comparison
239        function isEqual(array1, array2) {
240            return array1.length === array2.length && array1.every(function(element, index) {
241                return element === array2[index];
242            });
243        };
244
245        // <editor-fold desc="Shims">
246        if (!String.prototype.startsWith) {
247            (function() {
248                'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
249                var defineProperty = (function() {
250                    // IE 8 only supports `Object.defineProperty` on DOM elements
251                    try {
252                        var object = {};
253                        var $defineProperty = Object.defineProperty;
254                        var result = $defineProperty(object, object, object) && $defineProperty;
255                    } catch (error) {}
256                    return result;
257                }());
258                var toString = {}.toString;
259                var startsWith = function(search) {
260                    if (this == null) {
261                        throw new TypeError();
262                    }
263                    var string = String(this);
264                    if (search && toString.call(search) == '[object RegExp]') {
265                        throw new TypeError();
266                    }
267                    var stringLength = string.length;
268                    var searchString = String(search);
269                    var searchLength = searchString.length;
270                    var position = arguments.length > 1 ? arguments[1] : undefined;
271                    // `ToInteger`
272                    var pos = position ? Number(position) : 0;
273                    if (pos != pos) { // better `isNaN`
274                        pos = 0;
275                    }
276                    var start = Math.min(Math.max(pos, 0), stringLength);
277                    // Avoid the `indexOf` call if no match is possible
278                    if (searchLength + start > stringLength) {
279                        return false;
280                    }
281                    var index = -1;
282                    while (++index < searchLength) {
283                        if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) {
284                            return false;
285                        }
286                    }
287                    return true;
288                };
289                if (defineProperty) {
290                    defineProperty(String.prototype, 'startsWith', {
291                        'value': startsWith,
292                        'configurable': true,
293                        'writable': true
294                    });
295                } else {
296                    String.prototype.startsWith = startsWith;
297                }
298            }());
299        }
300
301        if (!Object.keys) {
302            Object.keys = function(
303                o, // object
304                k, // key
305                r // result array
306            ) {
307                // initialize object and result
308                r = [];
309                // iterate over object keys
310                for (k in o) {
311                    // fill result array with non-prototypical keys
312                    r.hasOwnProperty.call(o, k) && r.push(k);
313                }
314                // return result
315                return r;
316            };
317        }
318
319        if (HTMLSelectElement && !HTMLSelectElement.prototype.hasOwnProperty('selectedOptions')) {
320            Object.defineProperty(HTMLSelectElement.prototype, 'selectedOptions', {
321                get: function() {
322                    return this.querySelectorAll(':checked');
323                }
324            });
325        }
326
327        function getSelectedOptions(select, ignoreDisabled) {
328            var selectedOptions = select.selectedOptions,
329                options = [],
330                opt;
331
332            if (ignoreDisabled) {
333                for (var i = 0, len = selectedOptions.length; i < len; i++) {
334                    opt = selectedOptions[i];
335
336                    if (!(opt.disabled || opt.parentNode.tagName === 'OPTGROUP' && opt.parentNode.disabled)) {
337                        options.push(opt);
338                    }
339                }
340
341                return options;
342            }
343
344            return selectedOptions;
345        }
346
347        // much faster than $.val()
348        function getSelectValues(select, selectedOptions) {
349            var value = [],
350                options = selectedOptions || select.selectedOptions,
351                opt;
352
353            for (var i = 0, len = options.length; i < len; i++) {
354                opt = options[i];
355
356                if (!(opt.disabled || opt.parentNode.tagName === 'OPTGROUP' && opt.parentNode.disabled)) {
357                    value.push(opt.value);
358                }
359            }
360
361            if (!select.multiple) {
362                return !value.length ? null : value[0];
363            }
364
365            return value;
366        }
367
368        // set data-selected on select element if the value has been programmatically selected
369        // prior to initialization of bootstrap-select
370        // * consider removing or replacing an alternative method *
371        var valHooks = {
372            useDefault: false,
373            _set: $.valHooks.select.set
374        };
375
376        $.valHooks.select.set = function(elem, value) {
377            if (value && !valHooks.useDefault) $(elem).data('selected', true);
378
379            return valHooks._set.apply(this, arguments);
380        };
381
382        var changedArguments = null;
383
384        var EventIsSupported = (function() {
385            try {
386                new Event('change');
387                return true;
388            } catch (e) {
389                return false;
390            }
391        })();
392
393        $.fn.triggerNative = function(eventName) {
394            var el = this[0],
395                event;
396
397            if (el.dispatchEvent) { // for modern browsers & IE9+
398                if (EventIsSupported) {
399                    // For modern browsers
400                    event = new Event(eventName, {
401                        bubbles: true
402                    });
403                } else {
404                    // For IE since it doesn't support Event constructor
405                    event = document.createEvent('Event');
406                    event.initEvent(eventName, true, false);
407                }
408
409                el.dispatchEvent(event);
410            } else if (el.fireEvent) { // for IE8
411                event = document.createEventObject();
412                event.eventType = eventName;
413                el.fireEvent('on' + eventName, event);
414            } else {
415                // fall back to jQuery.trigger
416                this.trigger(eventName);
417            }
418        };
419        // </editor-fold>
420
421        function stringSearch(li, searchString, method, normalize) {
422            var stringTypes = [
423                    'display',
424                    'subtext',
425                    'tokens'
426                ],
427                searchSuccess = false;
428
429            for (var i = 0; i < stringTypes.length; i++) {
430                var stringType = stringTypes[i],
431                    string = li[stringType];
432
433                if (string) {
434                    string = string.toString();
435
436                    // Strip HTML tags. This isn't perfect, but it's much faster than any other method
437                    if (stringType === 'display') {
438                        string = string.replace(/<[^>]+>/g, '');
439                    }
440
441                    if (normalize) string = normalizeToBase(string);
442                    string = string.toUpperCase();
443
444                    if (method === 'contains') {
445                        searchSuccess = string.indexOf(searchString) >= 0;
446                    } else {
447                        searchSuccess = string.startsWith(searchString);
448                    }
449
450                    if (searchSuccess) break;
451                }
452            }
453
454            return searchSuccess;
455        }
456
457        function toInteger(value) {
458            return parseInt(value, 10) || 0;
459        }
460
461        // Borrowed from Lodash (_.deburr)
462        /** Used to map Latin Unicode letters to basic Latin letters. */
463        var deburredLetters = {
464            // Latin-1 Supplement block.
465            '\xc0': 'A',
466            '\xc1': 'A',
467            '\xc2': 'A',
468            '\xc3': 'A',
469            '\xc4': 'A',
470            '\xc5': 'A',
471            '\xe0': 'a',
472            '\xe1': 'a',
473            '\xe2': 'a',
474            '\xe3': 'a',
475            '\xe4': 'a',
476            '\xe5': 'a',
477            '\xc7': 'C',
478            '\xe7': 'c',
479            '\xd0': 'D',
480            '\xf0': 'd',
481            '\xc8': 'E',
482            '\xc9': 'E',
483            '\xca': 'E',
484            '\xcb': 'E',
485            '\xe8': 'e',
486            '\xe9': 'e',
487            '\xea': 'e',
488            '\xeb': 'e',
489            '\xcc': 'I',
490            '\xcd': 'I',
491            '\xce': 'I',
492            '\xcf': 'I',
493            '\xec': 'i',
494            '\xed': 'i',
495            '\xee': 'i',
496            '\xef': 'i',
497            '\xd1': 'N',
498            '\xf1': 'n',
499            '\xd2': 'O',
500            '\xd3': 'O',
501            '\xd4': 'O',
502            '\xd5': 'O',
503            '\xd6': 'O',
504            '\xd8': 'O',
505            '\xf2': 'o',
506            '\xf3': 'o',
507            '\xf4': 'o',
508            '\xf5': 'o',
509            '\xf6': 'o',
510            '\xf8': 'o',
511            '\xd9': 'U',
512            '\xda': 'U',
513            '\xdb': 'U',
514            '\xdc': 'U',
515            '\xf9': 'u',
516            '\xfa': 'u',
517            '\xfb': 'u',
518            '\xfc': 'u',
519            '\xdd': 'Y',
520            '\xfd': 'y',
521            '\xff': 'y',
522            '\xc6': 'Ae',
523            '\xe6': 'ae',
524            '\xde': 'Th',
525            '\xfe': 'th',
526            '\xdf': 'ss',
527            // Latin Extended-A block.
528            '\u0100': 'A',
529            '\u0102': 'A',
530            '\u0104': 'A',
531            '\u0101': 'a',
532            '\u0103': 'a',
533            '\u0105': 'a',
534            '\u0106': 'C',
535            '\u0108': 'C',
536            '\u010a': 'C',
537            '\u010c': 'C',
538            '\u0107': 'c',
539            '\u0109': 'c',
540            '\u010b': 'c',
541            '\u010d': 'c',
542            '\u010e': 'D',
543            '\u0110': 'D',
544            '\u010f': 'd',
545            '\u0111': 'd',
546            '\u0112': 'E',
547            '\u0114': 'E',
548            '\u0116': 'E',
549            '\u0118': 'E',
550            '\u011a': 'E',
551            '\u0113': 'e',
552            '\u0115': 'e',
553            '\u0117': 'e',
554            '\u0119': 'e',
555            '\u011b': 'e',
556            '\u011c': 'G',
557            '\u011e': 'G',
558            '\u0120': 'G',
559            '\u0122': 'G',
560            '\u011d': 'g',
561            '\u011f': 'g',
562            '\u0121': 'g',
563            '\u0123': 'g',
564            '\u0124': 'H',
565            '\u0126': 'H',
566            '\u0125': 'h',
567            '\u0127': 'h',
568            '\u0128': 'I',
569            '\u012a': 'I',
570            '\u012c': 'I',
571            '\u012e': 'I',
572            '\u0130': 'I',
573            '\u0129': 'i',
574            '\u012b': 'i',
575            '\u012d': 'i',
576            '\u012f': 'i',
577            '\u0131': 'i',
578            '\u0134': 'J',
579            '\u0135': 'j',
580            '\u0136': 'K',
581            '\u0137': 'k',
582            '\u0138': 'k',
583            '\u0139': 'L',
584            '\u013b': 'L',
585            '\u013d': 'L',
586            '\u013f': 'L',
587            '\u0141': 'L',
588            '\u013a': 'l',
589            '\u013c': 'l',
590            '\u013e': 'l',
591            '\u0140': 'l',
592            '\u0142': 'l',
593            '\u0143': 'N',
594            '\u0145': 'N',
595            '\u0147': 'N',
596            '\u014a': 'N',
597            '\u0144': 'n',
598            '\u0146': 'n',
599            '\u0148': 'n',
600            '\u014b': 'n',
601            '\u014c': 'O',
602            '\u014e': 'O',
603            '\u0150': 'O',
604            '\u014d': 'o',
605            '\u014f': 'o',
606            '\u0151': 'o',
607            '\u0154': 'R',
608            '\u0156': 'R',
609            '\u0158': 'R',
610            '\u0155': 'r',
611            '\u0157': 'r',
612            '\u0159': 'r',
613            '\u015a': 'S',
614            '\u015c': 'S',
615            '\u015e': 'S',
616            '\u0160': 'S',
617            '\u015b': 's',
618            '\u015d': 's',
619            '\u015f': 's',
620            '\u0161': 's',
621            '\u0162': 'T',
622            '\u0164': 'T',
623            '\u0166': 'T',
624            '\u0163': 't',
625            '\u0165': 't',
626            '\u0167': 't',
627            '\u0168': 'U',
628            '\u016a': 'U',
629            '\u016c': 'U',
630            '\u016e': 'U',
631            '\u0170': 'U',
632            '\u0172': 'U',
633            '\u0169': 'u',
634            '\u016b': 'u',
635            '\u016d': 'u',
636            '\u016f': 'u',
637            '\u0171': 'u',
638            '\u0173': 'u',
639            '\u0174': 'W',
640            '\u0175': 'w',
641            '\u0176': 'Y',
642            '\u0177': 'y',
643            '\u0178': 'Y',
644            '\u0179': 'Z',
645            '\u017b': 'Z',
646            '\u017d': 'Z',
647            '\u017a': 'z',
648            '\u017c': 'z',
649            '\u017e': 'z',
650            '\u0132': 'IJ',
651            '\u0133': 'ij',
652            '\u0152': 'Oe',
653            '\u0153': 'oe',
654            '\u0149': "'n",
655            '\u017f': 's'
656        };
657
658        /** Used to match Latin Unicode letters (excluding mathematical operators). */
vendor: 121,384 bytes, lines 659-3314
659        var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
660
661        /** Used to compose unicode character classes. */
662        var rsComboMarksRange = '\\u0300-\\u036f',
663            reComboHalfMarksRange = '\\ufe20-\\ufe2f',
664            rsComboSymbolsRange = '\\u20d0-\\u20ff',
665            rsComboMarksExtendedRange = '\\u1ab0-\\u1aff',
666            rsComboMarksSupplementRange = '\\u1dc0-\\u1dff',
667            rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange;
668
669        /** Used to compose unicode capture groups. */
670        var rsCombo = '[' + rsComboRange + ']';
671
672        /**
673         * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
674         * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
675         */
676        var reComboMark = RegExp(rsCombo, 'g');
677
678        function deburrLetter(key) {
679            return deburredLetters[key];
680        };
681
682        function normalizeToBase(string) {
683            string = string.toString();
684            return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');
685        }
686
687        // List of HTML entities for escaping.
688        var escapeMap = {
689            '&': '&amp;',
690            '<': '&lt;',
691            '>': '&gt;',
692            '"': '&quot;',
693            "'": '&#x27;',
694            '`': '&#x60;'
695        };
696
697        // Functions for escaping and unescaping strings to/from HTML interpolation.
698        var createEscaper = function(map) {
699            var escaper = function(match) {
700                return map[match];
701            };
702            // Regexes for identifying a key that needs to be escaped.
703            var source = '(?:' + Object.keys(map).join('|') + ')';
704            var testRegexp = RegExp(source);
705            var replaceRegexp = RegExp(source, 'g');
706            return function(string) {
707                string = string == null ? '' : '' + string;
708                return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
709            };
710        };
711
712        var htmlEscape = createEscaper(escapeMap);
713
714        /**
715         * ------------------------------------------------------------------------
716         * Constants
717         * ------------------------------------------------------------------------
718         */
719
720        var keyCodeMap = {
721            32: ' ',
722            48: '0',
723            49: '1',
724            50: '2',
725            51: '3',
726            52: '4',
727            53: '5',
728            54: '6',
729            55: '7',
730            56: '8',
731            57: '9',
732            59: ';',
733            65: 'A',
734            66: 'B',
735            67: 'C',
736            68: 'D',
737            69: 'E',
738            70: 'F',
739            71: 'G',
740            72: 'H',
741            73: 'I',
742            74: 'J',
743            75: 'K',
744            76: 'L',
745            77: 'M',
746            78: 'N',
747            79: 'O',
748            80: 'P',
749            81: 'Q',
750            82: 'R',
751            83: 'S',
752            84: 'T',
753            85: 'U',
754            86: 'V',
755            87: 'W',
756            88: 'X',
757            89: 'Y',
758            90: 'Z',
759            96: '0',
760            97: '1',
761            98: '2',
762            99: '3',
763            100: '4',
764            101: '5',
765            102: '6',
766            103: '7',
767            104: '8',
768            105: '9'
769        };
770
771        var keyCodes = {
772            ESCAPE: 27, // KeyboardEvent.which value for Escape (Esc) key
773            ENTER: 13, // KeyboardEvent.which value for Enter key
774            SPACE: 32, // KeyboardEvent.which value for space key
775            TAB: 9, // KeyboardEvent.which value for tab key
776            ARROW_UP: 38, // KeyboardEvent.which value for up arrow key
777            ARROW_DOWN: 40 // KeyboardEvent.which value for down arrow key
778        }
779
780        var version = {
781            success: false,
782            major: '3'
783        };
784
785        try {
786            version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.');
787            version.major = version.full[0];
788            version.success = true;
789        } catch (err) {
790            // do nothing
791        }
792
793        var selectId = 0;
794
795        var EVENT_KEY = '.bs.select';
796
797        var classNames = {
798            DISABLED: 'disabled',
799            DIVIDER: 'divider',
800            SHOW: 'open',
801            DROPUP: 'dropup',
802            MENU: 'dropdown-menu',
803            MENURIGHT: 'dropdown-menu-right',
804            MENULEFT: 'dropdown-menu-left',
805            // to-do: replace with more advanced template/customization options
806            BUTTONCLASS: 'btn-default',
807            POPOVERHEADER: 'popover-title',
808            ICONBASE: 'glyphicon',
809            TICKICON: 'glyphicon-ok'
810        }
811
812        var Selector = {
813            MENU: '.' + classNames.MENU
814        }
815
816        var elementTemplates = {
817            span: document.createElement('span'),
818            i: document.createElement('i'),
819            subtext: document.createElement('small'),
820            a: document.createElement('a'),
821            li: document.createElement('li'),
822            whitespace: document.createTextNode('\u00A0'),
823            fragment: document.createDocumentFragment()
824        }
825
826        elementTemplates.a.setAttribute('role', 'option');
827        if (version.major === '4') elementTemplates.a.className = 'dropdown-item';
828
829        elementTemplates.subtext.className = 'text-muted';
830
831        elementTemplates.text = elementTemplates.span.cloneNode(false);
832        elementTemplates.text.className = 'text';
833
834        elementTemplates.checkMark = elementTemplates.span.cloneNode(false);
835
836        var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN);
837        var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE);
838
839        var generateOption = {
840            li: function(content, classes, optgroup) {
841                var li = elementTemplates.li.cloneNode(false);
842
843                if (content) {
844                    if (content.nodeType === 1 || content.nodeType === 11) {
845                        li.appendChild(content);
846                    } else {
847                        li.innerHTML = content;
848                    }
849                }
850
851                if (typeof classes !== 'undefined' && classes !== '') li.className = classes;
852                if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup);
853
854                return li;
855            },
856
857            a: function(text, classes, inline) {
858                var a = elementTemplates.a.cloneNode(true);
859
860                if (text) {
861                    if (text.nodeType === 11) {
862                        a.appendChild(text);
863                    } else {
864                        a.insertAdjacentHTML('beforeend', text);
865                    }
866                }
867
868                if (typeof classes !== 'undefined' && classes !== '') a.classList.add.apply(a.classList, classes.split(' '));
869                if (inline) a.setAttribute('style', inline);
870
871                return a;
872            },
873
874            text: function(options, useFragment) {
875                var textElement = elementTemplates.text.cloneNode(false),
876                    subtextElement,
877                    iconElement;
878
879                if (options.content) {
880                    textElement.innerHTML = options.content;
881                } else {
882                    textElement.textContent = options.text;
883
884                    if (options.icon) {
885                        var whitespace = elementTemplates.whitespace.cloneNode(false);
886
887                        // need to use <i> for icons in the button to prevent a breaking change
888                        // note: switch to span in next major release
889                        iconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false);
890                        iconElement.className = this.options.iconBase + ' ' + options.icon;
891
892                        elementTemplates.fragment.appendChild(iconElement);
893                        elementTemplates.fragment.appendChild(whitespace);
894                    }
895
896                    if (options.subtext) {
897                        subtextElement = elementTemplates.subtext.cloneNode(false);
898                        subtextElement.textContent = options.subtext;
899                        textElement.appendChild(subtextElement);
900                    }
901                }
902
903                if (useFragment === true) {
904                    while (textElement.childNodes.length > 0) {
905                        elementTemplates.fragment.appendChild(textElement.childNodes[0]);
906                    }
907                } else {
908                    elementTemplates.fragment.appendChild(textElement);
909                }
910
911                return elementTemplates.fragment;
912            },
913
914            label: function(options) {
915                var textElement = elementTemplates.text.cloneNode(false),
916                    subtextElement,
917                    iconElement;
918
919                textElement.innerHTML = options.display;
920
921                if (options.icon) {
922                    var whitespace = elementTemplates.whitespace.cloneNode(false);
923
924                    iconElement = elementTemplates.span.cloneNode(false);
925                    iconElement.className = this.options.iconBase + ' ' + options.icon;
926
927                    elementTemplates.fragment.appendChild(iconElement);
928                    elementTemplates.fragment.appendChild(whitespace);
929                }
930
931                if (options.subtext) {
932                    subtextElement = elementTemplates.subtext.cloneNode(false);
933                    subtextElement.textContent = options.subtext;
934                    textElement.appendChild(subtextElement);
935                }
936
937                elementTemplates.fragment.appendChild(textElement);
938
939                return elementTemplates.fragment;
940            }
941        }
942
943        var Selectpicker = function(element, options) {
944            var that = this;
945
946            // bootstrap-select has been initialized - revert valHooks.select.set back to its original function
947            if (!valHooks.useDefault) {
948                $.valHooks.select.set = valHooks._set;
949                valHooks.useDefault = true;
950            }
951
952            this.$element = $(element);
953            this.$newElement = null;
954            this.$button = null;
955            this.$menu = null;
956            this.options = options;
957            this.selectpicker = {
958                main: {},
959                search: {},
960                current: {}, // current changes if a search is in progress
961                view: {},
962                isSearching: false,
963                keydown: {
964                    keyHistory: '',
965                    resetKeyHistory: {
966                        start: function() {
967                            return setTimeout(function() {
968                                that.selectpicker.keydown.keyHistory = '';
969                            }, 800);
970                        }
971                    }
972                }
973            };
974
975            this.sizeInfo = {};
976
977            // 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
978            // data-attribute)
979            if (this.options.title === null) {
980                this.options.title = this.$element.attr('title');
981            }
982
983            // Format window padding
984            var winPad = this.options.windowPadding;
985            if (typeof winPad === 'number') {
986                this.options.windowPadding = [winPad, winPad, winPad, winPad];
987            }
988
989            // Expose public methods
990            this.val = Selectpicker.prototype.val;
991            this.render = Selectpicker.prototype.render;
992            this.refresh = Selectpicker.prototype.refresh;
993            this.setStyle = Selectpicker.prototype.setStyle;
994            this.selectAll = Selectpicker.prototype.selectAll;
995            this.deselectAll = Selectpicker.prototype.deselectAll;
996            this.destroy = Selectpicker.prototype.destroy;
997            this.remove = Selectpicker.prototype.remove;
998            this.show = Selectpicker.prototype.show;
999            this.hide = Selectpicker.prototype.hide;
1000
1001            this.init();
1002        };
1003
1004        Selectpicker.VERSION = '1.13.14';
1005
1006        // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both.
1007        Selectpicker.DEFAULTS = {
1008            noneSelectedText: 'Nothing selected',
1009            noneResultsText: 'No results matched {0}',
1010            countSelectedText: function(numSelected, numTotal) {
1011                return (numSelected == 1) ? '{0} item selected' : '{0} items selected';
1012            },
1013            maxOptionsText: function(numAll, numGroup) {
1014                return [
1015                    (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)',
1016                    (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)'
1017                ];
1018            },
1019            selectAllText: 'Select All',
1020            deselectAllText: 'Deselect All',
1021            doneButton: false,
1022            doneButtonText: 'Close',
1023            multipleSeparator: ', ',
1024            styleBase: 'btn',
1025            style: classNames.BUTTONCLASS,
1026            size: 'auto',
1027            title: null,
1028            selectedTextFormat: 'values',
1029            width: false,
1030            container: false,
1031            hideDisabled: false,
1032            showSubtext: false,
1033            showIcon: true,
1034            showContent: true,
1035            dropupAuto: true,
1036            header: false,
1037            liveSearch: false,
1038            liveSearchPlaceholder: null,
1039            liveSearchNormalize: false,
1040            liveSearchStyle: 'contains',
1041            actionsBox: false,
1042            iconBase: classNames.ICONBASE,
1043            tickIcon: classNames.TICKICON,
1044            showTick: false,
1045            template: {
1046                caret: '<span class="caret"></span>'
1047            },
1048            maxOptions: false,
1049            mobile: false,
1050            selectOnTab: false,
1051            dropdownAlignRight: false,
1052            windowPadding: 0,
1053            virtualScroll: 600,
1054            display: false,
1055            sanitize: true,
1056            sanitizeFn: null,
1057            whiteList: DefaultWhitelist
1058        };
1059
1060        Selectpicker.prototype = {
1061
1062            constructor: Selectpicker,
1063
1064            init: function() {
1065                var that = this,
1066                    id = this.$element.attr('id');
1067
1068                selectId++;
1069                this.selectId = 'bs-select-' + selectId;
1070
1071                this.$element[0].classList.add('bs-select-hidden');
1072
1073                this.multiple = this.$element.prop('multiple');
1074                this.autofocus = this.$element.prop('autofocus');
1075
1076                if (this.$element[0].classList.contains('show-tick')) {
1077                    this.options.showTick = true;
1078                }
1079
1080                this.$newElement = this.createDropdown();
1081                this.buildData();
1082                this.$element
1083                    .after(this.$newElement)
1084                    .prependTo(this.$newElement);
1085
1086                this.$button = this.$newElement.children('button');
1087                this.$menu = this.$newElement.children(Selector.MENU);
1088                this.$menuInner = this.$menu.children('.inner');
1089                this.$searchbox = this.$menu.find('input');
1090
1091                this.$element[0].classList.remove('bs-select-hidden');
1092
1093                if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT);
1094
1095                if (typeof id !== 'undefined') {
1096                    this.$button.attr('data-id', id);
1097                }
1098
1099                this.checkDisabled();
1100                this.clickListener();
1101
1102                if (this.options.liveSearch) {
1103                    this.liveSearchListener();
1104                    this.focusedParent = this.$searchbox[0];
1105                } else {
1106                    this.focusedParent = this.$menuInner[0];
1107                }
1108
1109                this.setStyle();
1110                this.render();
1111                this.setWidth();
1112                if (this.options.container) {
1113                    this.selectPosition();
1114                } else {
1115                    this.$element.on('hide' + EVENT_KEY, function() {
1116                        if (that.isVirtual()) {
1117                            // empty menu on close
1118                            var menuInner = that.$menuInner[0],
1119                                emptyMenu = menuInner.firstChild.cloneNode(false);
1120
1121                            // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = ''
1122                            menuInner.replaceChild(emptyMenu, menuInner.firstChild);
1123                            menuInner.scrollTop = 0;
1124                        }
1125                    });
1126                }
1127                this.$menu.data('this', this);
1128                this.$newElement.data('this', this);
1129                if (this.options.mobile) this.mobile();
1130
1131                this.$newElement.on({
1132                    'hide.bs.dropdown': function(e) {
1133                        that.$element.trigger('hide' + EVENT_KEY, e);
1134                    },
1135                    'hidden.bs.dropdown': function(e) {
1136                        that.$element.trigger('hidden' + EVENT_KEY, e);
1137                    },
1138                    'show.bs.dropdown': function(e) {
1139                        that.$element.trigger('show' + EVENT_KEY, e);
1140                    },
1141                    'shown.bs.dropdown': function(e) {
1142                        that.$element.trigger('shown' + EVENT_KEY, e);
1143                    }
1144                });
1145
1146                if (that.$element[0].hasAttribute('required')) {
1147                    this.$element.on('invalid' + EVENT_KEY, function() {
1148                        that.$button[0].classList.add('bs-invalid');
1149
1150                        that.$element
1151                            .on('shown' + EVENT_KEY + '.invalid', function() {
1152                                that.$element
1153                                    .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened
1154                                    .off('shown' + EVENT_KEY + '.invalid');
1155                            })
1156                            .on('rendered' + EVENT_KEY, function() {
1157                                // if select is no longer invalid, remove the bs-invalid class
1158                                if (this.validity.valid) that.$button[0].classList.remove('bs-invalid');
1159                                that.$element.off('rendered' + EVENT_KEY);
1160                            });
1161
1162                        that.$button.on('blur' + EVENT_KEY, function() {
1163                            that.$element.trigger('focus').trigger('blur');
1164                            that.$button.off('blur' + EVENT_KEY);
1165                        });
1166                    });
1167                }
1168
1169                setTimeout(function() {
1170                    that.buildList();
1171                    that.$element.trigger('loaded' + EVENT_KEY);
1172                });
1173            },
1174
1175            createDropdown: function() {
1176                // Options
1177                // If we are multiple or showTick option is set, then add the show-tick class
1178                var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '',
1179                    multiselectable = this.multiple ? ' aria-multiselectable="true"' : '',
1180                    inputGroup = '',
1181                    autofocus = this.autofocus ? ' autofocus' : '';
1182
1183                if (version.major < 4 && this.$element.parent().hasClass('input-group')) {
1184                    inputGroup = ' input-group-btn';
1185                }
1186
1187                // Elements
1188                var drop,
1189                    header = '',
1190                    searchbox = '',
1191                    actionsbox = '',
1192                    donebutton = '';
1193
1194                if (this.options.header) {
1195                    header =
1196                        '<div class="' + classNames.POPOVERHEADER + '">' +
1197                        '<button type="button" class="close" aria-hidden="true">&times;</button>' +
1198                        this.options.header +
1199                        '</div>';
1200                }
1201
1202                if (this.options.liveSearch) {
1203                    searchbox =
1204                        '<div class="bs-searchbox">' +
1205                        '<input type="search" class="form-control" autocomplete="off"' +
1206                        (
1207                            this.options.liveSearchPlaceholder === null ? '' :
1208                            ' placeholder="' + htmlEscape(this.options.liveSearchPlaceholder) + '"'
1209                        ) +
1210                        ' role="combobox" aria-label="Search" aria-controls="' + this.selectId + '" aria-autocomplete="list">' +
1211                        '</div>';
1212                }
1213
1214                if (this.multiple && this.options.actionsBox) {
1215                    actionsbox =
1216                        '<div class="bs-actionsbox">' +
1217                        '<div class="btn-group btn-group-sm btn-block">' +
1218                        '<button type="button" class="actions-btn bs-select-all btn ' + classNames.BUTTONCLASS + '">' +
1219                        this.options.selectAllText +
1220                        '</button>' +
1221                        '<button type="button" class="actions-btn bs-deselect-all btn ' + classNames.BUTTONCLASS + '">' +
1222                        this.options.deselectAllText +
1223                        '</button>' +
1224                        '</div>' +
1225                        '</div>';
1226                }
1227
1228                if (this.multiple && this.options.doneButton) {
1229                    donebutton =
1230                        '<div class="bs-donebutton">' +
1231                        '<div class="btn-group btn-block">' +
1232                        '<button type="button" class="btn btn-sm ' + classNames.BUTTONCLASS + '">' +
1233                        this.options.doneButtonText +
1234                        '</button>' +
1235                        '</div>' +
1236                        '</div>';
1237                }
1238
1239                drop =
1240                    '<div class="dropdown bootstrap-select' + showTick + inputGroup + '">' +
1241                    '<button type="button" class="' + this.options.styleBase + ' dropdown-toggle" ' + (this.options.display === 'static' ? 'data-display="static"' : '') + 'data-toggle="dropdown"' + autofocus + ' role="combobox" aria-owns="' + this.selectId + '" aria-haspopup="listbox" aria-expanded="false">' +
1242                    '<div class="filter-option">' +
1243                    '<div class="filter-option-inner">' +
1244                    '<div class="filter-option-inner-inner"></div>' +
1245                    '</div> ' +
1246                    '</div>' +
1247                    (
1248                        version.major === '4' ? '' :
1249                        '<span class="bs-caret">' +
1250                        this.options.template.caret +
1251                        '</span>'
1252                    ) +
1253                    '</button>' +
1254                    '<div class="' + classNames.MENU + ' ' + (version.major === '4' ? '' : classNames.SHOW) + '">' +
1255                    header +
1256                    searchbox +
1257                    actionsbox +
1258                    '<div class="inner ' + classNames.SHOW + '" role="listbox" id="' + this.selectId + '" tabindex="-1" ' + multiselectable + '>' +
1259                    '<ul class="' + classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : '') + '" role="presentation">' +
1260                    '</ul>' +
1261                    '</div>' +
1262                    donebutton +
1263                    '</div>' +
1264                    '</div>';
1265
1266                return $(drop);
1267            },
1268
1269            setPositionData: function() {
1270                this.selectpicker.view.canHighlight = [];
1271                this.selectpicker.view.size = 0;
1272
1273                for (var i = 0; i < this.selectpicker.current.data.length; i++) {
1274                    var li = this.selectpicker.current.data[i],
1275                        canHighlight = true;
1276
1277                    if (li.type === 'divider') {
1278                        canHighlight = false;
1279                        li.height = this.sizeInfo.dividerHeight;
1280                    } else if (li.type === 'optgroup-label') {
1281                        canHighlight = false;
1282                        li.height = this.sizeInfo.dropdownHeaderHeight;
1283                    } else {
1284                        li.height = this.sizeInfo.liHeight;
1285                    }
1286
1287                    if (li.disabled) canHighlight = false;
1288
1289                    this.selectpicker.view.canHighlight.push(canHighlight);
1290
1291                    if (canHighlight) {
1292                        this.selectpicker.view.size++;
1293                        li.posinset = this.selectpicker.view.size;
1294                    }
1295
1296                    li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height;
1297                }
1298            },
1299
1300            isVirtual: function() {
1301                return (this.options.virtualScroll !== false) && (this.selectpicker.main.elements.length >= this.options.virtualScroll) || this.options.virtualScroll === true;
1302            },
1303
1304            createView: function(isSearching, setSize, refresh) {
1305                var that = this,
1306                    scrollTop = 0,
1307                    active = [],
1308                    selected,
1309                    prevActive;
1310
1311                this.selectpicker.isSearching = isSearching;
1312                this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main;
1313
1314                this.setPositionData();
1315
1316                if (setSize) {
1317                    if (refresh) {
1318                        scrollTop = this.$menuInner[0].scrollTop;
1319                    } else if (!that.multiple) {
1320                        var element = that.$element[0],
1321                            selectedIndex = (element.options[element.selectedIndex] || {}).liIndex;
1322
1323                        if (typeof selectedIndex === 'number' && that.options.size !== false) {
1324                            var selectedData = that.selectpicker.main.data[selectedIndex],
1325                                position = selectedData && selectedData.position;
1326
1327                            if (position) {
1328                                scrollTop = position - ((that.sizeInfo.menuInnerHeight + that.sizeInfo.liHeight) / 2);
1329                            }
1330                        }
1331                    }
1332                }
1333
1334                scroll(scrollTop, true);
1335
1336                this.$menuInner.off('scroll.createView').on('scroll.createView', function(e, updateValue) {
1337                    if (!that.noScroll) scroll(this.scrollTop, updateValue);
1338                    that.noScroll = false;
1339                });
1340
1341                function scroll(scrollTop, init) {
1342                    var size = that.selectpicker.current.elements.length,
1343                        chunks = [],
1344                        chunkSize,
1345                        chunkCount,
1346                        firstChunk,
1347                        lastChunk,
1348                        currentChunk,
1349                        prevPositions,
1350                        positionIsDifferent,
1351                        previousElements,
1352                        menuIsDifferent = true,
1353                        isVirtual = that.isVirtual();
1354
1355                    that.selectpicker.view.scrollTop = scrollTop;
1356
1357                    chunkSize = Math.ceil(that.sizeInfo.menuInnerHeight / that.sizeInfo.liHeight * 1.5); // number of options in a chunk
1358                    chunkCount = Math.round(size / chunkSize) || 1; // number of chunks
1359
1360                    for (var i = 0; i < chunkCount; i++) {
1361                        var endOfChunk = (i + 1) * chunkSize;
1362
1363                        if (i === chunkCount - 1) {
1364                            endOfChunk = size;
1365                        }
1366
1367                        chunks[i] = [
1368                            (i) * chunkSize + (!i ? 0 : 1),
1369                            endOfChunk
1370                        ];
1371
1372                        if (!size) break;
1373
1374                        if (currentChunk === undefined && scrollTop - 1 <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) {
1375                            currentChunk = i;
1376                        }
1377                    }
1378
1379                    if (currentChunk === undefined) currentChunk = 0;
1380
1381                    prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1];
1382
1383                    // always display previous, current, and next chunks
1384                    firstChunk = Math.max(0, currentChunk - 1);
1385                    lastChunk = Math.min(chunkCount - 1, currentChunk + 1);
1386
1387                    that.selectpicker.view.position0 = isVirtual === false ? 0 : (Math.max(0, chunks[firstChunk][0]) || 0);
1388                    that.selectpicker.view.position1 = isVirtual === false ? size : (Math.min(size, chunks[lastChunk][1]) || 0);
1389
1390                    positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1;
1391
1392                    if (that.activeIndex !== undefined) {
1393                        prevActive = that.selectpicker.main.elements[that.prevActiveIndex];
1394                        active = that.selectpicker.main.elements[that.activeIndex];
1395                        selected = that.selectpicker.main.elements[that.selectedIndex];
1396
1397                        if (init) {
1398                            if (that.activeIndex !== that.selectedIndex) {
1399                                that.defocusItem(active);
1400                            }
1401                            that.activeIndex = undefined;
1402                        }
1403
1404                        if (that.activeIndex && that.activeIndex !== that.selectedIndex) {
1405                            that.defocusItem(selected);
1406                        }
1407                    }
1408
1409                    if (that.prevActiveIndex !== undefined && that.prevActiveIndex !== that.activeIndex && that.prevActiveIndex !== that.selectedIndex) {
1410                        that.defocusItem(prevActive);
1411                    }
1412
1413                    if (init || positionIsDifferent) {
1414                        previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : [];
1415
1416                        if (isVirtual === false) {
1417                            that.selectpicker.view.visibleElements = that.selectpicker.current.elements;
1418                        } else {
1419                            that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1);
1420                        }
1421
1422                        that.setOptionStatus();
1423
1424                        // if searching, check to make sure the list has actually been updated before updating DOM
1425                        // this prevents unnecessary repaints
1426                        if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements);
1427
1428                        // if virtual scroll is disabled and not searching,
1429                        // menu should never need to be updated more than once
1430                        if ((init || isVirtual === true) && menuIsDifferent) {
1431                            var menuInner = that.$menuInner[0],
1432                                menuFragment = document.createDocumentFragment(),
1433                                emptyMenu = menuInner.firstChild.cloneNode(false),
1434                                marginTop,
1435                                marginBottom,
1436                                elements = that.selectpicker.view.visibleElements,
1437                                toSanitize = [];
1438
1439                            // replace the existing UL with an empty one - this is faster than $.empty()
1440                            menuInner.replaceChild(emptyMenu, menuInner.firstChild);
1441
1442                            for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) {
1443                                var element = elements[i],
1444                                    elText,
1445                                    elementData;
1446
1447                                if (that.options.sanitize) {
1448                                    elText = element.lastChild;
1449
1450                                    if (elText) {
1451                                        elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0];
1452
1453                                        if (elementData && elementData.content && !elementData.sanitized) {
1454                                            toSanitize.push(elText);
1455                                            elementData.sanitized = true;
1456                                        }
1457                                    }
1458                                }
1459
1460                                menuFragment.appendChild(element);
1461                            }
1462
1463                            if (that.options.sanitize && toSanitize.length) {
1464                                sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn);
1465                            }
1466
1467                            if (isVirtual === true) {
1468                                marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position);
1469                                marginBottom = (that.selectpicker.view.position1 > size - 1 ? 0 : that.selectpicker.current.data[size - 1].position - that.selectpicker.current.data[that.selectpicker.view.position1 - 1].position);
1470
1471                                menuInner.firstChild.style.marginTop = marginTop + 'px';
1472                                menuInner.firstChild.style.marginBottom = marginBottom + 'px';
1473                            } else {
1474                                menuInner.firstChild.style.marginTop = 0;
1475                                menuInner.firstChild.style.marginBottom = 0;
1476                            }
1477
1478                            menuInner.firstChild.appendChild(menuFragment);
1479
1480                            // if an option is encountered that is wider than the current menu width, update the menu width accordingly
1481                            // switch to ResizeObserver with increased browser support
1482                            if (isVirtual === true && that.sizeInfo.hasScrollBar) {
1483                                var menuInnerInnerWidth = menuInner.firstChild.offsetWidth;
1484
1485                                if (init && menuInnerInnerWidth < that.sizeInfo.menuInnerInnerWidth && that.sizeInfo.totalMenuWidth > that.sizeInfo.selectWidth) {
1486                                    menuInner.firstChild.style.minWidth = that.sizeInfo.menuInnerInnerWidth + 'px';
1487                                } else if (menuInnerInnerWidth > that.sizeInfo.menuInnerInnerWidth) {
1488                                    // set to 0 to get actual width of menu
1489                                    that.$menu[0].style.minWidth = 0;
1490
1491                                    var actualMenuWidth = menuInner.firstChild.offsetWidth;
1492
1493                                    if (actualMenuWidth > that.sizeInfo.menuInnerInnerWidth) {
1494                                        that.sizeInfo.menuInnerInnerWidth = actualMenuWidth;
1495                                        menuInner.firstChild.style.minWidth = that.sizeInfo.menuInnerInnerWidth + 'px';
1496                                    }
1497
1498                                    // reset to default CSS styling
1499                                    that.$menu[0].style.minWidth = '';
1500                                }
1501                            }
1502                        }
1503                    }
1504
1505                    that.prevActiveIndex = that.activeIndex;
1506
1507                    if (!that.options.liveSearch) {
1508                        that.$menuInner.trigger('focus');
1509                    } else if (isSearching && init) {
1510                        var index = 0,
1511                            newActive;
1512
1513                        if (!that.selectpicker.view.canHighlight[index]) {
1514                            index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true);
1515                        }
1516
1517                        newActive = that.selectpicker.view.visibleElements[index];
1518
1519                        that.defocusItem(that.selectpicker.view.currentActive);
1520
1521                        that.activeIndex = (that.selectpicker.current.data[index] || {}).index;
1522
1523                        that.focusItem(newActive);
1524                    }
1525                }
1526
1527                $(window)
1528                    .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView')
1529                    .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function() {
1530                        var isActive = that.$newElement.hasClass(classNames.SHOW);
1531
1532                        if (isActive) scroll(that.$menuInner[0].scrollTop);
1533                    });
1534            },
1535
1536            focusItem: function(li, liData, noStyle) {
1537                if (li) {
1538                    liData = liData || this.selectpicker.main.data[this.activeIndex];
1539                    var a = li.firstChild;
1540
1541                    if (a) {
1542                        a.setAttribute('aria-setsize', this.selectpicker.view.size);
1543                        a.setAttribute('aria-posinset', liData.posinset);
1544
1545                        if (noStyle !== true) {
1546                            this.focusedParent.setAttribute('aria-activedescendant', a.id);
1547                            li.classList.add('active');
1548                            a.classList.add('active');
1549                        }
1550                    }
1551                }
1552            },
1553
1554            defocusItem: function(li) {
1555                if (li) {
1556                    li.classList.remove('active');
1557                    if (li.firstChild) li.firstChild.classList.remove('active');
1558                }
1559            },
1560
1561            setPlaceholder: function() {
1562                var updateIndex = false;
1563
1564                if (this.options.title && !this.multiple) {
1565                    if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option');
1566
1567                    // this option doesn't create a new <li> element, but does add a new option at the start,
1568                    // so startIndex should increase to prevent having to check every option for the bs-title-option class
1569                    updateIndex = true;
1570
1571                    var element = this.$element[0],
1572                        isSelected = false,
1573                        titleNotAppended = !this.selectpicker.view.titleOption.parentNode;
1574
1575                    if (titleNotAppended) {
1576                        // Use native JS to prepend option (faster)
1577                        this.selectpicker.view.titleOption.className = 'bs-title-option';
1578                        this.selectpicker.view.titleOption.value = '';
1579
1580                        // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option.
1581                        // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs,
1582                        // if so, the select will have the data-selected attribute
1583                        var $opt = $(element.options[element.selectedIndex]);
1584                        isSelected = $opt.attr('selected') === undefined && this.$element.data('selected') === undefined;
1585                    }
1586
1587                    if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) {
1588                        element.insertBefore(this.selectpicker.view.titleOption, element.firstChild);
1589                    }
1590
1591                    // Set selected *after* appending to select,
1592                    // otherwise the option doesn't get selected in IE
1593                    // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11
1594                    if (isSelected) element.selectedIndex = 0;
1595                }
1596
1597                return updateIndex;
1598            },
1599
1600            buildData: function() {
1601                var optionSelector = ':not([hidden]):not([data-hidden="true"])',
1602                    mainData = [],
1603                    optID = 0,
1604                    startIndex = this.setPlaceholder() ? 1 : 0; // append the titleOption if necessary and skip the first option in the loop
1605
1606                if (this.options.hideDisabled) optionSelector += ':not(:disabled)';
1607
1608                var selectOptions = this.$element[0].querySelectorAll('select > *' + optionSelector);
1609
1610                function addDivider(config) {
1611                    var previousData = mainData[mainData.length - 1];
1612
1613                    // ensure optgroup doesn't create back-to-back dividers
1614                    if (
1615                        previousData &&
1616                        previousData.type === 'divider' &&
1617                        (previousData.optID || config.optID)
1618                    ) {
1619                        return;
1620                    }
1621
1622                    config = config || {};
1623                    config.type = 'divider';
1624
1625                    mainData.push(config);
1626                }
1627
1628                function addOption(option, config) {
1629                    config = config || {};
1630
1631                    config.divider = option.getAttribute('data-divider') === 'true';
1632
1633                    if (config.divider) {
1634                        addDivider({
1635                            optID: config.optID
1636                        });
1637                    } else {
1638                        var liIndex = mainData.length,
1639                            cssText = option.style.cssText,
1640                            inlineStyle = cssText ? htmlEscape(cssText) : '',
1641                            optionClass = (option.className || '') + (config.optgroupClass || '');
1642
1643                        if (config.optID) optionClass = 'opt ' + optionClass;
1644
1645                        config.optionClass = optionClass.trim();
1646                        config.inlineStyle = inlineStyle;
1647                        config.text = option.textContent;
1648
1649                        config.content = option.getAttribute('data-content');
1650                        config.tokens = option.getAttribute('data-tokens');
1651                        config.subtext = option.getAttribute('data-subtext');
1652                        config.icon = option.getAttribute('data-icon');
1653
1654                        option.liIndex = liIndex;
1655
1656                        config.display = config.content || config.text;
1657                        config.type = 'option';
1658                        config.index = liIndex;
1659                        config.option = option;
1660                        config.selected = !!option.selected;
1661                        config.disabled = config.disabled || !!option.disabled;
1662
1663                        mainData.push(config);
1664                    }
1665                }
1666
1667                function addOptgroup(index, selectOptions) {
1668                    var optgroup = selectOptions[index],
1669                        previous = selectOptions[index - 1],
1670                        next = selectOptions[index + 1],
1671                        options = optgroup.querySelectorAll('option' + optionSelector);
1672
1673                    if (!options.length) return;
1674
1675                    var config = {
1676                            display: htmlEscape(optgroup.label),
1677                            subtext: optgroup.getAttribute('data-subtext'),
1678                            icon: optgroup.getAttribute('data-icon'),
1679                            type: 'optgroup-label',
1680                            optgroupClass: ' ' + (optgroup.className || '')
1681                        },
1682                        headerIndex,
1683                        lastIndex;
1684
1685                    optID++;
1686
1687                    if (previous) {
1688                        addDivider({ optID: optID });
1689                    }
1690
1691                    config.optID = optID;
1692
1693                    mainData.push(config);
1694
1695                    for (var j = 0, len = options.length; j < len; j++) {
1696                        var option = options[j];
1697
1698                        if (j === 0) {
1699                            headerIndex = mainData.length - 1;
1700                            lastIndex = headerIndex + len;
1701                        }
1702
1703                        addOption(option, {
1704                            headerIndex: headerIndex,
1705                            lastIndex: lastIndex,
1706                            optID: config.optID,
1707                            optgroupClass: config.optgroupClass,
1708                            disabled: optgroup.disabled
1709                        });
1710                    }
1711
1712                    if (next) {
1713                        addDivider({ optID: optID });
1714                    }
1715                }
1716
1717                for (var len = selectOptions.length; startIndex < len; startIndex++) {
1718                    var item = selectOptions[startIndex];
1719
1720                    if (item.tagName !== 'OPTGROUP') {
1721                        addOption(item, {});
1722                    } else {
1723                        addOptgroup(startIndex, selectOptions);
1724                    }
1725                }
1726
1727                this.selectpicker.main.data = this.selectpicker.current.data = mainData;
1728            },
1729
1730            buildList: function() {
1731                var that = this,
1732                    selectData = this.selectpicker.main.data,
1733                    mainElements = [],
1734                    widestOptionLength = 0;
1735
1736                if ((that.options.showTick || that.multiple) && !elementTemplates.checkMark.parentNode) {
1737                    elementTemplates.checkMark.className = this.options.iconBase + ' ' + that.options.tickIcon + ' check-mark';
1738                    elementTemplates.a.appendChild(elementTemplates.checkMark);
1739                }
1740
1741                function buildElement(item) {
1742                    var liElement,
1743                        combinedLength = 0;
1744
1745                    switch (item.type) {
1746                        case 'divider':
1747                            liElement = generateOption.li(
1748                                false,
1749                                classNames.DIVIDER,
1750                                (item.optID ? item.optID + 'div' : undefined)
1751                            );
1752
1753                            break;
1754
1755                        case 'option':
1756                            liElement = generateOption.li(
1757                                generateOption.a(
1758                                    generateOption.text.call(that, item),
1759                                    item.optionClass,
1760                                    item.inlineStyle
1761                                ),
1762                                '',
1763                                item.optID
1764                            );
1765
1766                            if (liElement.firstChild) {
1767                                liElement.firstChild.id = that.selectId + '-' + item.index;
1768                            }
1769
1770                            break;
1771
1772                        case 'optgroup-label':
1773                            liElement = generateOption.li(
1774                                generateOption.label.call(that, item),
1775                                'dropdown-header' + item.optgroupClass,
1776                                item.optID
1777                            );
1778
1779                            break;
1780                    }
1781
1782                    mainElements.push(liElement);
1783
1784                    // count the number of characters in the option - not perfect, but should work in most cases
1785                    if (item.display) combinedLength += item.display.length;
1786                    if (item.subtext) combinedLength += item.subtext.length;
1787                    // if there is an icon, ensure this option's width is checked
1788                    if (item.icon) combinedLength += 1;
1789
1790                    if (combinedLength > widestOptionLength) {
1791                        widestOptionLength = combinedLength;
1792
1793                        // guess which option is the widest
1794                        // use this when calculating menu width
1795                        // not perfect, but it's fast, and the width will be updating accordingly when scrolling
1796                        that.selectpicker.view.widestOption = mainElements[mainElements.length - 1];
1797                    }
1798                }
1799
1800                for (var len = selectData.length, i = 0; i < len; i++) {
1801                    var item = selectData[i];
1802
1803                    buildElement(item);
1804                }
1805
1806                this.selectpicker.main.elements = this.selectpicker.current.elements = mainElements;
1807            },
1808
1809            findLis: function() {
1810                return this.$menuInner.find('.inner > li');
1811            },
1812
1813            render: function() {
1814                var that = this,
1815                    element = this.$element[0],
1816                    // ensure titleOption is appended and selected (if necessary) before getting selectedOptions
1817                    placeholderSelected = this.setPlaceholder() && element.selectedIndex === 0,
1818                    selectedOptions = getSelectedOptions(element, this.options.hideDisabled),
1819                    selectedCount = selectedOptions.length,
1820                    button = this.$button[0],
1821                    buttonInner = button.querySelector('.filter-option-inner-inner'),
1822                    multipleSeparator = document.createTextNode(this.options.multipleSeparator),
1823                    titleFragment = elementTemplates.fragment.cloneNode(false),
1824                    showCount,
1825                    countMax,
1826                    hasContent = false;
1827
1828                button.classList.toggle('bs-placeholder', that.multiple ? !selectedCount : !getSelectValues(element, selectedOptions));
1829
1830                this.tabIndex();
1831
1832                if (this.options.selectedTextFormat === 'static') {
1833                    titleFragment = generateOption.text.call(this, { text: this.options.title }, true);
1834                } else {
1835                    showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 1;
1836
1837                    // determine if the number of selected options will be shown (showCount === true)
1838                    if (showCount) {
1839                        countMax = this.options.selectedTextFormat.split('>');
1840                        showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2);
1841                    }
1842
1843                    // only loop through all selected options if the count won't be shown
1844                    if (showCount === false) {
1845                        if (!placeholderSelected) {
1846                            for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) {
1847                                if (selectedIndex < 50) {
1848                                    var option = selectedOptions[selectedIndex],
1849                                        thisData = this.selectpicker.main.data[option.liIndex],
1850                                        titleOptions = {};
1851
1852                                    if (this.multiple && selectedIndex > 0) {
1853                                        titleFragment.appendChild(multipleSeparator.cloneNode(false));
1854                                    }
1855
1856                                    if (option.title) {
1857                                        titleOptions.text = option.title;
1858                                    } else if (thisData) {
1859                                        if (thisData.content && that.options.showContent) {
1860                                            titleOptions.content = thisData.content.toString();
1861                                            hasContent = true;
1862                                        } else {
1863                                            if (that.options.showIcon) {
1864                                                titleOptions.icon = thisData.icon;
1865                                            }
1866                                            if (that.options.showSubtext && !that.multiple && thisData.subtext) titleOptions.subtext = ' ' + thisData.subtext;
1867                                            titleOptions.text = option.textContent.trim();
1868                                        }
1869                                    }
1870
1871                                    titleFragment.appendChild(generateOption.text.call(this, titleOptions, true));
1872                                } else {
1873                                    break;
1874                                }
1875                            }
1876
1877                            // add ellipsis
1878                            if (selectedCount > 49) {
1879                                titleFragment.appendChild(document.createTextNode('...'));
1880                            }
1881                        }
1882                    } else {
1883                        var optionSelector = ':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])';
1884                        if (this.options.hideDisabled) optionSelector += ':not(:disabled)';
1885
1886                        // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc.
1887                        var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length,
1888                            tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText;
1889
1890                        titleFragment = generateOption.text.call(this, {
1891                            text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString())
1892                        }, true);
1893                    }
1894                }
1895
1896                if (this.options.title == undefined) {
1897                    // use .attr to ensure undefined is returned if title attribute is not set
1898                    this.options.title = this.$element.attr('title');
1899                }
1900
1901                // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText
1902                if (!titleFragment.childNodes.length) {
1903                    titleFragment = generateOption.text.call(this, {
1904                        text: typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText
1905                    }, true);
1906                }
1907
1908                // strip all HTML tags and trim the result, then unescape any escaped tags
1909                button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim();
1910
1911                if (this.options.sanitize && hasContent) {
1912                    sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn);
1913                }
1914
1915                buttonInner.innerHTML = '';
1916                buttonInner.appendChild(titleFragment);
1917
1918                if (version.major < 4 && this.$newElement[0].classList.contains('bs3-has-addon')) {
1919                    var filterExpand = button.querySelector('.filter-expand'),
1920                        clone = buttonInner.cloneNode(true);
1921
1922                    clone.className = 'filter-expand';
1923
1924                    if (filterExpand) {
1925                        button.replaceChild(clone, filterExpand);
1926                    } else {
1927                        button.appendChild(clone);
1928                    }
1929                }
1930
1931                this.$element.trigger('rendered' + EVENT_KEY);
1932            },
1933
1934            /**
1935             * @param [style]
1936             * @param [status]
1937             */
1938            setStyle: function(newStyle, status) {
1939                var button = this.$button[0],
1940                    newElement = this.$newElement[0],
1941                    style = this.options.style.trim(),
1942                    buttonClass;
1943
1944                if (this.$element.attr('class')) {
1945                    this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, ''));
1946                }
1947
1948                if (version.major < 4) {
1949                    newElement.classList.add('bs3');
1950
1951                    if (newElement.parentNode.classList.contains('input-group') &&
1952                        (newElement.previousElementSibling || newElement.nextElementSibling) &&
1953                        (newElement.previousElementSibling || newElement.nextElementSibling).classList.contains('input-group-addon')
1954                    ) {
1955                        newElement.classList.add('bs3-has-addon');
1956                    }
1957                }
1958
1959                if (newStyle) {
1960                    buttonClass = newStyle.trim();
1961                } else {
1962                    buttonClass = style;
1963                }
1964
1965                if (status == 'add') {
1966                    if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' '));
1967                } else if (status == 'remove') {
1968                    if (buttonClass) button.classList.remove.apply(button.classList, buttonClass.split(' '));
1969                } else {
1970                    if (style) button.classList.remove.apply(button.classList, style.split(' '));
1971                    if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' '));
1972                }
1973            },
1974
1975            liHeight: function(refresh) {
1976                if (!refresh && (this.options.size === false || Object.keys(this.sizeInfo).length)) return;
1977
1978                var newElement = document.createElement('div'),
1979                    menu = document.createElement('div'),
1980                    menuInner = document.createElement('div'),
1981                    menuInnerInner = document.createElement('ul'),
1982                    divider = document.createElement('li'),
1983                    dropdownHeader = document.createElement('li'),
1984                    li = document.createElement('li'),
1985                    a = document.createElement('a'),
1986                    text = document.createElement('span'),
1987                    header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null,
1988                    search = this.options.liveSearch ? document.createElement('div') : null,
1989                    actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null,
1990                    doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null,
1991                    firstOption = this.$element.find('option')[0];
1992
1993                this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth;
1994
1995                text.className = 'text';
1996                a.className = 'dropdown-item ' + (firstOption ? firstOption.className : '');
1997                newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW;
1998                newElement.style.width = 0; // ensure button width doesn't affect natural width of menu when calculating
1999                if (this.options.width === 'auto') menu.style.minWidth = 0;
2000                menu.className = classNames.MENU + ' ' + classNames.SHOW;
2001                menuInner.className = 'inner ' + classNames.SHOW;
2002                menuInnerInner.className = classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : '');
2003                divider.className = classNames.DIVIDER;
2004                dropdownHeader.className = 'dropdown-header';
2005
2006                text.appendChild(document.createTextNode('\u200b'));
2007                a.appendChild(text);
2008                li.appendChild(a);
2009                dropdownHeader.appendChild(text.cloneNode(true));
2010
2011                if (this.selectpicker.view.widestOption) {
2012                    menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true));
2013                }
2014
2015                menuInnerInner.appendChild(li);
2016                menuInnerInner.appendChild(divider);
2017                menuInnerInner.appendChild(dropdownHeader);
2018                if (header) menu.appendChild(header);
2019                if (search) {
2020                    var input = document.createElement('input');
2021                    search.className = 'bs-searchbox';
2022                    input.className = 'form-control';
2023                    search.appendChild(input);
2024                    menu.appendChild(search);
2025                }
2026                if (actions) menu.appendChild(actions);
2027                menuInner.appendChild(menuInnerInner);
2028                menu.appendChild(menuInner);
2029                if (doneButton) menu.appendChild(doneButton);
2030                newElement.appendChild(menu);
2031
2032                document.body.appendChild(newElement);
2033
2034                var liHeight = li.offsetHeight,
2035                    dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0,
2036                    headerHeight = header ? header.offsetHeight : 0,
2037                    searchHeight = search ? search.offsetHeight : 0,
2038                    actionsHeight = actions ? actions.offsetHeight : 0,
2039                    doneButtonHeight = doneButton ? doneButton.offsetHeight : 0,
2040                    dividerHeight = $(divider).outerHeight(true),
2041                    // fall back to jQuery if getComputedStyle is not supported
2042                    menuStyle = window.getComputedStyle ? window.getComputedStyle(menu) : false,
2043                    menuWidth = menu.offsetWidth,
2044                    $menu = menuStyle ? null : $(menu),
2045                    menuPadding = {
2046                        vert: toInteger(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) +
2047                            toInteger(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) +
2048                            toInteger(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) +
2049                            toInteger(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')),
2050                        horiz: toInteger(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) +
2051                            toInteger(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) +
2052                            toInteger(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) +
2053                            toInteger(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth'))
2054                    },
2055                    menuExtras = {
2056                        vert: menuPadding.vert +
2057                            toInteger(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) +
2058                            toInteger(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2,
2059                        horiz: menuPadding.horiz +
2060                            toInteger(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) +
2061                            toInteger(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2
2062                    },
2063                    scrollBarWidth;
2064
2065                menuInner.style.overflowY = 'scroll';
2066
2067                scrollBarWidth = menu.offsetWidth - menuWidth;
2068
2069                document.body.removeChild(newElement);
2070
2071                this.sizeInfo.liHeight = liHeight;
2072                this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight;
2073                this.sizeInfo.headerHeight = headerHeight;
2074                this.sizeInfo.searchHeight = searchHeight;
2075                this.sizeInfo.actionsHeight = actionsHeight;
2076                this.sizeInfo.doneButtonHeight = doneButtonHeight;
2077                this.sizeInfo.dividerHeight = dividerHeight;
2078                this.sizeInfo.menuPadding = menuPadding;
2079                this.sizeInfo.menuExtras = menuExtras;
2080                this.sizeInfo.menuWidth = menuWidth;
2081                this.sizeInfo.menuInnerInnerWidth = menuWidth - menuPadding.horiz;
2082                this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth;
2083                this.sizeInfo.scrollBarWidth = scrollBarWidth;
2084                this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight;
2085
2086                this.setPositionData();
2087            },
2088
2089            getSelectPosition: function() {
2090                var that = this,
2091                    $window = $(window),
2092                    pos = that.$newElement.offset(),
2093                    $container = $(that.options.container),
2094                    containerPos;
2095
2096                if (that.options.container && $container.length && !$container.is('body')) {
2097                    containerPos = $container.offset();
2098                    containerPos.top += parseInt($container.css('borderTopWidth'));
2099                    containerPos.left += parseInt($container.css('borderLeftWidth'));
2100                } else {
2101                    containerPos = { top: 0, left: 0 };
2102                }
2103
2104                var winPad = that.options.windowPadding;
2105
2106                this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop();
2107                this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2];
2108                this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft();
2109                this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1];
2110                this.sizeInfo.selectOffsetTop -= winPad[0];
2111                this.sizeInfo.selectOffsetLeft -= winPad[3];
2112            },
2113
2114            setMenuSize: function(isAuto) {
2115                this.getSelectPosition();
2116
2117                var selectWidth = this.sizeInfo.selectWidth,
2118                    liHeight = this.sizeInfo.liHeight,
2119                    headerHeight = this.sizeInfo.headerHeight,
2120                    searchHeight = this.sizeInfo.searchHeight,
2121                    actionsHeight = this.sizeInfo.actionsHeight,
2122                    doneButtonHeight = this.sizeInfo.doneButtonHeight,
2123                    divHeight = this.sizeInfo.dividerHeight,
2124                    menuPadding = this.sizeInfo.menuPadding,
2125                    menuInnerHeight,
2126                    menuHeight,
2127                    divLength = 0,
2128                    minHeight,
2129                    _minHeight,
2130                    maxHeight,
2131                    menuInnerMinHeight,
2132                    estimate,
2133                    isDropup;
2134
2135                if (this.options.dropupAuto) {
2136                    // Get the estimated height of the menu without scrollbars.
2137                    // This is useful for smaller menus, where there might be plenty of room
2138                    // below the button without setting dropup, but we can't know
2139                    // the exact height of the menu until createView is called later
2140                    estimate = liHeight * this.selectpicker.current.elements.length + menuPadding.vert;
2141
2142                    isDropup = this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot;
2143
2144                    // ensure dropup doesn't change while searching (so menu doesn't bounce back and forth)
2145                    if (this.selectpicker.isSearching === true) {
2146                        isDropup = this.selectpicker.dropup;
2147                    }
2148
2149                    this.$newElement.toggleClass(classNames.DROPUP, isDropup);
2150                    this.selectpicker.dropup = isDropup;
2151                }
2152
2153                if (this.options.size === 'auto') {
2154                    _minHeight = this.selectpicker.current.elements.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0;
2155                    menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert;
2156                    minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight;
2157                    menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0);
2158
2159                    if (this.$newElement.hasClass(classNames.DROPUP)) {
2160                        menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert;
2161                    }
2162
2163                    maxHeight = menuHeight;
2164                    menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert;
2165                } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) {
2166                    for (var i = 0; i < this.options.size; i++) {
2167                        if (this.selectpicker.current.data[i].type === 'divider') divLength++;
2168                    }
2169
2170                    menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert;
2171                    menuInnerHeight = menuHeight - menuPadding.vert;
2172                    maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight;
2173                    minHeight = menuInnerMinHeight = '';
2174                }
2175
2176                this.$menu.css({
2177                    'max-height': maxHeight + 'px',
2178                    'overflow': 'hidden',
2179                    'min-height': minHeight + 'px'
2180                });
2181
2182                this.$menuInner.css({
2183                    'max-height': menuInnerHeight + 'px',
2184                    'overflow-y': 'auto',
2185                    'min-height': menuInnerMinHeight + 'px'
2186                });
2187
2188                // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView
2189                this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1);
2190
2191                if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) {
2192                    this.sizeInfo.hasScrollBar = true;
2193                    this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth;
2194                }
2195
2196                if (this.options.dropdownAlignRight === 'auto') {
2197                    this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth));
2198                }
2199
2200                if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update();
2201            },
2202
2203            setSize: function(refresh) {
2204                this.liHeight(refresh);
2205
2206                if (this.options.header) this.$menu.css('padding-top', 0);
2207
2208                if (this.options.size !== false) {
2209                    var that = this,
2210                        $window = $(window);
2211
2212                    this.setMenuSize();
2213
2214                    if (this.options.liveSearch) {
2215                        this.$searchbox
2216                            .off('input.setMenuSize propertychange.setMenuSize')
2217                            .on('input.setMenuSize propertychange.setMenuSize', function() {
2218                                return that.setMenuSize();
2219                            });
2220                    }
2221
2222                    if (this.options.size === 'auto') {
2223                        $window
2224                            .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize')
2225                            .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function() {
2226                                return that.setMenuSize();
2227                            });
2228                    } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) {
2229                        $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize');
2230                    }
2231                }
2232
2233                this.createView(false, true, refresh);
2234            },
2235
2236            setWidth: function() {
2237                var that = this;
2238
2239                if (this.options.width === 'auto') {
2240                    requestAnimationFrame(function() {
2241                        that.$menu.css('min-width', '0');
2242
2243                        that.$element.on('loaded' + EVENT_KEY, function() {
2244                            that.liHeight();
2245                            that.setMenuSize();
2246
2247                            // Get correct width if element is hidden
2248                            var $selectClone = that.$newElement.clone().appendTo('body'),
2249                                btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth();
2250
2251                            $selectClone.remove();
2252
2253                            // Set width to whatever's larger, button title or longest option
2254                            that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth);
2255                            that.$newElement.css('width', that.sizeInfo.selectWidth + 'px');
2256                        });
2257                    });
2258                } else if (this.options.width === 'fit') {
2259                    // Remove inline min-width so width can be changed from 'auto'
2260                    this.$menu.css('min-width', '');
2261                    this.$newElement.css('width', '').addClass('fit-width');
2262                } else if (this.options.width) {
2263                    // Remove inline min-width so width can be changed from 'auto'
2264                    this.$menu.css('min-width', '');
2265                    this.$newElement.css('width', this.options.width);
2266                } else {
2267                    // Remove inline min-width/width so width can be changed
2268                    this.$menu.css('min-width', '');
2269                    this.$newElement.css('width', '');
2270                }
2271                // Remove fit-width class if width is changed programmatically
2272                if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') {
2273                    this.$newElement[0].classList.remove('fit-width');
2274                }
2275            },
2276
2277            selectPosition: function() {
2278                this.$bsContainer = $('<div class="bs-container" />');
2279
2280                var that = this,
2281                    $container = $(this.options.container),
2282                    pos,
2283                    containerPos,
2284                    actualHeight,
2285                    getPlacement = function($element) {
2286                        var containerPosition = {},
2287                            // fall back to dropdown's default display setting if display is not manually set
2288                            display = that.options.display || (
2289                                // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default
2290                                $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display :
2291                                false
2292                            );
2293
2294                        that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP));
2295                        pos = $element.offset();
2296
2297                        if (!$container.is('body')) {
2298                            containerPos = $container.offset();
2299                            containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop();
2300                            containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft();
2301                        } else {
2302                            containerPos = { top: 0, left: 0 };
2303                        }
2304
2305                        actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight;
2306
2307                        // Bootstrap 4+ uses Popper for menu positioning
2308                        if (version.major < 4 || display === 'static') {
2309                            containerPosition.top = pos.top - containerPos.top + actualHeight;
2310                            containerPosition.left = pos.left - containerPos.left;
2311                        }
2312
2313                        containerPosition.width = $element[0].offsetWidth;
2314
2315                        that.$bsContainer.css(containerPosition);
2316                    };
2317
2318                this.$button.on('click.bs.dropdown.data-api', function() {
2319                    if (that.isDisabled()) {
2320                        return;
2321                    }
2322
2323                    getPlacement(that.$newElement);
2324
2325                    that.$bsContainer
2326                        .appendTo(that.options.container)
2327                        .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW))
2328                        .append(that.$menu);
2329                });
2330
2331                $(window)
2332                    .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId)
2333                    .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function() {
2334                        var isActive = that.$newElement.hasClass(classNames.SHOW);
2335
2336                        if (isActive) getPlacement(that.$newElement);
2337                    });
2338
2339                this.$element.on('hide' + EVENT_KEY, function() {
2340                    that.$menu.data('height', that.$menu.height());
2341                    that.$bsContainer.detach();
2342                });
2343            },
2344
2345            setOptionStatus: function(selectedOnly) {
2346                var that = this;
2347
2348                that.noScroll = false;
2349
2350                if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) {
2351                    for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) {
2352                        var liData = that.selectpicker.current.data[i + that.selectpicker.view.position0],
2353                            option = liData.option;
2354
2355                        if (option) {
2356                            if (selectedOnly !== true) {
2357                                that.setDisabled(
2358                                    liData.index,
2359                                    liData.disabled
2360                                );
2361                            }
2362
2363                            that.setSelected(
2364                                liData.index,
2365                                option.selected
2366                            );
2367                        }
2368                    }
2369                }
2370            },
2371
2372            /**
2373             * @param {number} index - the index of the option that is being changed
2374             * @param {boolean} selected - true if the option is being selected, false if being deselected
2375             */
2376            setSelected: function(index, selected) {
2377                var li = this.selectpicker.main.elements[index],
2378                    liData = this.selectpicker.main.data[index],
2379                    activeIndexIsSet = this.activeIndex !== undefined,
2380                    thisIsActive = this.activeIndex === index,
2381                    prevActive,
2382                    a,
2383                    // if current option is already active
2384                    // OR
2385                    // if the current option is being selected, it's NOT multiple, and
2386                    // activeIndex is undefined:
2387                    //  - when the menu is first being opened, OR
2388                    //  - after a search has been performed, OR
2389                    //  - when retainActive is false when selecting a new option (i.e. index of the newly selected option is not the same as the current activeIndex)
2390                    keepActive = thisIsActive || (selected && !this.multiple && !activeIndexIsSet);
2391
2392                liData.selected = selected;
2393
2394                a = li.firstChild;
2395
2396                if (selected) {
2397                    this.selectedIndex = index;
2398                }
2399
2400                li.classList.toggle('selected', selected);
2401
2402                if (keepActive) {
2403                    this.focusItem(li, liData);
2404                    this.selectpicker.view.currentActive = li;
2405                    this.activeIndex = index;
2406                } else {
2407                    this.defocusItem(li);
2408                }
2409
2410                if (a) {
2411                    a.classList.toggle('selected', selected);
2412
2413                    if (selected) {
2414                        a.setAttribute('aria-selected', true);
2415                    } else {
2416                        if (this.multiple) {
2417                            a.setAttribute('aria-selected', false);
2418                        } else {
2419                            a.removeAttribute('aria-selected');
2420                        }
2421                    }
2422                }
2423
2424                if (!keepActive && !activeIndexIsSet && selected && this.prevActiveIndex !== undefined) {
2425                    prevActive = this.selectpicker.main.elements[this.prevActiveIndex];
2426
2427                    this.defocusItem(prevActive);
2428                }
2429            },
2430
2431            /**
2432             * @param {number} index - the index of the option that is being disabled
2433             * @param {boolean} disabled - true if the option is being disabled, false if being enabled
2434             */
2435            setDisabled: function(index, disabled) {
2436                var li = this.selectpicker.main.elements[index],
2437                    a;
2438
2439                this.selectpicker.main.data[index].disabled = disabled;
2440
2441                a = li.firstChild;
2442
2443                li.classList.toggle(classNames.DISABLED, disabled);
2444
2445                if (a) {
2446                    if (version.major === '4') a.classList.toggle(classNames.DISABLED, disabled);
2447
2448                    if (disabled) {
2449                        a.setAttribute('aria-disabled', disabled);
2450                        a.setAttribute('tabindex', -1);
2451                    } else {
2452                        a.removeAttribute('aria-disabled');
2453                        a.setAttribute('tabindex', 0);
2454                    }
2455                }
2456            },
2457
2458            isDisabled: function() {
2459                return this.$element[0].disabled;
2460            },
2461
2462            checkDisabled: function() {
2463                if (this.isDisabled()) {
2464                    this.$newElement[0].classList.add(classNames.DISABLED);
2465                    this.$button.addClass(classNames.DISABLED).attr('tabindex', -1).attr('aria-disabled', true);
2466                } else {
2467                    if (this.$button[0].classList.contains(classNames.DISABLED)) {
2468                        this.$newElement[0].classList.remove(classNames.DISABLED);
2469                        this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false);
2470                    }
2471
2472                    if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) {
2473                        this.$button.removeAttr('tabindex');
2474                    }
2475                }
2476            },
2477
2478            tabIndex: function() {
2479                if (this.$element.data('tabindex') !== this.$element.attr('tabindex') &&
2480                    (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) {
2481                    this.$element.data('tabindex', this.$element.attr('tabindex'));
2482                    this.$button.attr('tabindex', this.$element.data('tabindex'));
2483                }
2484
2485                this.$element.attr('tabindex', -98);
2486            },
2487
2488            clickListener: function() {
2489                var that = this,
2490                    $document = $(document);
2491
2492                $document.data('spaceSelect', false);
2493
2494                this.$button.on('keyup', function(e) {
2495                    if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) {
2496                        e.preventDefault();
2497                        $document.data('spaceSelect', false);
2498                    }
2499                });
2500
2501                this.$newElement.on('show.bs.dropdown', function() {
2502                    if (version.major > 3 && !that.dropdown) {
2503                        that.dropdown = that.$button.data('bs.dropdown');
2504                        that.dropdown._menu = that.$menu[0];
2505                    }
2506                });
2507
2508                this.$button.on('click.bs.dropdown.data-api', function() {
2509                    if (!that.$newElement.hasClass(classNames.SHOW)) {
2510                        that.setSize();
2511                    }
2512                });
2513
2514                function setFocus() {
2515                    if (that.options.liveSearch) {
2516                        that.$searchbox.trigger('focus');
2517                    } else {
2518                        that.$menuInner.trigger('focus');
2519                    }
2520                }
2521
2522                function checkPopperExists() {
2523                    if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state.isCreated) {
2524                        setFocus();
2525                    } else {
2526                        requestAnimationFrame(checkPopperExists);
2527                    }
2528                }
2529
2530                this.$element.on('shown' + EVENT_KEY, function() {
2531                    if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) {
2532                        that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop;
2533                    }
2534
2535                    if (version.major > 3) {
2536                        requestAnimationFrame(checkPopperExists);
2537                    } else {
2538                        setFocus();
2539                    }
2540                });
2541
2542                // ensure posinset and setsize are correct before selecting an option via a click
2543                this.$menuInner.on('mouseenter', 'li a', function(e) {
2544                    var hoverLi = this.parentElement,
2545                        position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0,
2546                        index = Array.prototype.indexOf.call(hoverLi.parentElement.children, hoverLi),
2547                        hoverData = that.selectpicker.current.data[index + position0];
2548
2549                    that.focusItem(hoverLi, hoverData, true);
2550                });
2551
2552                this.$menuInner.on('click', 'li a', function(e, retainActive) {
2553                    var $this = $(this),
2554                        element = that.$element[0],
2555                        position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0,
2556                        clickedData = that.selectpicker.current.data[$this.parent().index() + position0],
2557                        clickedIndex = clickedData.index,
2558                        prevValue = getSelectValues(element),
2559                        prevIndex = element.selectedIndex,
2560                        prevOption = element.options[prevIndex],
2561                        triggerChange = true;
2562
2563                    // Don't close on multi choice menu
2564                    if (that.multiple && that.options.maxOptions !== 1) {
2565                        e.stopPropagation();
2566                    }
2567
2568                    e.preventDefault();
2569
2570                    // Don't run if the select is disabled
2571                    if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) {
2572                        var option = clickedData.option,
2573                            $option = $(option),
2574                            state = option.selected,
2575                            $optgroup = $option.parent('optgroup'),
2576                            $optgroupOptions = $optgroup.find('option'),
2577                            maxOptions = that.options.maxOptions,
2578                            maxOptionsGrp = $optgroup.data('maxOptions') || false;
2579
2580                        if (clickedIndex === that.activeIndex) retainActive = true;
2581
2582                        if (!retainActive) {
2583                            that.prevActiveIndex = that.activeIndex;
2584                            that.activeIndex = undefined;
2585                        }
2586
2587                        if (!that.multiple) { // Deselect all others if not multi select box
2588                            if (prevOption) prevOption.selected = false;
2589                            option.selected = true;
2590                            that.setSelected(clickedIndex, true);
2591                        } else { // Toggle the one we have chosen if we are multi select.
2592                            option.selected = !state;
2593
2594                            that.setSelected(clickedIndex, !state);
2595                            $this.trigger('blur');
2596
2597                            if (maxOptions !== false || maxOptionsGrp !== false) {
2598                                var maxReached = maxOptions < getSelectedOptions(element).length,
2599                                    maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length;
2600
2601                                if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) {
2602                                    if (maxOptions && maxOptions == 1) {
2603                                        element.selectedIndex = -1;
2604                                        option.selected = true;
2605                                        that.setOptionStatus(true);
2606                                    } else if (maxOptionsGrp && maxOptionsGrp == 1) {
2607                                        for (var i = 0; i < $optgroupOptions.length; i++) {
2608                                            var _option = $optgroupOptions[i];
2609                                            _option.selected = false;
2610                                            that.setSelected(_option.liIndex, false);
2611                                        }
2612
2613                                        option.selected = true;
2614                                        that.setSelected(clickedIndex, true);
2615                                    } else {
2616                                        var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText,
2617                                            maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText,
2618                                            maxTxt = maxOptionsArr[0].replace('{n}', maxOptions),
2619                                            maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp),
2620                                            $notify = $('<div class="notify"></div>');
2621                                        // If {var} is set in array, replace it
2622                                        /** @deprecated */
2623                                        if (maxOptionsArr[2]) {
2624                                            maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]);
2625                                            maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]);
2626                                        }
2627
2628                                        option.selected = false;
2629
2630                                        that.$menu.append($notify);
2631
2632                                        if (maxOptions && maxReached) {
2633                                            $notify.append($('<div>' + maxTxt + '</div>'));
2634                                            triggerChange = false;
2635                                            that.$element.trigger('maxReached' + EVENT_KEY);
2636                                        }
2637
2638                                        if (maxOptionsGrp && maxReachedGrp) {
2639                                            $notify.append($('<div>' + maxTxtGrp + '</div>'));
2640                                            triggerChange = false;
2641                                            that.$element.trigger('maxReachedGrp' + EVENT_KEY);
2642                                        }
2643
2644                                        setTimeout(function() {
2645                                            that.setSelected(clickedIndex, false);
2646                                        }, 10);
2647
2648                                        $notify[0].classList.add('fadeOut');
2649
2650                                        setTimeout(function() {
2651                                            $notify.remove();
2652                                        }, 1050);
2653                                    }
2654                                }
2655                            }
2656                        }
2657
2658                        if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) {
2659                            that.$button.trigger('focus');
2660                        } else if (that.options.liveSearch) {
2661                            that.$searchbox.trigger('focus');
2662                        }
2663
2664                        // Trigger select 'change'
2665                        if (triggerChange) {
2666                            if (that.multiple || prevIndex !== element.selectedIndex) {
2667                                // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed.
2668                                changedArguments = [option.index, $option.prop('selected'), prevValue];
2669                                that.$element
2670                                    .triggerNative('change');
2671                            }
2672                        }
2673                    }
2674                });
2675
2676                this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function(e) {
2677                    if (e.currentTarget == this) {
2678                        e.preventDefault();
2679                        e.stopPropagation();
2680                        if (that.options.liveSearch && !$(e.target).hasClass('close')) {
2681                            that.$searchbox.trigger('focus');
2682                        } else {
2683                            that.$button.trigger('focus');
2684                        }
2685                    }
2686                });
2687
2688                this.$menuInner.on('click', '.divider, .dropdown-header', function(e) {
2689                    e.preventDefault();
2690                    e.stopPropagation();
2691                    if (that.options.liveSearch) {
2692                        that.$searchbox.trigger('focus');
2693                    } else {
2694                        that.$button.trigger('focus');
2695                    }
2696                });
2697
2698                this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function() {
2699                    that.$button.trigger('click');
2700                });
2701
2702                this.$searchbox.on('click', function(e) {
2703                    e.stopPropagation();
2704                });
2705
2706                this.$menu.on('click', '.actions-btn', function(e) {
2707                    if (that.options.liveSearch) {
2708                        that.$searchbox.trigger('focus');
2709                    } else {
2710                        that.$button.trigger('focus');
2711                    }
2712
2713                    e.preventDefault();
2714                    e.stopPropagation();
2715
2716                    if ($(this).hasClass('bs-select-all')) {
2717                        that.selectAll();
2718                    } else {
2719                        that.deselectAll();
2720                    }
2721                });
2722
2723                this.$element
2724                    .on('change' + EVENT_KEY, function() {
2725                        that.render();
2726                        that.$element.trigger('changed' + EVENT_KEY, changedArguments);
2727                        changedArguments = null;
2728                    })
2729                    .on('focus' + EVENT_KEY, function() {
2730                        if (!that.options.mobile) that.$button.trigger('focus');
2731                    });
2732            },
2733
2734            liveSearchListener: function() {
2735                var that = this,
2736                    noResults = document.createElement('li');
2737
2738                this.$button.on('click.bs.dropdown.data-api', function() {
2739                    if (!!that.$searchbox.val()) {
2740                        that.$searchbox.val('');
2741                    }
2742                });
2743
2744                this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function(e) {
2745                    e.stopPropagation();
2746                });
2747
2748                this.$searchbox.on('input propertychange', function() {
2749                    var searchValue = that.$searchbox.val();
2750
2751                    that.selectpicker.search.elements = [];
2752                    that.selectpicker.search.data = [];
2753
2754                    if (searchValue) {
2755                        var i,
2756                            searchMatch = [],
2757                            q = searchValue.toUpperCase(),
2758                            cache = {},
2759                            cacheArr = [],
2760                            searchStyle = that._searchStyle(),
2761                            normalizeSearch = that.options.liveSearchNormalize;
2762
2763                        if (normalizeSearch) q = normalizeToBase(q);
2764
2765                        for (var i = 0; i < that.selectpicker.main.data.length; i++) {
2766                            var li = that.selectpicker.main.data[i];
2767
2768                            if (!cache[i]) {
2769                                cache[i] = stringSearch(li, q, searchStyle, normalizeSearch);
2770                            }
2771
2772                            if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) {
2773                                if (li.headerIndex > 0) {
2774                                    cache[li.headerIndex - 1] = true;
2775                                    cacheArr.push(li.headerIndex - 1);
2776                                }
2777
2778                                cache[li.headerIndex] = true;
2779                                cacheArr.push(li.headerIndex);
2780
2781                                cache[li.lastIndex + 1] = true;
2782                            }
2783
2784                            if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i);
2785                        }
2786
2787                        for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) {
2788                            var index = cacheArr[i],
2789                                prevIndex = cacheArr[i - 1],
2790                                li = that.selectpicker.main.data[index],
2791                                liPrev = that.selectpicker.main.data[prevIndex];
2792
2793                            if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) {
2794                                that.selectpicker.search.data.push(li);
2795                                searchMatch.push(that.selectpicker.main.elements[index]);
2796                            }
2797                        }
2798
2799                        that.activeIndex = undefined;
2800                        that.noScroll = true;
2801                        that.$menuInner.scrollTop(0);
2802                        that.selectpicker.search.elements = searchMatch;
2803                        that.createView(true);
2804
2805                        if (!searchMatch.length) {
2806                            noResults.className = 'no-results';
2807                            noResults.innerHTML = that.options.noneResultsText.replace('{0}', '"' + htmlEscape(searchValue) + '"');
2808                            that.$menuInner[0].firstChild.appendChild(noResults);
2809                        }
2810                    } else {
2811                        that.$menuInner.scrollTop(0);
2812                        that.createView(false);
2813                    }
2814                });
2815            },
2816
2817            _searchStyle: function() {
2818                return this.options.liveSearchStyle || 'contains';
2819            },
2820
2821            val: function(value) {
2822                var element = this.$element[0];
2823
2824                if (typeof value !== 'undefined') {
2825                    var prevValue = getSelectValues(element);
2826
2827                    changedArguments = [null, null, prevValue];
2828
2829                    this.$element
2830                        .val(value)
2831                        .trigger('changed' + EVENT_KEY, changedArguments);
2832
2833                    if (this.$newElement.hasClass(classNames.SHOW)) {
2834                        if (this.multiple) {
2835                            this.setOptionStatus(true);
2836                        } else {
2837                            var liSelectedIndex = (element.options[element.selectedIndex] || {}).liIndex;
2838
2839                            if (typeof liSelectedIndex === 'number') {
2840                                this.setSelected(this.selectedIndex, false);
2841                                this.setSelected(liSelectedIndex, true);
2842                            }
2843                        }
2844                    }
2845
2846                    this.render();
2847
2848                    changedArguments = null;
2849
2850                    return this.$element;
2851                } else {
2852                    return this.$element.val();
2853                }
2854            },
2855
2856            changeAll: function(status) {
2857                if (!this.multiple) return;
2858                if (typeof status === 'undefined') status = true;
2859
2860                var element = this.$element[0],
2861                    previousSelected = 0,
2862                    currentSelected = 0,
2863                    prevValue = getSelectValues(element);
2864
2865                element.classList.add('bs-select-hidden');
2866
2867                for (var i = 0, data = this.selectpicker.current.data, len = data.length; i < len; i++) {
2868                    var liData = data[i],
2869                        option = liData.option;
2870
2871                    if (option && !liData.disabled && liData.type !== 'divider') {
2872                        if (liData.selected) previousSelected++;
2873                        option.selected = status;
2874                        if (status === true) currentSelected++;
2875                    }
2876                }
2877
2878                element.classList.remove('bs-select-hidden');
2879
2880                if (previousSelected === currentSelected) return;
2881
2882                this.setOptionStatus();
2883
2884                changedArguments = [null, null, prevValue];
2885
2886                this.$element
2887                    .triggerNative('change');
2888            },
2889
2890            selectAll: function() {
2891                return this.changeAll(true);
2892            },
2893
2894            deselectAll: function() {
2895                return this.changeAll(false);
2896            },
2897
2898            toggle: function(e) {
2899                e = e || window.event;
2900
2901                if (e) e.stopPropagation();
2902
2903                this.$button.trigger('click.bs.dropdown.data-api');
2904            },
2905
2906            keydown: function(e) {
2907                var $this = $(this),
2908                    isToggle = $this.hasClass('dropdown-toggle'),
2909                    $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU),
2910                    that = $parent.data('this'),
2911                    $items = that.findLis(),
2912                    index,
2913                    isActive,
2914                    liActive,
2915                    activeLi,
2916                    offset,
2917                    updateScroll = false,
2918                    downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab,
2919                    isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab,
2920                    scrollTop = that.$menuInner[0].scrollTop,
2921                    isVirtual = that.isVirtual(),
2922                    position0 = isVirtual === true ? that.selectpicker.view.position0 : 0;
2923
2924                // do nothing if a function key is pressed
2925                if (e.which >= 112 && e.which <= 123) return;
2926
2927                isActive = that.$newElement.hasClass(classNames.SHOW);
2928
2929                if (!isActive &&
2930                    (
2931                        isArrowKey ||
2932                        (e.which >= 48 && e.which <= 57) ||
2933                        (e.which >= 96 && e.which <= 105) ||
2934                        (e.which >= 65 && e.which <= 90)
2935                    )
2936                ) {
2937                    that.$button.trigger('click.bs.dropdown.data-api');
2938
2939                    if (that.options.liveSearch) {
2940                        that.$searchbox.trigger('focus');
2941                        return;
2942                    }
2943                }
2944
2945                if (e.which === keyCodes.ESCAPE && isActive) {
2946                    e.preventDefault();
2947                    that.$button.trigger('click.bs.dropdown.data-api').trigger('focus');
2948                }
2949
2950                if (isArrowKey) { // if up or down
2951                    if (!$items.length) return;
2952
2953                    liActive = that.selectpicker.main.elements[that.activeIndex];
2954                    index = liActive ? Array.prototype.indexOf.call(liActive.parentElement.children, liActive) : -1;
2955
2956                    if (index !== -1) {
2957                        that.defocusItem(liActive);
2958                    }
2959
2960                    if (e.which === keyCodes.ARROW_UP) { // up
2961                        if (index !== -1) index--;
2962                        if (index + position0 < 0) index += $items.length;
2963
2964                        if (!that.selectpicker.view.canHighlight[index + position0]) {
2965                            index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0;
2966                            if (index === -1) index = $items.length - 1;
2967                        }
2968                    } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down
2969                        index++;
2970                        if (index + position0 >= that.selectpicker.view.canHighlight.length) index = 0;
2971
2972                        if (!that.selectpicker.view.canHighlight[index + position0]) {
2973                            index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true);
2974                        }
2975                    }
2976
2977                    e.preventDefault();
2978
2979                    var liActiveIndex = position0 + index;
2980
2981                    if (e.which === keyCodes.ARROW_UP) { // up
2982                        // scroll to bottom and highlight last option
2983                        if (position0 === 0 && index === $items.length - 1) {
2984                            that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight;
2985
2986                            liActiveIndex = that.selectpicker.current.elements.length - 1;
2987                        } else {
2988                            activeLi = that.selectpicker.current.data[liActiveIndex];
2989                            offset = activeLi.position - activeLi.height;
2990
2991                            updateScroll = offset < scrollTop;
2992                        }
2993                    } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down
2994                        // scroll to top and highlight first option
2995                        if (index === 0) {
2996                            that.$menuInner[0].scrollTop = 0;
2997
2998                            liActiveIndex = 0;
2999                        } else {
3000                            activeLi = that.selectpicker.current.data[liActiveIndex];
3001                            offset = activeLi.position - that.sizeInfo.menuInnerHeight;
3002
3003                            updateScroll = offset > scrollTop;
3004                        }
3005                    }
3006
3007                    liActive = that.selectpicker.current.elements[liActiveIndex];
3008
3009                    that.activeIndex = that.selectpicker.current.data[liActiveIndex].index;
3010
3011                    that.focusItem(liActive);
3012
3013                    that.selectpicker.view.currentActive = liActive;
3014
3015                    if (updateScroll) that.$menuInner[0].scrollTop = offset;
3016
3017                    if (that.options.liveSearch) {
3018                        that.$searchbox.trigger('focus');
3019                    } else {
3020                        $this.trigger('focus');
3021                    }
3022                } else if (
3023                    (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) ||
3024                    (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory)
3025                ) {
3026                    var searchMatch,
3027                        matches = [],
3028                        keyHistory;
3029
3030                    e.preventDefault();
3031
3032                    that.selectpicker.keydown.keyHistory += keyCodeMap[e.which];
3033
3034                    if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel);
3035                    that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start();
3036
3037                    keyHistory = that.selectpicker.keydown.keyHistory;
3038
3039                    // if all letters are the same, set keyHistory to just the first character when searching
3040                    if (/^(.)\1+$/.test(keyHistory)) {
3041                        keyHistory = keyHistory.charAt(0);
3042                    }
3043
3044                    // find matches
3045                    for (var i = 0; i < that.selectpicker.current.data.length; i++) {
3046                        var li = that.selectpicker.current.data[i],
3047                            hasMatch;
3048
3049                        hasMatch = stringSearch(li, keyHistory, 'startsWith', true);
3050
3051                        if (hasMatch && that.selectpicker.view.canHighlight[i]) {
3052                            matches.push(li.index);
3053                        }
3054                    }
3055
3056                    if (matches.length) {
3057                        var matchIndex = 0;
3058
3059                        $items.removeClass('active').find('a').removeClass('active');
3060
3061                        // either only one key has been pressed or they are all the same key
3062                        if (keyHistory.length === 1) {
3063                            matchIndex = matches.indexOf(that.activeIndex);
3064
3065                            if (matchIndex === -1 || matchIndex === matches.length - 1) {
3066                                matchIndex = 0;
3067                            } else {
3068                                matchIndex++;
3069                            }
3070                        }
3071
3072                        searchMatch = matches[matchIndex];
3073
3074                        activeLi = that.selectpicker.main.data[searchMatch];
3075
3076                        if (scrollTop - activeLi.position > 0) {
3077                            offset = activeLi.position - activeLi.height;
3078                            updateScroll = true;
3079                        } else {
3080                            offset = activeLi.position - that.sizeInfo.menuInnerHeight;
3081                            // if the option is already visible at the current scroll position, just keep it the same
3082                            updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight;
3083                        }
3084
3085                        liActive = that.selectpicker.main.elements[searchMatch];
3086
3087                        that.activeIndex = matches[matchIndex];
3088
3089                        that.focusItem(liActive);
3090
3091                        if (liActive) liActive.firstChild.focus();
3092
3093                        if (updateScroll) that.$menuInner[0].scrollTop = offset;
3094
3095                        $this.trigger('focus');
3096                    }
3097                }
3098
3099                // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu.
3100                if (
3101                    isActive &&
3102                    (
3103                        (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) ||
3104                        e.which === keyCodes.ENTER ||
3105                        (e.which === keyCodes.TAB && that.options.selectOnTab)
3106                    )
3107                ) {
3108                    if (e.which !== keyCodes.SPACE) e.preventDefault();
3109
3110                    if (!that.options.liveSearch || e.which !== keyCodes.SPACE) {
3111                        that.$menuInner.find('.active a').trigger('click', true); // retain active class
3112                        $this.trigger('focus');
3113
3114                        if (!that.options.liveSearch) {
3115                            // Prevent screen from scrolling if the user hits the spacebar
3116                            e.preventDefault();
3117                            // Fixes spacebar selection of dropdown items in FF & IE
3118                            $(document).data('spaceSelect', true);
3119                        }
3120                    }
3121                }
3122            },
3123
3124            mobile: function() {
3125                this.$element[0].classList.add('mobile-device');
3126            },
3127
3128            refresh: function() {
3129                // update options if data attributes have been changed
3130                var config = $.extend({}, this.options, this.$element.data());
3131                this.options = config;
3132
3133                this.checkDisabled();
3134                this.setStyle();
3135                this.render();
3136                this.buildData();
3137                this.buildList();
3138                this.setWidth();
3139
3140                this.setSize(true);
3141
3142                this.$element.trigger('refreshed' + EVENT_KEY);
3143            },
3144
3145            hide: function() {
3146                this.$newElement.hide();
3147            },
3148
3149            show: function() {
3150                this.$newElement.show();
3151            },
3152
3153            remove: function() {
3154                this.$newElement.remove();
3155                this.$element.remove();
3156            },
3157
3158            destroy: function() {
3159                this.$newElement.before(this.$element).remove();
3160
3161                if (this.$bsContainer) {
3162                    this.$bsContainer.remove();
3163                } else {
3164                    this.$menu.remove();
3165                }
3166
3167                this.$element
3168                    .off(EVENT_KEY)
3169                    .removeData('selectpicker')
3170                    .removeClass('bs-select-hidden selectpicker');
3171
3172                $(window).off(EVENT_KEY + '.' + this.selectId);
3173            }
3174        };
3175
3176        // SELECTPICKER PLUGIN DEFINITION
3177        // ==============================
3178        function Plugin(option) {
3179            // get the args of the outer function..
3180            var args = arguments;
3181            // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them
3182            // to get lost/corrupted in android 2.3 and IE9 #715 #775
3183            var _option = option;
3184
3185            [].shift.apply(args);
3186
3187            // if the version was not set successfully
3188            if (!version.success) {
3189                // try to retreive it again
3190                try {
3191                    version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.');
3192                } catch (err) {
3193                    // fall back to use BootstrapVersion if set
3194                    if (Selectpicker.BootstrapVersion) {
3195                        version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.');
3196                    } else {
3197                        version.full = [version.major, '0', '0'];
3198
3199                        console.warn(
3200                            'There was an issue retrieving Bootstrap\'s version. ' +
3201                            'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' +
3202                            'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.',
3203                            err
3204                        );
3205                    }
3206                }
3207
3208                version.major = version.full[0];
3209                version.success = true;
3210            }
3211
3212            if (version.major === '4') {
3213                // some defaults need to be changed if using Bootstrap 4
3214                // check to see if they have already been manually changed before forcing them to update
3215                var toUpdate = [];
3216
3217                if (Selectpicker.DEFAULTS.style === classNames.BUTTONCLASS) toUpdate.push({ name: 'style', className: 'BUTTONCLASS' });
3218                if (Selectpicker.DEFAULTS.iconBase === classNames.ICONBASE) toUpdate.push({ name: 'iconBase', className: 'ICONBASE' });
3219                if (Selectpicker.DEFAULTS.tickIcon === classNames.TICKICON) toUpdate.push({ name: 'tickIcon', className: 'TICKICON' });
3220
3221                classNames.DIVIDER = 'dropdown-divider';
3222                classNames.SHOW = 'show';
3223                classNames.BUTTONCLASS = 'btn-light';
3224                classNames.POPOVERHEADER = 'popover-header';
3225                classNames.ICONBASE = '';
3226                classNames.TICKICON = 'bs-ok-default';
3227
3228                for (var i = 0; i < toUpdate.length; i++) {
3229                    var option = toUpdate[i];
3230                    Selectpicker.DEFAULTS[option.name] = classNames[option.className];
3231                }
3232            }
3233
3234            var value;
3235            var chain = this.each(function() {
3236                var $this = $(this);
3237                if ($this.is('select')) {
3238                    var data = $this.data('selectpicker'),
3239                        options = typeof _option == 'object' && _option;
3240
3241                    if (!data) {
3242                        var dataAttributes = $this.data();
3243
3244                        for (var dataAttr in dataAttributes) {
3245                            if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {
3246                                delete dataAttributes[dataAttr];
3247                            }
3248                        }
3249
3250                        var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, dataAttributes, options);
3251                        config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template);
3252                        $this.data('selectpicker', (data = new Selectpicker(this, config)));
3253                    } else if (options) {
3254                        for (var i in options) {
3255                            if (options.hasOwnProperty(i)) {
3256                                data.options[i] = options[i];
3257                            }
3258                        }
3259                    }
3260
3261                    if (typeof _option == 'string') {
3262                        if (data[_option] instanceof Function) {
3263                            value = data[_option].apply(data, args);
3264                        } else {
3265                            value = data.options[_option];
3266                        }
3267                    }
3268                }
3269            });
3270
3271            if (typeof value !== 'undefined') {
3272                // noinspection JSUnusedAssignment
3273                return value;
3274            } else {
3275                return chain;
3276            }
3277        }
3278
3279        var old = $.fn.selectpicker;
3280        $.fn.selectpicker = Plugin;
3281        $.fn.selectpicker.Constructor = Selectpicker;
3282
3283        // SELECTPICKER NO CONFLICT
3284        // ========================
3285        $.fn.selectpicker.noConflict = function() {
3286            $.fn.selectpicker = old;
3287            return this;
3288        };
3289
3290        // get Bootstrap's keydown event handler for either Bootstrap 4 or Bootstrap 3
3291        var bootstrapKeydown = $.fn.dropdown.Constructor._dataApiKeydownHandler || $.fn.dropdown.Constructor.prototype.keydown;
3292
3293        $(document)
3294            .off('keydown.bs.dropdown.data-api')
3295            .on('keydown.bs.dropdown.data-api', ':not(.bootstrap-select) > [data-toggle="dropdown"]', bootstrapKeydown)
3296            .on('keydown.bs.dropdown.data-api', ':not(.bootstrap-select) > .dropdown-menu', bootstrapKeydown)
3297            .on('keydown' + EVENT_KEY, '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown)
3298            .on('focusin.modal', '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', function(e) {
3299                e.stopPropagation();
3300            });
3301
3302        // SELECTPICKER DATA-API
3303        // =====================
3304        $(window).on('load' + EVENT_KEY + '.data-api', function() {
3305            $('.selectpicker').each(function() {
3306                var $selectpicker = $(this);
3307                Plugin.call($selectpicker, $selectpicker.data());
3308            })
3309        });
3310    })(jQuery);
3311
3312
3313}));
3314//# sourceMappingURL=bootstrap-select.js.map

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.