UNPKG

bootstrap-multiselectsplitter

Version:

Bootstrap extended for multi select splitter

348 lines (275 loc) 12.4 kB
/** * Bootstrap multiselectsplitter plugin * Version: 1.0.1 * License: MIT * Homepage: https://github.com/poolerMF/bootstrap-multiselectsplitter */ +function ($) { 'use strict'; // CLASS DEFINITION // =============================== var MultiSelectSplitter = function (element, options) { this.init('multiselectsplitter', element, options); }; MultiSelectSplitter.DEFAULTS = { selectSize: null, maxSelectSize: null, clearOnFirstChange: false, onlySameGroup: false, // only if multiselect groupCounter: false, // only if multiselect maximumSelected: null, // only if multiselect, integer or function afterInitialize: null, maximumAlert: function (maximumSelected) { alert("Only " + maximumSelected + " values can be selected"); }, createFirstSelect: function (label, $originalSelect) { return '<option>' + label + '</option>'; }, createSecondSelect: function (label, $firstSelect) { return '<option>' + label + '</option>'; }, template: '<div class="row" data-multiselectsplitter-wrapper-selector>' + '<div class="col-xs-6 col-sm-6">' + '<select class="form-control" data-multiselectsplitter-firstselect-selector></select>' + '</div>' + ' <!-- Add the extra clearfix for only the required viewport -->' + '<div class="col-xs-6 col-sm-6">' + '<select class="form-control" data-multiselectsplitter-secondselect-selector></select>' + '</div>' + '</div>' }; MultiSelectSplitter.prototype.init = function (type, element, options) { var self = this; self.type = type; self.last$ElementSelected = []; self.initialized = false; self.$element = $(element); self.$element.hide(); self.options = $.extend({}, MultiSelectSplitter.DEFAULTS, options); // Add template. self.$element.after(self.options.template); // Define selected elements. self.$wrapper = self.$element.next('div[data-multiselectsplitter-wrapper-selector]'); self.$firstSelect = $('select[data-multiselectsplitter-firstselect-selector]', self.$wrapper); self.$secondSelect = $('select[data-multiselectsplitter-secondselect-selector]', self.$wrapper); var optgroupCount = 0; var longestOptionCount = 0; if (self.$element.find('optgroup').length == 0) { return; } self.$element.find('optgroup').each(function () { var label = $(this).attr('label'); var $option = $(self.options.createFirstSelect(label, self.$element)); $option.val(label); $option.attr('data-current-label', $option.text()); self.$firstSelect.append($option); var currentOptionCount = $(this).find('option').length; if (currentOptionCount > longestOptionCount) { longestOptionCount = currentOptionCount; } optgroupCount++; }); // Define $firstSelect and $secondSelect size attribute var selectSize = Math.max(optgroupCount, longestOptionCount); selectSize = Math.min(selectSize, 10); if (self.options.selectSize) { selectSize = self.options.selectSize; } else if (self.options.maxSelectSize) { selectSize = Math.min(selectSize, self.options.maxSelectSize); } self.$firstSelect.attr('size', selectSize); self.$secondSelect.attr('size', selectSize); // Set multiple if (self.$element.attr('multiple')) { self.$secondSelect.attr('multiple', 'multiple'); } // Set disabled if (self.$element.is(":disabled")) { self.disable(); } // Define events. self.$firstSelect.on('change', $.proxy(self.updateParentCategory, self)); self.$secondSelect.on('click change', $.proxy(self.updateChildCategory, self)); self.update = function () { if (self.$element.find('option').length < 1) { return; } var selectedOptions = self.$element.find('option:selected:first'); var selectedGroup; if (selectedOptions.length) { selectedGroup = selectedOptions.parent().attr('label'); } else { selectedGroup = self.$element.find('option:first').parent().attr('label'); } self.$firstSelect.find('option[value="' + selectedGroup + '"]').prop('selected', true); self.$firstSelect.trigger('change'); }; self.update(); self.initialized = true; if (self.options.afterInitialize) { self.options.afterInitialize(self.$firstSelect, self.$secondSelect); } }; MultiSelectSplitter.prototype.disable = function () { this.$secondSelect.prop('disabled', true); this.$firstSelect.prop('disabled', true); }; MultiSelectSplitter.prototype.enable = function () { this.$secondSelect.prop('disabled', false); this.$firstSelect.prop('disabled', false); }; MultiSelectSplitter.prototype.createSecondSelect = function () { var self = this; self.$secondSelect.empty(); $.each(self.$element.find('optgroup[label="' + self.$firstSelect.val() + '"] option'), function (index, element) { var value = $(this).val(); var label = $(this).text(); var $option = $(self.options.createSecondSelect(label, self.$firstSelect)); $option.val(value); $.each(self.$element.find('option:selected'), function (index, element) { if ($(element).val() == value) { $option.prop('selected', true); } }); self.$secondSelect.append($option); }); }; MultiSelectSplitter.prototype.updateParentCategory = function () { var self = this; self.last$ElementSelected = self.$element.find('option:selected'); if (self.options.clearOnFirstChange && self.initialized) { self.$element.find('option:selected').prop('selected', false); } self.createSecondSelect(); self.checkSelected(); self.updateCounter(); }; MultiSelectSplitter.prototype.updateCounter = function () { var self = this; if (!self.$element.attr('multiple') || !self.options.groupCounter) { return; } $.each(self.$firstSelect.find('option'), function (index, element) { var originalLabel = $(element).val(); var text = $(element).data('currentLabel'); var count = self.$element.find('optgroup[label="' + originalLabel + '"] option:selected').length; if (count > 0) { text += ' (' + count + ')'; } $(element).html(text); }); }; MultiSelectSplitter.prototype.checkSelected = function () { var self = this; if (!self.$element.attr('multiple') || !self.options.maximumSelected) { return; } var maximumSelected = 0; if (typeof self.options.maximumSelected == 'function') { maximumSelected = self.options.maximumSelected(self.$firstSelect, self.$secondSelect); } else { maximumSelected = self.options.maximumSelected; } if (maximumSelected < 1) { return; } var $actualElementSelected = self.$element.find('option:selected'); if ($actualElementSelected.length > maximumSelected) { self.$firstSelect.find('option:selected').prop('selected', false); self.$secondSelect.find('option:selected').prop('selected', false); if (self.initialized) { self.$element.find('option:selected').prop('selected', false); self.last$ElementSelected.prop('selected', true); } else { // after init, there is no last value $.each(self.$element.find('option:selected'), function (index, element) { if (index > maximumSelected - 1) { $(element).prop('selected', false); } }); } var firstSelectedOptGroupLabel = self.last$ElementSelected.first().parent().attr('label'); self.$firstSelect.find('option[value="' + firstSelectedOptGroupLabel + '"]').prop('selected', true); self.createSecondSelect(); self.options.maximumAlert(maximumSelected); } }; MultiSelectSplitter.prototype.basicUpdateChildCategory = function (event, isCtrlKey) { var self = this; self.last$ElementSelected = self.$element.find('option:selected'); var childValues = self.$secondSelect.val(); if (!$.isArray(childValues)) { childValues = [childValues]; } var parentLabel = self.$firstSelect.val(); var removeActualSelected = false; if (!self.$element.attr('multiple')) { removeActualSelected = true; } else { if (self.options.onlySameGroup) { $.each(self.$element.find('option:selected'), function (index, element) { if ($(element).parent().attr('label') != parentLabel) { removeActualSelected = true; return false; } }); } else { if (!isCtrlKey) { removeActualSelected = true; } } } if (removeActualSelected) { self.$element.find('option:selected').prop('selected', false); } else { $.each(self.$element.find('option:selected'), function (index, element) { if (parentLabel == $(element).parent().attr('label') && $.inArray($(element).val(), childValues) == -1) { $(element).prop('selected', false); } }); } $.each(childValues, function (index, value) { self.$element.find('option[value="' + value + '"]').prop('selected', true); }); self.checkSelected(); self.updateCounter(); self.$element.trigger('change'); // Required for external plugins. }; MultiSelectSplitter.prototype.updateChildCategory = function (event) { // There is no event.ctrlKey in event "change", so change function is called with timeout if (event.type == "change") { this.timeOut = setTimeout($.proxy(function () { this.basicUpdateChildCategory(event, event.ctrlKey); }, this), 10); } else if (event.type == "click") { clearTimeout(this.timeOut); this.basicUpdateChildCategory(event, event.ctrlKey) } }; MultiSelectSplitter.prototype.destroy = function () { this.$wrapper.remove(); this.$element.removeData(this.type); this.$element.show(); }; // PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this); var data = $this.data('multiselectsplitter'); var options = typeof option === 'object' && option; if (!data && option == 'destroy') { return; } if (!data) { $this.data('multiselectsplitter', ( data = new MultiSelectSplitter(this, options) )); } if (typeof option == 'string') { data[option](); } }); } $.fn.multiselectsplitter = Plugin; $.fn.multiselectsplitter.Constructor = MultiSelectSplitter; $.fn.multiselectsplitter.VERSION = '1.0.1'; }(jQuery);