PageSourceSearch

https://pasoschools.org/js/bootstrap-select.js

js pasoschools.org collected 2026-09-24 10:07:21 UTC 123,237 bytes, 3,481 lines download raw bytes

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

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.