bootstrap-select-rails 1.3.0.1 → 1.6.2

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