PageSourceSearch

https://wifidownunder.com/assets/js/plugins/bootstrap-selectpicker.js

js wifidownunder.com collected 2026-10-06 20:01:44 UTC 88,216 bytes, 1,914 lines download raw bytes

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

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.