Commit 8d43ff18 authored by David Stutz's avatar David Stutz

#162 and tests.

parent 03e9818a
/** /**
* bootstrap-multiselect.js 1.0.0 * bootstrap-multiselect.js
* https://github.com/davidstutz/bootstrap-multiselect * https://github.com/davidstutz/bootstrap-multiselect
* *
* Copyright 2012, 2013 David Stutz * Copyright 2012, 2013 David Stutz
...@@ -9,17 +9,15 @@ ...@@ -9,17 +9,15 @@
*/ */
!function($) {"use strict";// jshint ;_; !function($) {"use strict";// jshint ;_;
if ( typeof ko != 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) { if (typeof ko != 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
ko.bindingHandlers.multiselect = { ko.bindingHandlers.multiselect = {
init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {},
},
update : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { update : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var ms = $(element).data('multiselect'); var ms = $(element).data('multiselect');
if (!ms) { if (!ms) {
$(element).multiselect(ko.utils.unwrapObservable(valueAccessor())); $(element).multiselect(ko.utils.unwrapObservable(valueAccessor()));
} }
else else if (allBindingsAccessor().options && allBindingsAccessor().options().length !== ms.originalOptions.length) {
if (allBindingsAccessor().options && allBindingsAccessor().options().length !== ms.originalOptions.length) {
ms.updateOriginalOptions(); ms.updateOriginalOptions();
$(element).multiselect('rebuild'); $(element).multiselect('rebuild');
} }
...@@ -90,9 +88,9 @@ ...@@ -90,9 +88,9 @@
if (options.length == 0) { if (options.length == 0) {
return this.nonSelectedText + ' <b class="caret"></b>'; return this.nonSelectedText + ' <b class="caret"></b>';
} }
else else {
if (options.length > 3) { if (options.length > 3) {
return options.length + ' ' + this.nonSelectedText + ' <b class="caret"></b>'; return options.length + ' ' + this.nSelectedText + ' <b class="caret"></b>';
} }
else { else {
var selected = ''; var selected = '';
...@@ -103,6 +101,7 @@ ...@@ -103,6 +101,7 @@
}); });
return selected.substr(0, selected.length - 2) + ' <b class="caret"></b>'; return selected.substr(0, selected.length - 2) + ' <b class="caret"></b>';
} }
}
}, },
// Like the buttonText option to update the title of the button. // Like the buttonText option to update the title of the button.
buttonTitle: function(options, select) { buttonTitle: function(options, select) {
...@@ -175,7 +174,22 @@ ...@@ -175,7 +174,22 @@
} }
}, },
toggleActiveState: function(shouldBeActive) { // Create optgroup.
createOptgroup: function(group) {
var groupName = $(group).prop('label');
// Add a header for the group.
var $li = $('<li><label class="multiselect-group"></label></li>');
$('label', $li).text(groupName);
$('.multiselect-container', this.$container).append($li);
// Add the options of the group.
$('option', group).each($.proxy(function(index, element) {
this.createOptionValue(element);
}, this));
},
toggleActiveState: function() {
if (this.$select.attr('disabled') == undefined) { if (this.$select.attr('disabled') == undefined) {
$('button.multiselect.dropdown-toggle', this.$container).removeClass('disabled'); $('button.multiselect.dropdown-toggle', this.$container).removeClass('disabled');
} }
...@@ -202,26 +216,12 @@ ...@@ -202,26 +216,12 @@
// Support optgroups and options without a group simultaneously. // Support optgroups and options without a group simultaneously.
var tag = $(element).prop('tagName').toLowerCase(); var tag = $(element).prop('tagName').toLowerCase();
if (tag == 'optgroup') { if (tag == 'optgroup') {
var group = element; this.createOptgroup(element);
var groupName = $(group).prop('label');
// Add a header for the group.
var $li = $('<li><label class="multiselect-group"></label></li>');
$('label', $li).text(groupName);
$('.multiselect-container', this.$container).append($li);
// Add the options of the group.
$('option', group).each($.proxy(function(index, element) {
this.createOptionValue(element);
}, this));
} }
else else if (tag == 'option') {
if (tag == 'option') {
this.createOptionValue(element); this.createOptionValue(element);
} }
else { // Other illegal tags will be ignored.
// Ignore illegal tags.
}
}, this)); }, this));
// Bind the change event on the dropdown elements. // Bind the change event on the dropdown elements.
...@@ -239,6 +239,7 @@ ...@@ -239,6 +239,7 @@
} }
} }
// Get the corresponding option.
var $option = $('option', this.$select).filter(function() { var $option = $('option', this.$select).filter(function() {
return $(this).val() == $(event.target).val(); return $(this).val() == $(event.target).val();
}); });
...@@ -257,15 +258,16 @@ ...@@ -257,15 +258,16 @@
$option.prop('selected', true); $option.prop('selected', true);
if (this.options.multiple) { if (this.options.multiple) {
// Simply select additional option.
$option.attr('selected', 'selected'); $option.attr('selected', 'selected');
} }
else { else {
// Unselect all other options and corresponding checkboxes.
if (this.options.selectedClass) { if (this.options.selectedClass) {
$($checkboxesNotThis).parents('li').removeClass(this.options.selectedClass); $($checkboxesNotThis).parents('li').removeClass(this.options.selectedClass);
} }
$($checkboxesNotThis).prop('checked', false); $($checkboxesNotThis).prop('checked', false);
$optionsNotThis.removeAttr('selected').prop('selected', false); $optionsNotThis.removeAttr('selected').prop('selected', false);
// It's a single selection, so close. // It's a single selection, so close.
...@@ -275,9 +277,9 @@ ...@@ -275,9 +277,9 @@
if (this.options.selectedClass == "active") { if (this.options.selectedClass == "active") {
$optionsNotThis.parents("a").css("outline", ""); $optionsNotThis.parents("a").css("outline", "");
} }
} }
else { else {
// Unselect option.
$option.removeAttr('selected').prop('selected', false); $option.removeAttr('selected').prop('selected', false);
} }
...@@ -319,12 +321,10 @@ ...@@ -319,12 +321,10 @@
index--; index--;
} }
// Navigate down. // Navigate down.
else else if (event.keyCode == 40 && index < $items.length - 1) {
if (event.keyCode == 40 && index < $items.length - 1) {
index++; index++;
} }
else else if (!~index) {
if (!~index) {
index = 0; index = 0;
} }
...@@ -358,8 +358,7 @@ ...@@ -358,8 +358,7 @@
$('.multiselect-search', this.$container).val(this.query).on('click', function(event) { $('.multiselect-search', this.$container).val(this.query).on('click', function(event) {
event.stopPropagation(); event.stopPropagation();
}).on('keydown', $.proxy(function(event) { }).on('keydown', $.proxy(function(event) {
// This is useful to catch "keydown" events after the browser has // This is useful to catch "keydown" events after the browser has updated the control.
// updated the control.
clearTimeout(this.searchTimeout); clearTimeout(this.searchTimeout);
this.searchTimeout = this.asyncFunction($.proxy(function() { this.searchTimeout = this.asyncFunction($.proxy(function() {
...@@ -445,18 +444,21 @@ ...@@ -445,18 +444,21 @@
this.updateButtonText(); this.updateButtonText();
}, },
// Select an option by its value. // Select an option by its value or multiple options using an array of values.
select: function(selectValues) { select: function(selectValues) {
if(selectValues && !$.isArray(selectValues)) { if(selectValues && !$.isArray(selectValues)) {
selectValues = [selectValues]; selectValues = [selectValues];
} }
for(var i = 0; i < selectValues.length; i ++) { for (var i = 0; i < selectValues.length; i++) {
var value = selectValues[i]; var value = selectValues[i];
// Find corresponding option.
var $option = $('option', this.$select).filter(function() { var $option = $('option', this.$select).filter(function() {
return $(this).val() == value; return $(this).val() == value;
}); });
// Find corresponding checkbox.
var $checkbox = $('.multiselect-container li input', this.$container).filter(function() { var $checkbox = $('.multiselect-container li input', this.$container).filter(function() {
return $(this).val() == value; return $(this).val() == value;
}); });
...@@ -474,15 +476,17 @@ ...@@ -474,15 +476,17 @@
this.updateButtonText(); this.updateButtonText();
}, },
// Deselect an option by its value. // Deselect an option by its value or using an array of values.
deselect: function(deselectValues) { deselect: function(deselectValues) {
if(deselectValues && !$.isArray(deselectValues)) { if(deselectValues && !$.isArray(deselectValues)) {
deselectValues = [deselectValues]; deselectValues = [deselectValues];
} }
for(var i = 0; i < deselectValues.length; i ++) { for (var i = 0; i < deselectValues.length; i++) {
var value = deselectValues[i]; var value = deselectValues[i];
// Find option and corresponding checkbox.
var $option = $('option', this.$select).filter(function() { var $option = $('option', this.$select).filter(function() {
return $(this).val() == value; return $(this).val() == value;
}); });
...@@ -508,7 +512,7 @@ ...@@ -508,7 +512,7 @@
$('.multiselect-container', this.$container).html(''); $('.multiselect-container', this.$container).html('');
this.buildSelectAll(); this.buildSelectAll();
this.buildDropdown(this.$select, this.options); this.buildDropdown();
this.updateButtonText(); this.updateButtonText();
// Enable filtering. // Enable filtering.
...@@ -520,7 +524,6 @@ ...@@ -520,7 +524,6 @@
// Build select using the given data as options. // Build select using the given data as options.
dataprovider: function(dataprovider) { dataprovider: function(dataprovider) {
var optionDOM = ""; var optionDOM = "";
dataprovider.forEach(function (option) { dataprovider.forEach(function (option) {
optionDOM += '<option value="' + option.value + '">' + option.label + '</option>'; optionDOM += '<option value="' + option.value + '">' + option.label + '</option>';
}); });
...@@ -534,6 +537,7 @@ ...@@ -534,6 +537,7 @@
return $.extend({}, this.defaults, options); return $.extend({}, this.defaults, options);
}, },
// Update button text and button title.
updateButtonText: function() { updateButtonText: function() {
var options = this.getSelected(); var options = this.getSelected();
...@@ -580,6 +584,7 @@ ...@@ -580,6 +584,7 @@
$.fn.multiselect.Constructor = Multiselect; $.fn.multiselect.Constructor = Multiselect;
// Automatically init selects by their data-role.
$(function() { $(function() {
$("select[data-role=multiselect]").multiselect(); $("select[data-role=multiselect]").multiselect();
}); });
......
...@@ -37,6 +37,23 @@ ...@@ -37,6 +37,23 @@
</td> </td>
<td>Everything fine.</td> <td>Everything fine.</td>
</tr> </tr>
<tr id="test-build-optgroups-tr" class="success">
<th>Test build with optgroups</th>
<td>
<select id="test-build-optgroups-select" multiple="multiple">
<optgroup label="1,2">
<option value="1" selected="selected">1</option>
<option value="2">2</option>
</optgroup>
<optgroup label="3,4,5">
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</optgroup>
</select>
</td>
<td>Everything fine.</td>
</tr>
<tr id="test-build-selected-tr" class="success"> <tr id="test-build-selected-tr" class="success">
<th>Test build with selected options</th> <th>Test build with selected options</th>
<td> <td>
...@@ -83,103 +100,145 @@ ...@@ -83,103 +100,145 @@
$(document).ready(function() { $(document).ready(function() {
// Test build of multiselect. // Test build of multiselect.
var build = function() { var build = function(select, tr) {
$('#test-build-select').multiselect(); select.multiselect();
if ($('#test-build-select').length == 0) { if (select.length == 0) {
return 'Select not present anymore.'; return 'Select not present anymore.';
} }
if ($('#test-build-select').css('display') != 'none') { if (select.css('display') != 'none') {
return 'Select still visible (expected <code>display: none;</code>).'; return 'Select still visible (expected <code>display: none;</code>).';
} }
if ($('#test-build-tr button.multiselect').length == 0) { if ($('button.multiselect', tr).length == 0) {
return 'Multiselect button not present.'; return 'Multiselect button not present.';
} }
if ($('#test-build-select option').length != 5) { if ($('option', select).length != 5) {
return 'Not all options present anymore.'; return 'Not all options present anymore.';
} }
if ($('#test-build-tr ul.multiselect-container').length == 0) { if ($('ul.multiselect-container', tr).length == 0) {
return 'Unordered list <code>.multiselect-container</code> not present.'; return 'Unordered list <code>.multiselect-container</code> not present.';
} }
if ($('#test-build-tr ul.multiselect-container li').length != 5) { if ($('ul.multiselect-container li', tr).length != 5) {
return 'No list item for each option present.'; return 'No list item for each option present.';
} }
if ($('#test-build-tr ul.multiselect-container li a').length != 5) { if ($('ul.multiselect-container li a', tr).length != 5) {
return 'Not all list items come with an anchor inside.'; return 'Not all list items come with an anchor inside.';
} }
return false; return false;
}(); }($('#test-build-select'), $('#test-build-tr'));
if (build) { if (build) {
$('#test-build-tr').addClass('error'); $('#test-build-tr').removeClass('success').addClass('danger');
$('#test-build-tr td').last().html(build); $('#test-build-tr td').last().html(build);
} }
var buildSelected = function() { // Test build with optgroups.
$('#test-build-selected-select').multiselect(); var buildOptgroups = function(select, tr) {
select.multiselect();
if ($('optgroup', select).length != 2) {
return 'Optgroups not present anymore (2 expected).';
}
var first = $('optgroup', select).get(0);
var second = $('optgroup', select).get(1);
if ($('option', $(first)).length != 2) {
return 'First optgroup does not have 2 options.';
}
if ($('option', $(second)).length != 3) {
return 'Second optgroup does not have 3 options.';
}
// Check the corresponding labels.
if ($('label.multiselect-group', tr).length != 2) {
return 'Expected 2 labels within the unordered list.';
}
// Check labeling of groups.
var firstLabel = $('label.multiselect-group', tr).get(0);
var secondLabel = $('label.multiselect-group', tr).get(1);
if ($(firstLabel).text() != $(first).prop('label')) {
return 'First group labeled incorrectly.';
}
if ($(secondLabel).text() != $(second).prop('label')) {
return 'Second group labeled incorrectly.';
}
}($('#test-build-optgroups-select'), $('#test-build-optgroups-tr'));
if (buildOptgroups) {
$('#test-build-optgroups-tr').removeClass('success').addClass('danger');
$('#test-build-optgroups-tr td').last().html(build);
}
var buildSelected = function(select, tr ) {
select.multiselect();
if ($('#test-build-selected-select option:selected').length != 1) { if ($('option:selected', select).length != 1) {
return 'Multiselect did not adopt selected options (1 selected option).'; return 'Multiselect did not adopt selected options (1 selected option).';
} }
if ($('#test-build-selected-tr ul.multiselect-container li.active').length != 1) { if ($('ul.multiselect-container li.active', tr).length != 1) {
return 'Corresponding list item not set to <code>.active</code>.'; return 'Corresponding list item not set to <code>.active</code>.';
} }
return false; return false;
}(); }($('#test-build-selected-select'), $('#test-build-selected-tr'));
if (buildSelected) { if (buildSelected) {
$('#test-build-selected-tr').addClass('error'); $('#test-build-selected-tr').removeClass('success').addClass('danger');
$('#test-build-selected-tr td').last().html(buildSelected); $('#test-build-selected-tr td').last().html(buildSelected);
} }
// Test select. // Test select.
var select = function() { var select = function(select, tr) {
$('#test-deselect-select').multiselect(); select.multiselect();
// Check for no selected options and no active li's. // Check for no selected options and no active li's.
if ($('test-select-select option:selected').length > 0) { if ($('option:selected', select).length > 0) {
return 'There are already selected options (0 expected).'; return 'There are already selected options (0 expected).';
} }
if ($('#test-select-tr ul.multiselect-container li.active').length > 0) { if ($('ul.multiselect-container li.active', tr).length > 0) {
return 'There are already active list items (0 expected).'; return 'There are already active list items (0 expected).';
} }
$('#test-select-select').multiselect('select', 1); select.multiselect('select', 1);
if ($('#test-select-select option:selected').length != 1) { if ($('option:selected', select).length != 1) {
return 'Just selected an option - option not marked selected.'; return 'Just selected an option - option not marked selected.';
} }
if ($('#test-select-tr ul.multiselect-container li.active').length != 1) { if ($('ul.multiselect-container li.active', tr).length != 1) {
return 'Just selected an option - list item not set active.'; return 'Just selected an option - list item not set active.';
} }
if ($('#test-select-select option:selected').first().val() != 1) { if ($('option:selected', select).first().val() != 1) {
return 'Wrong option selected.'; return 'Wrong option selected.';
} }
$('#test-select-select').multiselect('select', [2, 3]); select.multiselect('select', [2, 3]);
if ($('#test-select-select option:selected').length != 3) { if ($('option:selected', select).length != 3) {
return 'Just selected two additional options - options not marked selected.'; return 'Just selected two additional options - options not marked selected.';
} }
if ($('#test-select-tr ul.multiselect-container li.active').length != 3) { if ($('ul.multiselect-container li.active', tr).length != 3) {
return 'Just selected two additional options - list items not set active.'; return 'Just selected two additional options - list items not set active.';
} }
var second = $('#test-select-select option:selected').get(1), var second = $('option:selected', select).get(1),
third = $('#test-select-select option:selected').get(2); third = $('option:selected', select).get(2);
if (second == undefined || second.length == 0) { if (second == undefined || second.length == 0) {
return 'Could not get second option.'; return 'Could not get second option.';
...@@ -192,53 +251,53 @@ ...@@ -192,53 +251,53 @@
if ($(second).val() != 2 || $(third).val() != 3) { if ($(second).val() != 2 || $(third).val() != 3) {
return 'Wrong options selected.'; return 'Wrong options selected.';
} }
}(); }($('#test-select-select'), $('#test-select-tr'));
if (select) { if (select) {
$('#test-select-tr').addClass('error'); $('#test-select-tr').removeClass('success').addClass('danger');
$('#test-select-tr td').last().html(select); $('#test-select-tr td').last().html(select);
} }
// Test deselect. // Test deselect.
var deselect = function() { var deselect = function(select, tr) {
$('#test-deselect-select').multiselect(); select.multiselect();
// Check for no selected options and no active li's. // Check for no selected options and no active li's.
if ($('#test-deselect-select option:selected').length != 3) { if ($('option:selected', select).length != 3) {
return 'There should be 3 options selected.'; return 'There should be 3 options selected.';
} }
if ($('#test-deselect-tr ul.multiselect-container li.active').length != 3) { if ($('ul.multiselect-container li.active', tr).length != 3) {
return 'There should be 3 list items set to active.'; return 'There should be 3 list items set to active.';
} }
$('#test-deselect-select').multiselect('deselect', 1); select.multiselect('deselect', 1);
if ($('#test-deselect-select option:selected').length != 2) { if ($('option:selected', select).length != 2) {
return 'Just deselected an option - option not marked deselected.'; return 'Just deselected an option - option not marked deselected.';
} }
if ($('#test-deselect-tr ul.multiselect-container li.active').length != 2) { if ($('ul.multiselect-container li.active', tr).length != 2) {
return 'Just deselected an option - list item not set inactive.'; return 'Just deselected an option - list item not set inactive.';
} }
if ($('#test-deselect-select option:selected').first().val() != 2) { if ($('option:selected', select).first().val() != 2) {
return 'Wrong option deselected.'; return 'Wrong option deselected.';
} }
$('#test-deselect-select').multiselect('deselect', [2, 3]); select.multiselect('deselect', [2, 3]);
if ($('#test-deselect-select option:selected').length > 0) { if ($('option:selected', select).length > 0) {
return 'Just deselected two additional options - options not marked deselected.'; return 'Just deselected two additional options - options not marked deselected.';
} }
if ($('#test-deselect-tr ul.multiselect-container li.active').length > 0) { if ($('ul.multiselect-container li.active', tr).length > 0) {
return 'Just deselected two additional options - list items not set unactive.'; return 'Just deselected two additional options - list items not set unactive.';
} }
}(); }($('#test-deselect-select'), $('#test-deselect-tr'));
if (deselect) { if (deselect) {
$('#test-deselect-tr').addClass('error'); $('#test-deselect-tr').removeClass('success').addClass('danger');
$('#test-deselect-tr td').last().html(deselect); $('#test-deselect-tr td').last().html(deselect);
} }
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment