Commit 1d67e62a authored by David Stutz's avatar David Stutz

#154

parent d4dbc81f
...@@ -188,6 +188,10 @@ ...@@ -188,6 +188,10 @@
includeSelectAllOption: true, includeSelectAllOption: true,
enableCaseInsensitiveFiltering: true enableCaseInsensitiveFiltering: true
}); });
$('#example41').multiselect({
includeSelectAllOption: true
});
}); });
</script> </script>
<p> <p>
...@@ -247,6 +251,21 @@ ...@@ -247,6 +251,21 @@
Multiselect with a 'Select all' option and filtering enabled using the <code>enableFiltering</code> option. Multiselect with a 'Select all' option and filtering enabled using the <code>enableFiltering</code> option.
</td> </td>
</tr> </tr>
<tr>
<td>
<select id="example41" multiple="multiple">
<option value="cheese" selected>Cheese</option>
<option value="tomatoes" selected>Tomatoes</option>
<option value="mozarella" selected>Mozzarella</option>
<option value="mushrooms" selected>Mushrooms</option>
<option value="pepperoni" selected>Pepperoni</option>
<option value="onions" selected>Onions</option>
</select>
</td>
<td>
The 'Select all' option automatically repsonds to the selection of the user. In particular, it responds to the initial state of the select.
</td>
</tr>
<tr> <tr>
<td> <td>
<select id="example3" multiple="multiple"> <select id="example3" multiple="multiple">
......
...@@ -32,6 +32,13 @@ ...@@ -32,6 +32,13 @@
}; };
} }
/**
* Constructor to create a new multiselect using the given select.
*
* @param {jQuery} select
* @param {Object} options
* @returns {Multiselect}
*/
function Multiselect(select, options) { function Multiselect(select, options) {
this.options = this.mergeOptions(options); this.options = this.mergeOptions(options);
...@@ -55,18 +62,24 @@ ...@@ -55,18 +62,24 @@
this.buildDropdown(); this.buildDropdown();
this.buildDropdownOptions(); this.buildDropdownOptions();
this.buildFilter(); this.buildFilter();
this.updateButtonText(); this.updateButtonText();
this.updateSelectAll();
this.$select.hide().after(this.$container); this.$select.hide().after(this.$container);
}; };
Multiselect.prototype = { Multiselect.prototype = {
// Default options.
defaults: { defaults: {
// Default text function will either print 'None selected' in case no /**
// option is selected, or a list of the selected options up to a length of 3 selected options by default. * Default text function will either print 'None selected' in case no
// If more than 3 options are selected, the number of selected options is printed. * option is selected or a list of the selected options up to a length of 3 selected options.
*
* @param {jQuery} options
* @param {jQuery} select
* @returns {String}
*/
buttonText: function(options, select) { buttonText: function(options, select) {
if (options.length === 0) { if (options.length === 0) {
return this.nonSelectedText + ' <b class="caret"></b>'; return this.nonSelectedText + ' <b class="caret"></b>';
...@@ -86,7 +99,12 @@ ...@@ -86,7 +99,12 @@
} }
} }
}, },
// Like the buttonText option to update the title of the button. /**
* Updates the title of the button similar to the buttonText function.
* @param {jQuery} options
* @param {jQuery} select
* @returns {@exp;selected@call;substr}
*/
buttonTitle: function(options, select) { buttonTitle: function(options, select) {
if (options.length === 0) { if (options.length === 0) {
return this.nonSelectedText; return this.nonSelectedText;
...@@ -99,19 +117,38 @@ ...@@ -99,19 +117,38 @@
return selected.substr(0, selected.length - 2); return selected.substr(0, selected.length - 2);
} }
}, },
// Create label /**
label: function( element ){ * Create a label.
*
* @param {jQuery} element
* @returns {String}
*/
label: function(element){
return $(element).attr('label') || $(element).html(); return $(element).attr('label') || $(element).html();
}, },
// Is triggered on change of the selected options. /**
* Triggered on change of the multiselect.
* Not triggered when selecting/deselecting options manually.
*
* @param {jQuery} option
* @param {Boolean} checked
*/
onChange : function(option, checked) { onChange : function(option, checked) {
}, },
// Triggered immediately when dropdown shown /**
* Triggered when the dropdown is shown.
*
* @param {jQuery} event
*/
onDropdownShow: function(event) { onDropdownShow: function(event) {
}, },
// Triggered immediately when dropdown hidden /**
* Triggered when the dropdown is hidden.
*
* @param {jQuery} event
*/
onDropdownHide: function(event) { onDropdownHide: function(event) {
}, },
...@@ -137,7 +174,6 @@ ...@@ -137,7 +174,6 @@
numberDisplayed: 3 numberDisplayed: 3
}, },
// Templates.
templates: { templates: {
button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"></button>', button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"></button>',
ul: '<ul class="multiselect-container dropdown-menu"></ul>', ul: '<ul class="multiselect-container dropdown-menu"></ul>',
...@@ -149,14 +185,19 @@ ...@@ -149,14 +185,19 @@
constructor: Multiselect, constructor: Multiselect,
/**
* Builds the container of the multiselect.
*/
buildContainer: function() { buildContainer: function() {
this.$container = $(this.options.buttonContainer); this.$container = $(this.options.buttonContainer);
this.$container.on('show.bs.dropdown', this.options.onDropdownShow); this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
}, },
/**
* Builds the button of the multiselect.
*/
buildButton: function() { buildButton: function() {
// Build button.
this.$button = $(this.templates.button).addClass(this.options.buttonClass); this.$button = $(this.templates.button).addClass(this.options.buttonClass);
// Adopt active state. // Adopt active state.
...@@ -183,7 +224,9 @@ ...@@ -183,7 +224,9 @@
this.$container.prepend(this.$button); this.$container.prepend(this.$button);
}, },
// Build dropdown container ul. /**
* Builds the ul representing the dropdown menu.
*/
buildDropdown: function() { buildDropdown: function() {
// Build ul. // Build ul.
...@@ -206,10 +249,14 @@ ...@@ -206,10 +249,14 @@
this.$container.append(this.$ul); this.$container.append(this.$ul);
}, },
// Build the dropdown and bind event handling. /**
* Build the dropdown options and binds all nessecary events.
* 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) {
// Support optgroups and options without a group simultaneously. // Support optgroups and options without a group simultaneously.
var tag = $(element).prop('tagName') var tag = $(element).prop('tagName')
.toLowerCase(); .toLowerCase();
...@@ -227,6 +274,7 @@ ...@@ -227,6 +274,7 @@
} }
} }
// Other illegal tags will be ignored. // Other illegal tags will be ignored.
}, this)); }, this));
...@@ -305,7 +353,9 @@ ...@@ -305,7 +353,9 @@
this.$select.change(); this.$select.change();
this.options.onChange($option, checked); this.options.onChange($option, checked);
this.updateButtonText(); this.updateButtonText();
this.updateSelectAll();
if(this.options.preventInputChangeEvent) { if(this.options.preventInputChangeEvent) {
return false; return false;
...@@ -353,7 +403,9 @@ ...@@ -353,7 +403,9 @@
if ($('input[type="text"]', this.$container).is(':focus')) { if ($('input[type="text"]', this.$container).is(':focus')) {
return; return;
} }
if ((event.keyCode === 9 || event.keyCode === 27) && this.$container.hasClass('open')) { if ((event.keyCode === 9 || event.keyCode === 27)
&& this.$container.hasClass('open')) {
// Close on tab or escape. // Close on tab or escape.
this.$button.click(); this.$button.click();
} }
...@@ -394,7 +446,11 @@ ...@@ -394,7 +446,11 @@
}, this)); }, this));
}, },
// Will build an dropdown element for the given option. /**
* Create an option using the given select option.
*
* @param {jQuery} element
*/
createOptionValue: function(element) { createOptionValue: function(element) {
if ($(element).is(':selected')) { if ($(element).is(':selected')) {
$(element).prop('selected', true); $(element).prop('selected', true);
...@@ -437,13 +493,21 @@ ...@@ -437,13 +493,21 @@
} }
}, },
// Create divider /**
* Creates a divider using the given select option.
*
* @param {jQuery} element
*/
createDivider: function(element) { createDivider: function(element) {
var $divider = $(this.templates.divider); var $divider = $(this.templates.divider);
this.$ul.append($divider); this.$ul.append($divider);
}, },
// Create optgroup. /**
* Creates an optgroup.
*
* @param {jQuery} group
*/
createOptgroup: function(group) { createOptgroup: function(group) {
var groupName = $(group).prop('label'); var groupName = $(group).prop('label');
...@@ -459,9 +523,12 @@ ...@@ -459,9 +523,12 @@
}, this)); }, this));
}, },
// Add the select all option to the select. /**
* Build the selct all.
* Checks if a select all ahs already been created.
*/
buildSelectAll: function() { buildSelectAll: function() {
var alreadyHasSelectAll = this.$select[0][0] ? this.$select[0][0].value === this.options.selectAllValue : false; var alreadyHasSelectAll = this.hasSelectAll();
// If options.includeSelectAllOption === true, add the include all checkbox. // If options.includeSelectAllOption === true, add the include all checkbox.
if (this.options.includeSelectAllOption && this.options.multiple && !alreadyHasSelectAll) { if (this.options.includeSelectAllOption && this.options.multiple && !alreadyHasSelectAll) {
...@@ -469,7 +536,9 @@ ...@@ -469,7 +536,9 @@
} }
}, },
// Build and bind filter. /**
* 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. // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
...@@ -534,13 +603,17 @@ ...@@ -534,13 +603,17 @@
} }
}, },
// Destroy - unbind - the plugin. /**
* Unbinds the whole plugin.
*/
destroy: function() { destroy: function() {
this.$container.remove(); this.$container.remove();
this.$select.show(); this.$select.show();
}, },
// Refreshs the checked options based on the current state of the select. /**
* Refreshs the multiselect based on the selected options of the select.
*/
refresh: function() { refresh: function() {
$('option', this.$select).each($.proxy(function(index, element) { $('option', this.$select).each($.proxy(function(index, element) {
var $input = $('li input', this.$ul).filter(function() { var $input = $('li input', this.$ul).filter(function() {
...@@ -578,9 +651,14 @@ ...@@ -578,9 +651,14 @@
}, this)); }, this));
this.updateButtonText(); this.updateButtonText();
this.updateSelectAll();
}, },
// Select an option by its value or multiple options using an array of values. /**
* Select all options of the given values.
*
* @param {Array} selectValues
*/
select: function(selectValues) { select: function(selectValues) {
if(selectValues && !$.isArray(selectValues)) { if(selectValues && !$.isArray(selectValues)) {
selectValues = [selectValues]; selectValues = [selectValues];
...@@ -604,7 +682,11 @@ ...@@ -604,7 +682,11 @@
this.updateButtonText(); this.updateButtonText();
}, },
// Deselect an option by its value or using an array of values. /**
* Deselects all options of the given values.
*
* @param {Array} deselectValues
*/
deselect: function(deselectValues) { deselect: function(deselectValues) {
if(deselectValues && !$.isArray(deselectValues)) { if(deselectValues && !$.isArray(deselectValues)) {
deselectValues = [deselectValues]; deselectValues = [deselectValues];
...@@ -629,7 +711,10 @@ ...@@ -629,7 +711,10 @@
this.updateButtonText(); this.updateButtonText();
}, },
// Rebuild the whole dropdown menu. /**
* Rebuild the plugin.
* Rebuilds the dropdown, the filter and the select all option.
*/
rebuild: function() { rebuild: function() {
this.$ul.html(''); this.$ul.html('');
...@@ -641,11 +726,17 @@ ...@@ -641,11 +726,17 @@
this.buildSelectAll(); this.buildSelectAll();
this.buildDropdownOptions(); this.buildDropdownOptions();
this.updateButtonText();
this.buildFilter(); this.buildFilter();
this.updateButtonText();
this.updateSelectAll();
}, },
// Build select using the given data as options. /**
* The provided data will be used to build the dropdown.
*
* @param {Array} dataprovider
*/
dataprovider: function(dataprovider) { dataprovider: function(dataprovider) {
var optionDOM = ""; var optionDOM = "";
dataprovider.forEach(function (option) { dataprovider.forEach(function (option) {
...@@ -656,31 +747,71 @@ ...@@ -656,31 +747,71 @@
this.rebuild(); this.rebuild();
}, },
// Enable button. /**
* Enable the multiselect.
*/
enable: function() { enable: function() {
this.$select.prop('disabled', false); this.$select.prop('disabled', false);
this.$button.prop('disabled', false) this.$button.prop('disabled', false)
.removeClass('disabled'); .removeClass('disabled');
}, },
// Disable button. /**
* Disable the multiselect.
*/
disable: function() { disable: function() {
this.$select.prop('disabled', true); this.$select.prop('disabled', true);
this.$button.prop('disabled', true) this.$button.prop('disabled', true)
.addClass('disabled'); .addClass('disabled');
}, },
// Set options. /**
* Set the options.
*
* @param {Array} options
*/
setOptions: function(options) { setOptions: function(options) {
this.options = this.mergeOptions(options); this.options = this.mergeOptions(options);
}, },
// Get options by merging defaults and given options. /**
* Merges the given options with the default options.
*
* @param {Array} options
* @returns {Array}
*/
mergeOptions: function(options) { mergeOptions: function(options) {
return $.extend({}, this.defaults, options); return $.extend({}, this.defaults, options);
}, },
// Update button text and button title. /**
* Checks whether a select all option is present.
*
* @returns {Boolean}
*/
hasSelectAll: function() {
return this.$select[0][0] ? this.$select[0][0].value === this.options.selectAllValue : false;
},
/**
* Updates the select all option based on the currently selected options.
*/
updateSelectAll: function() {
if (this.hasSelectAll()) {
var selected = this.getSelected();
if (selected.length === $('option', this.$select).length - 1) {
this.select(this.options.selectAllValue);
}
else {
this.deselect(this.options.selectAllValue);
}
}
},
/**
* Update the button text and its title base don the currenty selected options.
*/
updateButtonText: function() { updateButtonText: function() {
var options = this.getSelected(); var options = this.getSelected();
...@@ -692,27 +823,44 @@ ...@@ -692,27 +823,44 @@
}, },
// Get all selected options. /**
* Get all selected options.
*
* @returns {jQUery}
*/
getSelected: function() { getSelected: function() {
return $('option[value!="' + this.options.selectAllValue + '"]:selected', this.$select).filter(function() { return $('option[value!="' + this.options.selectAllValue + '"]:selected', this.$select).filter(function() {
return $(this).prop('selected'); return $(this).prop('selected');
}); });
}, },
// Get the corresponding option by ts value. /**
* Gets a select option by its value.
*
* @param {String} value
* @returns {jQuery}
*/
getOptionByValue: function(value) { getOptionByValue: function(value) {
return $('option', this.$select).filter(function() { return $('option', this.$select).filter(function() {
return $(this).val() === value; return $(this).val() === value;
}); });
}, },
// Get an input in the dropdown by its value. /**
* Get the input (radio/checkbox) by its value.
*
* @param {String} value
* @returns {jQuery}
*/
getInputByValue: function(value) { getInputByValue: function(value) {
return $('li input', this.$ul).filter(function() { return $('li input', this.$ul).filter(function() {
return $(this).val() === value; return $(this).val() === value;
}); });
}, },
/**
* Used for knockout integration.
*/
updateOriginalOptions: function() { updateOriginalOptions: function() {
this.originalOptions = this.$select.clone()[0].options; this.originalOptions = this.$select.clone()[0].options;
}, },
...@@ -744,7 +892,6 @@ ...@@ -744,7 +892,6 @@
$.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();
}); });
......
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