PageSourceSearch

https://www.jobenstock.fr/js/bootstrap-select.js

js jobenstock.fr collected 2026-10-09 23:14:30 UTC 59,787 bytes, 1,634 lines download raw bytes

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