From 6cef61cca5a8b749c3485cfa8899428a00d565c4 Mon Sep 17 00:00:00 2001 From: Sebastian Eckard Date: Tue, 13 Oct 2020 15:27:48 +0200 Subject: [PATCH 1/7] Basic migration to bootstrap 4.x.x --- bower.json | 2 +- dist/css/bootstrap-multiselect.css | 119 +- dist/css/bootstrap-multiselect.min.css | 1 + dist/js/bootstrap-multiselect.js | 560 +- dist/js/bootstrap-multiselect.min.js | 1 + dist/less/bootstrap-multiselect.less | 62 +- docs/css/bootstrap-3.3.2.min.css | 5 - docs/css/bootstrap-4.5.2.min.css | 7 + docs/css/fontawesome-5.15.1-web/all.css | 4619 +++++++++++++++ docs/css/fontawesome-5.15.1-web/all.min.css | 4 + .../fontawesome-5.15.1-web/fa-brands-400.eot | Bin 0 -> 136822 bytes .../fontawesome-5.15.1-web/fa-brands-400.svg | 3717 ++++++++++++ .../fontawesome-5.15.1-web/fa-brands-400.ttf | Bin 0 -> 136516 bytes .../fontawesome-5.15.1-web/fa-brands-400.woff | Bin 0 -> 92136 bytes .../fa-brands-400.woff2 | Bin 0 -> 78460 bytes .../fontawesome-5.15.1-web/fa-regular-400.eot | Bin 0 -> 34350 bytes .../fontawesome-5.15.1-web/fa-regular-400.svg | 801 +++ .../fontawesome-5.15.1-web/fa-regular-400.ttf | Bin 0 -> 34052 bytes .../fa-regular-400.woff | Bin 0 -> 16772 bytes .../fa-regular-400.woff2 | Bin 0 -> 13548 bytes .../fontawesome-5.15.1-web/fa-solid-900.eot | Bin 0 -> 204814 bytes .../fontawesome-5.15.1-web/fa-solid-900.svg | 5028 +++++++++++++++++ .../fontawesome-5.15.1-web/fa-solid-900.ttf | Bin 0 -> 204528 bytes .../fontawesome-5.15.1-web/fa-solid-900.woff | Bin 0 -> 104280 bytes .../fontawesome-5.15.1-web/fa-solid-900.woff2 | Bin 0 -> 80300 bytes docs/js/bootstrap-3.3.2.min.js | 7 - docs/js/bootstrap.bundle-4.5.2.min.js | 7 + index.html | 1109 ++-- require.html | 4 +- tests/SpecRunner.html | 2 +- 30 files changed, 15136 insertions(+), 919 deletions(-) create mode 100644 dist/css/bootstrap-multiselect.min.css create mode 100644 dist/js/bootstrap-multiselect.min.js delete mode 100644 docs/css/bootstrap-3.3.2.min.css create mode 100644 docs/css/bootstrap-4.5.2.min.css create mode 100644 docs/css/fontawesome-5.15.1-web/all.css create mode 100644 docs/css/fontawesome-5.15.1-web/all.min.css create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-brands-400.eot create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-brands-400.svg create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-brands-400.ttf create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-brands-400.woff create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-brands-400.woff2 create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-regular-400.eot create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-regular-400.svg create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-regular-400.ttf create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-regular-400.woff create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-regular-400.woff2 create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-solid-900.eot create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-solid-900.svg create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-solid-900.ttf create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-solid-900.woff create mode 100644 docs/fonts/fontawesome-5.15.1-web/fa-solid-900.woff2 delete mode 100644 docs/js/bootstrap-3.3.2.min.js create mode 100644 docs/js/bootstrap.bundle-4.5.2.min.js diff --git a/bower.json b/bower.json index e96bb7f5..e7c40b63 100644 --- a/bower.json +++ b/bower.json @@ -18,7 +18,7 @@ ], "dependencies": { "jquery": ">= 1.11.0", - "bootstrap": ">= 2.3.2" + "bootstrap": ">= 4.5.2" }, "ignore": [ "docs/.*", diff --git a/dist/css/bootstrap-multiselect.css b/dist/css/bootstrap-multiselect.css index 6a6b68a2..c716ebfc 100644 --- a/dist/css/bootstrap-multiselect.css +++ b/dist/css/bootstrap-multiselect.css @@ -1 +1,118 @@ -span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container .multiselect-reset .input-group{width:93%}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.checkbox,.multiselect-container>li>a>label.radio{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0} \ No newline at end of file +/** + * Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/) + * + * Apache License, Version 2.0: + * Copyright (c) 2012 - 2018 David Stutz + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a + * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * BSD 3-Clause License: + * Copyright (c) 2012 - 2018 David Stutz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of David Stutz nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +span.multiselect-native-select { + position: relative; +} +span.multiselect-native-select select { + border: 0 !important; + clip: rect(0 0 0 0) !important; + height: 1px !important; + margin: -1px -1px -1px -3px !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + width: 1px !important; + left: 50%; + top: 30px; +} +.multiselect.dropdown-toggle:after { + display: none; +} +.multiselect-container { + position: absolute; + list-style-type: none; + margin: 0; + padding: 0; +} +.multiselect-container .multiselect-reset .input-group { + width: 93%; +} +.multiselect-container > a.dropdown-item, +.multiselect-container > span.dropdown-item, +.multiselect-container > a.dropdown-toggle, +.multiselect-container > span.dropdown-toggle { + cursor: pointer; +} +.multiselect-container > a.dropdown-item-text, +.multiselect-container > span.dropdown-item-text { + text-decoration: none; + color: initial; +} +.multiselect-container > a.multiselect-group, +.multiselect-container > span.multiselect-group { + margin: 0; + padding: 3px 15px; + height: 100%; +} +.multiselect-container > a.multiselect-group-clickable label, +.multiselect-container > span.multiselect-group-clickable label { + cursor: pointer; +} +.multiselect-container > a > span, +.multiselect-container > span > span { + margin: 0; + height: 100%; + cursor: pointer; + font-weight: normal; + padding: 3px 20px; +} +.multiselect-container > a > span.form-check, +.multiselect-container > span > span.form-check { + margin: 0; +} +.multiselect-container > a > span > input[type="checkbox"], +.multiselect-container > span > span > input[type="checkbox"] { + margin-bottom: 5px; +} +.multiselect-container > a > span > .form-check-label, +.multiselect-container > span > span > .form-check-label { + cursor: pointer; +} +.btn-group > .btn-group:nth-child(2) > .multiselect.btn { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.form-inline .multiselect-container span.form-check { + padding: 3px 20px 3px 40px; +} diff --git a/dist/css/bootstrap-multiselect.min.css b/dist/css/bootstrap-multiselect.min.css new file mode 100644 index 00000000..bfe13b91 --- /dev/null +++ b/dist/css/bootstrap-multiselect.min.css @@ -0,0 +1 @@ +span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.dropdown-toggle.multiselect:after{display:none}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container .multiselect-reset .input-group{width:93%}.multiselect-container>a.dropdown-item,.multiselect-container>span.dropdown-item{cursor:pointer}.multiselect-container>a.dropdown-item-text,.multiselect-container>span.dropdown-item-text{text-decoration:none;color:initial}.multiselect-container>a .multiselect-all label,.multiselect-container>span .multiselect-all label{font-weight:700}.multiselect-container>a.multiselect-group,.multiselect-container>span.multiselect-group{margin:0;padding:3px 15px;height:100%}.multiselect-container>a.multiselect-group-clickable label,.multiselect-container>span.multiselect-group-clickable label{cursor:pointer}.multiselect-container>a>span,.multiselect-container>span>span{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px}.multiselect-container>a>span.form-check,.multiselect-container>span>span.form-check{margin:0}.multiselect-container>a>span>input[type=checkbox],.multiselect-container>span>span>input[type=checkbox]{margin-bottom:5px}.multiselect-container>a>span>.form-check-label,.multiselect-container>span>span>.form-check-label{cursor:pointer}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container span.form-check{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a span.form-check input[type=checkbox],.form-inline .multiselect-container li a span.form-check input[type=radio]{margin-left:-20px;margin-right:0} \ No newline at end of file diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index 2a028b77..234a8ca7 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -59,7 +59,7 @@ ko.bindingHandlers.multiselect = { after: ['options', 'value', 'selectedOptions', 'enable', 'disable'], - init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { var $element = $(element); var config = ko.toJS(valueAccessor()); @@ -69,9 +69,9 @@ var options = allBindings.get('options'); if (ko.isObservable(options)) { ko.computed({ - read: function() { + read: function () { options(); - setTimeout(function() { + setTimeout(function () { var ms = $element.data('multiselect'); if (ms) ms.updateOriginalOptions();//Not sure how beneficial this is. @@ -90,9 +90,9 @@ var value = allBindings.get('value'); if (ko.isObservable(value)) { ko.computed({ - read: function() { + read: function () { value(); - setTimeout(function() { + setTimeout(function () { $element.multiselect('refresh'); }, 1); }, @@ -107,9 +107,9 @@ var selectedOptions = allBindings.get('selectedOptions'); if (ko.isObservable(selectedOptions)) { ko.computed({ - read: function() { + read: function () { selectedOptions(); - setTimeout(function() { + setTimeout(function () { $element.multiselect('refresh'); }, 1); }, @@ -155,12 +155,12 @@ } } - ko.utils.domNodeDisposal.addDisposeCallback(element, function() { + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $element.multiselect('destroy'); }); }, - update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { var $element = $(element); var config = ko.toJS(valueAccessor()); @@ -248,9 +248,9 @@ * @param {jQuery} select * @returns {String} */ - buttonText: function(options, select) { + buttonText: function (options, select) { if (this.disabledText.length > 0 - && (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) { + && (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) { return this.disabledText; } @@ -258,9 +258,9 @@ return this.nonSelectedText; } else if (this.allSelectedText - && options.length === $('option', $(select)).length - && $('option', $(select)).length !== 1 - && this.multiple) { + && options.length === $('option', $(select)).length + && $('option', $(select)).length !== 1 + && this.multiple) { if (this.selectAllNumber) { return this.allSelectedText + ' (' + options.length + ')'; @@ -276,7 +276,7 @@ var selected = ''; var delimiter = this.delimiterText; - options.each(function() { + options.each(function () { var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); selected += label + delimiter; }); @@ -291,7 +291,7 @@ * @param {jQuery} select * @returns {@exp;selected@call;substr} */ - buttonTitle: function(options, select) { + buttonTitle: function (options, select) { if (options.length === 0) { return this.nonSelectedText; } @@ -306,7 +306,7 @@ return selected.substr(0, selected.length - this.delimiterText.length); } }, - checkboxName: function(option) { + checkboxName: function (option) { return false; // no checkbox name }, /** @@ -315,7 +315,7 @@ * @param {jQuery} element * @returns {String} */ - optionLabel: function(element){ + optionLabel: function (element) { return $(element).attr('label') || $(element).text(); }, /** @@ -324,7 +324,7 @@ * @param {jQuery} element * @returns {String} */ - optionClass: function(element) { + optionClass: function (element) { return $(element).attr('class') || ''; }, /** @@ -335,7 +335,7 @@ * @param {jQuery} option * @param {Boolean} checked */ - onChange : function(option, checked) { + onChange: function (option, checked) { }, /** @@ -343,7 +343,7 @@ * * @param {jQuery} event */ - onDropdownShow: function(event) { + onDropdownShow: function (event) { }, /** @@ -351,7 +351,7 @@ * * @param {jQuery} event */ - onDropdownHide: function(event) { + onDropdownHide: function (event) { }, /** @@ -359,7 +359,7 @@ * * @param {jQuery} event */ - onDropdownShown: function(event) { + onDropdownShown: function (event) { }, /** @@ -367,19 +367,19 @@ * * @param {jQuery} event */ - onDropdownHidden: function(event) { + onDropdownHidden: function (event) { }, /** * Triggered on select all. */ - onSelectAll: function() { + onSelectAll: function () { }, /** * Triggered on deselect all. */ - onDeselectAll: function() { + onDeselectAll: function () { }, /** @@ -388,7 +388,7 @@ * @param {jQuery} $select * @param {jQuery} $container */ - onInitialized: function($select, $container) { + onInitialized: function ($select, $container) { }, /** @@ -396,11 +396,11 @@ * * @param {jQuery} $filter */ - onFiltering: function($filter) { + onFiltering: function ($filter) { }, enableHTML: false, - buttonClass: 'btn btn-default', + buttonClass: 'custom-select', inheritClass: false, buttonWidth: 'auto', buttonContainer: '
', @@ -439,14 +439,14 @@ includeResetDivider: false, resetText: 'Reset', templates: { - button: '', - ul: '', - filter: '
  • ', - filterClearBtn: '', - li: '
  • ', - divider: '
  • ', - liGroup: '
  • ', - resetButton: '
  • ' + button: '', + ul: '', + filter: '
    ', + filterClearBtn: '
    ', + li: '', + divider: '', + liGroup: '', + resetButton: '
    ' } }, @@ -455,7 +455,7 @@ /** * Builds the container of the multiselect. */ - buildContainer: function() { + buildContainer: function () { this.$container = $(this.options.buttonContainer); this.$container.on('show.bs.dropdown', this.options.onDropdownShow); this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); @@ -466,7 +466,7 @@ /** * Builds the button of the multiselect. */ - buildButton: function() { + buildButton: function () { this.$button = $(this.options.templates.button).addClass(this.options.buttonClass); if (this.$select.attr('class') && this.options.inheritClass) { this.$button.addClass(this.$select.attr('class')); @@ -482,9 +482,9 @@ // Manually add button width if set. if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') { this.$button.css({ - 'width' : '100%', //this.options.buttonWidth, - 'overflow' : 'hidden', - 'text-overflow' : 'ellipsis' + 'width': '100%', //this.options.buttonWidth, + 'overflow': 'hidden', + 'text-overflow': 'ellipsis' }); this.$container.css({ 'width': this.options.buttonWidth @@ -503,13 +503,16 @@ /** * Builds the ul representing the dropdown menu. */ - buildDropdown: function() { + buildDropdown: function () { // Build ul. this.$ul = $(this.options.templates.ul); if (this.options.dropRight) { - this.$ul.addClass('pull-right'); + this.$container.addClass('dropright'); + } + else if (this.options.dropUp) { + this.$container.addClass("dropup"); } // Set max height of dropdown menu to activate auto scrollbar. @@ -522,18 +525,9 @@ }); } - if (this.options.dropUp) { - - 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)); - var moveCalc = height + 34; - - this.$ul.css({ - 'max-height': height + 'px', - 'overflow-y': 'auto', - 'overflow-x': 'hidden', - 'margin-top': "-" + moveCalc + 'px' - }); - } + this.$ul.on("touchstart click", function (e) { + e.stopPropagation(); + }); this.$container.append(this.$ul); }, @@ -543,9 +537,9 @@ * * Uses createDivider and createOptionValue to create the necessary options. */ - buildDropdownOptions: function() { + buildDropdownOptions: function () { - this.$select.children().each($.proxy(function(index, element) { + this.$select.children().each($.proxy(function (index, element) { var $element = $(element); // Support optgroups and options without a group simultaneously. @@ -574,8 +568,8 @@ }, this)); // Bind the change event on the dropdown elements. - $(this.$ul).off('change', 'li:not(.multiselect-group) input[type="checkbox"], li:not(.multiselect-group) input[type="radio"]'); - $(this.$ul).on('change', 'li:not(.multiselect-group) input[type="checkbox"], li:not(.multiselect-group) input[type="radio"]', $.proxy(function(event) { + $(this.$ul).off('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]'); + $(this.$ul).on('change', '> *:not(.multiselect-group) input[type="checkbox"], > *:not(.multiselect-group) input[type="radio"]', $.proxy(function (event) { var $target = $(event.target); var checked = $target.prop('checked') || false; @@ -584,11 +578,11 @@ // Apply or unapply the configured selected class. if (this.options.selectedClass) { if (checked) { - $target.closest('li') + $target.closest('.multiselect-option') .addClass(this.options.selectedClass); } else { - $target.closest('li') + $target.closest('.multiselect-option') .removeClass(this.options.selectedClass); } } @@ -620,7 +614,7 @@ else { // Unselect all other options and corresponding checkboxes. if (this.options.selectedClass) { - $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass); + $($checkboxesNotThis).closest('.dropdown-item').removeClass(this.options.selectedClass); } $($checkboxesNotThis).prop('checked', false); @@ -631,7 +625,7 @@ } if (this.options.selectedClass === "active") { - $optionsNotThis.closest("a").css("outline", ""); + $optionsNotThis.closest(".dropdown-item").css("outline", ""); } } else { @@ -653,34 +647,34 @@ this.$select.change(); this.updateButtonText(); - if(this.options.preventInputChangeEvent) { + if (this.options.preventInputChangeEvent) { return false; } }, this)); - $('li a', this.$ul).on('mousedown', function(e) { + $('.multiselect-option', this.$ul).on('mousedown', function (e) { if (e.shiftKey) { // Prevent selecting text by Shift+click return false; } }); - $(this.$ul).on('touchstart click', 'li a', $.proxy(function(event) { + $(this.$ul).on('touchstart click', 'a', $.proxy(function (event) { event.stopPropagation(); var $target = $(event.target); if (event.shiftKey && this.options.multiple) { - if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) + if (!$target.is("input")) { // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) event.preventDefault(); - $target = $target.find("input"); + $target = $target.closest(".multiselect-option").find("input"); $target.prop("checked", !$target.prop("checked")); } var checked = $target.prop('checked') || false; if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range - var from = this.$ul.find("li:visible").index($target.parents("li")); - var to = this.$ul.find("li:visible").index(this.lastToggledInput.parents("li")); + var from = this.$ul.find(".multiselect-option:visible").index($target.closest(".multiselect-option")); + var to = this.$ul.find(".multiselect-option:visible").index(this.lastToggledInput.closest(".multiselect-option")); if (from > to) { // Swap the indices var tmp = to; @@ -692,12 +686,12 @@ ++to; // Change the checkboxes and underlying options - var range = this.$ul.find("li").not(".multiselect-filter-hidden").slice(from, to).find("input"); + var range = this.$ul.find(".multiselect-option:not(.multiselect-filter-hidden)").slice(from, to).find("input"); range.prop('checked', checked); if (this.options.selectedClass) { - range.closest('li') + range.closest('.multiselect-option') .toggleClass(this.options.selectedClass, checked); } @@ -713,100 +707,132 @@ // Trigger the select "change" event $target.trigger("change"); } + else if (!$target.is('input')) { + var $checkbox = $target.closest('.multiselect-option, .multiselect-all').find('.form-check-input'); + if($checkbox.length > 0) { + $checkbox.prop('checked', !$checkbox.prop('checked')); + $checkbox.change(); + } + else if(this.options.enableClickableOptGroups && this.options.multiple && !$target.hasClass("caret-container")) { + var groupItem = $target; + if(!groupItem.hasClass("multiselect-group")){ + groupItem = $target.closest('.multiselect-group'); + } + $checkbox = groupItem.find(".form-check-input"); + if($checkbox.length > 0) { + $checkbox.prop('checked', !$checkbox.prop('checked')); + $checkbox.change(); + } + } + + event.preventDefault(); + } // Remembers last clicked option - if($target.is("input") && !$target.closest("li").is(".multiselect-item")){ + var $input = $target.closest(".multiselect-option").find("input[type='checkbox'], input[type='radio']"); + if ($input.length > 0) { this.lastToggledInput = $target; } + else { + this.lastToggledInput = null; + } $target.blur(); }, this)); - // Keyboard support. - this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) { + //Keyboard support. + this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function (event) { if ($('input[type="text"]', this.$container).is(':focus')) { return; } - if (event.keyCode === 9 && this.$container.hasClass('open')) { + if (event.keyCode === 9 && this.$container.hasClass('show')) { this.$button.click(); } else { - var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible"); + var $items = $(this.$container).find(".multiselect-option:not(.disabled), .multiselect-group:not(.disabled), .multiselect-all").filter(":visible"); if (!$items.length) { return; } var index = $items.index($items.filter(':focus')); - + // Navigation up. - if (event.keyCode === 38 && index > 0) { - index--; - } + // if (event.keyCode === 38 && index > 0) { + // index--; + // } // Navigate down. - else if (event.keyCode === 40 && index < $items.length - 1) { - index++; - } - else if (!~index) { - index = 0; - } + // else if (event.keyCode === 40 && index < $items.length - 1) { + // index++; + // } + // else if (!~index) { + // index = 0; + // } var $current = $items.eq(index); - $current.focus(); + //$current.focus(); - if (event.keyCode === 32 || event.keyCode === 13) { + if (event.keyCode === 32/* || event.keyCode === 13*/) { var $checkbox = $current.find('input'); $checkbox.prop("checked", !$checkbox.prop("checked")); $checkbox.change(); + + event.preventDefault(); } - event.stopPropagation(); - event.preventDefault(); + if(event.keyCode === 13) { + setTimeout(function() { + $current.focus(); + }, 0); + } + + //event.stopPropagation(); + //event.preventDefault(); } }, this)); if (this.options.enableClickableOptGroups && this.options.multiple) { - $("li.multiselect-group input", this.$ul).on("change", $.proxy(function(event) { + $(".multiselect-group input", this.$ul).on("change", $.proxy(function (event) { event.stopPropagation(); var $target = $(event.target); var checked = $target.prop('checked') || false; - var $li = $(event.target).closest('li'); - var $group = $li.nextUntil("li.multiselect-group") + var $item = $(event.target).closest('.dropdown-item'); + var $group = $item.nextUntil(".multiselect-group") .not('.multiselect-filter-hidden') .not('.disabled'); var $inputs = $group.find("input"); - var values = []; var $options = []; if (this.options.selectedClass) { if (checked) { - $li.addClass(this.options.selectedClass); + $item.addClass(this.options.selectedClass); } else { - $li.removeClass(this.options.selectedClass); + $item.removeClass(this.options.selectedClass); } } - $.each($inputs, $.proxy(function(index, input) { - var value = $(input).val(); + $.each($inputs, $.proxy(function (index, input) { + var $input = $(input); + var value = $input.val(); var $option = this.getOptionByValue(value); if (checked) { - $(input).prop('checked', true); - $(input).closest('li') + $input.prop('checked', true); + $input.closest('.dropdown-item') .addClass(this.options.selectedClass); $option.prop('selected', true); } else { - $(input).prop('checked', false); - $(input).closest('li') + $input.prop('checked', false); + $input.closest('.dropdown-item') .removeClass(this.options.selectedClass); $option.prop('selected', false); @@ -826,13 +852,13 @@ } if (this.options.enableCollapsibleOptGroups && this.options.multiple) { - $("li.multiselect-group .caret-container", this.$ul).on("click", $.proxy(function(event) { - var $li = $(event.target).closest('li'); - var $inputs = $li.nextUntil("li.multiselect-group") - .not('.multiselect-filter-hidden'); + $(".multiselect-group .caret-container", this.$ul).on("click", $.proxy(function (event) { + var $li = $(event.target).closest('.multiselect-group'); + var $inputs = $li.nextUntil(".multiselect-group") + .not('.multiselect-filter-hidden'); var visible = true; - $inputs.each(function() { + $inputs.each(function () { visible = visible && !$(this).hasClass('multiselect-collapsible-hidden'); }); @@ -846,18 +872,54 @@ } }, this)); - $("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); - $("li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px'); - $("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px'); + $(".multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); } }, + /** + * Create a checkbox container with input and label based on given values + * @param {JQuery} $item + * @param {String} label + * @param {String} name + * @param {String} value + * @param {String} inputType + * @returns {JQuery} + */ + createCheckbox: function ($item, label, name, value, title, inputType) { + var $wrapper = $(''); + $wrapper.addClass("form-check d-inline-flex"); + $wrapper.attr("title", label); + + if (this.options.enableHTML && $(label).length > 0) { + $wrapper.append($(label)); + } + else { + var $checkboxLabel = $('