PageSourceSearch

https://www.editme.com/bootstrap/js/bootstrap-select.min.js

js editme.com collected 2026-09-24 17:20:30 UTC 42,529 bytes, 1,129 lines download raw bytes

1(function ($) {
2  'use strict';
3
4  // Case insensitive search
5  $.expr[':'].icontains = function (obj, index, meta) {
6    return icontains($(obj).text(), meta[3]);
7  };
8
9  // Case and accent insensitive search
10  $.expr[':'].aicontains = function (obj, index, meta) {
11    return icontains($(obj).data('normalizedText') || $(obj).text(), meta[3]);
12  };
13
14  /**
15   * Actual implementation of the case insensitive search.
16   * @access private
17   * @param {String} haystack
18   * @param {String} needle
19   * @returns {boolean}
20   */
21  function icontains(haystack, needle) {
22    return haystack.toUpperCase().indexOf(needle.toUpperCase()) > -1;
23  }
24
25  /**
26   * Remove all diatrics from the given text.
27   * @access private
28   * @param {String} text
29   * @returns {String}
30   */
31  function normalizeToBase(text) {
32    var rExps = [
33      {re: /[\xC0-\xC6]/g, ch: "A"},
34      {re: /[\xE0-\xE6]/g, ch: "a"},
35      {re: /[\xC8-\xCB]/g, ch: "E"},
36      {re: /[\xE8-\xEB]/g, ch: "e"},
37      {re: /[\xCC-\xCF]/g, ch: "I"},
38      {re: /[\xEC-\xEF]/g, ch: "i"},
39      {re: /[\xD2-\xD6]/g, ch: "O"},
40      {re: /[\xF2-\xF6]/g, ch: "o"},
41      {re: /[\xD9-\xDC]/g, ch: "U"},
42      {re: /[\xF9-\xFC]/g, ch: "u"},
43      {re: /[\xC7-\xE7]/g, ch: "c"},
44      {re: /[\xD1]/g, ch: "N"},
45      {re: /[\xF1]/g, ch: "n"}
46    ];
47    $.each(rExps, function () {
48      text = text.replace(this.re, this.ch);
49    });
50    return text;
51  }
52
53  var Selectpicker = function (element, options, e) {
54    if (e) {
55      e.stopPropagation();
56      e.preventDefault();
57    }
58
59    this.$element = $(element);
60    this.$newElement = null;
61    this.$button = null;
62    this.$menu = null;
63    this.$lis = null;
64    this.options = options;
65
66    // 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
67    // data-attribute)
68    if (this.options.title === null) {
69      this.options.title = this.$element.attr('title');
70    }
71
72    //Expose public methods
73    this.val = Selectpicker.prototype.val;
74    this.render = Selectpicker.prototype.render;
75    this.refresh = Selectpicker.prototype.refresh;
76    this.setStyle = Selectpicker.prototype.setStyle;
77    this.selectAll = Selectpicker.prototype.selectAll;
78    this.deselectAll = Selectpicker.prototype.deselectAll;
79    this.destroy = Selectpicker.prototype.remove;
80    this.remove = Selectpicker.prototype.remove;
81    this.show = Selectpicker.prototype.show;
82    this.hide = Selectpicker.prototype.hide;
83
84    this.init();
85  };
86
87  Selectpicker.VERSION = '1.6.2';
88
89  // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both.
90  Selectpicker.DEFAULTS = {
91    noneSelectedText: 'Nothing selected',
92    noneResultsText: 'No results match',
93    countSelectedText: function (numSelected, numTotal) {
94      return (numSelected == 1) ? "{0} item selected" : "{0} items selected";
95    },
96    maxOptionsText: function (numAll, numGroup) {
97      var arr = [];
98
99      arr[0] = (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)';
100      arr[1] = (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)';
101
102      return arr;
103    },
104    selectAllText: 'Select All',
105    deselectAllText: 'Deselect All',
106    multipleSeparator: ', ',
107    style: 'btn-default',
108    size: 'auto',
109    title: null,
110    selectedTextFormat: 'values',
111    width: false,
112    container: false,
113    hideDisabled: false,
114    showSubtext: false,
115    showIcon: true,
116    showContent: true,
117    dropupAuto: true,
118    header: false,
119    liveSearch: false,
120    actionsBox: false,
121    iconBase: 'glyphicon',
122    tickIcon: 'glyphicon-ok',
123    maxOptions: false,
124    mobile: false,
125    selectOnTab: false,
126    dropdownAlignRight: false,
127    searchAccentInsensitive: false
128  };
129
130  Selectpicker.prototype = {
131
132    constructor: Selectpicker,
133
134    init: function () {
135      var that = this,
136          id = this.$element.attr('id');
137
138      this.$element.hide();
139      this.multiple = this.$element.prop('multiple');
140      this.autofocus = this.$element.prop('autofocus');
141      this.$newElement = this.createView();
142      this.$element.after(this.$newElement);
143      this.$menu = this.$newElement.find('> .dropdown-menu');
144      this.$button = this.$newElement.find('> button');
145      this.$searchbox = this.$newElement.find('input');
146
147      if (this.options.dropdownAlignRight)
148        this.$menu.addClass('dropdown-menu-right');
149
150      if (typeof id !== 'undefined') {
151        this.$button.attr('data-id', id);
152        $('label[for="' + id + '"]').click(function (e) {
153          e.preventDefault();
154          that.$button.focus();
155        });
156      }
157
158      this.checkDisabled();
159      this.clickListener();
160      if (this.options.liveSearch) this.liveSearchListener();
161      this.render();
162      this.liHeight();
163      this.setStyle();
164      this.setWidth();
165      if (this.options.container) this.selectPosition();
166      this.$menu.data('this', this);
167      this.$newElement.data('this', this);
168      if (this.options.mobile) this.mobile();
169    },
170
171    createDropdown: function () {
172      // Options
173      // If we are multiple, then add the show-tick class by default
174      var multiple = this.multiple ? ' show-tick' : '',
175          inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '',
176          autofocus = this.autofocus ? ' autofocus' : '',
177          btnSize = this.$element.parents().hasClass('form-group-lg') ? ' btn-lg' : (this.$element.parents().hasClass('form-group-sm') ? ' btn-sm' : '');
178      // Elements
179      var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">&times;</button>' + this.options.header + '</div>' : '';
180      var searchbox = this.options.liveSearch ? '<div class="bs-searchbox"><input type="text" class="input-block-level form-control" autocomplete="off" /></div>' : '';
181      var actionsbox = this.options.actionsBox ? '<div class="bs-actionsbox">' +
182          '<div class="btn-group btn-block">' +
183          '<button class="actions-btn bs-select-all btn btn-sm btn-default">' +
184          this.options.selectAllText +
185          '</button>' +
186          '<button class="actions-btn bs-deselect-all btn btn-sm btn-default">' +
187          this.options.deselectAllText +
188          '</button>' +
189          '</div>' +
190          '</div>' : '';
191      var drop =
192          '<div class="btn-group bootstrap-select' + multiple + inputGroup + '">' +
193              '<button type="button" class="btn dropdown-toggle selectpicker' + btnSize + '" data-toggle="dropdown"' + autofocus + '>' +
194              '<span class="filter-option pull-left"></span>&nbsp;' +
195              '<span class="caret"></span>' +
196              '</button>' +
197              '<div class="dropdown-menu open">' +
198              header +
199              searchbox +
200              actionsbox +
201              '<ul class="dropdown-menu inner selectpicker" role="menu">' +
202              '</ul>' +
203              '</div>' +
204              '</div>';
205
206      return $(drop);
207    },
208
209    createView: function () {
210      var $drop = this.createDropdown();
211      var $li = this.createLi();
212      $drop.find('ul').append($li);
213      return $drop;
214    },
215
216    reloadLi: function () {
217      //Remove all children.
218      this.destroyLi();
219      //Re build
220      var $li = this.createLi();
221      this.$menu.find('ul').append($li);
222    },
223
224    destroyLi: function () {
225      this.$menu.find('li').remove();
226    },
227
228    createLi: function () {
229      var that = this,
230          _li = [],
231          optID = 0;
232
233      // Helper functions
234      /**
235       * @param content
236       * @param [index]
237       * @param [classes]
238       * @returns {string}
239       */
240      var generateLI = function (content, index, classes) {
241        return '<li' +
242            (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
243            (typeof index !== 'undefined' | null === index ? ' data-original-index="' + index + '"' : '') +
244            '>' + content + '</li>';
245      };
246
247      /**
248       * @param text
249       * @param [classes]
250       * @param [inline]
251       * @param [optgroup]
252       * @returns {string}
253       */
254      var generateA = function (text, classes, inline, optgroup) {
255        var normText = normalizeToBase($.trim($("<div/>").html(text).text()).replace(/\s\s+/g, ' '));
256        return '<a tabindex="0"' +
257            (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') +
258            (typeof inline !== 'undefined' ? ' style="' + inline + '"' : '') +
259            (typeof optgroup !== 'undefined' ? 'data-optgroup="' + optgroup + '"' : '') +
260            ' data-normalized-text="' + normText + '"' +
261            '>
261' + text +
262            '<span class="' + that.options.iconBase + ' ' + that.options.tickIcon + ' check-mark"></span>' +
263            '</a>';
264      };
265
266      this.$element.find('option').each(function () {
267        var $this = $(this);
268
269        // Get the class and text for the option
270        var optionClass = $this.attr('class') || '',
271            inline = $this.attr('style'),
272            text = $this.data('content') ? $this.data('content') : $this.html(),
273            subtext = typeof $this.data('subtext') !== 'undefined' ? '<small class="muted text-muted">' + $this.data('subtext') + '</small>' : '',
274            icon = typeof $this.data('icon') !== 'undefined' ? '<span class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></span> ' : '',
275            isDisabled = $this.is(':disabled') || $this.parent().is(':disabled'),
276            index = $this[0].index;
277        if (icon !== '' && isDisabled) {
278          icon = '<span>' + icon + '</span>';
279        }
280
281        if (!$this.data('content')) {
282          // Prepend any icon and append any subtext to the main text.
283          text = icon + '<span class="text">' + text + subtext + '</span>';
284        }
285
286        if (that.options.hideDisabled && isDisabled) {
287          return;
288        }
289
290        if ($this.parent().is('optgroup') && $this.data('divider') !== true) {
291          if ($this.index() === 0) { // Is it the first option of the optgroup?
292            optID += 1;
293
294            // Get the opt group label
295            var label = $this.parent().attr('label');
296            var labelSubtext = typeof $this.parent().data('subtext') !== 'undefined' ? '<small class="muted text-muted">' + $this.parent().data('subtext') + '</small>' : '';
297            var labelIcon = $this.parent().data('icon') ? '<span class="' + that.options.iconBase + ' ' + $this.parent().data('icon') + '"></span> ' : '';
298            label = labelIcon + '<span class="text">' + label + labelSubtext + '</span>';
299
300            if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown?
301              _li.push(generateLI('', null, 'divider'));
302            }
303
304            _li.push(generateLI(label, null, 'dropdown-header'));
305          }
306
307          _li.push(generateLI(generateA(text, 'opt ' + optionClass, inline, optID), index));
308        } else if ($this.data('divider') === true) {
309          _li.push(generateLI('', index, 'divider'));
310        } else if ($this.data('hidden') === true) {
311          _li.push(generateLI(generateA(text, optionClass, inline), index, 'hide is-hidden'));
312        } else {
313          _li.push(generateLI(generateA(text, optionClass, inline), index));
314        }
315      });
316
317      //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
318      if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) {
319        this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected');
320      }
321
322      return $(_li.join(''));
323    },
324
325    findLis: function () {
326      if (this.$lis == null) this.$lis = this.$menu.find('li');
327      return this.$lis;
328    },
329
330    /**
331     * @param [updateLi] defaults to true
332     */
333    render: function (updateLi) {
334      var that = this;
335
336      //Update the LI to match the SELECT
337      if (updateLi !== false) {
338        this.$element.find('option').each(function (index) {
339          that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled'));
340          that.setSelected(index, $(this).is(':selected'));
341        });
342      }
343
344      this.tabIndex();
345      var notDisabled = this.options.hideDisabled ? ':not([disabled])' : '';
346      var selectedItems = this.$element.find('option:selected' + notDisabled).map(function () {
347        var $this = $(this);
348        var icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : '';
349        var subtext;
350        if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) {
351          subtext = ' <small class="muted text-muted">' + $this.data('subtext') + '</small>';
352        } else {
353          subtext = '';
354        }
355        if ($this.data('content') && that.options.showContent) {
356          return $this.data('content');
357        } else if (typeof $this.attr('title') !== 'undefined') {
358          return $this.attr('title');
359        } else {
360          return icon + $this.html() + subtext;
361        }
362      }).toArray();
363
364      //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled
365      //Convert all the values into a comma delimited string
366      var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator);
367
368      //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc..
369      if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) {
370        var max = this.options.selectedTextFormat.split('>');
371        if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) {
372          notDisabled = this.options.hideDisabled ? ', [disabled]' : '';
373          var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length,
374              tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText;
375          title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString());
376        }
377      }
378
379      this.options.title = this.$element.attr('title');
380
381      if (this.options.selectedTextFormat == 'static') {
382        title = this.options.title;
383      }
384
385      //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text
386      if (!title) {
387        title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText;
388      }
389
390      this.$button.attr('title', $.trim($("<div/>").html(title).text()).replace(/\s\s+/g, ' '));
391      this.$newElement.find('.filter-option').html(title);
392    },
393
394    /**
395     * @param [style]
396     * @param [status]
397     */
398    setStyle: function (style, status) {
399      if (this.$element.attr('class')) {
400        this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|validate\[.*\]/gi, ''));
401      }
402
403      var buttonClass = style ? style : this.options.style;
404
405      if (status == 'add') {
406        this.$button.addClass(buttonClass);
407      } else if (status == 'remove') {
408        this.$button.removeClass(buttonClass);
409      } else {
410        this.$button.removeClass(this.options.style);
411        this.$button.addClass(buttonClass);
412      }
413    },
414
415    liHeight: function () {
416      if (this.options.size === false) return;
417
418      var $selectClone = this.$menu.parent().clone().find('> .dropdown-toggle').prop('autofocus', false).end().appendTo('body'),
419          $menuClone = $selectClone.addClass('open').find('> .dropdown-menu'),
420          liHeight = $menuClone.find('li').not('.divider').not('.dropdown-header').filter(':visible').children('a').outerHeight(),
421          headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0,
422          searchHeight = this.options.liveSearch ? $menuClone.find('.bs-searchbox').outerHeight() : 0,
423          actionsHeight = this.options.actionsBox ? $menuClone.find('.bs-actionsbox').outerHeight() : 0;
424
425      $selectClone.remove();
426
427      this.$newElement
428          .data('liHeight', liHeight)
429          .data('headerHeight', headerHeight)
430          .data('searchHeight', searchHeight)
431          .data('actionsHeight', actionsHeight);
432    },
433
434    setSize: function () {
435      this.findLis();
436      var that = this,
437          menu = this.$menu,
438          menuInner = menu.find('.inner'),
439          selectHeight = this.$newElement.outerHeight(),
440          liHeight = this.$newElement.data('liHeight'),
441          headerHeight = this.$newElement.data('headerHeight'),
442          searchHeight = this.$newElement.data('searchHeight'),
443          actionsHeight = this.$newElement.data('actionsHeight'),
444          divHeight = this.$lis.filter('.divider').outerHeight(true),
445          menuPadding = parseInt(menu.css('padding-top')) +
446              parseInt(menu.css('padding-bottom')) +
447              parseInt(menu.css('border-top-width')) +
448              parseInt(menu.css('border-bottom-width')),
449          notDisabled = this.options.hideDisabled ? ', .disabled' : '',
450          $window = $(window),
451          menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2,
452          menuHeight,
453          selectOffsetTop,
454          selectOffsetBot,
455          posVert = function () {
456            // JQuery defines a scrollTop function, but in pure JS it's a property
457            //noinspection JSValidateTypes
458            selectOffsetTop = that.$newElement.offset().top - $window.scrollTop();
459            selectOffsetBot = $window.height() - selectOffsetTop - selectHeight;
460          };
461      posVert();
462      if (this.options.header) menu.css('padding-top', 0);
463
464      if (this.options.size == 'auto') {
465        var getSize = function () {
466          var minHeight,
467              lisVis = that.$lis.not('.hide');
468
469          posVert();
470          menuHeight = selectOffsetBot - menuExtras;
471
472          if (that.options.dropupAuto) {
473            that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height()));
474          }
475          if (that.$newElement.hasClass('dropup')) {
476            menuHeight = selectOffsetTop - menuExtras;
477          }
478
479          if ((lisVis.length + lisVis.filter('.dropdown-header').length) > 3) {
480            minHeight = liHeight * 3 + menuExtras - 2;
481          } else {
482            minHeight = 0;
483          }
484
485          menu.css({'max-height': menuHeight + 'px', 'overflow': 'hidden', 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + 'px'});
486          menuInner.css({'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', 'overflow-y': 'auto', 'min-height': Math.max(minHeight - menuPadding, 0) + 'px'});
487        };
488        getSize();
489        this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize);
490        $(window).off('resize.getSize').on('resize.getSize', getSize);
491        $(window).off('scroll.getSize').on('scroll.getSize', getSize);
492      } else if (this.options.size && this.options.size != 'auto' && menu.find('li' + notDisabled).length > this.options.size) {
493        var optIndex = this.$lis.not('.divider' + notDisabled).find(' > *').slice(0, this.options.size).last().parent().index();
494        var divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length;
495        menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding;
496        if (that.options.dropupAuto) {
497          //noinspection JSUnusedAssignment
498          this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height()));
499        }
500        menu.css({'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + 'px', 'overflow': 'hidden'});
501        menuInner.css({'max-height': menuHeight - menuPadding + 'px', 'overflow-y': 'auto'});
502      }
503    },
504
505    setWidth: function () {
506      if (this.options.width == 'auto') {
507        this.$menu.css('min-width', '0');
508
509        // Get correct width if element hidden
510        var selectClone = this.$newElement.clone().appendTo('body');
511        var ulWidth = selectClone.find('> .dropdown-menu').css('width');
512        var btnWidth = selectClone.css('width', 'auto').find('> button').css('width');
513        selectClone.remove();
514
515        // Set width to whatever's larger, button title or longest option
516        this.$newElement.css('width', Math.max(parseInt(ulWidth), parseInt(btnWidth)) + 'px');
517      } else if (this.options.width == 'fit') {
518        // Remove inline min-width so width can be changed from 'auto'
519        this.$menu.css('min-width', '');
520        this.$newElement.css('width', '').addClass('fit-width');
521      } else if (this.options.width) {
522        // Remove inline min-width so width can be changed from 'auto'
523        this.$menu.css('min-width', '');
524        this.$newElement.css('width', this.options.width);
525      } else {
526        // Remove inline min-width/width so width can be changed
527        this.$menu.css('min-width', '');
528        this.$newElement.css('width', '');
529      }
530      // Remove fit-width class if width is changed programmatically
531      if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') {
532        this.$newElement.removeClass('fit-width');
533      }
534    },
535
536    selectPosition: function () {
537      var that = this,
538          drop = '<div />',
539          $drop = $(drop),
540          pos,
541          actualHeight,
542          getPlacement = function ($element) {
543            $drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup'));
544            pos = $element.offset();
545            actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight;
546            $drop.css({'top': pos.top + actualHeight, 'left': pos.left, 'width': $element[0].offsetWidth, 'position': 'absolute'});
547          };
548      this.$newElement.on('click', function () {
549        if (that.isDisabled()) {
550          return;
551        }
552        getPlacement($(this));
553        $drop.appendTo(that.options.container);
554        $drop.toggleClass('open', !$(this).hasClass('open'));
555        $drop.append(that.$menu);
556      });
557      $(window).resize(function () {
558        getPlacement(that.$newElement);
559      });
560      $(window).on('scroll', function () {
561        getPlacement(that.$newElement);
562      });
563      $('html').on('click', function (e) {
564        if ($(e.target).closest(that.$newElement).length < 1) {
565          $drop.removeClass('open');
566        }
567      });
568    },
569
570    setSelected: function (index, selected) {
571      this.findLis();
572      this.$lis.filter('[data-original-index="' + index + '"]').toggleClass('selected', selected);
573    },
574
575    setDisabled: function (index, disabled) {
576      this.findLis();
577      if (disabled) {
578        this.$lis.filter('[data-original-index="' + index + '"]').addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1);
579      } else {
580        this.$lis.filter('[data-original-index="' + index + '"]').removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0);
581      }
582    },
583
584    isDisabled: function () {
585      return this.$element.is(':disabled');
586    },
587
588    checkDisabled: function () {
589      var that = this;
590
591      if (this.isDisabled()) {
592        this.$button.addClass('disabled').attr('tabindex', -1);
593      } else {
594        if (this.$button.hasClass('disabled')) {
595          this.$button.removeClass('disabled');
596        }
597
598        if (this.$button.attr('tabindex') == -1) {
599          if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex');
600        }
601      }
602
603      this.$button.click(function () {
604        return !that.isDisabled();
605      });
606    },
607
608    tabIndex: function () {
609      if (this.$element.is('[tabindex]')) {
610        this.$element.data('tabindex', this.$element.attr('tabindex'));
611        this.$button.attr('tabindex', this.$element.data('tabindex'));
612      }
613    },
614
615    clickListener: function () {
616      var that = this;
617
618      this.$newElement.on('touchstart.dropdown', '.dropdown-menu', function (e) {
619        e.stopPropagation();
620      });
621
622      this.$newElement.on('click', function () {
623        that.setSize();
624        if (!that.options.liveSearch && !that.multiple) {
625          setTimeout(function () {
626            that.$menu.find('.selected a').focus();
627          }, 10);
628        }
629      });
630
631      this.$menu.on('click', 'li a', function (e) {
632        var $this = $(this),
633            clickedIndex = $this.parent().data('originalIndex'),
634            prevValue = that.$element.val(),
635            prevIndex = that.$element.prop('selectedIndex');
636
637        // Don't close on multi choice menu
638        if (that.multiple) {
639          e.stopPropagation();
640        }
641
642        e.preventDefault();
643
644        //Don't run if we have been disabled
645        if (!that.isDisabled() && !$this.parent().hasClass('disabled')) {
646          var $options = that.$element.find('option'),
647              $option = $options.eq(clickedIndex),
648              state = $option.prop('selected'),
649              $optgroup = $option.parent('optgroup'),
650              maxOptions = that.options.maxOptions,
651              maxOptionsGrp = $optgroup.data('maxOptions') || false;
652
653          if (!that.multiple) { // Deselect all others if not multi select box
654            $options.prop('selected', false);
655            $option.prop('selected', true);
656            that.$menu.find('.selected').removeClass('selected');
657            that.setSelected(clickedIndex, true);
658          } else { // Toggle the one we have chosen if we are multi select.
659            $option.prop('selected', !state);
660            that.setSelected(clickedIndex, !state);
661            $this.blur();
662
663            if ((maxOptions !== false) || (maxOptionsGrp !== false)) {
664              var maxReached = maxOptions < $options.filter(':selected').length,
665                  maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length;
666
667              if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) {
668                if (maxOptions && maxOptions == 1) {
669                  $options.prop('selected', false);
670                  $option.prop('selected', true);
671                  that.$menu.find('.selected').removeClass('selected');
672                  that.setSelected(clickedIndex, true);
673                } else if (maxOptionsGrp && maxOptionsGrp == 1) {
674                  $optgroup.find('option:selected').prop('selected', false);
675                  $option.prop('selected', true);
676                  var optgroupID = $this.data('optgroup');
677
678                  that.$menu.find('.selected').has('a[data-optgroup="' + optgroupID + '"]').removeClass('selected');
679
680                  that.setSelected(clickedIndex, true);
681                } else {
682                  var maxOptionsArr = (typeof that.options.maxOptionsText === 'function') ?
683                          that.options.maxOptionsText(maxOptions, maxOptionsGrp) : that.options.maxOptionsText,
684                      maxTxt = maxOptionsArr[0].replace('{n}', maxOptions),
685                      maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp),
686                      $notify = $('<div class="notify"></div>');
687                  // If {var} is set in array, replace it
688                  /** @deprecated */
689                  if (maxOptionsArr[2]) {
690                    maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]);
691                    maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]);
692                  }
693
694                  $option.prop('selected', false);
695
696                  that.$menu.append($notify);
697
698                  if (maxOptions && maxReached) {
699                    $notify.append($('<div>' + maxTxt + '</div>'));
700                    that.$element.trigger('maxReached.bs.select');
701                  }
702
703                  if (maxOptionsGrp && maxReachedGrp) {
704                    $notify.append($('<div>' + maxTxtGrp + '</div>'));
705                    that.$element.trigger('maxReachedGrp.bs.select');
706                  }
707
708                  setTimeout(function () {
709                    that.setSelected(clickedIndex, false);
710                  }, 10);
711
712                  $notify.delay(750).fadeOut(300, function () {
713                    $(this).remove();
714                  });
715                }
716              }
717            }
718          }
719
720          if (!that.multiple) {
721            that.$button.focus();
722          } else if (that.options.liveSearch) {
723            that.$searchbox.focus();
724          }
725
726          // Trigger select 'change'
727          if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) {
728            that.$element.change();
729          }
730        }
731      });
732
733      this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) {
734        if (e.target == this) {
735          e.preventDefault();
736          e.stopPropagation();
737          if (!that.options.liveSearch) {
738            that.$button.focus();
739          } else {
740            that.$searchbox.focus();
741          }
742        }
743      });
744
745      this.$menu.on('click', 'li.divider, li.dropdown-header', function (e) {
746        e.preventDefault();
747        e.stopPropagation();
748        if (!that.options.liveSearch) {
749          that.$button.focus();
750        } else {
751          that.$searchbox.focus();
752        }
753      });
754
755      this.$menu.on('click', '.popover-title .close', function () {
756        that.$button.focus();
757      });
758
759      this.$searchbox.on('click', function (e) {
760        e.stopPropagation();
761      });
762
763
764      this.$menu.on('click', '.actions-btn', function (e) {
765        if (that.options.liveSearch) {
766          that.$searchbox.focus();
767        } else {
768          that.$button.focus();
769        }
770
771        e.preventDefault();
772        e.stopPropagation();
773
774        if ($(this).is('.bs-select-all')) {
775          that.selectAll();
776        } else {
777          that.deselectAll();
778        }
779        that.$element.change();
780      });
781
782      this.$element.change(function () {
783        that.render(false);
784      });
785    },
786
787    liveSearchListener: function () {
788      var that = this,
789          no_results = $('<li class="no-results"></li>');
790
791      this.$newElement.on('click.dropdown.data-api', function () {
792        that.$menu.find('.active').removeClass('active');
793        if (!!that.$searchbox.val()) {
794          that.$searchbox.val('');
795          that.$lis.not('.is-hidden').removeClass('hide');
796          if (!!no_results.parent().length) no_results.remove();
797        }
798        if (!that.multiple) that.$menu.find('.selected').addClass('active');
799        setTimeout(function () {
800          that.$searchbox.focus();
801        }, 10);
802      });
803
804      this.$searchbox.on('input propertychange', function () {
805        if (that.$searchbox.val()) {
806
807          if (that.options.searchAccentInsensitive) {
808            that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':aicontains(' + normalizeToBase(that.$searchbox.val()) + ')').parent().addClass('hide');
809          } else {
810            that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':icontains(' + that.$searchbox.val() + ')').parent().addClass('hide');
811          }
812
813          if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) {
814            if (!!no_results.parent().length) no_results.remove();
815            no_results.html(that.options.noneResultsText + ' "' + that.$searchbox.val() + '"').show();
816            that.$menu.find('li').last().after(no_results);
817          } else if (!!no_results.parent().length) {
818            no_results.remove();
819          }
820
821        } else {
822          that.$lis.not('.is-hidden').removeClass('hide');
823          if (!!no_results.parent().length) no_results.remove();
824        }
825
826        that.$menu.find('li.active').removeClass('active');
827        that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus();
828        $(this).focus();
829      });
830    },
831
832    val: function (value) {
833      if (typeof value !== 'undefined') {
834        this.$element.val(value);
835        this.render();
836
837        return this.$element;
838      } else {
839        return this.$element.val();
840      }
841    },
842
843    selectAll: function () {
844      this.findLis();
845      this.$lis.not('.divider').not('.disabled').not('.selected').filter(':visible').find('a').click();
846    },
847
848    deselectAll: function () {
849      this.findLis();
850      this.$lis.not('.divider').not('.disabled').filter('.selected').filter(':visible').find('a').click();
851    },
852
853    keydown: function (e) {
854      var $this = $(this),
855          $parent = ($this.is('input')) ? $this.parent().parent() : $this.parent(),
856          $items,
857          that = $parent.data('this'),
858          index,
859          next,
860          first,
861          last,
862          prev,
863          nextPrev,
864          prevIndex,
865          isActive,
866          keyCodeMap = {
867            32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 59: ';',
868            65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l',
869            77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x',
870            89: 'y', 90: 'z', 96: '0', 97: '1', 98: '2', 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9'
871          };
872
873      if (that.options.liveSearch) $parent = $this.parent().parent();
874
875      if (that.options.container) $parent = that.$menu;
876
877      $items = $('[role=menu] li a', $parent);
878
879      isActive = that.$menu.parent().hasClass('open');
880
881      if (!isActive && /([0-9]|[A-z])/.test(String.fromCharCode(e.keyCode))) {
882        if (!that.options.container) {
883          that.setSize();
884          that.$menu.parent().addClass('open');
885          isActive = true;
886        } else {
887          that.$newElement.trigger('click');
888        }
889        that.$searchbox.focus();
890      }
891
892      if (that.options.liveSearch) {
893        if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && that.$menu.find('.active').length === 0) {
894          e.preventDefault();
895          that.$menu.parent().removeClass('open');
896          that.$button.focus();
897        }
898        $items = $('[role=menu] li:not(.divider):not(.dropdown-header):visible', $parent);
899        if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) {
900          if ($items.filter('.active').length === 0) {
901            if (that.options.searchAccentInsensitive) {
902              $items = that.$newElement.find('li').filter(':aicontains(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')');
903            } else {
904              $items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')');
905            }
906          }
907        }
908      }
909
910      if (!$items.length) return;
911
912      if (/(38|40)/.test(e.keyCode.toString(10))) {
913        index = $items.index($items.filter(':focus'));
914        first = $items.parent(':not(.disabled):visible').first().index();
915        last = $items.parent(':not(.disabled):visible').last().index();
916        next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index();
917        prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index();
918        nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index();
919
920        if (that.options.liveSearch) {
921          $items.each(function (i) {
922            if ($(this).is(':not(.disabled)')) {
923              $(this).data('index', i);
924            }
925          });
926          index = $items.index($items.filter('.active'));
927          first = $items.filter(':not(.disabled):visible').first().data('index');
928          last = $items.filter(':not(.disabled):visible').last().data('index');
929          next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index');
930          prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index');
931          nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index');
932        }
933
934        prevIndex = $this.data('prevIndex');
935
936        if (e.keyCode == 38) {
937          if (that.options.liveSearch) index -= 1;
938          if (index != nextPrev && index > prev) index = prev;
939          if (index < first) index = first;
940          if (index == prevIndex) index = last;
941        }
942
943        if (e.keyCode == 40) {
944          if (that.options.liveSearch) index += 1;
945          if (index == -1) index = 0;
946          if (index != nextPrev && index < next) index = next;
947          if (index > last) index = last;
948          if (index == prevIndex) index = first;
949        }
950
951        $this.data('prevIndex', index);
952
953        if (!that.options.liveSearch) {
954          $items.eq(index).focus();
955        } else {
956          e.preventDefault();
957          if (!$this.is('.dropdown-toggle')) {
958            $items.removeClass('active');
959            $items.eq(index).addClass('active').find('a').focus();
960            $this.focus();
961          }
962        }
963
964      } else if (!$this.is('input')) {
965        var keyIndex = [],
966            count,
967            prevKey;
968
969        $items.each(function () {
970          if ($(this).parent().is(':not(.disabled)')) {
971            if ($.trim($(this).text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) {
972              keyIndex.push($(this).parent().index());
973            }
974          }
975        });
976
977        count = $(document).data('keycount');
978        count++;
979        $(document).data('keycount', count);
980
981        prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1);
982
983        if (prevKey != keyCodeMap[e.keyCode]) {
984          count = 1;
985          $(document).data('keycount', count);
986        } else if (count >= keyIndex.length) {
987          $(document).data('keycount', 0);
988          if (count > keyIndex.length) count = 1;
989        }
990
991        $items.eq(keyIndex[count - 1]).focus();
992      }
993
994      // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu.
995      if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) {
996        if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault();
997        if (!that.options.liveSearch) {
998          $(':focus').click();
999        } else if (!/(32)/.test(e.keyCode.toString(10))) {
1000          that.$menu.find('.active a').click();
1001          $this.focus();
1002        }
1003        $(document).data('keycount', 0);
1004      }
1005
1006      if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) {
1007        that.$menu.parent().removeClass('open');
1008        that.$button.focus();
1009      }
1010    },
1011
1012    mobile: function () {
1013      this.$element.addClass('mobile-device').appendTo(this.$newElement);
1014      if (this.options.container) this.$menu.hide();
1015    },
1016
1017    refresh: function () {
1018      this.$lis = null;
1019      this.reloadLi();
1020      this.render();
1021      this.setWidth();
1022      this.setStyle();
1023      this.checkDisabled();
1024      this.liHeight();
1025    },
1026
1027    update: function () {
1028      this.reloadLi();
1029      this.setWidth();
1030      this.setStyle();
1031      this.checkDisabled();
1032      this.liHeight();
1033    },
1034
1035    hide: function () {
1036      this.$newElement.hide();
1037    },
1038
1039    show: function () {
1040      this.$newElement.show();
1041    },
1042
1043    remove: function () {
1044      this.$newElement.remove();
1045      this.$element.remove();
1046    }
1047  };
1048
1049  // SELECTPICKER PLUGIN DEFINITION
1050  // ==============================
1051  function Plugin(option, event) {
1052    // get the args of the outer function..
1053    var args = arguments;
1054    // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them
1055    // to get lost
1056    //noinspection JSDuplicatedDeclaration
1057    var _option = option,
1058        option = args[0],
1059        event = args[1];
1060    [].shift.apply(args);
1061
1062    // This fixes a bug in the js implementation on android 2.3 #715
1063    if (typeof option == 'undefined') {
1064      option = _option;
1065    }
1066
1067    var value;
1068    var chain = this.each(function () {
1069      var $this = $(this);
1070      if ($this.is('select')) {
1071        var data = $this.data('selectpicker'),
1072            options = typeof option == 'object' && option;
1073
1074        if (!data) {
1075          var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options);
1076          $this.data('selectpicker', (data = new Selectpicker(this, config, event)));
1077        } else if (options) {
1078          for (var i in options) {
1079            if (options.hasOwnProperty(i)) {
1080              data.options[i] = options[i];
1081            }
1082          }
1083        }
1084
1085        if (typeof option == 'string') {
1086          if (data[option] instanceof Function) {
1087            value = data[option].apply(data, args);
1088          } else {
1089            value = data.options[option];
1090          }
1091        }
1092      }
1093    });
1094
1095    if (typeof value !== 'undefined') {
1096      //noinspection JSUnusedAssignment
1097      return value;
1098    } else {
1099      return chain;
1100    }
1101  }
1102
1103  var old = $.fn.selectpicker;
1104  $.fn.selectpicker = Plugin;
1105  $.fn.selectpicker.Constructor = Selectpicker;
1106
1107  // SELECTPICKER NO CONFLICT
1108  // ========================
1109  $.fn.selectpicker.noConflict = function () {
1110    $.fn.selectpicker = old;
1111    return this;
1112  };
1113
1114  $(document)
1115      .data('keycount', 0)
1116      .on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', Selectpicker.prototype.keydown)
1117      .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', function (e) {
1118        e.stopPropagation();
1119      });
1120
1121  // SELECTPICKER DATA-API
1122  // =====================
1123  $(window).on('load.bs.select.data-api', function () {
1124    $('.selectpicker').each(function () {
1125      var $selectpicker = $(this);
1126      Plugin.call($selectpicker, $selectpicker.data());
1127    })
1128  });
1129})(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.