diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index 9a50a18a..36908150 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -48,7 +48,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()); @@ -58,9 +58,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. @@ -79,9 +79,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); }, @@ -96,9 +96,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); }, @@ -144,12 +144,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()); @@ -175,13 +175,14 @@ function Multiselect(select, options) { this.$select = $(select); - this.options = this.mergeOptions($.extend({}, options, this.$select.data())); // Placeholder via data attributes if (this.$select.attr("data-placeholder")) { - this.options.nonSelectedText = this.$select.data("placeholder"); + options.nonSelectedText = this.$select.data("placeholder"); } + this.options = this.mergeOptions($.extend({}, options, this.$select.data())); + // Initialization. // We have to clone to create a new reference. this.originalOptions = this.$select.clone()[0].options; @@ -215,12 +216,11 @@ this.updateOptGroups(); } - this.options.wasDisabled = this.$select.prop('disabled'); if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { this.disable(); } - this.$select.wrap('').after(this.$container); + this.$select.wrap('').after(this.$container); this.options.onInitialized(this.$select, this.$container); } @@ -236,9 +236,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; } @@ -264,7 +264,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; }); @@ -279,7 +279,7 @@ * @param {jQuery} select * @returns {@exp;selected@call;substr} */ - buttonTitle: function(options, select) { + buttonTitle: function (options, select) { if (options.length === 0) { return this.nonSelectedText; } @@ -294,7 +294,7 @@ return selected.substr(0, selected.length - this.delimiterText.length); } }, - checkboxName: function(option) { + checkboxName: function (option) { return false; // no checkbox name }, /** @@ -303,7 +303,7 @@ * @param {jQuery} element * @returns {String} */ - optionLabel: function(element){ + optionLabel: function (element) { return $(element).attr('label') || $(element).text(); }, /** @@ -312,7 +312,7 @@ * @param {jQuery} element * @returns {String} */ - optionClass: function(element) { + optionClass: function (element) { return $(element).attr('class') || ''; }, /** @@ -323,7 +323,7 @@ * @param {jQuery} option * @param {Boolean} checked */ - onChange : function(option, checked) { + onChange: function (option, checked) { }, /** @@ -331,7 +331,7 @@ * * @param {jQuery} event */ - onDropdownShow: function(event) { + onDropdownShow: function (event) { }, /** @@ -339,7 +339,7 @@ * * @param {jQuery} event */ - onDropdownHide: function(event) { + onDropdownHide: function (event) { }, /** @@ -347,7 +347,7 @@ * * @param {jQuery} event */ - onDropdownShown: function(event) { + onDropdownShown: function (event) { }, /** @@ -355,19 +355,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 () { }, /** @@ -376,7 +376,7 @@ * @param {jQuery} $select * @param {jQuery} $container */ - onInitialized: function($select, $container) { + onInitialized: function ($select, $container) { }, /** @@ -384,7 +384,7 @@ * * @param {jQuery} $filter */ - onFiltering: function($filter) { + onFiltering: function ($filter) { }, enableHTML: false, @@ -411,6 +411,7 @@ enableClickableOptGroups: false, enableCollapsibleOptGroups: false, filterPlaceholder: 'Search', + filterByGroup: false, // possible options: 'text', 'value', 'both' filterBehavior: 'text', includeFilterClearBtn: true, @@ -438,7 +439,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); @@ -449,7 +450,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')); @@ -465,9 +466,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 @@ -486,7 +487,7 @@ /** * Builds the ul representing the dropdown menu. */ - buildDropdown: function() { + buildDropdown: function () { // Build ul. this.$ul = $(this.options.templates.ul); @@ -507,7 +508,7 @@ 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 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({ @@ -526,9 +527,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. @@ -557,7 +558,7 @@ }, this)); // Bind the change event on the dropdown elements. - $('li:not(.multiselect-group) input', this.$ul).on('change', $.proxy(function(event) { + $('li:not(.multiselect-group) input', this.$ul).on('change', $.proxy(function (event) { var $target = $(event.target); var checked = $target.prop('checked') || false; @@ -585,10 +586,10 @@ if (isSelectAllOption) { if (checked) { - this.selectAll(this.options.selectAllJustVisible, true); + this.selectAll(this.options.selectAllJustVisible); } else { - this.deselectAll(this.options.selectAllJustVisible, true); + this.deselectAll(this.options.selectAllJustVisible); } } else { @@ -635,25 +636,25 @@ this.$select.change(); this.updateButtonText(); - if(this.options.preventInputChangeEvent) { + if (this.options.preventInputChangeEvent) { return false; } }, this)); - $('li a', this.$ul).on('mousedown', function(e) { + $('li a', this.$ul).on('mousedown', function (e) { if (e.shiftKey) { // Prevent selecting text by Shift+click return false; } }); - $('li a', this.$ul).on('touchstart click', $.proxy(function(event) { + $('li a', this.$ul).on('touchstart click', $.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("label")) { // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) event.preventDefault(); $target = $target.find("input"); $target.prop("checked", !$target.prop("checked")); @@ -697,7 +698,7 @@ } // Remembers last clicked option - if($target.is("input") && !$target.closest("li").is(".multiselect-item")){ + if ($target.is("input") && !$target.closest("li").is(".multiselect-item")) { this.lastToggledInput = $target; } @@ -705,7 +706,7 @@ }, this)); // Keyboard support. - this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) { + this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function (event) { if ($('input[type="text"]', this.$container).is(':focus')) { return; } @@ -726,7 +727,7 @@ if (event.keyCode === 38 && index > 0) { index--; } - // Navigate down. + // Navigate down. else if (event.keyCode === 40 && index < $items.length - 1) { index++; } @@ -750,7 +751,7 @@ }, this)); if (this.options.enableClickableOptGroups && this.options.multiple) { - $("li.multiselect-group input", this.$ul).on("change", $.proxy(function(event) { + $("li.multiselect-group input", this.$ul).on("change", $.proxy(function (event) { event.stopPropagation(); var $target = $(event.target); @@ -766,16 +767,7 @@ var values = []; var $options = []; - if (this.options.selectedClass) { - if (checked) { - $li.addClass(this.options.selectedClass); - } - else { - $li.removeClass(this.options.selectedClass); - } - } - - $.each($inputs, $.proxy(function(index, input) { + $.each($inputs, $.proxy(function (index, input) { var value = $(input).val(); var $option = this.getOptionByValue(value); @@ -807,13 +799,13 @@ } if (this.options.enableCollapsibleOptGroups && this.options.multiple) { - $("li.multiselect-group .caret-container", this.$ul).on("click", $.proxy(function(event) { + $("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'); var visible = true; - $inputs.each(function() { + $inputs.each(function () { visible = visible && $(this).is(':visible'); }); @@ -838,7 +830,7 @@ * * @param {jQuery} element */ - createOptionValue: function(element) { + createOptionValue: function (element) { var $element = $(element); if ($element.is(':selected')) { $element.prop('selected', true); @@ -906,7 +898,7 @@ * * @param {jQuery} element */ - createDivider: function(element) { + createDivider: function (element) { var $divider = $(this.options.templates.divider); this.$ul.append($divider); }, @@ -916,7 +908,7 @@ * * @param {jQuery} group */ - createOptgroup: function(group) { + createOptgroup: function (group) { var label = $(group).attr("label"); var value = $(group).attr("value"); var $li = $('
  • '); @@ -945,7 +937,7 @@ this.$ul.append($li); - $("option", group).each($.proxy(function($, group) { + $("option", group).each($.proxy(function ($, group) { this.createOptionValue(group); }, this)) }, @@ -955,7 +947,7 @@ * * Checks if a select all has already been created. */ - buildSelectAll: function() { + buildSelectAll: function () { if (typeof this.options.selectAllValue === 'number') { this.options.selectAllValue = this.options.selectAllValue.toString(); } @@ -1003,7 +995,7 @@ /** * Builds the filter. */ - buildFilter: function() { + buildFilter: function () { // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { @@ -1015,9 +1007,9 @@ $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); // Adds optional filter clear button - if(this.options.includeFilterClearBtn) { + if (this.options.includeFilterClearBtn) { var clearBtn = $(this.options.templates.filterClearBtn); - clearBtn.on('click', $.proxy(function(event){ + clearBtn.on('click', $.proxy(function (event) { clearTimeout(this.searchTimeout); this.$filter.find('.multiselect-search').val(''); @@ -1035,24 +1027,24 @@ this.$ul.prepend(this.$filter); - this.$filter.val(this.query).on('click', function(event) { + this.$filter.val(this.query).on('click', function (event) { event.stopPropagation(); - }).on('input keydown', $.proxy(function(event) { + }).on('input keydown', $.proxy(function (event) { // Cancel enter key default behaviour if (event.which === 13) { - event.preventDefault(); - } + event.preventDefault(); + } // This is useful to catch "keydown" events after the browser has updated the control. clearTimeout(this.searchTimeout); - this.searchTimeout = this.asyncFunction($.proxy(function() { + this.searchTimeout = this.asyncFunction($.proxy(function () { if (this.query !== event.target.value) { this.query = event.target.value; var currentGroup, currentGroupVisible; - $.each($('li', this.$ul), $.proxy(function(index, element) { + $.each($('li', this.$ul), $.proxy(function (index, element) { var value = $('input', element).length > 0 ? $('input', element).val() : ""; var text = $('label', element).text(); @@ -1075,7 +1067,9 @@ if (this.options.enableCaseInsensitiveFiltering) { filterCandidate = filterCandidate.toLowerCase(); + filterCandidate = removeDiacritics(filterCandidate); this.query = this.query.toLowerCase(); + this.query = removeDiacritics(this.query); } if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') { @@ -1088,27 +1082,64 @@ showElement = true; } - // Toggle current element (group or group item) according to showElement boolean. - $(element).toggle(showElement) - .toggleClass('multiselect-filter-hidden', !showElement); + if (this.options.filterByGroup) { + // Toggle current element (group or group item) according to showElement boolean. + $(element).toggle(showElement) + .toggleClass('multiselect-filter-hidden', !showElement); - // Differentiate groups and group items. - if ($(element).hasClass('multiselect-group')) { - // Remember group status. - currentGroup = element; - currentGroupVisible = showElement; + // Differentiate groups and group items. + if ($(element).hasClass('multiselect-group')) { + // Remember group status. + currentGroup = element; + currentGroupVisible = showElement; + } + else { + // Show group name when at least one of its items is visible. + if (showElement) { + if (currentGroupVisible) { + $(currentGroup).show() + .removeClass('multiselect-filter-hidden'); + $(element).show() + .removeClass('multiselect-filter-hidden'); + } + else { + $(currentGroup).hide() + .addClass('multiselect-filter-hidden'); + $(element).hide() + .addClass('multiselect-filter-hidden'); + } + } + + // Show all group items when group name satisfies filter. + if (!showElement && currentGroupVisible) { + $(element).show() + .removeClass('multiselect-filter-hidden'); + } + } } else { - // Show group name when at least one of its items is visible. - if (showElement) { - $(currentGroup).show() - .removeClass('multiselect-filter-hidden'); + // Toggle current element (group or group item) according to showElement boolean. + $(element).toggle(showElement) + .toggleClass('multiselect-filter-hidden', !showElement); + + // Differentiate groups and group items. + if ($(element).hasClass('multiselect-group')) { + // Remember group status. + currentGroup = element; + currentGroupVisible = showElement; } - - // Show all group items when group name satisfies filter. - if (!showElement && currentGroupVisible) { - $(element).show() - .removeClass('multiselect-filter-hidden'); + else { + // Show group name when at least one of its items is visible. + if (showElement) { + $(currentGroup).show() + .removeClass('multiselect-filter-hidden'); + } + + // Show all group items when group name satisfies filter. + if (!showElement && currentGroupVisible) { + $(element).show() + .removeClass('multiselect-filter-hidden'); + } } } } @@ -1132,13 +1163,9 @@ /** * Unbinds the whole plugin. */ - destroy: function() { + destroy: function () { this.$container.remove(); this.$select.show(); - - // reset original state - this.$select.prop('disabled', this.options.wasDisabled); - this.$select.data('multiselect', null); }, @@ -1205,8 +1232,8 @@ * @param {Array} selectValues * @param {Boolean} triggerOnChange */ - select: function(selectValues, triggerOnChange) { - if(!$.isArray(selectValues)) { + select: function (selectValues, triggerOnChange) { + if (!$.isArray(selectValues)) { selectValues = [selectValues]; } @@ -1220,7 +1247,7 @@ var $option = this.getOptionByValue(value); var $checkbox = this.getInputByValue(value); - if($option === undefined || $checkbox === undefined) { + if ($option === undefined || $checkbox === undefined) { continue; } @@ -1271,8 +1298,8 @@ * @param {Array} deselectValues * @param {Boolean} triggerOnChange */ - deselect: function(deselectValues, triggerOnChange) { - if(!$.isArray(deselectValues)) { + deselect: function (deselectValues, triggerOnChange) { + if (!$.isArray(deselectValues)) { deselectValues = [deselectValues]; } @@ -1286,7 +1313,7 @@ var $option = this.getOptionByValue(value); var $checkbox = this.getInputByValue(value); - if($option === undefined || $checkbox === undefined) { + if ($option === undefined || $checkbox === undefined) { continue; } @@ -1325,28 +1352,28 @@ var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); - if(justVisible) { - $('input:enabled' , visibleLis).prop('checked', true); + if (justVisible) { + $('input:enabled', visibleLis).prop('checked', true); visibleLis.addClass(this.options.selectedClass); - $('input:enabled' , visibleLis).each($.proxy(function(index, element) { + $('input:enabled', visibleLis).each($.proxy(function (index, element) { var value = $(element).val(); var option = this.getOptionByValue(value); $(option).prop('selected', true); }, this)); } else { - $('input:enabled' , allLis).prop('checked', true); + $('input:enabled', allLis).prop('checked', true); allLis.addClass(this.options.selectedClass); - $('input:enabled' , allLis).each($.proxy(function(index, element) { + $('input:enabled', allLis).each($.proxy(function (index, element) { var value = $(element).val(); var option = this.getOptionByValue(value); $(option).prop('selected', true); }, this)); } - $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', true); + $('li input[value="' + this.options.selectAllValue + '"]').prop('checked', true); if (this.options.enableClickableOptGroups && this.options.multiple) { this.updateOptGroups(); @@ -1370,28 +1397,28 @@ var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); - if(justVisible) { - $('input[type="checkbox"]:enabled' , visibleLis).prop('checked', false); + if (justVisible) { + $('input[type="checkbox"]:enabled', visibleLis).prop('checked', false); visibleLis.removeClass(this.options.selectedClass); - $('input[type="checkbox"]:enabled' , visibleLis).each($.proxy(function(index, element) { + $('input[type="checkbox"]:enabled', visibleLis).each($.proxy(function (index, element) { var value = $(element).val(); var option = this.getOptionByValue(value); $(option).prop('selected', false); }, this)); } else { - $('input[type="checkbox"]:enabled' , allLis).prop('checked', false); + $('input[type="checkbox"]:enabled', allLis).prop('checked', false); allLis.removeClass(this.options.selectedClass); - $('input[type="checkbox"]:enabled' , allLis).each($.proxy(function(index, element) { + $('input[type="checkbox"]:enabled', allLis).each($.proxy(function (index, element) { var value = $(element).val(); var option = this.getOptionByValue(value); $(option).prop('selected', false); }, this)); } - $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', false); + $('li input[value="' + this.options.selectAllValue + '"]').prop('checked', false); if (this.options.enableClickableOptGroups && this.options.multiple) { this.updateOptGroups(); @@ -1407,7 +1434,7 @@ * * Rebuilds the dropdown, the filter and the select all option. */ - rebuild: function() { + rebuild: function () { this.$ul.html(''); // Important to distinguish between radios and checkboxes. @@ -1439,7 +1466,7 @@ /** * The provided data will be used to build the dropdown. */ - dataprovider: function(dataprovider) { + dataprovider: function (dataprovider) { var groupCounter = 0; var $select = this.$select.empty(); @@ -1455,7 +1482,7 @@ disabled: !!option.disabled }); - forEach(option.children, function(subOption) { // add children option tags + forEach(option.children, function (subOption) { // add children option tags var attributes = { value: subOption.value, label: subOption.label || subOption.value, @@ -1465,10 +1492,10 @@ }; //Loop through attributes object and add key-value for each attribute - for (var key in subOption.attributes) { + for (var key in subOption.attributes) { attributes['data-' + key] = subOption.attributes[key]; - } - //Append original attributes + new data attributes to option + } + //Append original attributes + new data attributes to option $tag.append($('