{"version":3,"names":[],"mappings":"","sources":["bootstrap-multiselect.js"],"sourcesContent":["/**\r\n * Bootstrap Multiselect (http://davidstutz.de/bootstrap-multiselect/)\r\n *\r\n * Apache License, Version 2.0:\r\n * Copyright (c) 2012 - 2018 David Stutz\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\r\n * use this file except in compliance with the License. You may obtain a\r\n * copy of the License at http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\r\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r\n * License for the specific language governing permissions and limitations\r\n * under the License.\r\n *\r\n * BSD 3-Clause License:\r\n * Copyright (c) 2012 - 2018 David Stutz\r\n * All rights reserved.\r\n *\r\n * Redistribution and use in source and binary forms, with or without\r\n * modification, are permitted provided that the following conditions are met:\r\n * - Redistributions of source code must retain the above copyright notice,\r\n * this list of conditions and the following disclaimer.\r\n * - Redistributions in binary form must reproduce the above copyright notice,\r\n * this list of conditions and the following disclaimer in the documentation\r\n * and/or other materials provided with the distribution.\r\n * - Neither the name of David Stutz nor the names of its contributors may be\r\n * used to endorse or promote products derived from this software without\r\n * specific prior written permission.\r\n *\r\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\r\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\r\n * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r\n * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR\r\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\r\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\r\n * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r\n * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r\n */\r\n(function (root, factory) {\r\n // check to see if 'knockout' AMD module is specified if using requirejs\r\n if (typeof define === 'function' && define.amd &&\r\n typeof require === 'function' && typeof require.specified === 'function' && require.specified('knockout')) {\r\n\r\n // AMD. Register as an anonymous module.\r\n define(['jquery', 'knockout'], factory);\r\n } else {\r\n // Browser globals\r\n factory(root.jQuery, root.ko);\r\n }\r\n})(this, function ($, ko) {\r\n \"use strict\";// jshint ;_;\r\n\r\n if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {\r\n ko.bindingHandlers.multiselect = {\r\n after: ['options', 'value', 'selectedOptions', 'enable', 'disable'],\r\n\r\n init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {\r\n var $element = $(element);\r\n var config = ko.toJS(valueAccessor());\r\n\r\n $element.multiselect(config);\r\n\r\n if (allBindings.has('options')) {\r\n var options = allBindings.get('options');\r\n if (ko.isObservable(options)) {\r\n ko.computed({\r\n read: function() {\r\n options();\r\n setTimeout(function() {\r\n var ms = $element.data('multiselect');\r\n if (ms)\r\n ms.updateOriginalOptions();//Not sure how beneficial this is.\r\n $element.multiselect('rebuild');\r\n }, 1);\r\n },\r\n disposeWhenNodeIsRemoved: element\r\n });\r\n }\r\n }\r\n\r\n //value and selectedOptions are two-way, so these will be triggered even by our own actions.\r\n //It needs some way to tell if they are triggered because of us or because of outside change.\r\n //It doesn't loop but it's a waste of processing.\r\n if (allBindings.has('value')) {\r\n var value = allBindings.get('value');\r\n if (ko.isObservable(value)) {\r\n ko.computed({\r\n read: function() {\r\n value();\r\n setTimeout(function() {\r\n $element.multiselect('refresh');\r\n }, 1);\r\n },\r\n disposeWhenNodeIsRemoved: element\r\n }).extend({ rateLimit: 100, notifyWhenChangesStop: true });\r\n }\r\n }\r\n\r\n //Switched from arrayChange subscription to general subscription using 'refresh'.\r\n //Not sure performance is any better using 'select' and 'deselect'.\r\n if (allBindings.has('selectedOptions')) {\r\n var selectedOptions = allBindings.get('selectedOptions');\r\n if (ko.isObservable(selectedOptions)) {\r\n ko.computed({\r\n read: function() {\r\n selectedOptions();\r\n setTimeout(function() {\r\n $element.multiselect('refresh');\r\n }, 1);\r\n },\r\n disposeWhenNodeIsRemoved: element\r\n }).extend({ rateLimit: 100, notifyWhenChangesStop: true });\r\n }\r\n }\r\n\r\n var setEnabled = function (enable) {\r\n setTimeout(function () {\r\n if (enable)\r\n $element.multiselect('enable');\r\n else\r\n $element.multiselect('disable');\r\n });\r\n };\r\n\r\n if (allBindings.has('enable')) {\r\n var enable = allBindings.get('enable');\r\n if (ko.isObservable(enable)) {\r\n ko.computed({\r\n read: function () {\r\n setEnabled(enable());\r\n },\r\n disposeWhenNodeIsRemoved: element\r\n }).extend({ rateLimit: 100, notifyWhenChangesStop: true });\r\n } else {\r\n setEnabled(enable);\r\n }\r\n }\r\n\r\n if (allBindings.has('disable')) {\r\n var disable = allBindings.get('disable');\r\n if (ko.isObservable(disable)) {\r\n ko.computed({\r\n read: function () {\r\n setEnabled(!disable());\r\n },\r\n disposeWhenNodeIsRemoved: element\r\n }).extend({ rateLimit: 100, notifyWhenChangesStop: true });\r\n } else {\r\n setEnabled(!disable);\r\n }\r\n }\r\n\r\n ko.utils.domNodeDisposal.addDisposeCallback(element, function() {\r\n $element.multiselect('destroy');\r\n });\r\n },\r\n\r\n update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {\r\n var $element = $(element);\r\n var config = ko.toJS(valueAccessor());\r\n\r\n $element.multiselect('setOptions', config);\r\n $element.multiselect('rebuild');\r\n }\r\n };\r\n }\r\n\r\n function forEach(array, callback) {\r\n for (var index = 0; index < array.length; ++index) {\r\n callback(array[index], index);\r\n }\r\n }\r\n\r\n /**\r\n * Constructor to create a new multiselect using the given select.\r\n *\r\n * @param {jQuery} select\r\n * @param {Object} options\r\n * @returns {Multiselect}\r\n */\r\n function Multiselect(select, options) {\r\n\r\n this.$select = $(select);\r\n this.options = this.mergeOptions($.extend({}, options, this.$select.data()));\r\n\r\n // Placeholder via data attributes\r\n if (this.$select.attr(\"data-placeholder\")) {\r\n this.options.nonSelectedText = this.$select.data(\"placeholder\");\r\n }\r\n\r\n // Initialization.\r\n // We have to clone to create a new reference.\r\n this.originalOptions = this.$select.clone()[0].options;\r\n this.query = '';\r\n this.searchTimeout = null;\r\n this.lastToggledInput = null;\r\n\r\n this.options.multiple = this.$select.attr('multiple') === \"multiple\";\r\n this.options.onChange = $.proxy(this.options.onChange, this);\r\n this.options.onSelectAll = $.proxy(this.options.onSelectAll, this);\r\n this.options.onDeselectAll = $.proxy(this.options.onDeselectAll, this);\r\n this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this);\r\n this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this);\r\n this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this);\r\n this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this);\r\n this.options.onInitialized = $.proxy(this.options.onInitialized, this);\r\n this.options.onFiltering = $.proxy(this.options.onFiltering, this);\r\n\r\n // Build select all if enabled.\r\n this.buildContainer();\r\n this.buildButton();\r\n this.buildDropdown();\r\n this.buildReset();\r\n this.buildSelectAll();\r\n this.buildDropdownOptions();\r\n this.buildFilter();\r\n\r\n this.updateButtonText();\r\n this.updateSelectAll(true);\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n\r\n this.options.wasDisabled = this.$select.prop('disabled');\r\n if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {\r\n this.disable();\r\n }\r\n\r\n this.$select.wrap('').after(this.$container);\r\n this.options.onInitialized(this.$select, this.$container);\r\n }\r\n\r\n Multiselect.prototype = {\r\n\r\n defaults: {\r\n /**\r\n * Default text function will either print 'None selected' in case no\r\n * option is selected or a list of the selected options up to a length\r\n * of 3 selected options.\r\n *\r\n * @param {jQuery} options\r\n * @param {jQuery} select\r\n * @returns {String}\r\n */\r\n buttonText: function(options, select) {\r\n if (this.disabledText.length > 0\r\n && (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) {\r\n\r\n return this.disabledText;\r\n }\r\n else if (options.length === 0) {\r\n return this.nonSelectedText;\r\n }\r\n else if (this.allSelectedText\r\n && options.length === $('option', $(select)).length\r\n && $('option', $(select)).length !== 1\r\n && this.multiple) {\r\n\r\n if (this.selectAllNumber) {\r\n return this.allSelectedText + ' (' + options.length + ')';\r\n }\r\n else {\r\n return this.allSelectedText;\r\n }\r\n }\r\n else if (this.numberDisplayed != 0 && options.length > this.numberDisplayed) {\r\n return options.length + ' ' + this.nSelectedText;\r\n }\r\n else {\r\n var selected = '';\r\n var delimiter = this.delimiterText;\r\n\r\n options.each(function() {\r\n var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();\r\n selected += label + delimiter;\r\n });\r\n\r\n return selected.substr(0, selected.length - this.delimiterText.length);\r\n }\r\n },\r\n /**\r\n * Updates the title of the button similar to the buttonText function.\r\n *\r\n * @param {jQuery} options\r\n * @param {jQuery} select\r\n * @returns {@exp;selected@call;substr}\r\n */\r\n buttonTitle: function(options, select) {\r\n if (options.length === 0) {\r\n return this.nonSelectedText;\r\n }\r\n else {\r\n var selected = '';\r\n var delimiter = this.delimiterText;\r\n\r\n options.each(function () {\r\n var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text();\r\n selected += label + delimiter;\r\n });\r\n return selected.substr(0, selected.length - this.delimiterText.length);\r\n }\r\n },\r\n checkboxName: function(option) {\r\n return false; // no checkbox name\r\n },\r\n /**\r\n * Create a label.\r\n *\r\n * @param {jQuery} element\r\n * @returns {String}\r\n */\r\n optionLabel: function(element){\r\n return $(element).attr('label') || $(element).text();\r\n },\r\n /**\r\n * Create a class.\r\n *\r\n * @param {jQuery} element\r\n * @returns {String}\r\n */\r\n optionClass: function(element) {\r\n return $(element).attr('class') || '';\r\n },\r\n /**\r\n * Triggered on change of the multiselect.\r\n *\r\n * Not triggered when selecting/deselecting options manually.\r\n *\r\n * @param {jQuery} option\r\n * @param {Boolean} checked\r\n */\r\n onChange : function(option, checked) {\r\n\r\n },\r\n /**\r\n * Triggered when the dropdown is shown.\r\n *\r\n * @param {jQuery} event\r\n */\r\n onDropdownShow: function(event) {\r\n\r\n },\r\n /**\r\n * Triggered when the dropdown is hidden.\r\n *\r\n * @param {jQuery} event\r\n */\r\n onDropdownHide: function(event) {\r\n\r\n },\r\n /**\r\n * Triggered after the dropdown is shown.\r\n *\r\n * @param {jQuery} event\r\n */\r\n onDropdownShown: function(event) {\r\n\r\n },\r\n /**\r\n * Triggered after the dropdown is hidden.\r\n *\r\n * @param {jQuery} event\r\n */\r\n onDropdownHidden: function(event) {\r\n\r\n },\r\n /**\r\n * Triggered on select all.\r\n */\r\n onSelectAll: function() {\r\n\r\n },\r\n /**\r\n * Triggered on deselect all.\r\n */\r\n onDeselectAll: function() {\r\n\r\n },\r\n /**\r\n * Triggered after initializing.\r\n *\r\n * @param {jQuery} $select\r\n * @param {jQuery} $container\r\n */\r\n onInitialized: function($select, $container) {\r\n\r\n },\r\n /**\r\n * Triggered on filtering.\r\n *\r\n * @param {jQuery} $filter\r\n */\r\n onFiltering: function($filter) {\r\n\r\n },\r\n enableHTML: false,\r\n buttonClass: 'btn form-control',\r\n inheritClass: false,\r\n buttonWidth: 'auto',\r\n buttonContainer: '
',\r\n dropRight: false,\r\n dropUp: false,\r\n selectedClass: 'active',\r\n // Maximum height of the dropdown menu.\r\n // If maximum height is exceeded a scrollbar will be displayed.\r\n maxHeight: false,\r\n includeSelectAllOption: false,\r\n includeSelectAllIfMoreThan: 0,\r\n selectAllText: ' Select all',\r\n selectAllValue: 'multiselect-all',\r\n selectAllName: false,\r\n selectAllNumber: true,\r\n selectAllJustVisible: true,\r\n enableFiltering: false,\r\n enableCaseInsensitiveFiltering: false,\r\n enableFullValueFiltering: false,\r\n enableClickableOptGroups: false,\r\n enableCollapsibleOptGroups: false,\r\n collapseOptGroupsByDefault: false,\r\n filterPlaceholder: 'Search',\r\n // possible options: 'text', 'value', 'both'\r\n filterBehavior: 'text',\r\n includeFilterClearBtn: true,\r\n preventInputChangeEvent: false,\r\n nonSelectedText: '請選擇',\r\n nSelectedText: '個選擇',\r\n allSelectedText: '已選擇全部',\r\n numberDisplayed: 3,\r\n disableIfEmpty: false,\r\n disabledText: '',\r\n delimiterText: ', ',\r\n includeResetOption: false,\r\n includeResetDivider: false,\r\n resetText: 'Reset',\r\n templates: {\r\n button: '',\r\n ul: '',\r\n filter: '
  • ',\r\n filterClearBtn: '',\r\n li: '
  • ',\r\n divider: '
  • ',\r\n liGroup: '
  • ',\r\n resetButton: '
  • '\r\n }\r\n },\r\n\r\n constructor: Multiselect,\r\n\r\n /**\r\n * Builds the container of the multiselect.\r\n */\r\n buildContainer: function() {\r\n this.$container = $(this.options.buttonContainer);\r\n this.$container.on('show.bs.dropdown', this.options.onDropdownShow);\r\n this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);\r\n this.$container.on('shown.bs.dropdown', this.options.onDropdownShown);\r\n this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden);\r\n },\r\n\r\n /**\r\n * Builds the button of the multiselect.\r\n */\r\n buildButton: function() {\r\n this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);\r\n if (this.$select.attr('class') && this.options.inheritClass) {\r\n this.$button.addClass(this.$select.attr('class'));\r\n }\r\n // Adopt active state.\r\n if (this.$select.prop('disabled')) {\r\n this.disable();\r\n }\r\n else {\r\n this.enable();\r\n }\r\n\r\n // Manually add button width if set.\r\n if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {\r\n this.$button.css({\r\n 'width' : '100%', //this.options.buttonWidth,\r\n 'overflow' : 'hidden',\r\n 'text-overflow' : 'ellipsis'\r\n });\r\n this.$container.css({\r\n 'width': this.options.buttonWidth\r\n });\r\n }\r\n\r\n // Keep the tab index from the select.\r\n var tabindex = this.$select.attr('tabindex');\r\n if (tabindex) {\r\n this.$button.attr('tabindex', tabindex);\r\n }\r\n\r\n this.$container.prepend(this.$button);\r\n },\r\n\r\n /**\r\n * Builds the ul representing the dropdown menu.\r\n */\r\n buildDropdown: function() {\r\n\r\n // Build ul.\r\n this.$ul = $(this.options.templates.ul);\r\n\r\n if (this.options.dropRight) {\r\n this.$ul.addClass('pull-right');\r\n }\r\n\r\n // Set max height of dropdown menu to activate auto scrollbar.\r\n if (this.options.maxHeight) {\r\n // TODO: Add a class for this option to move the css declarations.\r\n this.$ul.css({\r\n 'max-height': this.options.maxHeight + 'px',\r\n 'overflow-y': 'auto',\r\n 'overflow-x': 'hidden'\r\n });\r\n }\r\n\r\n if (this.options.dropUp) {\r\n\r\n var height = Math.min(this.options.maxHeight, $('option[data-role!=\"divider\"]', this.$select).length*26 + $('option[data-role=\"divider\"]', this.$select).length*19 + (this.options.includeSelectAllOption ? 26 : 0) + (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering ? 44 : 0));\r\n var moveCalc = height + 34;\r\n\r\n this.$ul.css({\r\n 'max-height': height + 'px',\r\n 'overflow-y': 'auto',\r\n 'overflow-x': 'hidden',\r\n 'margin-top': \"-\" + moveCalc + 'px'\r\n });\r\n }\r\n\r\n this.$container.append(this.$ul);\r\n },\r\n\r\n /**\r\n * Build the dropdown options and binds all necessary events.\r\n *\r\n * Uses createDivider and createOptionValue to create the necessary options.\r\n */\r\n buildDropdownOptions: function() {\r\n\r\n this.$select.children().each($.proxy(function(index, element) {\r\n\r\n var $element = $(element);\r\n // Support optgroups and options without a group simultaneously.\r\n var tag = $element.prop('tagName')\r\n .toLowerCase();\r\n\r\n if ($element.prop('value') === this.options.selectAllValue) {\r\n return;\r\n }\r\n\r\n if (tag === 'optgroup') {\r\n this.createOptgroup(element);\r\n }\r\n else if (tag === 'option') {\r\n\r\n if ($element.data('role') === 'divider') {\r\n this.createDivider();\r\n }\r\n else {\r\n this.createOptionValue(element);\r\n }\r\n\r\n }\r\n\r\n // Other illegal tags will be ignored.\r\n }, this));\r\n\r\n // Bind the change event on the dropdown elements.\r\n $(this.$ul).off('change', 'li:not(.multiselect-group) input[type=\"checkbox\"], li:not(.multiselect-group) input[type=\"radio\"]');\r\n $(this.$ul).on('change', 'li:not(.multiselect-group) input[type=\"checkbox\"], li:not(.multiselect-group) input[type=\"radio\"]', $.proxy(function(event) {\r\n var $target = $(event.target);\r\n\r\n var checked = $target.prop('checked') || false;\r\n var isSelectAllOption = $target.val() === this.options.selectAllValue;\r\n\r\n // Apply or unapply the configured selected class.\r\n if (this.options.selectedClass) {\r\n if (checked) {\r\n $target.closest('li')\r\n .addClass(this.options.selectedClass);\r\n }\r\n else {\r\n $target.closest('li')\r\n .removeClass(this.options.selectedClass);\r\n }\r\n }\r\n\r\n // Get the corresponding option.\r\n var value = $target.val();\r\n var $option = this.getOptionByValue(value);\r\n\r\n var $optionsNotThis = $('option', this.$select).not($option);\r\n var $checkboxesNotThis = $('input', this.$container).not($target);\r\n\r\n if (isSelectAllOption) {\r\n\r\n if (checked) {\r\n this.selectAll(this.options.selectAllJustVisible, true);\r\n }\r\n else {\r\n this.deselectAll(this.options.selectAllJustVisible, true);\r\n }\r\n }\r\n else {\r\n if (checked) {\r\n $option.prop('selected', true);\r\n\r\n if (this.options.multiple) {\r\n // Simply select additional option.\r\n $option.prop('selected', true);\r\n }\r\n else {\r\n // Unselect all other options and corresponding checkboxes.\r\n if (this.options.selectedClass) {\r\n $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass);\r\n }\r\n\r\n $($checkboxesNotThis).prop('checked', false);\r\n $optionsNotThis.prop('selected', false);\r\n\r\n // It's a single selection, so close.\r\n this.$button.click();\r\n }\r\n\r\n if (this.options.selectedClass === \"active\") {\r\n $optionsNotThis.closest(\"a\").css(\"outline\", \"\");\r\n }\r\n }\r\n else {\r\n // Unselect option.\r\n $option.prop('selected', false);\r\n }\r\n\r\n // To prevent select all from firing onChange: #575\r\n this.options.onChange($option, checked);\r\n\r\n // Do not update select all or optgroups on select all change!\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n }\r\n\r\n this.$select.change();\r\n this.updateButtonText();\r\n\r\n if(this.options.preventInputChangeEvent) {\r\n return false;\r\n }\r\n }, this));\r\n\r\n $('li a', this.$ul).on('mousedown', function(e) {\r\n if (e.shiftKey) {\r\n // Prevent selecting text by Shift+click\r\n return false;\r\n }\r\n });\r\n\r\n $(this.$ul).on('touchstart click', 'li a', $.proxy(function(event) {\r\n event.stopPropagation();\r\n\r\n var $target = $(event.target);\r\n\r\n if (event.shiftKey && this.options.multiple) {\r\n if($target.is(\"label\")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431)\r\n event.preventDefault();\r\n $target = $target.find(\"input\");\r\n $target.prop(\"checked\", !$target.prop(\"checked\"));\r\n }\r\n var checked = $target.prop('checked') || false;\r\n\r\n if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range\r\n var from = this.$ul.find(\"li:visible\").index($target.parents(\"li\"));\r\n var to = this.$ul.find(\"li:visible\").index(this.lastToggledInput.parents(\"li\"));\r\n\r\n if (from > to) { // Swap the indices\r\n var tmp = to;\r\n to = from;\r\n from = tmp;\r\n }\r\n\r\n // Make sure we grab all elements since slice excludes the last index\r\n ++to;\r\n\r\n // Change the checkboxes and underlying options\r\n var range = this.$ul.find(\"li\").not(\".multiselect-filter-hidden\").slice(from, to).find(\"input\");\r\n\r\n range.prop('checked', checked);\r\n\r\n if (this.options.selectedClass) {\r\n range.closest('li')\r\n .toggleClass(this.options.selectedClass, checked);\r\n }\r\n\r\n for (var i = 0, j = range.length; i < j; i++) {\r\n var $checkbox = $(range[i]);\r\n\r\n var $option = this.getOptionByValue($checkbox.val());\r\n\r\n $option.prop('selected', checked);\r\n }\r\n }\r\n\r\n // Trigger the select \"change\" event\r\n $target.trigger(\"change\");\r\n }\r\n\r\n // Remembers last clicked option\r\n if($target.is(\"input\") && !$target.closest(\"li\").is(\".multiselect-item\")){\r\n this.lastToggledInput = $target;\r\n }\r\n\r\n $target.blur();\r\n }, this));\r\n\r\n // Keyboard support.\r\n this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {\r\n if ($('input[type=\"text\"]', this.$container).is(':focus')) {\r\n return;\r\n }\r\n\r\n if (event.keyCode === 9 && this.$container.hasClass('open')) {\r\n this.$button.click();\r\n }\r\n else {\r\n var $items = $(this.$container).find(\"li:not(.divider):not(.disabled) a\").filter(\":visible\");\r\n\r\n if (!$items.length) {\r\n return;\r\n }\r\n\r\n var index = $items.index($items.filter(':focus'));\r\n \r\n // Navigation up.\r\n if (event.keyCode === 38 && index > 0) {\r\n index--;\r\n }\r\n // Navigate down.\r\n else if (event.keyCode === 40 && index < $items.length - 1) {\r\n index++;\r\n }\r\n else if (!~index) {\r\n index = 0;\r\n }\r\n\r\n var $current = $items.eq(index);\r\n $current.focus();\r\n\r\n if (event.keyCode === 32 || event.keyCode === 13) {\r\n var $checkbox = $current.find('input');\r\n\r\n $checkbox.prop(\"checked\", !$checkbox.prop(\"checked\"));\r\n $checkbox.change();\r\n }\r\n\r\n event.stopPropagation();\r\n event.preventDefault();\r\n }\r\n }, this));\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n $(\"li.multiselect-group input\", this.$ul).on(\"change\", $.proxy(function(event) {\r\n event.stopPropagation();\r\n\r\n var $target = $(event.target);\r\n var checked = $target.prop('checked') || false;\r\n\r\n var $li = $(event.target).closest('li');\r\n var $group = $li.nextUntil(\"li.multiselect-group\")\r\n .not('.multiselect-filter-hidden')\r\n .not('.disabled');\r\n\r\n var $inputs = $group.find(\"input\");\r\n\r\n var values = [];\r\n var $options = [];\r\n\r\n if (this.options.selectedClass) {\r\n if (checked) {\r\n $li.addClass(this.options.selectedClass);\r\n }\r\n else {\r\n $li.removeClass(this.options.selectedClass);\r\n }\r\n }\r\n\r\n $.each($inputs, $.proxy(function(index, input) {\r\n var value = $(input).val();\r\n var $option = this.getOptionByValue(value);\r\n\r\n if (checked) {\r\n $(input).prop('checked', true);\r\n $(input).closest('li')\r\n .addClass(this.options.selectedClass);\r\n\r\n $option.prop('selected', true);\r\n }\r\n else {\r\n $(input).prop('checked', false);\r\n $(input).closest('li')\r\n .removeClass(this.options.selectedClass);\r\n\r\n $option.prop('selected', false);\r\n }\r\n\r\n $options.push(this.getOptionByValue(value));\r\n }, this))\r\n\r\n // Cannot use select or deselect here because it would call updateOptGroups again.\r\n\r\n this.options.onChange($options, checked);\r\n\r\n this.$select.change();\r\n this.updateButtonText();\r\n this.updateSelectAll();\r\n }, this));\r\n }\r\n\r\n if (this.options.enableCollapsibleOptGroups && this.options.multiple) {\r\n $(\"li.multiselect-group .caret-container\", this.$ul).on(\"click\", $.proxy(function(event) {\r\n var $li = $(event.target).closest('li');\r\n var $inputs = $li.nextUntil(\"li.multiselect-group\")\r\n .not('.multiselect-filter-hidden');\r\n\r\n var visible = true;\r\n $inputs.each(function() {\r\n visible = visible && !$(this).hasClass('multiselect-collapsible-hidden');\r\n });\r\n\r\n if (visible) {\r\n $inputs.hide()\r\n .addClass('multiselect-collapsible-hidden');\r\n }\r\n else {\r\n $inputs.show()\r\n .removeClass('multiselect-collapsible-hidden');\r\n }\r\n }, this));\r\n\r\n $(\"li.multiselect-all\", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea');\r\n $(\"li.multiselect-all > a > label.checkbox\", this.$ul).css('padding', '3px 20px 3px 35px');\r\n $(\"li.multiselect-group > a > input\", this.$ul).css('margin', '4px 0px 5px -20px');\r\n }\r\n },\r\n\r\n /**\r\n * Create an option using the given select option.\r\n *\r\n * @param {jQuery} element\r\n */\r\n createOptionValue: function(element) {\r\n var $element = $(element);\r\n if ($element.is(':selected')) {\r\n $element.prop('selected', true);\r\n }\r\n\r\n // Support the label attribute on options.\r\n var label = this.options.optionLabel(element);\r\n var classes = this.options.optionClass(element);\r\n var value = $element.val();\r\n var inputType = this.options.multiple ? \"checkbox\" : \"radio\";\r\n\r\n var $li = $(this.options.templates.li);\r\n var $label = $('label', $li);\r\n $label.addClass(inputType);\r\n $label.attr(\"title\", label);\r\n $li.addClass(classes);\r\n\r\n // Hide all children items when collapseOptGroupsByDefault is true\r\n if (this.options.collapseOptGroupsByDefault && $(element).parent().prop(\"tagName\").toLowerCase() === \"optgroup\") {\r\n $li.addClass(\"multiselect-collapsible-hidden\");\r\n $li.hide();\r\n }\r\n\r\n if (this.options.enableHTML) {\r\n $label.html(\" \" + label);\r\n }\r\n else {\r\n $label.text(\" \" + label);\r\n }\r\n\r\n var $checkbox = $('').attr('type', inputType);\r\n\r\n var name = this.options.checkboxName($element);\r\n if (name) {\r\n $checkbox.attr('name', name);\r\n }\r\n\r\n $label.prepend($checkbox);\r\n\r\n var selected = $element.prop('selected') || false;\r\n $checkbox.val(value);\r\n\r\n if (value === this.options.selectAllValue) {\r\n $li.addClass(\"multiselect-item multiselect-all\");\r\n $checkbox.parent().parent()\r\n .addClass('multiselect-all');\r\n }\r\n\r\n $label.attr('title', $element.attr('title'));\r\n\r\n this.$ul.append($li);\r\n\r\n if ($element.is(':disabled')) {\r\n $checkbox.attr('disabled', 'disabled')\r\n .prop('disabled', true)\r\n .closest('a')\r\n .attr(\"tabindex\", \"-1\")\r\n .closest('li')\r\n .addClass('disabled');\r\n }\r\n\r\n $checkbox.prop('checked', selected);\r\n\r\n if (selected && this.options.selectedClass) {\r\n $checkbox.closest('li')\r\n .addClass(this.options.selectedClass);\r\n }\r\n },\r\n\r\n /**\r\n * Creates a divider using the given select option.\r\n *\r\n * @param {jQuery} element\r\n */\r\n createDivider: function(element) {\r\n var $divider = $(this.options.templates.divider);\r\n this.$ul.append($divider);\r\n },\r\n\r\n /**\r\n * Creates an optgroup.\r\n *\r\n * @param {jQuery} group\r\n */\r\n createOptgroup: function(group) {\r\n var label = $(group).attr(\"label\");\r\n var value = $(group).attr(\"value\");\r\n var $li = $('
  • ');\r\n\r\n var classes = this.options.optionClass(group);\r\n $li.addClass(classes);\r\n\r\n if (this.options.enableHTML) {\r\n $('label b', $li).html(\" \" + label);\r\n }\r\n else {\r\n $('label b', $li).text(\" \" + label);\r\n }\r\n\r\n if (this.options.enableCollapsibleOptGroups && this.options.multiple) {\r\n $('a', $li).append('');\r\n }\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n $('a label', $li).prepend('');\r\n }\r\n\r\n if ($(group).is(':disabled')) {\r\n $li.addClass('disabled');\r\n }\r\n\r\n this.$ul.append($li);\r\n\r\n $(\"option\", group).each($.proxy(function($, group) {\r\n this.createOptionValue(group);\r\n }, this))\r\n },\r\n\r\n /**\r\n * Build the reset.\r\n *\r\n */\r\n buildReset: function() {\r\n if (this.options.includeResetOption) {\r\n\r\n // Check whether to add a divider after the reset.\r\n if (this.options.includeResetDivider) {\r\n this.$ul.prepend($(this.options.templates.divider));\r\n }\r\n\r\n var $resetButton = $(this.options.templates.resetButton);\r\n\r\n if (this.options.enableHTML) {\r\n $('a', $resetButton).html(this.options.resetText);\r\n }\r\n else {\r\n $('a', $resetButton).text(this.options.resetText);\r\n }\r\n\r\n $('a', $resetButton).click($.proxy(function(){\r\n this.clearSelection();\r\n }, this));\r\n\r\n this.$ul.prepend($resetButton);\r\n }\r\n },\r\n\r\n /**\r\n * Build the select all.\r\n *\r\n * Checks if a select all has already been created.\r\n */\r\n buildSelectAll: function() {\r\n if (typeof this.options.selectAllValue === 'number') {\r\n this.options.selectAllValue = this.options.selectAllValue.toString();\r\n }\r\n\r\n var alreadyHasSelectAll = this.hasSelectAll();\r\n\r\n if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple\r\n && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {\r\n\r\n // Check whether to add a divider after the select all.\r\n if (this.options.includeSelectAllDivider) {\r\n this.$ul.prepend($(this.options.templates.divider));\r\n }\r\n\r\n var $li = $(this.options.templates.li);\r\n $('label', $li).addClass(\"checkbox\");\r\n\r\n if (this.options.enableHTML) {\r\n $('label', $li).html(\" \" + this.options.selectAllText);\r\n }\r\n else {\r\n $('label', $li).text(\" \" + this.options.selectAllText);\r\n }\r\n\r\n if (this.options.selectAllName) {\r\n $('label', $li).prepend('');\r\n }\r\n else {\r\n $('label', $li).prepend('');\r\n }\r\n\r\n var $checkbox = $('input', $li);\r\n $checkbox.val(this.options.selectAllValue);\r\n\r\n $li.addClass(\"multiselect-item multiselect-all\");\r\n $checkbox.parent().parent()\r\n .addClass('multiselect-all');\r\n\r\n this.$ul.prepend($li);\r\n\r\n $checkbox.prop('checked', false);\r\n }\r\n },\r\n\r\n /**\r\n * Builds the filter.\r\n */\r\n buildFilter: function() {\r\n\r\n // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.\r\n if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {\r\n var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering);\r\n\r\n if (this.$select.find('option').length >= enableFilterLength) {\r\n\r\n this.$filter = $(this.options.templates.filter);\r\n $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);\r\n\r\n // Adds optional filter clear button\r\n if(this.options.includeFilterClearBtn) {\r\n var clearBtn = $(this.options.templates.filterClearBtn);\r\n clearBtn.on('click', $.proxy(function(event){\r\n clearTimeout(this.searchTimeout);\r\n\r\n this.query = '';\r\n this.$filter.find('.multiselect-search').val('');\r\n $('li', this.$ul).show().removeClass('multiselect-filter-hidden');\r\n\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n\r\n }, this));\r\n this.$filter.find('.input-group').append(clearBtn);\r\n }\r\n\r\n this.$ul.prepend(this.$filter);\r\n\r\n this.$filter.val(this.query).on('click', function(event) {\r\n event.stopPropagation();\r\n }).on('input keydown', $.proxy(function(event) {\r\n // Cancel enter key default behaviour\r\n if (event.which === 13) {\r\n event.preventDefault();\r\n }\r\n\r\n // This is useful to catch \"keydown\" events after the browser has updated the control.\r\n clearTimeout(this.searchTimeout);\r\n\r\n this.searchTimeout = this.asyncFunction($.proxy(function() {\r\n\r\n if (this.query !== event.target.value) {\r\n this.query = event.target.value;\r\n\r\n var currentGroup, currentGroupVisible;\r\n $.each($('li', this.$ul), $.proxy(function(index, element) {\r\n var value = $('input', element).length > 0 ? $('input', element).val() : \"\";\r\n var text = $('label', element).text();\r\n\r\n var filterCandidate = '';\r\n if ((this.options.filterBehavior === 'text')) {\r\n filterCandidate = text;\r\n }\r\n else if ((this.options.filterBehavior === 'value')) {\r\n filterCandidate = value;\r\n }\r\n else if (this.options.filterBehavior === 'both') {\r\n filterCandidate = text + '\\n' + value;\r\n }\r\n\r\n if (value !== this.options.selectAllValue && text) {\r\n\r\n // By default lets assume that element is not\r\n // interesting for this search.\r\n var showElement = false;\r\n\r\n if (this.options.enableCaseInsensitiveFiltering) {\r\n filterCandidate = filterCandidate.toLowerCase();\r\n this.query = this.query.toLowerCase();\r\n }\r\n\r\n if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') {\r\n var valueToMatch = filterCandidate.trim().substring(0, this.query.length);\r\n if (this.query.indexOf(valueToMatch) > -1) {\r\n showElement = true;\r\n }\r\n }\r\n else if (filterCandidate.indexOf(this.query) > -1) {\r\n showElement = true;\r\n }\r\n\r\n // Toggle current element (group or group item) according to showElement boolean.\r\n if(!showElement){\r\n $(element).css('display', 'none');\r\n $(element).addClass('multiselect-filter-hidden');\r\n }\r\n if(showElement){\r\n $(element).css('display', 'block');\r\n $(element).removeClass('multiselect-filter-hidden');\r\n }\r\n\r\n // Differentiate groups and group items.\r\n if ($(element).hasClass('multiselect-group')) {\r\n // Remember group status.\r\n currentGroup = element;\r\n currentGroupVisible = showElement;\r\n }\r\n else {\r\n // Show group name when at least one of its items is visible.\r\n if (showElement) {\r\n $(currentGroup).show()\r\n .removeClass('multiselect-filter-hidden');\r\n }\r\n\r\n // Show all group items when group name satisfies filter.\r\n if (!showElement && currentGroupVisible) {\r\n $(element).show()\r\n .removeClass('multiselect-filter-hidden');\r\n }\r\n }\r\n }\r\n }, this));\r\n }\r\n\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n\r\n this.options.onFiltering(event.target);\r\n\r\n }, this), 300, this);\r\n }, this));\r\n }\r\n }\r\n },\r\n\r\n /**\r\n * Unbinds the whole plugin.\r\n */\r\n destroy: function() {\r\n this.$container.remove();\r\n this.$select.show();\r\n\r\n // reset original state\r\n this.$select.prop('disabled', this.options.wasDisabled);\r\n\r\n this.$select.data('multiselect', null);\r\n },\r\n\r\n /**\r\n * Refreshs the multiselect based on the selected options of the select.\r\n */\r\n refresh: function () {\r\n var inputs = {};\r\n $('li input', this.$ul).each(function() {\r\n inputs[$(this).val()] = $(this);\r\n });\r\n\r\n $('option', this.$select).each($.proxy(function (index, element) {\r\n var $elem = $(element);\r\n var $input = inputs[$(element).val()];\r\n\r\n if ($elem.is(':selected')) {\r\n $input.prop('checked', true);\r\n\r\n if (this.options.selectedClass) {\r\n $input.closest('li')\r\n .addClass(this.options.selectedClass);\r\n }\r\n }\r\n else {\r\n $input.prop('checked', false);\r\n\r\n if (this.options.selectedClass) {\r\n $input.closest('li')\r\n .removeClass(this.options.selectedClass);\r\n }\r\n }\r\n\r\n if ($elem.is(\":disabled\")) {\r\n $input.attr('disabled', 'disabled')\r\n .prop('disabled', true)\r\n .closest('li')\r\n .addClass('disabled');\r\n }\r\n else {\r\n $input.prop('disabled', false)\r\n .closest('li')\r\n .removeClass('disabled');\r\n }\r\n }, this));\r\n\r\n this.updateButtonText();\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n },\r\n\r\n /**\r\n * Select all options of the given values.\r\n *\r\n * If triggerOnChange is set to true, the on change event is triggered if\r\n * and only if one value is passed.\r\n *\r\n * @param {Array} selectValues\r\n * @param {Boolean} triggerOnChange\r\n */\r\n select: function(selectValues, triggerOnChange) {\r\n if(!$.isArray(selectValues)) {\r\n selectValues = [selectValues];\r\n }\r\n\r\n for (var i = 0; i < selectValues.length; i++) {\r\n var value = selectValues[i];\r\n\r\n if (value === null || value === undefined) {\r\n continue;\r\n }\r\n\r\n var $option = this.getOptionByValue(value);\r\n var $checkbox = this.getInputByValue(value);\r\n\r\n if($option === undefined || $checkbox === undefined) {\r\n continue;\r\n }\r\n\r\n if (!this.options.multiple) {\r\n this.deselectAll(false);\r\n }\r\n\r\n if (this.options.selectedClass) {\r\n $checkbox.closest('li')\r\n .addClass(this.options.selectedClass);\r\n }\r\n\r\n $checkbox.prop('checked', true);\r\n $option.prop('selected', true);\r\n\r\n if (triggerOnChange) {\r\n this.options.onChange($option, true);\r\n }\r\n }\r\n\r\n this.updateButtonText();\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n },\r\n\r\n /**\r\n * Clears all selected items.\r\n */\r\n clearSelection: function () {\r\n this.deselectAll(false);\r\n this.updateButtonText();\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n },\r\n\r\n /**\r\n * Deselects all options of the given values.\r\n *\r\n * If triggerOnChange is set to true, the on change event is triggered, if\r\n * and only if one value is passed.\r\n *\r\n * @param {Array} deselectValues\r\n * @param {Boolean} triggerOnChange\r\n */\r\n deselect: function(deselectValues, triggerOnChange) {\r\n if(!$.isArray(deselectValues)) {\r\n deselectValues = [deselectValues];\r\n }\r\n\r\n for (var i = 0; i < deselectValues.length; i++) {\r\n var value = deselectValues[i];\r\n\r\n if (value === null || value === undefined) {\r\n continue;\r\n }\r\n\r\n var $option = this.getOptionByValue(value);\r\n var $checkbox = this.getInputByValue(value);\r\n\r\n if($option === undefined || $checkbox === undefined) {\r\n continue;\r\n }\r\n\r\n if (this.options.selectedClass) {\r\n $checkbox.closest('li')\r\n .removeClass(this.options.selectedClass);\r\n }\r\n\r\n $checkbox.prop('checked', false);\r\n $option.prop('selected', false);\r\n\r\n if (triggerOnChange) {\r\n this.options.onChange($option, false);\r\n }\r\n }\r\n\r\n this.updateButtonText();\r\n this.updateSelectAll();\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n },\r\n\r\n /**\r\n * Selects all enabled & visible options.\r\n *\r\n * If justVisible is true or not specified, only visible options are selected.\r\n *\r\n * @param {Boolean} justVisible\r\n * @param {Boolean} triggerOnSelectAll\r\n */\r\n selectAll: function (justVisible, triggerOnSelectAll) {\r\n\r\n var justVisible = typeof justVisible === 'undefined' ? true : justVisible;\r\n var allLis = $(\"li:not(.divider):not(.disabled):not(.multiselect-group)\", this.$ul);\r\n var visibleLis = $(\"li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)\", this.$ul).filter(':visible');\r\n\r\n if(justVisible) {\r\n $('input:enabled' , visibleLis).prop('checked', true);\r\n visibleLis.addClass(this.options.selectedClass);\r\n\r\n $('input:enabled' , visibleLis).each($.proxy(function(index, element) {\r\n var value = $(element).val();\r\n var option = this.getOptionByValue(value);\r\n $(option).prop('selected', true);\r\n }, this));\r\n }\r\n else {\r\n $('input:enabled' , allLis).prop('checked', true);\r\n allLis.addClass(this.options.selectedClass);\r\n\r\n $('input:enabled' , allLis).each($.proxy(function(index, element) {\r\n var value = $(element).val();\r\n var option = this.getOptionByValue(value);\r\n $(option).prop('selected', true);\r\n }, this));\r\n }\r\n\r\n $('li input[value=\"' + this.options.selectAllValue + '\"]', this.$ul).prop('checked', true);\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n\r\n if (triggerOnSelectAll) {\r\n this.options.onSelectAll();\r\n }\r\n },\r\n\r\n /**\r\n * Deselects all options.\r\n *\r\n * If justVisible is true or not specified, only visible options are deselected.\r\n *\r\n * @param {Boolean} justVisible\r\n */\r\n deselectAll: function (justVisible, triggerOnDeselectAll) {\r\n\r\n var justVisible = typeof justVisible === 'undefined' ? true : justVisible;\r\n var allLis = $(\"li:not(.divider):not(.disabled):not(.multiselect-group)\", this.$ul);\r\n var visibleLis = $(\"li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)\", this.$ul).filter(':visible');\r\n\r\n if(justVisible) {\r\n $('input[type=\"checkbox\"]:enabled' , visibleLis).prop('checked', false);\r\n visibleLis.removeClass(this.options.selectedClass);\r\n\r\n $('input[type=\"checkbox\"]:enabled' , visibleLis).each($.proxy(function(index, element) {\r\n var value = $(element).val();\r\n var option = this.getOptionByValue(value);\r\n $(option).prop('selected', false);\r\n }, this));\r\n }\r\n else {\r\n $('input[type=\"checkbox\"]:enabled' , allLis).prop('checked', false);\r\n allLis.removeClass(this.options.selectedClass);\r\n\r\n $('input[type=\"checkbox\"]:enabled' , allLis).each($.proxy(function(index, element) {\r\n var value = $(element).val();\r\n var option = this.getOptionByValue(value);\r\n $(option).prop('selected', false);\r\n }, this));\r\n }\r\n\r\n $('li input[value=\"' + this.options.selectAllValue + '\"]', this.$ul).prop('checked', false);\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n\r\n if (triggerOnDeselectAll) {\r\n this.options.onDeselectAll();\r\n }\r\n },\r\n\r\n /**\r\n * Rebuild the plugin.\r\n *\r\n * Rebuilds the dropdown, the filter and the select all option.\r\n */\r\n rebuild: function() {\r\n this.$ul.html('');\r\n\r\n // Important to distinguish between radios and checkboxes.\r\n this.options.multiple = this.$select.attr('multiple') === \"multiple\";\r\n\r\n this.buildSelectAll();\r\n this.buildDropdownOptions();\r\n this.buildFilter();\r\n\r\n this.updateButtonText();\r\n this.updateSelectAll(true);\r\n\r\n if (this.options.enableClickableOptGroups && this.options.multiple) {\r\n this.updateOptGroups();\r\n }\r\n\r\n if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) {\r\n this.disable();\r\n }\r\n else {\r\n this.enable();\r\n }\r\n\r\n if (this.options.dropRight) {\r\n this.$ul.addClass('pull-right');\r\n }\r\n },\r\n\r\n /**\r\n * The provided data will be used to build the dropdown.\r\n */\r\n dataprovider: function(dataprovider) {\r\n\r\n var groupCounter = 0;\r\n var $select = this.$select.empty();\r\n\r\n $.each(dataprovider, function (index, option) {\r\n var $tag;\r\n\r\n if ($.isArray(option.children)) { // create optiongroup tag\r\n groupCounter++;\r\n\r\n $tag = $('').attr({\r\n label: option.label || 'Group ' + groupCounter,\r\n disabled: !!option.disabled,\r\n value: option.value\r\n });\r\n\r\n forEach(option.children, function(subOption) { // add children option tags\r\n var attributes = {\r\n value: subOption.value,\r\n label: subOption.label || subOption.value,\r\n title: subOption.title,\r\n selected: !!subOption.selected,\r\n disabled: !!subOption.disabled\r\n };\r\n\r\n //Loop through attributes object and add key-value for each attribute\r\n for (var key in subOption.attributes) {\r\n attributes['data-' + key] = subOption.attributes[key];\r\n }\r\n //Append original attributes + new data attributes to option\r\n $tag.append($('