/** * bootstrap-multiselect.js 1.0.0 * https://github.com/davidstutz/bootstrap-multiselect * * Copyright 2012 David Stutz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ !function ($) { "use strict"; // jshint ;_; if(typeof ko != 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect){ ko.bindingHandlers.multiselect = { init: function (element) { var ms = $(element).data('multiselect'); if(!ms) throw new Error("Bootstrap-multiselect's multiselect() has to be called on element before applying the Knockout View model!"); var prev = ms.options.onChange; ms.options.onChange = function(option, checked){ // We dont want to refresh the multiselect since it would delete / recreate all items $(element).data('blockRefresh', true); // Force the binding to be updated by triggering the change event on the select element $(element).trigger('change'); // Call any defined change handler return prev(option, checked); } }, update: function (element) { var blockRefresh = $(element).data('blockRefresh') || false; if (!blockRefresh) { $(element).multiselect("rebuild"); } $.data(element, 'blockRefresh', false); } }; } function Multiselect(select, options) { this.options = this.getOptions(options); this.$select = $(select); // Manually add the multiple attribute, if its not already set. if (!this.$select.attr('multiple')) { this.$select.attr('multiple', true); } this.$container = $(this.options.buttonContainer) .append('') .append(''); if (this.options.buttonWidth) { $('button', this.$container).css({ 'width': this.options.buttonWidth }); } // Set max height of dropdown menu to activate auto scrollbar. if (this.options.maxHeight) { $('ul', this.$container).css({ 'max-height': this.options.maxHeight + 'px', 'overflow-y': 'auto', 'overflow-x': 'hidden' }); } this.buildDrowdown(); this.$select .hide() .after(this.$container); }; Multiselect.prototype = { 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. // If more than 3 options are selected, the number of selected options is printed. buttonText: function(options) { if (options.length == 0) { return 'None selected '; } else if (options.length > 3) { return options.length + ' selected '; } else { var selected = ''; options.each(function() { selected += $(this).text() + ', '; }); return selected.substr(0, selected.length -2) + ' '; } }, // Is triggered on change of the selected options. onChange: function(option, checked) { }, buttonClass: 'btn', buttonWidth: 'auto', buttonContainer: '
', // Maximum height of the dropdown menu. // If maximum height is exceeded a scrollbar will be displayed. maxHeight: false, }, constructor: Multiselect, // Will build an dropdown element for the given option. createOptionValue: function(element) { if ($(element).is(':selected')) { $(element).attr('selected', 'selected'); $(element).prop('selected', 'selected'); } $('ul', this.$container).append('
  • '); var selected = $(element).prop('selected') || false; var checkbox = $('ul li input[value="' + $(element).val() + '"]', this.$container); if ($(element).is(':disabled')) { checkbox.attr('disabled', 'disabled').prop('disabled', 'disabled').parents('li').addClass('disabled') } checkbox.prop('checked', selected); if (selected) { checkbox.parents('li').addClass('active'); } }, // Build the dropdown and bind event handling. buildDrowdown: function() { if ($('optgroup', this.$select).length > 0) { $('optgroup', this.$select).each($.proxy(function(index, group) { var groupName = $(group).prop('label'); // Add a header for the group. $('ul', this.$container).append('
  • '); // Add the options of the group. $('option', group).each($.proxy(function(index, element) { this.createOptionValue(element); }, this)); }, this)); } else { $('option', this.$select).each($.proxy(function(index, element) { this.createOptionValue(element); }, this)); } // Bind the change event on the dropdown elements. $('ul li input[type="checkbox"]', this.$container).on('change', $.proxy(function(event) { var checked = $(event.target).prop('checked') || false; if (checked) { $(event.target).parents('li').addClass('active'); } else { $(event.target).parents('li').removeClass('active'); } var option = $('option[value="' + $(event.target).val() + '"]', this.$select); if (checked) { option.attr('selected', 'selected'); option.prop('selected', 'selected'); } else { option.removeAttr('selected'); } var options = $('option:selected', this.$select); $('button', this.$container).html(this.options.buttonText(options)); this.options.onChange(option, checked); }, this)); $('ul li a', this.$container).on('click', function(event) { event.stopPropagation(); }); }, // Destroy - unbind - the plugin. destroy: function() { this.$container.remove(); this.$select.show(); }, // Refreshs the checked options based on the current state of the select. refresh: function() { $('option', this.$select).each($.proxy(function(index, element) { if ($(element).is(':selected')) { $('ul li input[value="' + $(element).val() + '"]', this.$container).prop('checked', true); $('ul li input[value="' + $(element).val() + '"]', this.$container).parents('li').addClass('active'); } else { $('ul li input[value="' + $(element).val() + '"]', this.$container).prop('checked', false); $('ul li input[value="' + $(element).val() + '"]', this.$container).parents('li').removeClass('active'); } }, this)); $('button', this.$container).html(this.options.buttonText($('option:selected', this.$select))); }, // Select an option by its value. select: function(value) { var option = $('option[value="' + value + '"]', this.$select); var checkbox = $('ul li input[value="' + value + '"]', this.$container); checkbox.parents('li').addClass('active'); checkbox.prop('checked', true); option.attr('selected', 'selected'); option.prop('selected', 'selected'); var options = $('option:selected', this.$select); $('button', this.$container).html(this.options.buttonText(options)); }, // Deselect an option by its value. deselect: function(value) { var option = $('option[value="' + value + '"]', this.$select); var checkbox = $('ul li input[value="' + value + '"]', this.$container); checkbox.parents('li').removeClass('active'); checkbox.prop('checked', false); option.removeAttr('selected'); option.removeProp('selected'); var options = $('option:selected', this.$select); $('button', this.$container).html(this.options.buttonText(options)); }, // Rebuild the whole dropdown menu. rebuild: function() { $('ul', this.$container).html(''); this.buildDrowdown(this.$select, this.options); }, // Get options by merging defaults and given options. getOptions: function(options) { return $.extend({}, this.defaults, options); } }; $.fn.multiselect = function (option, parameter) { return this.each(function () { var data = $(this).data('multiselect'), options = typeof option == 'object' && option; // Initialize the multiselect. if (!data) { $(this).data('multiselect', (data = new Multiselect(this, options))); } // Call multiselect method. if (typeof option == 'string') { data[option](parameter); } }); } }(window.jQuery);