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
*
* Copyright 2012, 2013 David Stutz
......@@ -9,17 +9,15 @@
*/
!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 = {
init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
},
init : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {},
update : function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var ms = $(element).data('multiselect');
if (!ms) {
$(element).multiselect(ko.utils.unwrapObservable(valueAccessor()));
}
else
if (allBindingsAccessor().options && allBindingsAccessor().options().length !== ms.originalOptions.length) {
else if (allBindingsAccessor().options && allBindingsAccessor().options().length !== ms.originalOptions.length) {
ms.updateOriginalOptions();
$(element).multiselect('rebuild');
}
......@@ -90,9 +88,9 @@
if (options.length == 0) {
return this.nonSelectedText + ' <b class="caret"></b>';
}
else
else {
if (options.length > 3) {
return options.length + ' ' + this.nonSelectedText + ' <b class="caret"></b>';
return options.length + ' ' + this.nSelectedText + ' <b class="caret"></b>';
}
else {
var selected = '';
......@@ -103,6 +101,7 @@
});
return selected.substr(0, selected.length - 2) + ' <b class="caret"></b>';
}
}
},
// Like the buttonText option to update the title of the button.
buttonTitle: function(options, select) {
......@@ -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) {
$('button.multiselect.dropdown-toggle', this.$container).removeClass('disabled');
}
......@@ -202,26 +216,12 @@
// Support optgroups and options without a group simultaneously.
var tag = $(element).prop('tagName').toLowerCase();
if (tag == 'optgroup') {
var group = 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));
this.createOptgroup(element);
}
else
if (tag == 'option') {
else if (tag == 'option') {
this.createOptionValue(element);
}
else {
// Ignore illegal tags.
}
// Other illegal tags will be ignored.
}, this));
// Bind the change event on the dropdown elements.
......@@ -239,6 +239,7 @@
}
}
// Get the corresponding option.
var $option = $('option', this.$select).filter(function() {
return $(this).val() == $(event.target).val();
});
......@@ -257,15 +258,16 @@
$option.prop('selected', true);
if (this.options.multiple) {
// Simply select additional option.
$option.attr('selected', 'selected');
}
else {
// Unselect all other options and corresponding checkboxes.
if (this.options.selectedClass) {
$($checkboxesNotThis).parents('li').removeClass(this.options.selectedClass);
}
$($checkboxesNotThis).prop('checked', false);
$optionsNotThis.removeAttr('selected').prop('selected', false);
// It's a single selection, so close.
......@@ -275,9 +277,9 @@
if (this.options.selectedClass == "active") {
$optionsNotThis.parents("a").css("outline", "");
}
}
else {
// Unselect option.
$option.removeAttr('selected').prop('selected', false);
}
......@@ -319,12 +321,10 @@
index--;
}
// Navigate down.
else
if (event.keyCode == 40 && index < $items.length - 1) {
else if (event.keyCode == 40 && index < $items.length - 1) {
index++;
}
else
if (!~index) {
else if (!~index) {
index = 0;
}
......@@ -358,8 +358,7 @@
$('.multiselect-search', this.$container).val(this.query).on('click', function(event) {
event.stopPropagation();
}).on('keydown', $.proxy(function(event) {
// This is useful to catch "keydown" events after the browser has
// updated the control.
// This is useful to catch "keydown" events after the browser has updated the control.
clearTimeout(this.searchTimeout);
this.searchTimeout = this.asyncFunction($.proxy(function() {
......@@ -445,18 +444,21 @@
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) {
if(selectValues && !$.isArray(selectValues)) {
selectValues = [selectValues];
}
for(var i = 0; i < selectValues.length; i ++) {
for (var i = 0; i < selectValues.length; i++) {
var value = selectValues[i];
// Find corresponding option.
var $option = $('option', this.$select).filter(function() {
return $(this).val() == value;
});
// Find corresponding checkbox.
var $checkbox = $('.multiselect-container li input', this.$container).filter(function() {
return $(this).val() == value;
});
......@@ -474,15 +476,17 @@
this.updateButtonText();
},
// Deselect an option by its value.
// Deselect an option by its value or using an array of values.
deselect: function(deselectValues) {
if(deselectValues && !$.isArray(deselectValues)) {
deselectValues = [deselectValues];
}
for(var i = 0; i < deselectValues.length; i ++) {
for (var i = 0; i < deselectValues.length; i++) {
var value = deselectValues[i];
// Find option and corresponding checkbox.
var $option = $('option', this.$select).filter(function() {
return $(this).val() == value;
});
......@@ -508,7 +512,7 @@
$('.multiselect-container', this.$container).html('');
this.buildSelectAll();
this.buildDropdown(this.$select, this.options);
this.buildDropdown();
this.updateButtonText();
// Enable filtering.
......@@ -520,7 +524,6 @@
// Build select using the given data as options.
dataprovider: function(dataprovider) {
var optionDOM = "";
dataprovider.forEach(function (option) {
optionDOM += '<option value="' + option.value + '">' + option.label + '</option>';
});
......@@ -534,6 +537,7 @@
return $.extend({}, this.defaults, options);
},
// Update button text and button title.
updateButtonText: function() {
var options = this.getSelected();
......@@ -580,6 +584,7 @@
$.fn.multiselect.Constructor = Multiselect;
// Automatically init selects by their data-role.
$(function() {
$("select[data-role=multiselect]").multiselect();
});
......
......@@ -37,6 +37,23 @@
</td>
<td>Everything fine.</td>
</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">
<th>Test build with selected options</th>
<td>
......@@ -83,103 +100,145 @@
$(document).ready(function() {
// Test build of multiselect.
var build = function() {
$('#test-build-select').multiselect();
var build = function(select, tr) {
select.multiselect();
if ($('#test-build-select').length == 0) {
if (select.length == 0) {
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>).';
}
if ($('#test-build-tr button.multiselect').length == 0) {
if ($('button.multiselect', tr).length == 0) {
return 'Multiselect button not present.';
}
if ($('#test-build-select option').length != 5) {
if ($('option', select).length != 5) {
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.';
}
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.';
}
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 false;
}();
}($('#test-build-select'), $('#test-build-tr'));
if (build) {
$('#test-build-tr').addClass('error');
$('#test-build-tr').removeClass('success').addClass('danger');
$('#test-build-tr td').last().html(build);
}
var buildSelected = function() {
$('#test-build-selected-select').multiselect();
// Test build with optgroups.
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).';
}
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 false;
}();
}($('#test-build-selected-select'), $('#test-build-selected-tr'));
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 select.
var select = function() {
$('#test-deselect-select').multiselect();
var select = function(select, tr) {
select.multiselect();
// 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).';
}
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).';
}
$('#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.';
}
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.';
}
if ($('#test-select-select option:selected').first().val() != 1) {
if ($('option:selected', select).first().val() != 1) {
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.';
}
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.';
}
var second = $('#test-select-select option:selected').get(1),
third = $('#test-select-select option:selected').get(2);
var second = $('option:selected', select).get(1),
third = $('option:selected', select).get(2);
if (second == undefined || second.length == 0) {
return 'Could not get second option.';
......@@ -192,53 +251,53 @@
if ($(second).val() != 2 || $(third).val() != 3) {
return 'Wrong options selected.';
}
}();
}($('#test-select-select'), $('#test-select-tr'));
if (select) {
$('#test-select-tr').addClass('error');
$('#test-select-tr').removeClass('success').addClass('danger');
$('#test-select-tr td').last().html(select);
}
// Test deselect.
var deselect = function() {
$('#test-deselect-select').multiselect();
var deselect = function(select, tr) {
select.multiselect();
// 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.';
}
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.';
}
$('#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.';
}
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.';
}
if ($('#test-deselect-select option:selected').first().val() != 2) {
if ($('option:selected', select).first().val() != 2) {
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.';
}
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.';
}
}();
}($('#test-deselect-select'), $('#test-deselect-tr'));
if (deselect) {
$('#test-deselect-tr').addClass('error');
$('#test-deselect-tr').removeClass('success').addClass('danger');
$('#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