diff --git a/dist/js/bootstrap-multiselect.js b/dist/js/bootstrap-multiselect.js index b5ac66d6..48d2728f 100644 --- a/dist/js/bootstrap-multiselect.js +++ b/dist/js/bootstrap-multiselect.js @@ -1378,6 +1378,58 @@ } }, + /** + * Set the current selection + * + * Helper function to set which options are currently selected by invoking + * 'select' for all unselected values that appear in the given list and + * invoking 'deselect' for all selected values that do NOT appear in the + * given list + * + * @param {Array} selection + * @param {Boolean} triggerOnChange + */ + setSelected: function (selection, triggerOnChange) { + if (!$.isArray(selection)) { + selection = [selection]; + } + + // Get all of the enabled options + var opts = $("option:enabled", this.$select); + var selectedOpts = opts.filter(":selected"); + var notSelectedOpts = opts.not(":selected"); + var values; + var val; + + // Find all the not-selected options + values = notSelectedOpts.map(function () { + // If their value is in the array, it should be selected so + // return that value (otherwise, return null to omit it from the result) + val = $(this).val(); + return ($.inArray(val, selection) > -1) ? val : null; + }).get(); + + if ( values.length ) + { + // Select all that need to be selected + this.select(values, triggerOnChange); + } + + // Find all the selected options + values = selectedOpts.map(function () { + // If their value is NOT in the array, it should be deselected so + // return that value (otherwise, return null to omit it from the result) + val = $(this).val(); + return ($.inArray(val, selection) == -1) ? val : null; + }).get(); + + if ( values.length ) + { + // Deselect all that should not be selected + this.deselect(values, triggerOnChange); + } + }, + /** * Rebuild the plugin. * diff --git a/index.html b/index.html index 66143e2b..83fa0dda 100644 --- a/index.html +++ b/index.html @@ -3750,6 +3750,160 @@

Methods

}); }); </script> + + + + + + + .multiselect('setSelected', selection) + + +

+ Explicitly sets which options are selected (also works with an array of values). Any currently selected options + that do not appear in the given array will be deselected. Any deselected options that do appear in the given array + will be selected. +

+ +
+
+ +
+ + +
+
+
+
+
+<script type="text/javascript">
+    $(document).ready(function() {
+        $('#example-setSelected').multiselect();
+
+        $('#example-setSelected-button').on('click', function() {
+            $('#example-setSelected').multiselect('setSelected', ['1', '2', '4']);
+
+            alert('Selected 1, 2 and 4.  Deselected 3, 5, and 6.');
+        });
+    });
+</script>
+
+
+ +

+ The method provides an additional parameter: .multiselect('setSelected', selection, triggerOnChange). If the third parameter is set to true, the method will manually trigger the onChange option. +

+ +
+
+ +
+ + +
+
+
+
+
+<script type="text/javascript">
+    $(document).ready(function() {
+        $('#example-setSelected-onChange').multiselect({
+            onChange: function(option, checked, select) {
+                alert('onChange triggered: ' + $(option).val() + ' was ' + (checked ? '' : 'de') + 'selected.');
+            }
+        });
+
+        $('#example-setSelected-onChange-button').on('click', function() {
+            $('#example-setSelected-onChange').multiselect('setSelected', '1', true);
+        });
+    });
+</script>
+
+
+ +

+ The above parameter also works when multiple options change (selected or deselected). Note that onChange is called for each selected and deselected option individually. +

+ +
+
+ +
+ + +
+
+
+
+
+<script type="text/javascript">
+    $(document).ready(function() {
+        $('#example-setSelected-onChange-array').multiselect({
+            onChange: function(option, checked, select) {
+                alert('onChange triggered: ' + $(option).val() + ' was ' + (checked ? '' : 'de') + 'selected.');
+            }
+        });
+
+        $('#example-setSelected-onChange-array-button').on('click', function() {
+            $('#example-setSelected-onChange-array').multiselect('setSelected', ['2','4'], true);
+        });
+    });
+</script>
 
diff --git a/tests/spec/bootstrap-multiselect.js b/tests/spec/bootstrap-multiselect.js index 280b5d20..4e003933 100644 --- a/tests/spec/bootstrap-multiselect.js +++ b/tests/spec/bootstrap-multiselect.js @@ -3,19 +3,19 @@ describe('Bootstrap Multiselect "Core".', function() { beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 100; i++) { var $option = $(''); - + if (i < 10) { $option.prop('selected', true); } - + $select.append($option); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', onInitialized: function($select) { @@ -26,182 +26,182 @@ describe('Bootstrap Multiselect "Core".', function() { } }); }); - + it('Should add the container after the select.', function() { expect($('#multiselect-container').length).toBe(1); }); - + it('Should add the multiselect button.', function() { expect($('#multiselect-container .multiselect').length).toBe(1); }); - + it('Should add the dropdown menu.', function() { expect($('#multiselect-container .dropdown-menu').length).toBe(1); }); - + it('Should add an li element with checkbox and label for each option.', function() { expect($('#multiselect-container li').length).toBe(99); expect($('#multiselect-container label').length).toBe(99); expect($('#multiselect-container input[type="checkbox"]').length).toBe(99); }); - + it('Should preserve selected options.', function() { expect($('#multiselect-container input[type="checkbox"]:checked').length).toBe(9); expect($('#multiselect option:selected').length).toBe(9); }); - + it('Should be able to select options by value.', function() { $('#multiselect').multiselect('select', '10'); - + expect($('#multiselect option[value="10"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(true); }); - + it('Select method should handle "null" and "undefined" correctly.', function() { expect($('#multiselect option:selected').length).toBe(9); - + $('#multiselect').multiselect('select', null); expect($('#multiselect option:selected').length).toBe(9); - + $('#multiselect').multiselect('select', undefined); expect($('#multiselect option:selected').length).toBe(9); }); - + it('Should be able to deselect options by value.', function() { $('#multiselect').multiselect('select', '10'); $('#multiselect').multiselect('deselect', '10'); - + expect($('#multiselect option[value="10"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(false); }); - + it('Deselect method should handle "null" and "undefined" correctly.', function() { expect($('#multiselect option:selected').length).toBe(9); - + $('#multiselect').multiselect('deselect', null); expect($('#multiselect option:selected').length).toBe(9); - + $('#multiselect').multiselect('deselect', undefined); expect($('#multiselect option:selected').length).toBe(9); }); - + it('Should be able to select options using an array of values.', function() { $('#multiselect').multiselect('select', ['10', '11']); - + expect($('#multiselect option[value="10"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(true); - + expect($('#multiselect option[value="11"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="11"]').prop('checked')).toBe(true); }); - + it('Should be able to deselect options using an array of values.', function() { $('#multiselect').multiselect('select', ['10', '11']); $('#multiselect').multiselect('deselect', ['10', '11']); - + expect($('#multiselect option[value="10"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(false); - + expect($('#multiselect option[value="11"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="11"]').prop('checked')).toBe(false); }); - + it('Should be able to disable the multiselect', function() { $('#multiselect').multiselect('disable'); - + expect($('#multiselect').prop('disabled')).toBe(true); }); - + it('Should be able to enable the multiselect', function() { $('#multiselect').multiselect('disable'); $('#multiselect').multiselect('enable'); - + expect($('#multiselect').prop('disabled')).toBe(false); }); - + it('Should be able to select all options.', function() { $('#multiselect').multiselect('selectAll'); - + for (var i = 1; i < 100; i++) { expect($('#multiselect option[value="' + i.toString() + '"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="' + i.toString() + '"]').prop('checked')).toBe(true); } }); - + it('Should be able to deselect all options.', function() { $('#multiselect').multiselect('selectAll'); - + for (var i = 1; i < 100; i++) { expect($('#multiselect option[value="' + i.toString() + '"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="' + i.toString() + '"]').prop('checked')).toBe(true); } - + $('#multiselect').multiselect('deselectAll'); - + for (var i = 1; i < 100; i++) { expect($('#multiselect option[value="' + i.toString() + '"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="' + i.toString() + '"]').prop('checked')).toBe(false); } }); - + it('Should update the checkboxes according to the selected options after using refresh.', function() { for (var i = 10; i < 20; i++) { $('#multiselect option[value="' + i + '"]').prop('selected', true); } - + expect($('#multiselect option:selected').length).toBe(19); expect($('#multiselect-container input[type="checkbox"]:checked').length).toBe(9); - + $('#multiselect').multiselect('refresh'); - + expect($('#multiselect-container input[type="checkbox"]:checked').length).toBe(19); - + for (var i = 10; i < 20; i++) { expect($('#multiselect option[value="' + i + '"]').prop('selected')).toBe(true); } }); - + it('Should remove container, button and ul after destroy.', function() { $('#multiselect').multiselect('destroy'); - + // Destroy should remove container, button and ul. expect($('#multiselect-container.multiselect-container').length).toBe(0); expect($('#multiselect-container .multiselect').length).toBe(0); expect($('#multiselect-container .dropdown-menu').length).toBe(0); }); - + it('Should select an option when checkbox is changed (change event).', function() { $('#multiselect-container li input[value="10"]').prop('checked', true); $('#multiselect-container li input[value="10"]').trigger('change'); - + expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(true); expect($('#multiselect option[value="10"]').prop('selected')).toBe(true); }); - + it('Should deselect an option when checkbox is changed (change event).', function() { $('#multiselect-container li input[value="10"]').prop('checked', true); $('#multiselect-container li input[value="10"]').trigger('change'); - + // Already checked above. - + $('#multiselect-container li input[value="10"]').prop('checked', false); $('#multiselect-container li input[value="10"]').trigger('change'); - + expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(false); expect($('#multiselect option[value="10"]').prop('selected')).toBe(false); }); - + it('Should select an option when checkbox is clicked.', function() { $('#multiselect-container li input[value="10"]').click(); - + expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(true); expect($('#multiselect option[value="10"]').prop('selected')).toBe(true); }); - + it('Should deselect an option when checkbox is clicked.', function() { $('#multiselect-container li input[value="10"]').click(); $('#multiselect-container li input[value="10"]').click(); - + expect($('#multiselect-container input[value="10"]').prop('checked')).toBe(false); expect($('#multiselect option[value="10"]').prop('selected')).toBe(false); }); @@ -225,29 +225,29 @@ describe('Bootstrap Multiselect "Core".', function() { describe('Bootstrap Multiselect "Single Selection"', function() { beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 100; i++) { $select.append(''); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
' }); }); - + it('Should create radio buttons instead of checkboxes.', function() { expect($('#multiselect-container input[type="radio"]').length).toBe(99); expect($('#multiselect-container input[type="checkbox"]').length).toBe(0); - + // Browser selects one option per default. expect($('#multiselect option:selected').length).toBe(1); }); - + it('Only one option at a time can be selected.', function() { expect($('#multiselect option:selected').length).toBe(1); - + var i = 0; $('#multiselect-container input').each(function() { if (i === 0) { @@ -257,18 +257,18 @@ describe('Bootstrap Multiselect "Single Selection"', function() { else { expect($(this).prop('checked')).toBe(false); $(this).click(); - + expect($('#multiselect option:selected').length).toBe(1); expect($(this).prop('checked')).toBe(true); i++; } }); }); - + it('Deselect all should work.', function() { expect($('#multiselect option:selected').length).toBe(1); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); @@ -276,16 +276,16 @@ describe('Bootstrap Multiselect "Single Selection"', function() { }); describe('Bootstrap Multiselect "Clickable Optgroups"', function() { - + // Count the number of onChanges fired. var fired = 0; - + beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 11; i++) { var $optgroup = $(''); - + for (var j = 1; j < 11; j++) { if (i == 1) { $optgroup.append(''); @@ -294,12 +294,12 @@ describe('Bootstrap Multiselect "Clickable Optgroups"', function() { $optgroup.append(''); } } - + $select.append($optgroup); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', enableClickableOptGroups: true, @@ -309,20 +309,20 @@ describe('Bootstrap Multiselect "Clickable Optgroups"', function() { } }); }); - + it('Should correctly create inputs for optgroups.', function() { expect($('#multiselect-container li.multiselect-group').length).toBe(10); expect($('#multiselect-container li.multiselect-group input').length).toBe(10); - + $('#multiselect-container label.multiselect-group').each(function() { expect($('input', $(this)).length).toBe(10); }); }); - + it('Groups should be clickable and correctly initialized.', function() { expect($('#multiselect option:selected').length).toBe(10); expect(fired).toBe(0); - + var i = 0; $('#multiselect-container li.multiselect-group').each(function() { if (i == 0) { @@ -338,7 +338,7 @@ describe('Bootstrap Multiselect "Clickable Optgroups"', function() { } else { $('label', $(this)).click(); - + expect($('option:selected', $('#multiselect optgroup')[i]).length).toBe(10); expect($('#multiselect option:selected').length).toBe(20); @@ -348,27 +348,27 @@ describe('Bootstrap Multiselect "Clickable Optgroups"', function() { i++; }); }); - + it('Clickable groups should fire onChange only once.', function() { expect($('#multiselect option:selected').length).toBe(10); - + fired = 0; expect(fired).toBe(0); - + var i = 0; $('#multiselect-container li.multiselect-group').each(function() { $('label', $(this)).click(); - + // Selected expect(fired).toBe(1); fired = 0; - + $('label', $(this)).click(); - + // Deselected expect(fired).toBe(1); fired = 0; - + i++; }); }); @@ -457,7 +457,7 @@ describe('Bootstrap Multiselect "Clickable Optgroups"', function() { $('#multiselect').multiselect('select', values); expect($('input', $group).prop('checked')).toBe(true); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); @@ -465,25 +465,25 @@ describe('Bootstrap Multiselect "Clickable Optgroups"', function() { }); describe('Bootstrap Multiselect "Collapsible Optgroups"', function() { - + // Count the number of onChanges fired. var fired = 0; - + beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 11; i++) { var $optgroup = $(''); - + for (var j = 1; j < 11; j++) { $optgroup.append(''); } - + $select.append($optgroup); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', enableCollapsibleOptGroups: true, @@ -492,52 +492,52 @@ describe('Bootstrap Multiselect "Collapsible Optgroups"', function() { } }); }); - + it('Should correctly create headers for optgroups.', function() { expect($('#multiselect-container li.multiselect-group').length).toBe(10); - + $('#multiselect-container label.multiselect-group').each(function() { expect($('input', $(this)).length).toBe(10); }); }); - + if ('Should not create inputs.', function() { expect($('#multiselect-container li.multiselect-group input').length).toBe(0); }); - + it('Groups should not be clickable.', function() { expect($('#multiselect option:selected').length).toBe(0); - + var i = 0; $('#multiselect-container li.multiselect-group').each(function() { $('label', $(this)).click(); expect($('option:selected', $('#multiselect optgroup')[i]).length).toBe(0); expect($('#multiselect option:selected').length).toBe(0); - + $('label', $(this)).click(); i++; }); }); - + it('Should be collapsible.', function() { var $group = $('#multiselect-container li.multiselect-group:first'); $('.caret-container', $group).click(); - + var $lis = $group.nextUntil('li.multiselect-group'); $lis.each(function() { expect($(this).hasClass('multiselect-collapsible-hidden')).toBe(true); expect($(this).hasClass('multiselect-collapsible-hidden')).toBe($(this).is(':hidden')); }); - + $('.caret-container', $group).click(); - + var $lis = $group.nextUntil('li.multiselect-group'); $lis.each(function() { expect($(this).hasClass('multiselect-collapsible-hidden')).toBe(false); expect($(this).hasClass('multiselect-collapsible-hidden')).toBe($(this).is(':hidden')); }); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); @@ -545,25 +545,25 @@ describe('Bootstrap Multiselect "Collapsible Optgroups"', function() { }); describe('Bootstrap Multiselect "Clickable+Collapsible Optgroups"', function() { - + // Count the number of onChanges fired. var fired = 0; - + beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 11; i++) { var $optgroup = $(''); - + for (var j = 1; j < 11; j++) { $optgroup.append(''); } - + $select.append($optgroup); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', enableClickableOptGroups: true, @@ -573,73 +573,73 @@ describe('Bootstrap Multiselect "Clickable+Collapsible Optgroups"', function() { } }); }); - + it('Should correctly create inputs for optgroups.', function() { expect($('#multiselect-container li.multiselect-group').length).toBe(10); expect($('#multiselect-container li.multiselect-group input').length).toBe(10); - + $('#multiselect-container label.multiselect-group').each(function() { expect($('input', $(this)).length).toBe(10); }); }); - + it('Groups should be clickable.', function() { expect($('#multiselect option:selected').length).toBe(0); - + var i = 0; $('#multiselect-container li.multiselect-group').each(function() { $('label', $(this)).click(); expect($('option:selected', $('#multiselect optgroup')[i]).length).toBe(10); expect($('#multiselect option:selected').length).toBe(10); - + $('label', $(this)).click(); i++; }); }); - + it('Clickable groups should fire onChange only once.', function() { expect($('#multiselect option:selected').length).toBe(0); - + fired = 0; expect(fired).toBe(0); - + var i = 0; $('#multiselect-container li.multiselect-group').each(function() { $('label', $(this)).click(); - + // Selected expect(fired).toBe(1); fired = 0; - + $('label', $(this)).click(); - + // Deselected expect(fired).toBe(1); fired = 0; - + i++; }); }); - + it('Should be collapsible.', function() { var $group = $('#multiselect-container li.multiselect-group:first'); $('.caret-container', $group).click(); - + var $lis = $group.nextUntil('li.multiselect-group'); $lis.each(function() { expect($(this).hasClass('multiselect-collapsible-hidden')).toBe(true); expect($(this).hasClass('multiselect-collapsible-hidden')).toBe($(this).is(':hidden')); }); - + $('.caret-container', $group).click(); - + var $lis = $group.nextUntil('li.multiselect-group'); $lis.each(function() { expect($(this).hasClass('multiselect-collapsible-hidden')).toBe(false); expect($(this).hasClass('multiselect-collapsible-hidden')).toBe($(this).is(':hidden')); }); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); @@ -647,25 +647,25 @@ describe('Bootstrap Multiselect "Clickable+Collapsible Optgroups"', function() { }); describe('Bootstrap Multiselect "Clickable+Collapsible+SelectAll Optgroups"', function() { - + // Count the number of onChanges fired. var fired = 0; - + beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 11; i++) { var $optgroup = $(''); - + for (var j = 1; j < 11; j++) { $optgroup.append(''); } - + $select.append($optgroup); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', enableClickableOptGroups: true, @@ -674,26 +674,26 @@ describe('Bootstrap Multiselect "Clickable+Collapsible+SelectAll Optgroups"', fu selectAllValue: 'multiselect-all' }); }); - + it('Should handle option groups differently, i.e. not set class to active.', function() { // Otherwise they are hidden. $('#multiselect-container input[value="multiselect-all"]').click(); - + var $groups = $('#multiselect-container li.multiselect-group'); $groups.each(function() { expect($(this).hasClass('active')).toBe(false); }); - + var $lis = $('#multiselect-container li:not(.multiselect-group)'); $lis.each(function() { expect($(this).hasClass('active')).toBe(true); }); }); - + it('Should select all options (including option groups).', function() { //$('#multiselect-container li.multiselect-group .caret-container').click(); $('#multiselect-container input[value="multiselect-all"]').click(); - + var $lis = $('#multiselect-container li'); $lis.each(function() { expect($('input', this).prop('checked')).toBe(true); @@ -712,7 +712,7 @@ describe('Bootstrap Multiselect "Clickable+Collapsible+SelectAll Optgroups"', fu expect($('#multiselect-container li input[value="multiselect-all"]').prop('checked')).toBe(true); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); @@ -722,14 +722,14 @@ describe('Bootstrap Multiselect "Clickable+Collapsible+SelectAll Optgroups"', fu describe('Bootstrap Multiselect "Dataprovider"', function() { beforeEach(function() { var $select = $(''); - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
' }); }); - + var options = [ {label: 'Option 1', value: '1', selected: true, title: 'Option 1 Title'}, {label: 'Option 2', value: '2', title: 'Option 2 Title'}, @@ -738,51 +738,51 @@ describe('Bootstrap Multiselect "Dataprovider"', function() { {label: 'Option 5', value: '5', title: 'Option 5 Title'}, {label: 'Option 6', value: '6', title: 'Option 6 Title'} ]; - + var options_attributes = [ {label: 'Option 1', value: '1', attributes: {'some-attribute': 'test'}} ]; - + it("Should be able to add options.", function() { $('#multiselect').multiselect('dataprovider', options); expect($('#multiselect option').length).toBe(6); expect($('#multiselect-container input').length).toBe(6); - + expect($('#multiselect option[value="1"]').length).toBe(1); expect($('#multiselect option[value="1"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="1"]').prop('checked')).toBe(true); - + expect($('#multiselect option[value="2"]').length).toBe(1); expect($('#multiselect option[value="2"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="2"]').prop('checked')).toBe(false); - + expect($('#multiselect option[value="3"]').length).toBe(1); expect($('#multiselect option[value="3"]').prop('selected')).toBe(true); expect($('#multiselect-container input[value="3"]').prop('checked')).toBe(true); - + expect($('#multiselect option[value="4"]').length).toBe(1); expect($('#multiselect option[value="4"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="4"]').prop('checked')).toBe(false); - + expect($('#multiselect option[value="5"]').length).toBe(1); expect($('#multiselect option[value="5"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="5"]').prop('checked')).toBe(false); - + expect($('#multiselect option[value="6"]').length).toBe(1); expect($('#multiselect option[value="6"]').prop('selected')).toBe(false); expect($('#multiselect-container input[value="6"]').prop('checked')).toBe(false); }); - + it("Should be able to define title.", function() { $('#multiselect').multiselect('dataprovider', options); - + expect($('#multiselect option[value="1"]').attr('title')).toBe('Option 1 Title'); expect($('#multiselect option[value="2"]').attr('title')).toBe('Option 2 Title'); expect($('#multiselect option[value="3"]').attr('title')).toBe('Option 3 Title'); expect($('#multiselect option[value="4"]').attr('title')).toBe('Option 4 Title'); expect($('#multiselect option[value="5"]').attr('title')).toBe('Option 5 Title'); expect($('#multiselect option[value="6"]').attr('title')).toBe('Option 6 Title'); - + expect($('#multiselect-container input[value="1"]').closest('label').attr('title')).toBe('Option 1 Title'); expect($('#multiselect-container input[value="2"]').closest('label').attr('title')).toBe('Option 2 Title'); expect($('#multiselect-container input[value="3"]').closest('label').attr('title')).toBe('Option 3 Title'); @@ -790,13 +790,13 @@ describe('Bootstrap Multiselect "Dataprovider"', function() { expect($('#multiselect-container input[value="5"]').closest('label').attr('title')).toBe('Option 5 Title'); expect($('#multiselect-container input[value="6"]').closest('label').attr('title')).toBe('Option 6 Title'); }); - + it("Should be able to define data attributes.", function() { $('#multiselect').multiselect('dataprovider', options_attributes) expect($('#multiselect option[value="1"]').attr('value')).toBe('1'); expect($('#multiselect option[value="1"]').attr('data-some-attribute')).toBe('test'); }); - + var optgroups = [ { label: 'Group 1', children: [ @@ -813,18 +813,18 @@ describe('Bootstrap Multiselect "Dataprovider"', function() { ] } ]; - + it('Should be able to handle optgroups.', function() { $('#multiselect').multiselect('dataprovider', optgroups); - + expect($('#multiselect optgroup').length).toBe(2); expect($('#multiselect option').length).toBe(6); expect($('#multiselect-container input').length).toBe(6); - + expect($('#multiselect optgroup[label="Group 1"] option').length).toBe(3); expect($('#multiselect optgroup[label="Group 2"] option').length).toBe(3); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); @@ -832,21 +832,21 @@ describe('Bootstrap Multiselect "Dataprovider"', function() { }); describe('Bootstrap Multiselect "Select All".', function() { - + var onSelectAllTriggered = false; var onDeselectAllTriggered = false; - + var fired = 0; - + beforeEach(function() { var $select = $(''); - + for (var i = 1; i < 100; i++) { $select.append(''); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', includeSelectAllOption: true, @@ -862,48 +862,48 @@ describe('Bootstrap Multiselect "Select All".', function() { } }); }); - + it('Should not add an additional option to the select.', function() { expect($('#multiselect option[value="multiselect-all"]').length).toBe(0); expect($('#multiselect-container input[value="multiselect-all"]').length).toBe(1); expect($('#multiselect option').length).toBe(99); expect($('#multiselect-container input').length).toBe(100); }); - + it('Should update the select all when all options are selected by the "select" method.', function() { expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(false); - + for (var i = 1; i < 100; i++) { $('#multiselect').multiselect('select', i.toString()); expect($('#multiselect option[value="' + i.toString() + '"]').prop('selected')).toBe(true); } - + expect($('#multiselect option:selected').length).toBe(99); expect($('#multiselect-container input').length).toBe(100); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(true); }); - + it('Should update the select all when all options are deselected by the "deselect" method (first all options are selected as before).', function() { expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(false); - + for (var i = 1; i < 100; i++) { $('#multiselect').multiselect('select', i.toString()); } - + expect($('#multiselect option:selected').length).toBe(99); expect($('#multiselect-container input:checked').length).toBe(100); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(true); - + for (var i = 1; i < 100; i++) { $('#multiselect').multiselect('deselect', i.toString()); } - + expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(false); }); - + it('Should update the select all option when all options are selected by the change event.', function() { expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(false); @@ -992,9 +992,9 @@ describe('Bootstrap Multiselect "Select All".', function() { it('Should trigger onSelectAll/onDeselectAll based on the click event.', function() { expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(false); - + $('#multiselect-container input[value!="multiselect-all"]').click(); - + expect($('#multiselect option:selected').length).toBe(99); expect($('#multiselect-container input[value="multiselect-all"]').prop('checked')).toBe(true); @@ -1015,53 +1015,221 @@ describe('Bootstrap Multiselect "Select All".', function() { $('#multiselect').multiselect('deselectAll', true, true); expect(onDeselectAllTriggered).toBe(true); }); - + afterEach(function() { $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); }); }); +describe('Bootstrap Multiselect "setSelection".', function () { + + var onChangeSelected = []; + var onChangeDeselected = []; + var onChangeFired = 0; + var $select; + + beforeEach(function () { + $select = $(''); + + for (var i = 1; i < 100; i++) { + $select.append(''); + } + + $('body').append($select); + + $select.multiselect({ + onChange: function (option, checked) { + ++onChangeFired; + + (checked ? onChangeSelected : onChangeDeselected).push($(option).val()); + } + }); + + onChangeFired = 0; + onChangeSelected = []; + onChangeDeselected = []; + }); + + var VALS_A = [3, 25, 19, 76]; + var VALS_B = [11, 23, 92]; + var VALS_C = [37, 69, 40]; + var VALS_AB = VALS_A.concat(VALS_B); + var VALS_BC = VALS_B.concat(VALS_C); + var VALS_ABC = VALS_A.concat(VALS_B).concat(VALS_C); + + function sortInt(x, y) { return (x - y); } + + VALS_A = VALS_A.sort(sortInt).map(function (n) { return n.toString(); }); + VALS_B = VALS_B.sort(sortInt).map(function (n) { return n.toString(); }); + VALS_C = VALS_C.sort(sortInt).map(function (n) { return n.toString(); }); + VALS_AB = VALS_AB.sort(sortInt).map(function (n) { return n.toString(); }); + VALS_BC = VALS_BC.sort(sortInt).map(function (n) { return n.toString(); }); + VALS_ABC = VALS_ABC.sort(sortInt).map(function (n) { return n.toString(); }); + + it('Should be able to set selected values', function () { + var values = VALS_AB; + + expect($('option:selected', $select).length).toBe(0); + expect($select.val()).toBeNull(); + + $select.multiselect("setSelected", values); + for (var i = 0; i < values.length; ++i) { + expect($('option[value="' + values[i].toString() + '"]').prop('selected')).toBe(true); + } + expect($select.val()).toEqual(values); + }); + + it('Should be able to deselect values', function () { + var values = VALS_AB; + var i; + + // Verify initial state + expect($('option:selected', $select).length).toBe(0); + expect($select.val()).toBeNull(); + + // Select some values and test the state + $select.multiselect("select", values); + expect($select.val()).toEqual(values); + for (i = 0; i < VALS_C.length; ++i) { + expect($('option[value="' + VALS_C[i].toString() + '"]').prop('selected')).toBe(false); + } + for (i = 0; i < values.length; ++i) { + expect($('option[value="' + values[i].toString() + '"]').prop('selected')).toBe(true); + } + + // Set the selection to a different set of values + $select.multiselect("setSelected", VALS_C); + expect($select.val()).toEqual(VALS_C); + for (i = 0; i < VALS_C.length; ++i) { + expect($('option[value="' + VALS_C[i].toString() + '"]').prop('selected')).toBe(true); + } + for (i = 0; i < values.lengths; ++i) { + expect($('option[value="' + values[i].toString() + '"]').prop('selected')).toBe(false); + } + }); + + it('Should be able to receive onChange notifications for only the values that changed', function () { + var initVals = VALS_AB; + var setVals = VALS_BC; + var i; + + expect(onChangeFired).toBe(0); + expect(onChangeSelected.length).toBe(0); + expect(onChangeDeselected.length).toBe(0); + + $select.multiselect("setSelected", initVals, true); + expect(onChangeSelected).toEqual(initVals); + expect($select.val()).toEqual(initVals); + expect(onChangeSelected).toEqual($select.val()); + expect(onChangeDeselected.length).toBe(0); + expect(onChangeFired).toBe(initVals.length); + for (i = 0; i < VALS_A.length; ++i) { + expect($('option[value="' + VALS_A[i].toString() + '"]').prop('selected')).toBe(true); + } + for (i = 0; i < VALS_B.length; ++i) { + expect($('option[value="' + VALS_B[i].toString() + '"]').prop('selected')).toBe(true); + } + for (i = 0; i < VALS_C.length; ++i) { + expect($('option[value="' + VALS_C[i].toString() + '"]').prop('selected')).toBe(false); + } + + onChangeSelected = []; + onChangeFired = 0; + $select.multiselect("setSelected", setVals, true); // Unselect "A", Select "C", Leave "B" Selected + expect($select.val()).toEqual(setVals); + expect(onChangeSelected).toEqual(VALS_C); + expect(onChangeDeselected).toEqual(VALS_A); + expect(onChangeFired).toBe(VALS_C.length + VALS_A.length); + for (i = 0; i < VALS_A.length; ++i) { + expect($('option[value="' + VALS_A[i].toString() + '"]').prop('selected')).toBe(false); + } + for (i = 0; i < VALS_B.length; ++i) { + expect($('option[value="' + VALS_B[i].toString() + '"]').prop('selected')).toBe(true); + } + for (i = 0; i < VALS_C.length; ++i) { + expect($('option[value="' + VALS_C[i].toString() + '"]').prop('selected')).toBe(true); + } + }); + + it('Should not fire onChange notifications if they\'re not requested', function () { + expect(onChangeFired).toBe(0); + expect(onChangeSelected.length).toBe(0); + expect(onChangeDeselected.length).toBe(0); + + // Set a new selection + $select.multiselect('setSelected', VALS_AB, false); + + // Verify the selection changed but events did not fire + expect($select.val()).toEqual(VALS_AB); + expect(onChangeFired).toBe(0); + expect(onChangeSelected.length).toBe(0); + expect(onChangeDeselected.length).toBe(0); + + // Set a new selection + $select.multiselect('setSelected', VALS_BC, false); + + // Verify the selection changed but events did not fire + expect($select.val()).toEqual(VALS_BC); + expect(onChangeFired).toBe(0); + expect(onChangeSelected.length).toBe(0); + expect(onChangeDeselected.length).toBe(0); + + // Set a new selection + $select.multiselect('setSelected', VALS_ABC, false); + + // Verify the selection changed but events did not fire + expect($select.val()).toEqual(VALS_ABC); + expect(onChangeFired).toBe(0); + expect(onChangeSelected.length).toBe(0); + expect(onChangeDeselected.length).toBe(0); + }); + + afterEach(function () { + $select.multiselect('destroy'); + $select.remove(); + }); +}); describe('Bootstrap Multiselect Specific Issues.', function() { - + it('#393', function() { var $select = $(''); - + for (var i = 1; i < 100; i++) { $select.append(''); } - + $('body').append($select); - + $select.multiselect({ buttonContainer: '
', includeSelectAllOption: true, selectAllValue: 0 }); - + expect($('#multiselect-container input[value="0"]').length).toBe(1); expect($('#multiselect-container input[value="0"]').prop('checked')).toBe(false); - + $('#multiselect').multiselect('selectAll'); - + expect($('#multiselect option:selected').length).toBe(99); expect($('#multiselect-container input[value="0"]').prop('checked')).toBe(true); - + $('#multiselect').multiselect('deselectAll'); - + expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="0"]').prop('checked')).toBe(false); - + $('#multiselect-container input[value="0"]').click(); - + expect($('#multiselect option:selected').length).toBe(99); expect($('#multiselect-container input[value="0"]').prop('checked')).toBe(true); - + $('#multiselect-container input[value="0"]').click(); - + expect($('#multiselect option:selected').length).toBe(0); expect($('#multiselect-container input[value="0"]').prop('checked')).toBe(false); - + $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); }); @@ -1096,22 +1264,22 @@ describe('Bootstrap Multiselect Specific Issues.', function() { $('#multiselect').remove(); $selection.remove(); }); - + it('#679', function() { var $select = $(''); - + for (var i = 1; i < 11; i++) { var $optgroup = $(''); - + for (var j = 1; j < 11; j++) { $optgroup.append(''); } - + $select.append($optgroup); } - + $('body').append($select); - + var fired = 0; $select.multiselect({ buttonContainer: '
', @@ -1121,27 +1289,27 @@ describe('Bootstrap Multiselect Specific Issues.', function() { fired++; } }); - + expect($('#multiselect option:selected').length).toBe(0); expect(fired).toBe(0); - + var i = 0; $('#multiselect-container li.multiselect-group').each(function() { $('label', $(this)).click(); - + // Selected expect(fired).toBe(1); fired = 0; - + $('label', $(this)).click(); - + // Deselected expect(fired).toBe(1); fired = 0; - + i++; }); - + $('#multiselect').multiselect('destroy'); $('#multiselect').remove(); }); @@ -1204,7 +1372,7 @@ describe('Knockout Binding.', function() { expect($testArea.find('option:checked').length).toEqual(2); expect($testArea.find('option:checked').map(function() { return $(this).text().trim() }).toArray()).toEqual(['option1', 'option2']); - expect($testArea.next().find('button.multiselect').text().trim()).toEqual('All selected (2)'); + expect($testArea.next().find('button.multiselect').text().trim()).toEqual('All selected (2)'); // add another option viewModel.myOptions.push('wacky option'); @@ -1213,6 +1381,6 @@ describe('Knockout Binding.', function() { expect($testArea.find('option:checked').length).toEqual(2); expect($testArea.find('option:checked').map(function() { return $(this).text().trim() }).toArray()).toEqual(['option1', 'option2']); expect($testArea.find('option').map(function() { return $(this).text().trim() }).toArray()).toEqual(['option1', 'option2', 'wacky option']); - expect($testArea.next().find('button.multiselect').text().trim()).toEqual('2 selected'); + expect($testArea.next().find('button.multiselect').text().trim()).toEqual('2 selected'); }); });