PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.0
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.0
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | js/bootstrap-multiselect.js +335 -585 6.4.25.0 View file →
@@ -1,9 +1,9 @@
1 1 /**
2 2 * Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/)
3 3 *
4 4 * Apache License, Version 2.0:
5 - * Copyright (c) 2012 - 2021 David Stutz
5 + * Copyright (c) 2012 - 2018 David Stutz
6 6 *
7 7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 8 * use this file except in compliance with the License. You may obtain a
9 9 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0
@@ -14,9 +14,9 @@
14 14 * License for the specific language governing permissions and limitations
15 15 * under the License.
16 16 *
17 17 * BSD 3-Clause License:
18 - * Copyright (c) 2012 - 2021 David Stutz
18 + * Copyright (c) 2012 - 2018 David Stutz
19 19 * All rights reserved.
20 20 *
21 21 * Redistribution and use in source and binary forms, with or without
22 22 * modification, are permitted provided that the following conditions are met:
@@ -40,20 +40,9 @@
40 40 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
41 41 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42 42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 43 */
44 - (function (root, factory) {
45 - // check to see if 'knockout' AMD module is specified if using requirejs
46 - if (typeof define === 'function' && define.amd &&
47 - typeof require === 'function' && typeof require.specified === 'function' && require.specified('knockout')) {
48 -
49 - // AMD. Register as an anonymous module.
50 - define(['jquery', 'knockout'], factory);
51 - } else {
52 - // Browser globals
53 - factory(root.jQuery, root.ko);
54 - }
55 -})(this, function ($, ko) {
44 +!function ($) {
56 45 "use strict";// jshint ;_;
57 46
58 47 if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
59 48 ko.bindingHandlers.multiselect = {
@@ -58,9 +47,9 @@
58 47 if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
59 48 ko.bindingHandlers.multiselect = {
60 49 after: ['options', 'value', 'selectedOptions', 'enable', 'disable'],
61 50
62 - init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
51 + init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
63 52 var $element = $(element);
64 53 var config = ko.toJS(valueAccessor());
65 54
66 55 $element.multiselect(config);
@@ -68,11 +57,11 @@
68 57 if (allBindings.has('options')) {
69 58 var options = allBindings.get('options');
70 59 if (ko.isObservable(options)) {
71 60 ko.computed({
72 - read: function () {
61 + read: function() {
73 62 options();
74 - setTimeout(function () {
63 + setTimeout(function() {
75 64 var ms = $element.data('multiselect');
76 65 if (ms)
77 66 ms.updateOriginalOptions();//Not sure how beneficial this is.
78 67 $element.multiselect('rebuild');
@@ -89,11 +78,11 @@
89 78 if (allBindings.has('value')) {
90 79 var value = allBindings.get('value');
91 80 if (ko.isObservable(value)) {
92 81 ko.computed({
93 - read: function () {
82 + read: function() {
94 83 value();
95 - setTimeout(function () {
84 + setTimeout(function() {
96 85 $element.multiselect('refresh');
97 86 }, 1);
98 87 },
99 88 disposeWhenNodeIsRemoved: element
@@ -106,11 +95,11 @@
106 95 if (allBindings.has('selectedOptions')) {
107 96 var selectedOptions = allBindings.get('selectedOptions');
108 97 if (ko.isObservable(selectedOptions)) {
109 98 ko.computed({
110 - read: function () {
99 + read: function() {
111 100 selectedOptions();
112 - setTimeout(function () {
101 + setTimeout(function() {
113 102 $element.multiselect('refresh');
114 103 }, 1);
115 104 },
116 105 disposeWhenNodeIsRemoved: element
@@ -154,14 +143,14 @@
154 143 setEnabled(!disable);
155 144 }
156 145 }
157 146
158 - ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
147 + ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
159 148 $element.multiselect('destroy');
160 149 });
161 150 },
162 151
163 - update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
152 + update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
164 153 var $element = $(element);
165 154 var config = ko.toJS(valueAccessor());
166 155
167 156 $element.multiselect('setOptions', config);
@@ -218,9 +207,8 @@
218 207 this.buildReset();
219 208 this.buildSelectAll();
220 209 this.buildDropdownOptions();
221 210 this.buildFilter();
222 - this.buildButtons();
223 211
224 212 this.updateButtonText();
225 213 this.updateSelectAll(true);
226 214
@@ -228,19 +216,13 @@
228 216 this.updateOptGroups();
229 217 }
230 218
231 219 this.options.wasDisabled = this.$select.prop('disabled');
232 - if (this.options.disableIfEmpty && $('option', this.$select).length <= 0 && !this.options.wasDisabled) {
233 - this.disable(true);
220 + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
221 + this.disable();
234 222 }
235 223
236 224 this.$select.wrap('<span class="multiselect-native-select" />').after(this.$container);
237 - this.$select.prop('tabindex', '-1');
238 -
239 - if (this.options.widthSynchronizationMode !== 'never') {
240 - this.synchronizeButtonAndPopupWidth();
241 - }
242 -
243 225 this.options.onInitialized(this.$select, this.$container);
244 226 }
245 227
246 228 Multiselect.prototype = {
@@ -254,35 +236,37 @@
254 236 * @param {jQuery} options
255 237 * @param {jQuery} select
256 238 * @returns {String}
257 239 */
258 - buttonText: function (selectedOptions, select) {
259 - if (this.disabledText.length > 0 && select.prop('disabled')) {
240 + buttonText: function(options, select) {
241 + if (this.disabledText.length > 0
242 + && (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) {
243 +
260 244 return this.disabledText;
261 245 }
262 - else if (selectedOptions.length === 0) {
246 + else if (options.length === 0) {
263 247 return this.nonSelectedText;
264 248 }
265 249 else if (this.allSelectedText
266 - && selectedOptions.length === $('option', $(select)).length
267 - && $('option', $(select)).length !== 1
268 - && this.multiple) {
250 + && options.length === $('option', $(select)).length
251 + && $('option', $(select)).length !== 1
252 + && this.multiple) {
269 253
270 254 if (this.selectAllNumber) {
271 - return this.allSelectedText + ' (' + selectedOptions.length + ')';
255 + return this.allSelectedText + ' (' + options.length + ')';
272 256 }
273 257 else {
274 258 return this.allSelectedText;
275 259 }
276 260 }
277 - else if (this.numberDisplayed != 0 && selectedOptions.length > this.numberDisplayed) {
278 - return selectedOptions.length + ' ' + this.nSelectedText;
261 + else if (this.numberDisplayed != 0 && options.length > this.numberDisplayed) {
262 + return options.length + ' ' + this.nSelectedText;
279 263 }
280 264 else {
281 265 var selected = '';
282 266 var delimiter = this.delimiterText;
283 267
284 - selectedOptions.each(function () {
268 + options.each(function() {
285 269 var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();
286 270 selected += label + delimiter;
287 271 });
288 272
@@ -295,9 +279,9 @@
295 279 * @param {jQuery} options
296 280 * @param {jQuery} select
297 281 * @returns {@exp;selected@call;substr}
298 282 */
299 - buttonTitle: function (options, select) {
283 + buttonTitle: function(options, select) {
300 284 if (options.length === 0) {
301 285 return this.nonSelectedText;
302 286 }
303 287 else {
@@ -310,9 +294,9 @@
310 294 });
311 295 return selected.substr(0, selected.length - this.delimiterText.length);
312 296 }
313 297 },
314 - checkboxName: function (option) {
298 + checkboxName: function(option) {
315 299 return false; // no checkbox name
316 300 },
317 301 /**
318 302 * Create a label.
@@ -319,9 +303,9 @@
319 303 *
320 304 * @param {jQuery} element
321 305 * @returns {String}
322 306 */
323 - optionLabel: function (element) {
307 + optionLabel: function(element){
324 308 return $(element).attr('label') || $(element).text();
325 309 },
326 310 /**
327 311 * Create a class.
@@ -328,9 +312,9 @@
328 312 *
329 313 * @param {jQuery} element
330 314 * @returns {String}
331 315 */
332 - optionClass: function (element) {
316 + optionClass: function(element) {
333 317 return $(element).attr('class') || '';
334 318 },
335 319 /**
336 320 * Triggered on change of the multiselect.
@@ -339,9 +323,9 @@
339 323 *
340 324 * @param {jQuery} option
341 325 * @param {Boolean} checked
342 326 */
343 - onChange: function (option, checked) {
327 + onChange : function(option, checked) {
344 328
345 329 },
346 330 /**
347 331 * Triggered when the dropdown is shown.
@@ -347,9 +331,9 @@
347 331 * Triggered when the dropdown is shown.
348 332 *
349 333 * @param {jQuery} event
350 334 */
351 - onDropdownShow: function (event) {
335 + onDropdownShow: function(event) {
352 336
353 337 },
354 338 /**
355 339 * Triggered when the dropdown is hidden.
@@ -355,9 +339,9 @@
355 339 * Triggered when the dropdown is hidden.
356 340 *
357 341 * @param {jQuery} event
358 342 */
359 - onDropdownHide: function (event) {
343 + onDropdownHide: function(event) {
360 344
361 345 },
362 346 /**
363 347 * Triggered after the dropdown is shown.
@@ -363,9 +347,9 @@
363 347 * Triggered after the dropdown is shown.
364 348 *
365 349 * @param {jQuery} event
366 350 */
367 - onDropdownShown: function (event) {
351 + onDropdownShown: function(event) {
368 352
369 353 },
370 354 /**
371 355 * Triggered after the dropdown is hidden.
@@ -371,21 +355,21 @@
371 355 * Triggered after the dropdown is hidden.
372 356 *
373 357 * @param {jQuery} event
374 358 */
375 - onDropdownHidden: function (event) {
359 + onDropdownHidden: function(event) {
376 360
377 361 },
378 362 /**
379 363 * Triggered on select all.
380 364 */
381 - onSelectAll: function () {
365 + onSelectAll: function() {
382 366
383 367 },
384 368 /**
385 369 * Triggered on deselect all.
386 370 */
387 - onDeselectAll: function () {
371 + onDeselectAll: function() {
388 372
389 373 },
390 374 /**
391 375 * Triggered after initializing.
@@ -392,9 +376,9 @@
392 376 *
393 377 * @param {jQuery} $select
394 378 * @param {jQuery} $container
395 379 */
396 - onInitialized: function ($select, $container) {
380 + onInitialized: function($select, $container) {
397 381
398 382 },
399 383 /**
400 384 * Triggered on filtering.
@@ -400,13 +384,13 @@
400 384 * Triggered on filtering.
401 385 *
402 386 * @param {jQuery} $filter
403 387 */
404 - onFiltering: function ($filter) {
388 + onFiltering: function($filter) {
405 389
406 390 },
407 391 enableHTML: false,
408 - buttonClass: 'custom-select',
392 + buttonClass: 'btn btn-default',
409 393 inheritClass: false,
410 394 buttonWidth: 'auto',
411 395 buttonContainer: '<div class="btn-group" />',
412 396 dropRight: false,
@@ -435,9 +419,8 @@
435 419 preventInputChangeEvent: false,
436 420 nonSelectedText: 'None selected',
437 421 nSelectedText: 'selected',
438 422 allSelectedText: 'All selected',
439 - resetButtonText: 'Reset',
440 423 numberDisplayed: 3,
441 424 disableIfEmpty: false,
442 425 disabledText: '',
443 426 delimiterText: ', ',
@@ -443,23 +426,17 @@
443 426 delimiterText: ', ',
444 427 includeResetOption: false,
445 428 includeResetDivider: false,
446 429 resetText: 'Reset',
447 - indentGroupOptions: true,
448 - // possible options: 'never', 'always', 'ifPopupIsSmaller', 'ifPopupIsWider'
449 - widthSynchronizationMode: 'never',
450 - buttonTextAlignment: 'center',
451 - enableResetButton: false,
452 430 templates: {
453 - button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span></button>',
454 - popupContainer: '<div class="multiselect-container dropdown-menu"></div>',
455 - filter: '<div class="multiselect-filter d-flex align-items-center"><i class="fas fa-sm fa-search text-muted"></i><input type="search" class="multiselect-search form-control" /></div>',
456 - buttonGroup: '<div class="multiselect-buttons btn-group" style="display:flex;"></div>',
457 - buttonGroupReset: '<button type="button" class="multiselect-reset btn btn-secondary btn-block"></button>',
458 - option: '<button type="button" class="multiselect-option dropdown-item"></button>',
459 - divider: '<div class="dropdown-divider"></div>',
460 - optionGroup: '<button type="button" class="multiselect-group dropdown-item"></button>',
461 - resetButton: '<div class="multiselect-reset text-center p-2"><button type="button" class="btn btn-sm btn-block btn-outline-secondary"></button></div>'
431 + button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>',
432 + ul: '<ul class="multiselect-container dropdown-menu"></ul>',
433 + filter: '<li class="multiselect-item multiselect-filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text" /></div></li>',
434 + filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default multiselect-clear-filter" type="button"><i class="glyphicon glyphicon-remove-circle"></i></button></span>',
435 + li: '<li><a tabindex="0"><label></label></a></li>',
436 + divider: '<li class="multiselect-item divider"></li>',
437 + liGroup: '<li class="multiselect-item multiselect-group"><label></label></li>',
438 + resetButton: '<li class="multiselect-reset text-center"><div class="input-group"><a class="btn btn-default btn-block"></a></div></li>'
462 439 }
463 440 },
464 441
465 442 constructor: Multiselect,
@@ -466,20 +443,11 @@
466 443
467 444 /**
468 445 * Builds the container of the multiselect.
469 446 */
470 - buildContainer: function () {
447 + buildContainer: function() {
471 448 this.$container = $(this.options.buttonContainer);
472 - if (this.options.widthSynchronizationMode !== 'never') {
473 - this.$container.on('show.bs.dropdown', $.proxy(function () {
474 - // the width needs to be synchronized again in case the width of the button changed in between
475 - this.synchronizeButtonAndPopupWidth();
476 - this.options.onDropdownShow();
477 - }, this));
478 - }
479 - else {
480 - this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
481 - }
449 + this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
482 450 this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
483 451 this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);
484 452 this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);
485 453 },
@@ -486,9 +454,9 @@
486 454
487 455 /**
488 456 * Builds the button of the multiselect.
489 457 */
490 - buildButton: function () {
458 + buildButton: function() {
491 459 this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
492 460 if (this.$select.attr('class') && this.options.inheritClass) {
493 461 this.$button.addClass(this.$select.attr('class'));
494 462 }
@@ -502,9 +470,11 @@
502 470
503 471 // Manually add button width if set.
504 472 if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
505 473 this.$button.css({
506 - 'width': '100%' //this.options.buttonWidth,
474 + 'width' : '100%', //this.options.buttonWidth,
475 + 'overflow' : 'hidden',
476 + 'text-overflow' : 'ellipsis'
507 477 });
508 478 this.$container.css({
509 479 'width': this.options.buttonWidth
510 480 });
@@ -509,22 +479,8 @@
509 479 'width': this.options.buttonWidth
510 480 });
511 481 }
512 482
513 - if (this.options.buttonTextAlignment) {
514 - switch (this.options.buttonTextAlignment) {
515 - case 'left':
516 - this.$button.addClass('text-left');
517 - break;
518 - case 'center':
519 - this.$button.addClass('text-center');
520 - break;
521 - case 'right':
522 - this.$button.addClass('text-right');
523 - break;
524 - }
525 - }
526 -
527 483 // Keep the tab index from the select.
528 484 var tabindex = this.$select.attr('tabindex');
529 485 if (tabindex) {
530 486 this.$button.attr('tabindex', tabindex);
@@ -533,26 +489,23 @@
533 489 this.$container.prepend(this.$button);
534 490 },
535 491
536 492 /**
537 - * Builds the popup container representing the dropdown menu.
493 + * Builds the ul representing the dropdown menu.
538 494 */
539 - buildDropdown: function () {
495 + buildDropdown: function() {
540 496
541 - // Build popup container.
542 - this.$popupContainer = $(this.options.templates.popupContainer);
497 + // Build ul.
498 + this.$ul = $(this.options.templates.ul);
543 499
544 500 if (this.options.dropRight) {
545 - this.$container.addClass('dropright');
501 + this.$ul.addClass('pull-right');
546 502 }
547 - else if (this.options.dropUp) {
548 - this.$container.addClass("dropup");
549 - }
550 503
551 504 // Set max height of dropdown menu to activate auto scrollbar.
552 505 if (this.options.maxHeight) {
553 506 // TODO: Add a class for this option to move the css declarations.
554 - this.$popupContainer.css({
507 + this.$ul.css({
555 508 'max-height': this.options.maxHeight + 'px',
556 509 'overflow-y': 'auto',
557 510 'overflow-x': 'hidden'
558 511 });
@@ -557,37 +510,22 @@
557 510 'overflow-x': 'hidden'
558 511 });
559 512 }
560 513
561 - if (this.options.widthSynchronizationMode !== 'never') {
562 - this.$popupContainer.css('overflow-x', 'hidden');
563 - }
514 + if (this.options.dropUp) {
564 515
565 - this.$popupContainer.on("touchstart click", function (e) {
566 - e.stopPropagation();
567 - });
516 + var height = Math.min(this.options.maxHeight, $('option[data-role!="divider"]', this.$select).length*26 + $('option[data-role="divider"]', this.$select).length*19 + (this.options.includeSelectAllOption ? 26 : 0) + (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering ? 44 : 0));
517 + var moveCalc = height + 34;
568 518
569 - this.$container.append(this.$popupContainer);
570 - },
571 -
572 - synchronizeButtonAndPopupWidth: function () {
573 - if (!this.$popupContainer || this.options.widthSynchronizationMode === 'never') {
574 - return;
519 + this.$ul.css({
520 + 'max-height': height + 'px',
521 + 'overflow-y': 'auto',
522 + 'overflow-x': 'hidden',
523 + 'margin-top': "-" + moveCalc + 'px'
524 + });
575 525 }
576 526
577 - var buttonWidth = this.$button.outerWidth();
578 - switch (this.options.widthSynchronizationMode) {
579 - case 'always':
580 - this.$popupContainer.css('min-width', buttonWidth);
581 - this.$popupContainer.css('max-width', buttonWidth);
582 - break;
583 - case 'ifPopupIsSmaller':
584 - this.$popupContainer.css('min-width', buttonWidth);
585 - break;
586 - case 'ifPopupIsWider':
587 - this.$popupContainer.css('max-width', buttonWidth);
588 - break;
589 - }
527 + this.$container.append(this.$ul);
590 528 },
591 529
592 530 /**
593 531 * Build the dropdown options and binds all necessary events.
@@ -593,11 +531,11 @@
593 531 * Build the dropdown options and binds all necessary events.
594 532 *
595 533 * Uses createDivider and createOptionValue to create the necessary options.
596 534 */
597 - buildDropdownOptions: function () {
535 + buildDropdownOptions: function() {
598 536
599 - this.$select.children().each($.proxy(function (index, element) {
537 + this.$select.children().each($.proxy(function(index, element) {
600 538
601 539 var $element = $(element);
602 540 // Support optgroups and options without a group simultaneously.
603 541 var tag = $element.prop('tagName')
@@ -615,9 +553,9 @@
615 553 if ($element.data('role') === 'divider') {
616 554 this.createDivider();
617 555 }
618 556 else {
619 - this.createOptionValue(element, false);
557 + this.createOptionValue(element);
620 558 }
621 559
622 560 }
623 561
@@ -624,10 +562,10 @@
624 562 // Other illegal tags will be ignored.
625 563 }, this));
626 564
627 565 // Bind the change event on the dropdown elements.
628 - $(this.$popupContainer).off('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]');
629 - $(this.$popupContainer).on('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]', $.proxy(function (event) {
566 + $(this.$ul).off('change', 'li:not(.multiselect-group) input[type="checkbox"], li:not(.multiselect-group) input[type="radio"]');
567 + $(this.$ul).on('change', 'li:not(.multiselect-group) input[type="checkbox"], li:not(.multiselect-group) input[type="radio"]', $.proxy(function(event) {
630 568 var $target = $(event.target);
631 569
632 570 var checked = $target.prop('checked') || false;
633 571 var isSelectAllOption = $target.val() === this.options.selectAllValue;
@@ -634,13 +572,13 @@
634 572
635 573 // Apply or unapply the configured selected class.
636 574 if (this.options.selectedClass) {
637 575 if (checked) {
638 - $target.closest('.multiselect-option')
576 + $target.closest('li')
639 577 .addClass(this.options.selectedClass);
640 578 }
641 579 else {
642 - $target.closest('.multiselect-option')
580 + $target.closest('li')
643 581 .removeClass(this.options.selectedClass);
644 582 }
645 583 }
646 584
@@ -670,9 +608,9 @@
670 608 }
671 609 else {
672 610 // Unselect all other options and corresponding checkboxes.
673 611 if (this.options.selectedClass) {
674 - $($checkboxesNotThis).closest('.dropdown-item').removeClass(this.options.selectedClass);
612 + $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass);
675 613 }
676 614
677 615 $($checkboxesNotThis).prop('checked', false);
678 616 $optionsNotThis.prop('selected', false);
@@ -681,9 +619,9 @@
681 619 this.$button.click();
682 620 }
683 621
684 622 if (this.options.selectedClass === "active") {
685 - $optionsNotThis.closest(".dropdown-item").css("outline", "");
623 + $optionsNotThis.closest("a").css("outline", "");
686 624 }
687 625 }
688 626 else {
689 627 // Unselect option.
@@ -703,15 +641,14 @@
703 641
704 642 this.$select.change();
705 643 this.updateButtonText();
706 644
707 - if (this.options.preventInputChangeEvent) {
645 + if(this.options.preventInputChangeEvent) {
708 646 return false;
709 647 }
710 648 }, this));
711 649
712 - $('.multiselect-option', this.$popupContainer).off('mousedown');
713 - $('.multiselect-option', this.$popupContainer).on('mousedown', function (e) {
650 + $('li a', this.$ul).on('mousedown', function(e) {
714 651 if (e.shiftKey) {
715 652 // Prevent selecting text by Shift+click
716 653 return false;
717 654 }
@@ -716,25 +653,24 @@
716 653 return false;
717 654 }
718 655 });
719 656
720 - $(this.$popupContainer).off('touchstart click', '.multiselect-option, .multiselect-all, .multiselect-group');
721 - $(this.$popupContainer).on('touchstart click', '.multiselect-option, .multiselect-all, .multiselect-group', $.proxy(function (event) {
657 + $(this.$ul).on('touchstart click', 'li a', $.proxy(function(event) {
722 658 event.stopPropagation();
723 659
724 660 var $target = $(event.target);
725 661
726 662 if (event.shiftKey && this.options.multiple) {
727 - if (!$target.is("input")) { // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)
663 + if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)
728 664 event.preventDefault();
729 - $target = $target.closest(".multiselect-option").find("input");
665 + $target = $target.find("input");
730 666 $target.prop("checked", !$target.prop("checked"));
731 667 }
732 668 var checked = $target.prop('checked') || false;
733 669
734 670 if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range
735 - var from = this.$popupContainer.find(".multiselect-option:visible").index($target.closest(".multiselect-option"));
736 - var to = this.$popupContainer.find(".multiselect-option:visible").index(this.lastToggledInput.closest(".multiselect-option"));
671 + var from = this.$ul.find("li:visible").index($target.parents("li"));
672 + var to = this.$ul.find("li:visible").index(this.lastToggledInput.parents("li"));
737 673
738 674 if (from > to) { // Swap the indices
739 675 var tmp = to;
740 676 to = from;
@@ -744,14 +680,14 @@
744 680 // Make sure we grab all elements since slice excludes the last index
745 681 ++to;
746 682
747 683 // Change the checkboxes and underlying options
748 - var range = this.$popupContainer.find(".multiselect-option:not(.multiselect-filter-hidden)").slice(from, to).find("input");
684 + var range = this.$ul.find("li").not(".multiselect-filter-hidden").slice(from, to).find("input");
749 685
750 686 range.prop('checked', checked);
751 687
752 688 if (this.options.selectedClass) {
753 - range.closest('.multiselect-option')
689 + range.closest('li')
754 690 .toggleClass(this.options.selectedClass, checked);
755 691 }
756 692
757 693 for (var i = 0, j = range.length; i < j; i++) {
@@ -765,126 +701,102 @@
765 701
766 702 // Trigger the select "change" event
767 703 $target.trigger("change");
768 704 }
769 - else if (!$target.is('input')) {
770 - var $checkbox = $target.closest('.multiselect-option, .multiselect-all').find('.form-check-input');
771 - if ($checkbox.length > 0) {
772 - if (this.options.multiple || !$checkbox.prop('checked')) {
773 - $checkbox.prop('checked', !$checkbox.prop('checked'));
774 - $checkbox.change();
775 - }
776 - }
777 - else if (this.options.enableClickableOptGroups && this.options.multiple && !$target.hasClass("caret-container")) {
778 - var groupItem = $target;
779 - if (!groupItem.hasClass("multiselect-group")) {
780 - groupItem = $target.closest('.multiselect-group');
781 - }
782 - $checkbox = groupItem.find(".form-check-input");
783 - if ($checkbox.length > 0) {
784 - $checkbox.prop('checked', !$checkbox.prop('checked'));
785 - $checkbox.change();
786 - }
787 - }
788 705
789 - event.preventDefault();
790 - }
791 -
792 706 // Remembers last clicked option
793 - var $input = $target.closest(".multiselect-option").find("input[type='checkbox'], input[type='radio']");
794 - if ($input.length > 0) {
707 + if($target.is("input") && !$target.closest("li").is(".multiselect-item")){
795 708 this.lastToggledInput = $target;
796 709 }
797 - else {
798 - this.lastToggledInput = null;
799 - }
800 710
801 - $target.blur();
711 + $target.trigger( 'blur' );
802 712 }, this));
803 713
804 - //Keyboard support.
805 - this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function (event) {
806 - var $items = $(this.$container).find(".multiselect-option:not(.disabled), .multiselect-group:not(.disabled), .multiselect-all").filter(":visible");
807 - var index = $items.index($items.filter(':focus'));
808 - var $search = $('.multiselect-search', this.$container);
714 + // Keyboard support.
715 + this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {
716 + if ($('input[type="text"]', this.$container).is(':focus')) {
717 + return;
718 + }
809 719
810 - // keyCode 9 == Tab
811 - if (event.keyCode === 9 && this.$container.hasClass('show')) {
720 + if (event.keyCode === 9 && this.$container.hasClass('open')) {
812 721 this.$button.click();
813 722 }
814 - // keyCode 13 = Enter
815 - else if (event.keyCode == 13) {
816 - var $current = $items.eq(index);
817 - setTimeout(function () {
818 - $current.focus();
819 - }, 1);
820 - }
821 - // keyCode 38 = Arrow Up
822 - else if (event.keyCode == 38) {
823 - if (index == 0 && !$search.is(':focus')) {
824 - setTimeout(function () {
825 - $search.focus();
826 - }, 1);
723 + else {
724 + var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible");
725 +
726 + if (!$items.length) {
727 + return;
827 728 }
828 - }
829 - // keyCode 40 = Arrow Down
830 - else if (event.keyCode == 40) {
831 - if ($search.is(':focus')) {
832 - var $first = $items.eq(0);
833 - setTimeout(function () {
834 - $search.blur();
835 - $first.focus();
836 - }, 1);
729 +
730 + var index = $items.index($items.filter(':focus'));
731 +
732 + // Navigation up.
733 + if (event.keyCode === 38 && index > 0) {
734 + index--;
837 735 }
838 - else if (index == -1) {
839 - setTimeout(function () {
840 - $search.focus();
841 - }, 1);
736 + // Navigate down.
737 + else if (event.keyCode === 40 && index < $items.length - 1) {
738 + index++;
842 739 }
740 + else if (!~index) {
741 + index = 0;
742 + }
743 +
744 + var $current = $items.eq(index);
745 + $current.trigger( 'focus' );
746 +
747 + if (event.keyCode === 32 || event.keyCode === 13) {
748 + var $checkbox = $current.find('input');
749 +
750 + $checkbox.prop("checked", !$checkbox.prop("checked"));
751 + $checkbox.change();
752 + }
753 +
754 + event.stopPropagation();
755 + event.preventDefault();
843 756 }
844 757 }, this));
845 758
846 759 if (this.options.enableClickableOptGroups && this.options.multiple) {
847 - $(".multiselect-group input", this.$popupContainer).off("change");
848 - $(".multiselect-group input", this.$popupContainer).on("change", $.proxy(function (event) {
760 + $("li.multiselect-group input", this.$ul).on("change", $.proxy(function(event) {
849 761 event.stopPropagation();
850 762
851 763 var $target = $(event.target);
852 764 var checked = $target.prop('checked') || false;
853 765
854 - var $item = $(event.target).closest('.dropdown-item');
855 - var $group = $item.nextUntil(".multiselect-group")
766 + var $li = $(event.target).closest('li');
767 + var $group = $li.nextUntil("li.multiselect-group")
856 768 .not('.multiselect-filter-hidden')
857 769 .not('.disabled');
858 770
859 771 var $inputs = $group.find("input");
860 772
773 + var values = [];
861 774 var $options = [];
862 775
863 776 if (this.options.selectedClass) {
864 777 if (checked) {
865 - $item.addClass(this.options.selectedClass);
778 + $li.addClass(this.options.selectedClass);
866 779 }
867 780 else {
868 - $item.removeClass(this.options.selectedClass);
781 + $li.removeClass(this.options.selectedClass);
869 782 }
870 783 }
871 784
872 - $.each($inputs, $.proxy(function (index, input) {
873 - var $input = $(input);
874 - var value = $input.val();
785 + $.each($inputs, $.proxy(function(index, input) {
786 + var value = $(input).val();
875 787 var $option = this.getOptionByValue(value);
876 788
877 789 if (checked) {
878 - $input.prop('checked', true);
879 - $input.closest('.dropdown-item')
790 + $(input).prop('checked', true);
791 + $(input).closest('li')
880 792 .addClass(this.options.selectedClass);
881 793
882 794 $option.prop('selected', true);
883 795 }
884 796 else {
885 - $input.prop('checked', false);
886 - $input.closest('.dropdown-item')
797 + $(input).prop('checked', false);
798 + $(input).closest('li')
887 799 .removeClass(this.options.selectedClass);
888 800
889 801 $option.prop('selected', false);
890 802 }
@@ -902,16 +814,15 @@
902 814 }, this));
903 815 }
904 816
905 817 if (this.options.enableCollapsibleOptGroups && this.options.multiple) {
906 - $(".multiselect-group .caret-container", this.$popupContainer).off("click");
907 - $(".multiselect-group .caret-container", this.$popupContainer).on("click", $.proxy(function (event) {
908 - var $group = $(event.target).closest('.multiselect-group');
909 - var $inputs = $group.nextUntil(".multiselect-group")
910 - .not('.multiselect-filter-hidden');
818 + $("li.multiselect-group .caret-container", this.$ul).on("click", $.proxy(function(event) {
819 + var $li = $(event.target).closest('li');
820 + var $inputs = $li.nextUntil("li.multiselect-group")
821 + .not('.multiselect-filter-hidden');
911 822
912 823 var visible = true;
913 - $inputs.each(function () {
824 + $inputs.each(function() {
914 825 visible = visible && !$(this).hasClass('multiselect-collapsible-hidden');
915 826 });
916 827
917 828 if (visible) {
@@ -922,47 +833,13 @@
922 833 $inputs.show()
923 834 .removeClass('multiselect-collapsible-hidden');
924 835 }
925 836 }, this));
926 - }
927 - },
928 837
929 - /**
930 - * Create a checkbox container with input and label based on given values
931 - * @param {JQuery} $item
932 - * @param {String} label
933 - * @param {String} name
934 - * @param {String} value
935 - * @param {String} inputType
936 - * @returns {JQuery}
937 - */
938 - createCheckbox: function ($item, labelContent, name, value, title, inputType) {
939 - var $wrapper = $('<span />');
940 - $wrapper.addClass("form-check");
941 -
942 - if (this.options.enableHTML && $(labelContent).length > 0) {
943 - var $checkboxLabel = $('<label class="form-check-label" />');
944 - $checkboxLabel.html(labelContent);
945 - $wrapper.append($checkboxLabel);
838 + $("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea');
839 + $("li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px');
840 + $("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px');
946 841 }
947 - else {
948 - var $checkboxLabel = $('<label class="form-check-label" />');
949 - $checkboxLabel.text(labelContent);
950 - $wrapper.append($checkboxLabel);
951 - }
952 -
953 - var $checkbox = $('<input class="form-check-input"/>').attr('type', inputType);
954 - $checkbox.val(value);
955 - $wrapper.prepend($checkbox);
956 -
957 - if (name) {
958 - $checkbox.attr('name', name);
959 - }
960 -
961 - $item.prepend($wrapper);
962 - $item.attr("title", title || labelContent);
963 -
964 - return $checkbox;
965 842 },
966 843
967 844 /**
968 845 * Create an option using the given select option.
@@ -968,9 +845,9 @@
968 845 * Create an option using the given select option.
969 846 *
970 847 * @param {jQuery} element
971 848 */
972 - createOptionValue: function (element, isGroupOption) {
849 + createOptionValue: function(element) {
973 850 var $element = $(element);
974 851 if ($element.is(':selected')) {
975 852 $element.prop('selected', true);
976 853 }
@@ -979,41 +856,56 @@
979 856 var label = this.options.optionLabel(element);
980 857 var classes = this.options.optionClass(element);
981 858 var value = $element.val();
982 859 var inputType = this.options.multiple ? "checkbox" : "radio";
983 - var title = $element.attr('title');
984 860
985 - var $option = $(this.options.templates.option);
986 - $option.addClass(classes);
861 + var $li = $(this.options.templates.li);
862 + var $label = $('label', $li);
863 + $label.addClass(inputType);
864 + $label.attr("title", label);
865 + $li.addClass(classes);
987 866
988 - if (isGroupOption && this.options.indentGroupOptions) {
989 - $option.addClass("multiselect-group-option-indented")
867 + // Hide all children items when collapseOptGroupsByDefault is true
868 + if (this.options.collapseOptGroupsByDefault && $(element).parent().prop("tagName").toLowerCase() === "optgroup") {
869 + $li.addClass("multiselect-collapsible-hidden");
870 + $li.hide();
990 871 }
991 872
992 - // Hide all children items when collapseOptGroupsByDefault is true
993 - if (this.options.collapseOptGroupsByDefault && $(element).parent().prop("tagName").toLowerCase() === "optgroup") {
994 - $option.addClass("multiselect-collapsible-hidden");
995 - $option.hide();
873 + if (this.options.enableHTML) {
874 + $label.html(" " + label);
996 875 }
876 + else {
877 + $label.text(" " + label);
878 + }
997 879
880 + var $checkbox = $('<input/>').attr('type', inputType);
881 +
998 882 var name = this.options.checkboxName($element);
999 - var $checkbox = this.createCheckbox($option, label, name, value, title, inputType);
883 + if (name) {
884 + $checkbox.attr('name', name);
885 + }
1000 886
887 + $label.prepend($checkbox);
888 +
1001 889 var selected = $element.prop('selected') || false;
890 + $checkbox.val(value);
1002 891
1003 892 if (value === this.options.selectAllValue) {
1004 - $option.addClass("multiselect-all");
1005 - $option.removeClass("multiselect-option");
893 + $li.addClass("multiselect-item multiselect-all");
1006 894 $checkbox.parent().parent()
1007 895 .addClass('multiselect-all');
1008 896 }
1009 897
1010 - this.$popupContainer.append($option);
898 + $label.attr('title', $element.attr('title'));
1011 899
900 + this.$ul.append($li);
901 +
1012 902 if ($element.is(':disabled')) {
1013 903 $checkbox.attr('disabled', 'disabled')
1014 904 .prop('disabled', true)
1015 - .closest('.dropdown-item')
905 + .closest('a')
906 + .attr("tabindex", "-1")
907 + .closest('li')
1016 908 .addClass('disabled');
1017 909 }
1018 910
1019 911 $checkbox.prop('checked', selected);
@@ -1018,9 +910,9 @@
1018 910
1019 911 $checkbox.prop('checked', selected);
1020 912
1021 913 if (selected && this.options.selectedClass) {
1022 - $checkbox.closest('.dropdown-item')
914 + $checkbox.closest('li')
1023 915 .addClass(this.options.selectedClass);
1024 916 }
1025 917 },
1026 918
@@ -1028,11 +920,11 @@
1028 920 * Creates a divider using the given select option.
1029 921 *
1030 922 * @param {jQuery} element
1031 923 */
1032 - createDivider: function (element) {
924 + createDivider: function(element) {
1033 925 var $divider = $(this.options.templates.divider);
1034 - this.$popupContainer.append($divider);
926 + this.$ul.append($divider);
1035 927 },
1036 928
1037 929 /**
1038 930 * Creates an optgroup.
@@ -1038,46 +930,40 @@
1038 930 * Creates an optgroup.
1039 931 *
1040 932 * @param {jQuery} group
1041 933 */
1042 - createOptgroup: function (group) {
1043 - var $group = $(group);
1044 - var label = $group.attr("label");
1045 - var value = $group.attr("value");
1046 - var title = $group.attr('title');
934 + createOptgroup: function(group) {
935 + var label = $(group).attr("label");
936 + var value = $(group).attr("value");
937 + var $li = $('<li class="multiselect-item multiselect-group"><a href="javascript:void(0);"><label><b></b></label></a></li>');
1047 938
1048 - var $groupOption = $("<span class='multiselect-group dropdown-item-text'></span>");
939 + var classes = this.options.optionClass(group);
940 + $li.addClass(classes);
1049 941
1050 - if (this.options.enableClickableOptGroups && this.options.multiple) {
1051 - $groupOption = $(this.options.templates.optionGroup);
1052 - var $checkbox = this.createCheckbox($groupOption, label, null, value, title, "checkbox");
942 + if (this.options.enableHTML) {
943 + $('label b', $li).html(" " + label);
1053 944 }
1054 945 else {
1055 - if (this.options.enableHTML) {
1056 - $groupOption.html(" " + label);
1057 - }
1058 - else {
1059 - $groupOption.text(" " + label);
1060 - }
946 + $('label b', $li).text(" " + label);
1061 947 }
1062 948
1063 - var classes = this.options.optionClass(group);
1064 - $groupOption.addClass(classes);
949 + if (this.options.enableCollapsibleOptGroups && this.options.multiple) {
950 + $('a', $li).append('<span class="caret-container"><b class="caret"></b></span>');
951 + }
1065 952
1066 - if (this.options.enableCollapsibleOptGroups && this.options.multiple) {
1067 - $groupOption.find('.form-check').addClass('d-inline-block');
1068 - $groupOption.append('<span class="caret-container dropdown-toggle pl-1"></span>');
953 + if (this.options.enableClickableOptGroups && this.options.multiple) {
954 + $('a label', $li).prepend('<input type="checkbox" value="' + value + '"/>');
1069 955 }
1070 956
1071 - if ($group.is(':disabled')) {
1072 - $groupOption.addClass('disabled');
957 + if ($(group).is(':disabled')) {
958 + $li.addClass('disabled');
1073 959 }
1074 960
1075 - this.$popupContainer.append($groupOption);
961 + this.$ul.append($li);
1076 962
1077 - $("option", group).each($.proxy(function ($, group) {
1078 - this.createOptionValue(group, true);
1079 - }, this));
963 + $("option", group).each($.proxy(function($, group) {
964 + this.createOptionValue(group);
965 + }, this))
1080 966 },
1081 967
1082 968 /**
1083 969 * Build the reset.
@@ -1082,32 +968,30 @@
1082 968 /**
1083 969 * Build the reset.
1084 970 *
1085 971 */
1086 - buildReset: function () {
972 + buildReset: function() {
1087 973 if (this.options.includeResetOption) {
1088 974
1089 975 // Check whether to add a divider after the reset.
1090 976 if (this.options.includeResetDivider) {
1091 - var divider = $(this.options.templates.divider);
1092 - divider.addClass("mt-0");
1093 - this.$popupContainer.prepend(divider);
977 + this.$ul.prepend($(this.options.templates.divider));
1094 978 }
1095 979
1096 980 var $resetButton = $(this.options.templates.resetButton);
1097 981
1098 982 if (this.options.enableHTML) {
1099 - $('button', $resetButton).html(this.options.resetText);
983 + $('a', $resetButton).html(this.options.resetText);
1100 984 }
1101 985 else {
1102 - $('button', $resetButton).text(this.options.resetText);
986 + $('a', $resetButton).text(this.options.resetText);
1103 987 }
1104 988
1105 - $('button', $resetButton).click($.proxy(function () {
989 + $('a', $resetButton).click($.proxy(function(){
1106 990 this.clearSelection();
1107 991 }, this));
1108 992
1109 - this.$popupContainer.prepend($resetButton);
993 + this.$ul.prepend($resetButton);
1110 994 }
1111 995 },
1112 996
1113 997 /**
@@ -1114,9 +998,9 @@
1114 998 * Build the select all.
1115 999 *
1116 1000 * Checks if a select all has already been created.
1117 1001 */
1118 - buildSelectAll: function () {
1002 + buildSelectAll: function() {
1119 1003 if (typeof this.options.selectAllValue === 'number') {
1120 1004 this.options.selectAllValue = this.options.selectAllValue.toString();
1121 1005 }
1122 1006
@@ -1122,24 +1006,41 @@
1122 1006
1123 1007 var alreadyHasSelectAll = this.hasSelectAll();
1124 1008
1125 1009 if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple
1126 - && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {
1010 + && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {
1127 1011
1128 1012 // Check whether to add a divider after the select all.
1129 1013 if (this.options.includeSelectAllDivider) {
1130 - this.$popupContainer.prepend($(this.options.templates.divider));
1014 + this.$ul.prepend($(this.options.templates.divider));
1131 1015 }
1132 1016
1133 - var $option = $(this.options.templates.li || this.options.templates.option);
1134 - var $checkbox = this.createCheckbox($option, this.options.selectAllText, this.options.selectAllName, this.options.selectAllValue, this.options.selectAllText, "checkbox");
1017 + var $li = $(this.options.templates.li);
1018 + $('label', $li).addClass("checkbox");
1135 1019
1136 - $option.addClass("multiselect-all");
1137 - $option.removeClass("multiselect-option");
1138 - $option.find(".form-check-label").addClass("font-weight-bold");
1020 + if (this.options.enableHTML) {
1021 + $('label', $li).html(" " + this.options.selectAllText);
1022 + }
1023 + else {
1024 + $('label', $li).text(" " + this.options.selectAllText);
1025 + }
1139 1026
1140 - this.$popupContainer.prepend($option);
1027 + if (this.options.selectAllName) {
1028 + $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />');
1029 + }
1030 + else {
1031 + $('label', $li).prepend('<input type="checkbox" />');
1032 + }
1141 1033
1034 + var $checkbox = $('input', $li);
1035 + $checkbox.val(this.options.selectAllValue);
1036 +
1037 + $li.addClass("multiselect-item multiselect-all");
1038 + $checkbox.parent().parent()
1039 + .addClass('multiselect-all');
1040 +
1041 + this.$ul.prepend($li);
1042 +
1142 1043 $checkbox.prop('checked', false);
1143 1044 }
1144 1045 },
1145 1046
@@ -1145,9 +1046,9 @@
1145 1046
1146 1047 /**
1147 1048 * Builds the filter.
1148 1049 */
1149 - buildFilter: function () {
1050 + buildFilter: function() {
1150 1051
1151 1052 // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
1152 1053 if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
1153 1054 var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering);
@@ -1156,27 +1057,17 @@
1156 1057
1157 1058 this.$filter = $(this.options.templates.filter);
1158 1059 $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);
1159 1060
1160 - // Handles optional filter clear button
1161 - if (!this.options.includeFilterClearBtn) {
1162 - this.$filter.find(".multiselect-search").attr("type", "text");
1163 -
1164 - // Remove clear button if the old design of the filter with input groups and separated clear button is used
1165 - this.$filter.find(".multiselect-clear-filter").remove();
1166 - }
1167 - else {
1168 - // Firefox does not support a clear button in search inputs right now therefore it must be added manually
1169 - if (this.isFirefox() && this.$filter.find(".multiselect-clear-filter").length === 0) {
1170 - this.$filter.append("<i class='fas fa-times text-muted multiselect-clear-filter multiselect-moz-clear-filter'></i>");
1171 - }
1172 -
1173 - this.$filter.find(".multiselect-clear-filter").on('click', $.proxy(function (event) {
1061 + // Adds optional filter clear button
1062 + if(this.options.includeFilterClearBtn) {
1063 + var clearBtn = $(this.options.templates.filterClearBtn);
1064 + clearBtn.on('click', $.proxy(function(event){
1174 1065 clearTimeout(this.searchTimeout);
1175 1066
1176 1067 this.query = '';
1177 1068 this.$filter.find('.multiselect-search').val('');
1178 - $('.dropdown-item', this.$popupContainer).show().removeClass('multiselect-filter-hidden');
1069 + $('li', this.$ul).show().removeClass('multiselect-filter-hidden');
1179 1070
1180 1071 this.updateSelectAll();
1181 1072
1182 1073 if (this.options.enableClickableOptGroups && this.options.multiple) {
@@ -1183,41 +1074,33 @@
1183 1074 this.updateOptGroups();
1184 1075 }
1185 1076
1186 1077 }, this));
1078 + this.$filter.find('.input-group').append(clearBtn);
1187 1079 }
1188 1080
1189 - this.$popupContainer.prepend(this.$filter);
1081 + this.$ul.prepend(this.$filter);
1190 1082
1191 - this.$filter.val(this.query).on('click', function (event) {
1083 + this.$filter.val(this.query).on('click', function(event) {
1192 1084 event.stopPropagation();
1193 - }).on('input keydown', $.proxy(function (event) {
1085 + }).on('input keydown', $.proxy(function(event) {
1194 1086 // Cancel enter key default behaviour
1195 1087 if (event.which === 13) {
1196 - event.preventDefault();
1197 - }
1088 + event.preventDefault();
1089 + }
1198 1090
1199 - if (this.isFirefox() && this.options.includeFilterClearBtn) {
1200 - if (event.target.value) {
1201 - this.$filter.find(".multiselect-moz-clear-filter").show();
1202 - }
1203 - else {
1204 - this.$filter.find(".multiselect-moz-clear-filter").hide();
1205 - }
1206 - }
1207 -
1208 1091 // This is useful to catch "keydown" events after the browser has updated the control.
1209 1092 clearTimeout(this.searchTimeout);
1210 1093
1211 - this.searchTimeout = this.asyncFunction($.proxy(function () {
1094 + this.searchTimeout = this.asyncFunction($.proxy(function() {
1212 1095
1213 1096 if (this.query !== event.target.value) {
1214 1097 this.query = event.target.value;
1215 1098
1216 1099 var currentGroup, currentGroupVisible;
1217 - $.each($('.multiselect-option, .multiselect-group', this.$popupContainer), $.proxy(function (index, element) {
1100 + $.each($('li', this.$ul), $.proxy(function(index, element) {
1218 1101 var value = $('input', element).length > 0 ? $('input', element).val() : "";
1219 - var text = $('.form-check-label', element).text();
1102 + var text = $('label', element).text();
1220 1103
1221 1104 var filterCandidate = '';
1222 1105 if ((this.options.filterBehavior === 'text')) {
1223 1106 filterCandidate = text;
@@ -1250,15 +1133,15 @@
1250 1133 showElement = true;
1251 1134 }
1252 1135
1253 1136 // Toggle current element (group or group item) according to showElement boolean.
1254 - if (!showElement) {
1255 - $(element).css('display', 'none');
1256 - $(element).addClass('multiselect-filter-hidden');
1137 + if(!showElement){
1138 + $(element).css('display', 'none');
1139 + $(element).addClass('multiselect-filter-hidden');
1257 1140 }
1258 - if (showElement) {
1259 - $(element).css('display', 'block');
1260 - $(element).removeClass('multiselect-filter-hidden');
1141 + if(showElement){
1142 + $(element).css('display', 'block');
1143 + $(element).removeClass('multiselect-filter-hidden');
1261 1144 }
1262 1145
1263 1146 // Differentiate groups and group items.
1264 1147 if ($(element).hasClass('multiselect-group')) {
@@ -1288,10 +1171,8 @@
1288 1171 if (this.options.enableClickableOptGroups && this.options.multiple) {
1289 1172 this.updateOptGroups();
1290 1173 }
1291 1174
1292 - this.updatePopupPosition();
1293 -
1294 1175 this.options.onFiltering(event.target);
1295 1176
1296 1177 }, this), 300, this);
1297 1178 }, this));
@@ -1299,68 +1180,12 @@
1299 1180 }
1300 1181 },
1301 1182
1302 1183 /**
1303 - * Builds the filter.
1304 - */
1305 - buildButtons: function () {
1306 - if (this.options.enableResetButton) {
1307 - var $buttonGroup = $(this.options.templates.buttonGroup);
1308 - this.$buttonGroupReset = $(this.options.templates.buttonGroupReset).text(this.options.resetButtonText);
1309 - $buttonGroup.append(this.$buttonGroupReset);
1310 - this.$popupContainer.prepend($buttonGroup);
1311 -
1312 - // We save all options that were previously selected.
1313 - this.defaultSelection = {};
1314 - $('option', this.$select).each($.proxy(function(index, element) {
1315 - var $option = $(element);
1316 - this.defaultSelection[$option.val()] = $option.prop('selected');
1317 - }, this));
1318 -
1319 - this.$buttonGroupReset.on('click', $.proxy(function(event) {
1320 - $('option', this.$select).each($.proxy(function(index, element) {
1321 - var $option = $(element);
1322 - $option.prop('selected', this.defaultSelection[$option.val()]);
1323 - }, this));
1324 - this.refresh();
1325 -
1326 - if (this.options.enableFiltering) {
1327 - this.$filter.trigger('keydown');
1328 - $('input', this.$filter).val('');
1329 - }
1330 - }, this));
1331 - }
1332 - },
1333 -
1334 - updatePopupPosition: function() {
1335 - // prevent gaps between popup and select when filter is used (#1199)
1336 - var transformMatrix = this.$popupContainer.css("transform");
1337 - var matrixType = transformMatrix.substring(0, transformMatrix.indexOf('('));
1338 - var values = transformMatrix.substring(transformMatrix.indexOf('(') + 1, transformMatrix.length - 1);
1339 - var valuesArray = values.split(',');
1340 -
1341 - var valueIndex = 5;
1342 - if(matrixType === "matrix3d") {
1343 - valueIndex = 13;
1344 - }
1345 -
1346 - var yTransformation = valuesArray[valueIndex];
1347 - // Need to check to avoid errors when testing and in some other situations.
1348 - yTransformation = typeof yTransformation === 'undefined' ? 0 : yTransformation.trim();
1349 - if (yTransformation < 0) {
1350 - yTransformation = this.$popupContainer.css("height").replace('px', '') * -1;
1351 - valuesArray[valueIndex] = yTransformation;
1352 - transformMatrix = matrixType + '(' + valuesArray.join(',') + ')';
1353 - this.$popupContainer.css("transform", transformMatrix);
1354 - }
1355 - },
1356 -
1357 - /**
1358 1184 * Unbinds the whole plugin.
1359 1185 */
1360 - destroy: function () {
1186 + destroy: function() {
1361 1187 this.$container.remove();
1362 - this.$select.unwrap();
1363 1188 this.$select.show();
1364 1189
1365 1190 // reset original state
1366 1191 this.$select.prop('disabled', this.options.wasDisabled);
@@ -1372,10 +1197,10 @@
1372 1197 * Refreshs the multiselect based on the selected options of the select.
1373 1198 */
1374 1199 refresh: function () {
1375 1200 var inputs = {};
1376 - $('.multiselect-option input', this.$popupContainer).each(function () {
1377 - inputs[$(this).val()] = $(this);
1201 + $('li input', this.$ul).each(function() {
1202 + inputs[$(this).val()] = $(this);
1378 1203 });
1379 1204
1380 1205 $('option', this.$select).each($.proxy(function (index, element) {
1381 1206 var $elem = $(element);
@@ -1384,9 +1209,9 @@
1384 1209 if ($elem.is(':selected')) {
1385 1210 $input.prop('checked', true);
1386 1211
1387 1212 if (this.options.selectedClass) {
1388 - $input.closest('.multiselect-option')
1213 + $input.closest('li')
1389 1214 .addClass(this.options.selectedClass);
1390 1215 }
1391 1216 }
1392 1217 else {
@@ -1392,9 +1217,9 @@
1392 1217 else {
1393 1218 $input.prop('checked', false);
1394 1219
1395 1220 if (this.options.selectedClass) {
1396 - $input.closest('.multiselect-option')
1221 + $input.closest('li')
1397 1222 .removeClass(this.options.selectedClass);
1398 1223 }
1399 1224 }
1400 1225
@@ -1400,14 +1225,14 @@
1400 1225
1401 1226 if ($elem.is(":disabled")) {
1402 1227 $input.attr('disabled', 'disabled')
1403 1228 .prop('disabled', true)
1404 - .closest('.multiselect-option')
1229 + .closest('li')
1405 1230 .addClass('disabled');
1406 1231 }
1407 1232 else {
1408 1233 $input.prop('disabled', false)
1409 - .closest('.multiselect-option')
1234 + .closest('li')
1410 1235 .removeClass('disabled');
1411 1236 }
1412 1237 }, this));
1413 1238
@@ -1427,10 +1252,10 @@
1427 1252 *
1428 1253 * @param {Array} selectValues
1429 1254 * @param {Boolean} triggerOnChange
1430 1255 */
1431 - select: function (selectValues, triggerOnChange) {
1432 - if (!$.isArray(selectValues)) {
1256 + select: function(selectValues, triggerOnChange) {
1257 + if(!$.isArray(selectValues)) {
1433 1258 selectValues = [selectValues];
1434 1259 }
1435 1260
1436 1261 for (var i = 0; i < selectValues.length; i++) {
@@ -1442,14 +1267,18 @@
1442 1267
1443 1268 var $option = this.getOptionByValue(value);
1444 1269 var $checkbox = this.getInputByValue(value);
1445 1270
1446 - if ($option === undefined || $checkbox === undefined) {
1271 + if($option === undefined || $checkbox === undefined) {
1447 1272 continue;
1448 1273 }
1449 1274
1275 + if (!this.options.multiple) {
1276 + this.deselectAll(false);
1277 + }
1278 +
1450 1279 if (this.options.selectedClass) {
1451 - $checkbox.closest('.dropdown-item')
1280 + $checkbox.closest('li')
1452 1281 .addClass(this.options.selectedClass);
1453 1282 }
1454 1283
1455 1284 $checkbox.prop('checked', true);
@@ -1454,17 +1283,8 @@
1454 1283
1455 1284 $checkbox.prop('checked', true);
1456 1285 $option.prop('selected', true);
1457 1286
1458 - if (!this.options.multiple) {
1459 - var $checkboxesNotThis = $('input', this.$container).not($checkbox);
1460 - $($checkboxesNotThis).prop('checked', false);
1461 - $($checkboxesNotThis).closest('.multiselect-option').removeClass("active")
1462 -
1463 - var $optionsNotThis = $('option', this.$select).not($option);
1464 - $optionsNotThis.prop('selected', false);
1465 - }
1466 -
1467 1287 if (triggerOnChange) {
1468 1288 this.options.onChange($option, true);
1469 1289 }
1470 1290 }
@@ -1498,15 +1318,10 @@
1498 1318 *
1499 1319 * @param {Array} deselectValues
1500 1320 * @param {Boolean} triggerOnChange
1501 1321 */
1502 - deselect: function (deselectValues, triggerOnChange) {
1503 - if (!this.options.multiple) {
1504 - // In single selection mode at least on option needs to be selected
1505 - return;
1506 - }
1507 -
1508 - if (!$.isArray(deselectValues)) {
1322 + deselect: function(deselectValues, triggerOnChange) {
1323 + if(!$.isArray(deselectValues)) {
1509 1324 deselectValues = [deselectValues];
1510 1325 }
1511 1326
1512 1327 for (var i = 0; i < deselectValues.length; i++) {
@@ -1518,14 +1333,14 @@
1518 1333
1519 1334 var $option = this.getOptionByValue(value);
1520 1335 var $checkbox = this.getInputByValue(value);
1521 1336
1522 - if ($option === undefined || $checkbox === undefined) {
1337 + if($option === undefined || $checkbox === undefined) {
1523 1338 continue;
1524 1339 }
1525 1340
1526 1341 if (this.options.selectedClass) {
1527 - $checkbox.closest('.dropdown-item')
1342 + $checkbox.closest('li')
1528 1343 .removeClass(this.options.selectedClass);
1529 1344 }
1530 1345
1531 1346 $checkbox.prop('checked', false);
@@ -1552,57 +1367,42 @@
1552 1367 * @param {Boolean} justVisible
1553 1368 * @param {Boolean} triggerOnSelectAll
1554 1369 */
1555 1370 selectAll: function (justVisible, triggerOnSelectAll) {
1556 - if (!this.options.multiple) {
1557 - // In single selection mode only one option can be selected at a time
1558 - return;
1559 - }
1560 1371
1561 - // Record all changes, i.e., options selected that were not selected before.
1562 - var selected = [];
1563 1372 var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
1373 + var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul);
1374 + var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible');
1564 1375
1565 - if (justVisible) {
1566 - var visibleOptions = $(".multiselect-option:not(.disabled):not(.multiselect-filter-hidden)", this.$popupContainer);
1567 - $('input:enabled', visibleOptions).prop('checked', true);
1568 - visibleOptions.addClass(this.options.selectedClass);
1376 + if(justVisible) {
1377 + $('input:enabled' , visibleLis).prop('checked', true);
1378 + visibleLis.addClass(this.options.selectedClass);
1569 1379
1570 - $('input:enabled', visibleOptions).each($.proxy(function (index, element) {
1380 + $('input:enabled' , visibleLis).each($.proxy(function(index, element) {
1571 1381 var value = $(element).val();
1572 1382 var option = this.getOptionByValue(value);
1573 - if (!$(option).prop('selected')) {
1574 - selected.push(option);
1575 - }
1576 1383 $(option).prop('selected', true);
1577 1384 }, this));
1578 1385 }
1579 1386 else {
1580 - var allOptions = $(".multiselect-option:not(.disabled)", this.$popupContainer);
1581 - $('input:enabled', allOptions).prop('checked', true);
1582 - allOptions.addClass(this.options.selectedClass);
1387 + $('input:enabled' , allLis).prop('checked', true);
1388 + allLis.addClass(this.options.selectedClass);
1583 1389
1584 - $('input:enabled', allOptions).each($.proxy(function (index, element) {
1390 + $('input:enabled' , allLis).each($.proxy(function(index, element) {
1585 1391 var value = $(element).val();
1586 1392 var option = this.getOptionByValue(value);
1587 - if (!$(option).prop('selected')) {
1588 - selected.push(option);
1589 - }
1590 1393 $(option).prop('selected', true);
1591 1394 }, this));
1592 1395 }
1593 1396
1594 - $('.multiselect-option input[value="' + this.options.selectAllValue + '"]', this.$popupContainer).prop('checked', true);
1397 + $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', true);
1595 1398
1596 1399 if (this.options.enableClickableOptGroups && this.options.multiple) {
1597 1400 this.updateOptGroups();
1598 1401 }
1599 1402
1600 - this.updateButtonText();
1601 - this.updateSelectAll();
1602 -
1603 1403 if (triggerOnSelectAll) {
1604 - this.options.onSelectAll(selected);
1404 + this.options.onSelectAll();
1605 1405 }
1606 1406 },
1607 1407
1608 1408 /**
@@ -1612,57 +1412,42 @@
1612 1412 *
1613 1413 * @param {Boolean} justVisible
1614 1414 */
1615 1415 deselectAll: function (justVisible, triggerOnDeselectAll) {
1616 - if (!this.options.multiple) {
1617 - // In single selection mode at least on option needs to be selected
1618 - return;
1619 - }
1620 1416
1621 - // Record changes, i.e., those options that are deselected but were not deselected before.
1622 - var deselected = [];
1623 1417 var justVisible = typeof justVisible === 'undefined' ? true : justVisible;
1418 + var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul);
1419 + var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible');
1624 1420
1625 - if (justVisible) {
1626 - var visibleOptions = $(".multiselect-option:not(.disabled):not(.multiselect-filter-hidden)", this.$popupContainer);
1627 - $('input[type="checkbox"]:enabled', visibleOptions).prop('checked', false);
1628 - visibleOptions.removeClass(this.options.selectedClass);
1421 + if(justVisible) {
1422 + $('input[type="checkbox"]:enabled' , visibleLis).prop('checked', false);
1423 + visibleLis.removeClass(this.options.selectedClass);
1629 1424
1630 - $('input[type="checkbox"]:enabled', visibleOptions).each($.proxy(function (index, element) {
1425 + $('input[type="checkbox"]:enabled' , visibleLis).each($.proxy(function(index, element) {
1631 1426 var value = $(element).val();
1632 1427 var option = this.getOptionByValue(value);
1633 - if ($(option).prop('selected')) {
1634 - deselected.push(option);
1635 - }
1636 1428 $(option).prop('selected', false);
1637 1429 }, this));
1638 1430 }
1639 1431 else {
1640 - var allOptions = $(".multiselect-option:not(.disabled):not(.multiselect-group)", this.$popupContainer);
1641 - $('input[type="checkbox"]:enabled', allOptions).prop('checked', false);
1642 - allOptions.removeClass(this.options.selectedClass);
1432 + $('input[type="checkbox"]:enabled' , allLis).prop('checked', false);
1433 + allLis.removeClass(this.options.selectedClass);
1643 1434
1644 - $('input[type="checkbox"]:enabled', allOptions).each($.proxy(function (index, element) {
1435 + $('input[type="checkbox"]:enabled' , allLis).each($.proxy(function(index, element) {
1645 1436 var value = $(element).val();
1646 1437 var option = this.getOptionByValue(value);
1647 - if ($(option).prop('selected')) {
1648 - deselected.push(option);
1649 - }
1650 1438 $(option).prop('selected', false);
1651 1439 }, this));
1652 1440 }
1653 1441
1654 - $('.multiselect-all input[value="' + this.options.selectAllValue + '"]', this.$popupContainer).prop('checked', false);
1442 + $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', false);
1655 1443
1656 1444 if (this.options.enableClickableOptGroups && this.options.multiple) {
1657 1445 this.updateOptGroups();
1658 1446 }
1659 1447
1660 - this.updateButtonText();
1661 - this.updateSelectAll();
1662 -
1663 1448 if (triggerOnDeselectAll) {
1664 - this.options.onDeselectAll(deselected);
1449 + this.options.onDeselectAll();
1665 1450 }
1666 1451 },
1667 1452
1668 1453 /**
@@ -1669,10 +1454,10 @@
1669 1454 * Rebuild the plugin.
1670 1455 *
1671 1456 * Rebuilds the dropdown, the filter and the select all option.
1672 1457 */
1673 - rebuild: function () {
1674 - this.$popupContainer.html('');
1458 + rebuild: function() {
1459 + this.$ul.html('');
1675 1460
1676 1461 // Important to distinguish between radios and checkboxes.
1677 1462 this.options.multiple = this.$select.attr('multiple') === "multiple";
1678 1463
@@ -1678,9 +1463,8 @@
1678 1463
1679 1464 this.buildSelectAll();
1680 1465 this.buildDropdownOptions();
1681 1466 this.buildFilter();
1682 - this.buildButtons();
1683 1467
1684 1468 this.updateButtonText();
1685 1469 this.updateSelectAll(true);
1686 1470
@@ -1687,35 +1471,24 @@
1687 1471 if (this.options.enableClickableOptGroups && this.options.multiple) {
1688 1472 this.updateOptGroups();
1689 1473 }
1690 1474
1691 - if (this.options.disableIfEmpty) {
1692 - if ($('option', this.$select).length <= 0) {
1693 - if (!this.$select.prop('disabled')) {
1694 - this.disable(true);
1695 - }
1696 - }
1697 - else if (this.$select.data("disabled-by-option")) {
1698 - this.enable();
1699 - }
1475 + if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {
1476 + this.disable();
1700 1477 }
1478 + else {
1479 + this.enable();
1480 + }
1701 1481
1702 1482 if (this.options.dropRight) {
1703 - this.$container.addClass('dropright');
1483 + this.$ul.addClass('pull-right');
1704 1484 }
1705 - else if (this.options.dropUp) {
1706 - this.$container.addClass('dropup');
1707 - }
1708 -
1709 - if (this.options.widthSynchronizationMode !== 'never') {
1710 - this.synchronizeButtonAndPopupWidth();
1711 - }
1712 1485 },
1713 1486
1714 1487 /**
1715 1488 * The provided data will be used to build the dropdown.
1716 1489 */
1717 - dataprovider: function (dataprovider) {
1490 + dataprovider: function(dataprovider) {
1718 1491
1719 1492 var groupCounter = 0;
1720 1493 var $select = this.$select.empty();
1721 1494
@@ -1730,9 +1503,9 @@
1730 1503 disabled: !!option.disabled,
1731 1504 value: option.value
1732 1505 });
1733 1506
1734 - forEach(option.children, function (subOption) { // add children option tags
1507 + forEach(option.children, function(subOption) { // add children option tags
1735 1508 var attributes = {
1736 1509 value: subOption.value,
1737 1510 label: subOption.label || subOption.value,
1738 1511 title: subOption.title,
@@ -1740,12 +1513,12 @@
1740 1513 disabled: !!subOption.disabled
1741 1514 };
1742 1515
1743 1516 //Loop through attributes object and add key-value for each attribute
1744 - for (var key in subOption.attributes) {
1517 + for (var key in subOption.attributes) {
1745 1518 attributes['data-' + key] = subOption.attributes[key];
1746 - }
1747 - //Append original attributes + new data attributes to option
1519 + }
1520 + //Append original attributes + new data attributes to option
1748 1521 $tag.append($('<option/>').attr(attributes));
1749 1522 });
1750 1523 }
1751 1524 else {
@@ -1759,9 +1532,9 @@
1759 1532 'disabled': !!option['disabled']
1760 1533 };
1761 1534 //Loop through attributes object and add key-value for each attribute
1762 1535 for (var key in option.attributes) {
1763 - attributes['data-' + key] = option.attributes[key];
1536 + attributes['data-' + key] = option.attributes[key];
1764 1537 }
1765 1538 //Append original attributes + new data attributes to option
1766 1539 $tag = $('<option/>').attr(attributes);
1767 1540
@@ -1776,32 +1549,21 @@
1776 1549
1777 1550 /**
1778 1551 * Enable the multiselect.
1779 1552 */
1780 - enable: function () {
1553 + enable: function() {
1781 1554 this.$select.prop('disabled', false);
1782 1555 this.$button.prop('disabled', false)
1783 1556 .removeClass('disabled');
1784 -
1785 - this.updateButtonText();
1786 1557 },
1787 1558
1788 1559 /**
1789 1560 * Disable the multiselect.
1790 1561 */
1791 - disable: function (disableByOption) {
1562 + disable: function() {
1792 1563 this.$select.prop('disabled', true);
1793 1564 this.$button.prop('disabled', true)
1794 1565 .addClass('disabled');
1795 -
1796 - if (disableByOption) {
1797 - this.$select.data("disabled-by-option", true);
1798 - }
1799 - else {
1800 - this.$select.data("disabled-by-option", null);
1801 - }
1802 -
1803 - this.updateButtonText();
1804 1566 },
1805 1567
1806 1568 /**
1807 1569 * Set the options.
@@ -1807,9 +1569,9 @@
1807 1569 * Set the options.
1808 1570 *
1809 1571 * @param {Array} options
1810 1572 */
1811 - setOptions: function (options) {
1573 + setOptions: function(options) {
1812 1574 this.options = this.mergeOptions(options);
1813 1575 },
1814 1576
1815 1577 /**
@@ -1817,9 +1579,9 @@
1817 1579 *
1818 1580 * @param {Array} options
1819 1581 * @returns {Array}
1820 1582 */
1821 - mergeOptions: function (options) {
1583 + mergeOptions: function(options) {
1822 1584 return $.extend(true, {}, this.defaults, this.options, options);
1823 1585 },
1824 1586
1825 1587 /**
@@ -1826,26 +1588,26 @@
1826 1588 * Checks whether a select all checkbox is present.
1827 1589 *
1828 1590 * @returns {Boolean}
1829 1591 */
1830 - hasSelectAll: function () {
1831 - return $('.multiselect-all', this.$popupContainer).length > 0;
1592 + hasSelectAll: function() {
1593 + return $('li.multiselect-all', this.$ul).length > 0;
1832 1594 },
1833 1595
1834 1596 /**
1835 1597 * Update opt groups.
1836 1598 */
1837 - updateOptGroups: function () {
1838 - var $groups = $('.multiselect-group', this.$popupContainer)
1599 + updateOptGroups: function() {
1600 + var $groups = $('li.multiselect-group', this.$ul)
1839 1601 var selectedClass = this.options.selectedClass;
1840 1602
1841 - $groups.each(function () {
1842 - var $options = $(this).nextUntil('.multiselect-group')
1603 + $groups.each(function() {
1604 + var $options = $(this).nextUntil('li.multiselect-group')
1843 1605 .not('.multiselect-filter-hidden')
1844 1606 .not('.disabled');
1845 1607
1846 1608 var checked = true;
1847 - $options.each(function () {
1609 + $options.each(function() {
1848 1610 var $input = $('input', this);
1849 1611
1850 1612 if (!$input.prop('checked')) {
1851 1613 checked = false;
@@ -1867,23 +1629,23 @@
1867 1629
1868 1630 /**
1869 1631 * Updates the select all checkbox based on the currently displayed and selected checkboxes.
1870 1632 */
1871 - updateSelectAll: function (notTriggerOnSelectAll) {
1633 + updateSelectAll: function(notTriggerOnSelectAll) {
1872 1634 if (this.hasSelectAll()) {
1873 - var allBoxes = $(".multiselect-option:not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled", this.$popupContainer);
1635 + var allBoxes = $("li:not(.multiselect-item):not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled", this.$ul);
1874 1636 var allBoxesLength = allBoxes.length;
1875 1637 var checkedBoxesLength = allBoxes.filter(":checked").length;
1876 - var selectAllItem = $(".multiselect-all", this.$popupContainer);
1877 - var selectAllInput = selectAllItem.find("input");
1638 + var selectAllLi = $("li.multiselect-all", this.$ul);
1639 + var selectAllInput = selectAllLi.find("input");
1878 1640
1879 1641 if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) {
1880 1642 selectAllInput.prop("checked", true);
1881 - selectAllItem.addClass(this.options.selectedClass);
1643 + selectAllLi.addClass(this.options.selectedClass);
1882 1644 }
1883 1645 else {
1884 1646 selectAllInput.prop("checked", false);
1885 - selectAllItem.removeClass(this.options.selectedClass);
1647 + selectAllLi.removeClass(this.options.selectedClass);
1886 1648 }
1887 1649 }
1888 1650 },
1889 1651
@@ -1889,9 +1651,9 @@
1889 1651
1890 1652 /**
1891 1653 * Update the button text and its title based on the currently selected options.
1892 1654 */
1893 - updateButtonText: function () {
1655 + updateButtonText: function() {
1894 1656 var options = this.getSelected();
1895 1657
1896 1658 // First update the displayed button text.
1897 1659 if (this.options.enableHTML) {
@@ -1902,9 +1664,8 @@
1902 1664 }
1903 1665
1904 1666 // Now update the title attribute of the button.
1905 1667 $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select));
1906 - this.$button.trigger('change');
1907 1668 },
1908 1669
1909 1670 /**
1910 1671 * Get all selected options.
@@ -1910,9 +1671,9 @@
1910 1671 * Get all selected options.
1911 1672 *
1912 1673 * @returns {jQUery}
1913 1674 */
1914 - getSelected: function () {
1675 + getSelected: function() {
1915 1676 return $('option', this.$select).filter(":selected");
1916 1677 },
1917 1678
1918 1679 /**
@@ -1941,9 +1702,9 @@
1941 1702 * @returns {jQuery}
1942 1703 */
1943 1704 getInputByValue: function (value) {
1944 1705
1945 - var checkboxes = $('.multiselect-option input:not(.multiselect-search)', this.$popupContainer);
1706 + var checkboxes = $('li input:not(.multiselect-search)', this.$ul);
1946 1707 var valueToCompare = value.toString();
1947 1708
1948 1709 for (var i = 0; i < checkboxes.length; i = i + 1) {
1949 1710 var checkbox = checkboxes[i];
@@ -1955,38 +1716,27 @@
1955 1716
1956 1717 /**
1957 1718 * Used for knockout integration.
1958 1719 */
1959 - updateOriginalOptions: function () {
1720 + updateOriginalOptions: function() {
1960 1721 this.originalOptions = this.$select.clone()[0].options;
1961 1722 },
1962 1723
1963 - asyncFunction: function (callback, timeout, self) {
1724 + asyncFunction: function(callback, timeout, self) {
1964 1725 var args = Array.prototype.slice.call(arguments, 3);
1965 - return setTimeout(function () {
1726 + return setTimeout(function() {
1966 1727 callback.apply(self || window, args);
1967 1728 }, timeout);
1968 1729 },
1969 1730
1970 - setAllSelectedText: function (allSelectedText) {
1731 + setAllSelectedText: function(allSelectedText) {
1971 1732 this.options.allSelectedText = allSelectedText;
1972 1733 this.updateButtonText();
1973 - },
1974 -
1975 - isFirefox: function () {
1976 - var firefoxIdentifier = 'firefox';
1977 - var valueNotFoundIndex = -1;
1978 -
1979 - if (navigator && navigator.userAgent) {
1980 - return navigator.userAgent.toLocaleLowerCase().indexOf(firefoxIdentifier) > valueNotFoundIndex;
1981 - }
1982 -
1983 - return false;
1984 1734 }
1985 1735 };
1986 1736
1987 - $.fn.multiselect = function (option, parameter, extraOptions) {
1988 - return this.each(function () {
1737 + $.fn.multiselect = function(option, parameter, extraOptions) {
1738 + return this.each(function() {
1989 1739 var data = $(this).data('multiselect');
1990 1740 var options = typeof option === 'object' && option;
1991 1741
1992 1742 // Initialize the multiselect.
@@ -2007,9 +1757,9 @@
2007 1757 };
2008 1758
2009 1759 $.fn.multiselect.Constructor = Multiselect;
2010 1760
2011 - $(function () {
1761 + $(function() {
2012 1762 $("select[data-role=multiselect]").multiselect();
2013 1763 });
2014 1764
2015 -});
1765 +}(window.jQuery);