text: /**
   * bootstrap-multiselect.js
   * https://github.com/davidstutz/bootstrap-multiselect
   *
   * Copyright 2012 - 2014 David Stutz
   *
   * Dual licensed under the BSD-3-Clause and the Apache License, Version 2.0.
   */
  !function($) {
  
      "use strict";// jshint ;_;
  
      if (Array.prototype.forEach === null || Array.prototype.forEach === undefined) {
          Array.prototype.forEach = function (func) {
              var index;
              for (index = 0; index < this.length; ++index) {
                  func(this[index]);
              }
          };
      }
  
      if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) {
          ko.bindingHandlers.multiselect = {
  
              init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  
                  var listOfSelectedItems = allBindingsAccessor().selectedOptions,
                      config = ko.utils.unwrapObservable(valueAccessor());
  
                  $(element).multiselect(config);
  
                  if (isObservableArray(listOfSelectedItems)) {
                     //set the initial selection state on the multi-select list
                      $(element).multiselect('select', ko.utils.unwrapObservable(listOfSelectedItems));
                      // Subscribe to the selectedOptions: ko.observableArray
                      listOfSelectedItems.subscribe(function (changes) {
                          var addedArray = [], deletedArray = [];
                          changes.forEach(function (change) {
                              switch (change.status) {
                                  case 'added':
                                      addedArray.push(change.value);
                                      break;
                                  case 'deleted':
                                      deletedArray.push(change.value);
                                      break;
                              }
                          });
                          if (addedArray.length > 0) {
                              $(element).multiselect('select', addedArray);
                          };
                          if (deletedArray.length > 0) {
                              $(element).multiselect('deselect', deletedArray);
                          };
                      }, null, "arrayChange");
                  }
              },
  
              update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
  
                  var listOfItems = allBindingsAccessor().options,
                      ms = $(element).data('multiselect'),
                      config = ko.utils.unwrapObservable(valueAccessor());
  
                  if (isObservableArray(listOfItems)) {
                      // Subscribe to the options: ko.observableArray incase it changes later
                      listOfItems.subscribe(function (theArray) {
                          $(element).multiselect('rebuild');
                      });
                  }
  
                  if (!ms) {
                      $(element).multiselect(config);
                  }
                  else {
                      ms.updateOriginalOptions();
                  }
              }
          };
      }
  
      function isObservableArray(obj) {
          return ko.isObservable(obj) && !(obj.destroyAll === undefined);
      }
  
      /**
       * Constructor to create a new multiselect using the given select.
       *
       * @param {jQuery} select
       * @param {Object} options
       * @returns {Multiselect}
       */
      function Multiselect(select, options) {
  
          this.options = this.mergeOptions(options);
          this.$select = $(select);
  
          // Initialization.
          // We have to clone to create a new reference.
          this.originalOptions = this.$select.clone()[0].options;
          this.query = '';
          this.searchTimeout = null;
  
          this.options.multiple = this.$select.attr('multiple') === "multiple";
          this.options.onChange = $.proxy(this.options.onChange, this);
          this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this);
          this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this);
  
          // Build select all if enabled.
          this.buildContainer();
          this.buildButton();
          this.buildDropdown();
          this.buildSelectAll();
          this.buildDropdownOptions();
          this.buildFilter();
  
          this.updateButtonText();
          this.updateSelectAll();
  
          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.
               *
               * @param {jQuery} options
               * @param {jQuery} select
               * @returns {String}
               */
              buttonText: function(options, select) {
                  if (options.length === 0) {
                      return this.nonSelectedText + ' <b class="caret"></b>';
                  }
                  else {
                      if (options.length > this.numberDisplayed) {
                          return options.length + ' ' + this.nSelectedText + ' <b class="caret"></b>';
                      }
                      else {
                          var selected = '';
                          options.each(function() {
                              var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).html();
  
                              selected += label + ', ';
                          });
                          return selected.substr(0, selected.length - 2) + ' <b class="caret"></b>';
                      }
                  }
              },
              /**
               * 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) {
                  if (options.length === 0) {
                      return this.nonSelectedText;
                  }
                  else {
                      var selected = '';
                      options.each(function () {
                          selected += $(this).text() + ', ';
                      });
                      return selected.substr(0, selected.length - 2);
                  }
              },
              /**
               * Create a label.
               *
               * @param {jQuery} element
               * @returns {String}
               */
              label: function(element){
                  return $(element).attr('label') || $(element).html();
              },
              /**
               * Triggered on change of the multiselect.
               * Not triggered when selecting/deselecting options manually.
               *
               * @param {jQuery} option
               * @param {Boolean} checked
               */
              onChange : function(option, checked) {
  
              },
              /**
               * Triggered when the dropdown is shown.
               *
               * @param {jQuery} event
               */
              onDropdownShow: function(event) {
  
              },
              /**
               * Triggered when the dropdown is hidden.
               *
               * @param {jQuery} event
               */
              onDropdownHide: function(event) {
  
              },
              buttonClass: 'btn btn-default',
              dropRight: false,
              selectedClass: 'active',
              buttonWidth: 'auto',
              buttonContainer: '<div class="btn-group" />',
              // Maximum height of the dropdown menu.
              // If maximum height is exceeded a scrollbar will be displayed.
              maxHeight: false,
              checkboxName: 'multiselect',
              includeSelectAllOption: false,
              includeSelectAllIfMoreThan: 0,
              selectAllText: ' Select all',
              selectAllValue: 'multiselect-all',
              enableFiltering: false,
              enableCaseInsensitiveFiltering: false,
              filterPlaceholder: 'Search',
              // possible options: 'text', 'value', 'both'
              filterBehavior: 'text',
              preventInputChangeEvent: false,
              nonSelectedText: 'None selected',
              nSelectedText: 'selected',
              numberDisplayed: 3,
              templates: {
                  button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"></button>',
                  ul: '<ul class="multiselect-container dropdown-menu"></ul>',
                  filter: '<li class="multiselect-item filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>',
                  li: '<li><a href="javascript:void(0);"><label></label></a></li>',
                  divider: '<li class="multiselect-item divider"></li>',
                  liGroup: '<li class="multiselect-item group"><label class="multiselect-group"></label></li>'
              }
          },
  
          constructor: Multiselect,
  
          /**
           * Builds the container of the multiselect.
           */
          buildContainer: function() {
              this.$container = $(this.options.buttonContainer);
              this.$container.on('show.bs.dropdown', this.options.onDropdownShow);
              this.$container.on('hide.bs.dropdown', this.options.onDropdownHide);
          },
  
          /**
           * Builds the button of the multiselect.
           */
          buildButton: function() {
              this.$button = $(this.options.templates.button).addClass(this.options.buttonClass);
  
              // Adopt active state.
              if (this.$select.prop('disabled')) {
                  this.disable();
              }
              else {
                  this.enable();
              }
  
              // Manually add button width if set.
              if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') {
                  this.$button.css({
                      'width' : this.options.buttonWidth
                  });
                  this.$container.css({
                      'width': this.options.buttonWidth
                  });
              }
  
              // Keep the tab index from the select.
              var tabindex = this.$select.attr('tabindex');
              if (tabindex) {
                  this.$button.attr('tabindex', tabindex);
              }
  
              this.$container.prepend(this.$button);
          },
  
          /**
           * Builds the ul representing the dropdown menu.
           */
          buildDropdown: function() {
  
              // Build ul.
              this.$ul = $(this.options.templates.ul);
  
              if (this.options.dropRight) {
                  this.$ul.addClass('pull-right');
              }
  
              // Set max height of dropdown menu to activate auto scrollbar.
              if (this.options.maxHeight) {
                  // TODO: Add a class for this option to move the css declarations.
                  this.$ul.css({
                      'max-height': this.options.maxHeight + 'px',
                      'overflow-y': 'auto',
                      'overflow-x': 'hidden'
                  });
              }
  
              this.$container.append(this.$ul);
          },
  
          /**
           * Build the dropdown options and binds all nessecary events.
           * Uses createDivider and createOptionValue to create the necessary options.
           */
          buildDropdownOptions: function() {
  
              this.$select.children().each($.proxy(function(index, element) {
  
                  // Support optgroups and options without a group simultaneously.
                  var tag = $(element).prop('tagName')
                      .toLowerCase();
  
                  if ($(element).prop('value') == this.options.selectAllValue) {
                      return;
                  }
  
                  if (tag === 'optgroup') {
                      this.createOptgroup(element);
                  }
                  else if (tag === 'option') {
  
                      if ($(element).data('role') === 'divider') {
                          this.createDivider();
                      }
                      else {
                          this.createOptionValue(element);
                      }
  
                  }
  
                  // Other illegal tags will be ignored.
              }, this));
  
              // Bind the change event on the dropdown elements.
              $('li input', this.$ul).on('change', $.proxy(function(event) {
                  var $target = $(event.target);
  
                  var checked = $target.prop('checked') || false;
                  var isSelectAllOption = $target.val() === this.options.selectAllValue;
  
                  // Apply or unapply the configured selected class.
                  if (this.options.selectedClass) {
                      if (checked) {
                          $target.parents('li')
                              .addClass(this.options.selectedClass);
                      }
                      else {
                          $target.parents('li')
                              .removeClass(this.options.selectedClass);
                      }
                  }
  
                  // Get the corresponding option.
                  var value = $target.val();
                  var $option = this.getOptionByValue(value);
  
                  var $optionsNotThis = $('option', this.$select).not($option);
                  var $checkboxesNotThis = $('input', this.$container).not($target);
  
                  if (isSelectAllOption) {
                      if (checked) {
                          this.selectall();
                      }
                      else {
                          this.deselectall();
                      }
                  }
  
                  if(!isSelectAllOption){
                      if (checked) {
                          $option.prop('selected', true);
  
                          if (this.options.multiple) {
                              // Simply select additional option.
                              $option.prop('selected', true);
                          }
                          else {
                              // Unselect all other options and corresponding checkboxes.
                              if (this.options.selectedClass) {
                                  $($checkboxesNotThis).parents('li').removeClass(this.options.selectedClass);
                              }
  
                              $($checkboxesNotThis).prop('checked', false);
                              $optionsNotThis.prop('selected', false);
  
                              // It's a single selection, so close.
                              this.$button.click();
                          }
  
                          if (this.options.selectedClass === "active") {
                              $optionsNotThis.parents("a").css("outline", "");
                          }
                      }
                      else {
                          // Unselect option.
                          $option.prop('selected', false);
                      }
                  }
  
                  this.$select.change();
  
                  this.updateButtonText();
                  this.updateSelectAll();
  
                  this.options.onChange($option, checked);
  
                  if(this.options.preventInputChangeEvent) {
                      return false;
                  }
              }, this));
  
              $('li a', this.$ul).on('touchstart click', function(event) {
                  event.stopPropagation();
  
                  var $target = $(event.target);
  
                  if (event.shiftKey) {
                      var checked = $target.prop('checked') || false;
  
                      if (checked) {
                          var prev = $target.parents('li:last')
                              .siblings('li[class="active"]:first');
  
                          var currentIdx = $target.parents('li')
                              .index();
                          var prevIdx = prev.index();
  
                          if (currentIdx > prevIdx) {
                              $target.parents("li:last").prevUntil(prev).each(
                                  function() {
                                      $(this).find("input:first").prop("checked", true)
                                          .trigger("change");
                                  }
                              );
                          }
                          else {
                              $target.parents("li:last").nextUntil(prev).each(
                                  function() {
                                      $(this).find("input:first").prop("checked", true)
                                          .trigger("change");
                                  }
                              );
                          }
                      }
                  }
  
                  $target.blur();
              });
  
              // Keyboard support.
              this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) {
                  if ($('input[type="text"]', this.$container).is(':focus')) {
                      return;
                  }
                  if ((event.keyCode === 9 || event.keyCode === 27)
                          && this.$container.hasClass('open')) {
  
                      // Close on tab or escape.
                      this.$button.click();
                  }
                  else {
                      var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible");
  
                      if (!$items.length) {
                          return;
                      }
  
                      var index = $items.index($items.filter(':focus'));
  
                      // Navigation up.
                      if (event.keyCode === 38 && index > 0) {
                          index--;
                      }
                      // Navigate down.
                      else if (event.keyCode === 40 && index < $items.length - 1) {
                          index++;
                      }
                      else if (!~index) {
                          index = 0;
                      }
  
                      var $current = $items.eq(index);
                      $current.focus();
  
                      if (event.keyCode === 32 || event.keyCode === 13) {
                          var $checkbox = $current.find('input');
  
                          $checkbox.prop("checked", !$checkbox.prop("checked"));
                          $checkbox.change();
                      }
  
                      event.stopPropagation();
                      event.preventDefault();
                  }
              }, this));
          },
  
          /**
           * Create an option using the given select option.
           *
           * @param {jQuery} element
           */
          createOptionValue: function(element) {
              if ($(element).is(':selected')) {
                  $(element).prop('selected', true);
              }
  
              // Support the label attribute on options.
              var label = this.options.label(element);
              var value = $(element).val();
              var inputType = this.options.multiple ? "checkbox" : "radio";
  
              var $li = $(this.options.templates.li);
              $('label', $li).addClass(inputType);
              $('label', $li).append('<input type="' + inputType + '" name="' + this.options.checkboxName + '" />');
  
              var selected = $(element).prop('selected') || false;
              var $checkbox = $('input', $li);
              $checkbox.val(value);
  
              if (value === this.options.selectAllValue) {
                  $li.addClass("multiselect-item multiselect-all");
                  $checkbox.parent().parent()
                      .addClass('multiselect-all');
              }
  
              $('label', $li).append(" " + label);
  
              this.$ul.append($li);
  
              if ($(element).is(':disabled')) {
                  $checkbox.attr('disabled', 'disabled')
                      .prop('disabled', true)
                      .parents('a')
                      .attr("tabindex", "-1")
                      .parents('li')
                      .addClass('disabled');
              }
  
              $checkbox.prop('checked', selected);
  
              if (selected && this.options.selectedClass) {
                  $checkbox.parents('li')
                      .addClass(this.options.selectedClass);
              }
          },
  
          /**
           * Creates a divider using the given select option.
           *
           * @param {jQuery} element
           */
          createDivider: function(element) {
              var $divider = $(this.options.templates.divider);
              this.$ul.append($divider);
          },
  
          /**
           * Creates an optgroup.
           *
           * @param {jQuery} group
           */
          createOptgroup: function(group) {
              var groupName = $(group).prop('label');
  
              // Add a header for the group.
              var $li = $(this.options.templates.liGroup);
              $('label', $li).text(groupName);
  
              this.$ul.append($li);
  
              if ($(group).is(':disabled')) {
                  $li.addClass('disabled');
              }
  
              // Add the options of the group.
              $('option', group).each($.proxy(function(index, element) {
                  this.createOptionValue(element);
              }, this));
          },
  
          /**
           * Build the selct all.
           * Checks if a select all has already been created.
           */
          buildSelectAll: function() {
              var alreadyHasSelectAll = this.hasSelectAll();
  
              if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple
                      && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) {
  
                  // Check whether to add a divider after the select all.
                  if (this.options.includeSelectAllDivider) {
                      this.$ul.prepend($(this.options.templates.divider));
                  }
  
                  var $li = $(this.options.templates.li);
                  $('label', $li).addClass("checkbox");
                  $('label', $li).append('<input type="checkbox" name="' + this.options.checkboxName + '" />');
  
                  var $checkbox = $('input', $li);
                  $checkbox.val(this.options.selectAllValue);
  
                  $li.addClass("multiselect-item multiselect-all");
                  $checkbox.parent().parent()
                      .addClass('multiselect-all');
  
                  $('label', $li).append(" " + this.options.selectAllText);
  
                  this.$ul.prepend($li);
  
                  $checkbox.prop('checked', false);
              }
          },
  
          /**
           * Builds the filter.
           */
          buildFilter: function() {
  
              // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength.
              if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) {
                  var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering);
  
                  if (this.$select.find('option').length >= enableFilterLength) {
  
                      this.$filter = $(this.options.templates.filter);
                      $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder);
                      this.$ul.prepend(this.$filter);
  
                      this.$filter.val(this.query).on('click', function(event) {
                          event.stopPropagation();
                      }).on('input keydown', $.proxy(function(event) {
                          // This is useful to catch "keydown" events after the browser has updated the control.
                          clearTimeout(this.searchTimeout);
  
                          this.searchTimeout = this.asyncFunction($.proxy(function() {
  
                              if (this.query !== event.target.value) {
                                  this.query = event.target.value;
  
                                  $.each($('li', this.$ul), $.proxy(function(index, element) {
                                      var value = $('input', element).val();
                                      var text = $('label', element).text();
  
                                      var filterCandidate = '';
                                      if ((this.options.filterBehavior === 'text')) {
                                          filterCandidate = text;
                                      }
                                      else if ((this.options.filterBehavior === 'value')) {
                                          filterCandidate = value;
                                      }
                                      else if (this.options.filterBehavior === 'both') {
                                          filterCandidate = text + '\n' + value;
                                      }
  
                                      if (value !== this.options.selectAllValue && text) {
                                          // by default lets assume that element is not
                                          // interesting for this search
                                          var showElement = false;
  
                                          if (this.options.enableCaseInsensitiveFiltering && filterCandidate.toLowerCase().indexOf(this.query.toLowerCase()) > -1) {
                                              showElement = true;
                                          }
                                          else if (filterCandidate.indexOf(this.query) > -1) {
                                              showElement = true;
                                          }
  
                                          if (showElement) {
                                              $(element).show().removeClass("filter-hidden");
                                          }
                                          else {
                                              $(element).hide().addClass("filter-hidden");
                                          }
                                      }
                                  }, this));
                              }
  
                              this.updateSelectAll();
                          }, this), 300, this);
                      }, this));
                  }
              }
          },
  
          /**
           * Unbinds the whole plugin.
           */
          destroy: function() {
              this.$container.remove();
              this.$select.show();
              this.$select.data('multiselect', null);
          },
  
          /**
           * Refreshs the multiselect based on the selected options of the select.
           */
          refresh: function() {
              $('option', this.$select).each($.proxy(function(index, element) {
                  var $input = $('li input', this.$ul).filter(function() {
                      return $(this).val() === $(element).val();
                  });
  
                  if ($(element).is(':selected')) {
                      $input.prop('checked', true);
  
                      if (this.options.selectedClass) {
                          $input.parents('li')
                              .addClass(this.options.selectedClass);
                      }
                  }
                  else {
                      $input.prop('checked', false);
  
                      if (this.options.selectedClass) {
                          $input.parents('li')
                              .removeClass(this.options.selectedClass);
                      }
                  }
  
                  if ($(element).is(":disabled")) {
                      $input.attr('disabled', 'disabled')
                          .prop('disabled', true)
                          .parents('li')
                          .addClass('disabled');
                  }
                  else {
                      $input.prop('disabled', false)
                          .parents('li')
                          .removeClass('disabled');
                  }
              }, this));
  
              this.updateButtonText();
              this.updateSelectAll();
          },
  
          /**
           * Select all options of the given values.
           *
           * @param {Array} selectValues
           */
          select: function(selectValues) {
              if(!$.isArray(selectValues)) {
                  selectValues = [selectValues];
              }
  
              for (var i = 0; i < selectValues.length; i++) {
                  var value = selectValues[i];
  
                  var $option = this.getOptionByValue(value);
                  var $checkbox = this.getInputByValue(value);
  
                  if($option === void(0) || $checkbox === void(0))
                  {
                    continue;
                  }
  
                  if (this.options.selectedClass) {
                      $checkbox.parents('li')
                          .addClass(this.options.selectedClass);
                  }
  
                  $checkbox.prop('checked', true);
                  $option.prop('selected', true);
              }
  
              this.updateButtonText();
          },
  
          /**
           * Clears all selected items
           *
           */
          clearSelection: function () {
              this.deselectall(false);
              this.updateButtonText();
              this.updateSelectAll();
          },
  
          /**
           * Deselects all options of the given values.
           *
           * @param {Array} deselectValues
           */
          deselect: function(deselectValues) {
              if(!$.isArray(deselectValues)) {
                  deselectValues = [deselectValues];
              }
  
              for (var i = 0; i < deselectValues.length; i++) {
  
                  var value = deselectValues[i];
  
                  var $option = this.getOptionByValue(value);
                  var $checkbox = this.getInputByValue(value);
  
                  if($option === void(0) || $checkbox === void(0))
                  {
                    continue;
                  }
  
                  if (this.options.selectedClass) {
                      $checkbox.parents('li')
                          .removeClass(this.options.selectedClass);
                  }
  
                  $checkbox.prop('checked', false);
                  $option.prop('selected', false);
              }
  
              this.updateButtonText();
          },
  
          /**
           * Selects all enabled & visible options.
           *
           */
          selectall: function () {
              var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul),
                  visibleCheckboxes = allCheckboxes.filter(":visible"),
                  allCheckboxesCount = allCheckboxes.length,
                  visibleCheckboxesCount = visibleCheckboxes.length;
  
              visibleCheckboxes.prop('checked', true);
              $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").addClass(this.options.selectedClass);
  
              if (allCheckboxesCount === visibleCheckboxesCount) {
                  $("option:enabled", this.$select).prop('selected', true);
              }
              else {
                  var values = visibleCheckboxes.map(function() { return $(this).val() }).get();
                  $("option:enabled", this.$select).filter(function(index){ return $.inArray($(this).val(), values) !== -1; }).prop('selected', true);
              }
          },
  
          /**
           * Deselects all options.
           * If justVisible is true or not specified, only visible options are deselected.
           *
           * @param {Boolean} justVisible
           */
          deselectall: function (justVisible) {
              var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul),
                  justVisible = typeof justVisible === 'undefined' ? true : justVisible,
                  visibleCheckboxes = void(0);
  
              if(justVisible) {
                  var values = void(0);
                  visibleCheckboxes = allCheckboxes.filter(":visible");
                  visibleCheckboxes.prop('checked', false);
  
                  values = visibleCheckboxes.map(function() { return $(this).val() }).get();
  
                  $("option:enabled", this.$select).filter(function(index){ return $.inArray($(this).val(), values) !== -1; }).prop('selected', false);
  
                  $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").removeClass(this.options.selectedClass);
  
              }else {
                  allCheckboxes.prop('checked', false);
                  $("option:enabled", this.$select).prop('selected', false);
                  $("li:not(.divider):not(.disabled)", this.$ul).removeClass(this.options.selectedClass);
              }
          },
  
          /**
           * Rebuild the plugin.
           * Rebuilds the dropdown, the filter and the select all option.
           */
          rebuild: function() {
              this.$ul.html('');
  
              // Important to distinguish between radios and checkboxes.
              this.options.multiple = this.$select.attr('multiple') === "multiple";
  
              this.buildSelectAll();
              this.buildDropdownOptions();
              this.buildFilter();
  
              this.updateButtonText();
              this.updateSelectAll();
  
              if (this.options.dropRight) {
                  this.$ul.addClass('pull-right');
              }
          },
  
          /**
           * The provided data will be used to build the dropdown.
           *
           * @param {Array} dataprovider
           */
          dataprovider: function(dataprovider) {
              var optionDOM = "";
              var groupCounter = 0;
  
              $.each(dataprovider, function (index, option) {
                  if ($.isArray(option.children)) {
                      groupCounter++;
                      optionDOM += '<optgroup label="' + (option.title || 'Group ' + groupCounter) + '">';
  
                      option.children.forEach(function(subOption) {
                          optionDOM += '<option value="' + subOption.value + '">' + (subOption.label || subOption.value) + '</option>';
                      });
  
                      optionDOM += '</optgroup>';
                  }
                  else {
                      optionDOM += '<option value="' + option.value + '">' + (option.label || option.value) + '</option>';
                  }
              });
  
              this.$select.html(optionDOM);
              this.rebuild();
          },
  
          /**
           * Enable the multiselect.
           */
          enable: function() {
              this.$select.prop('disabled', false);
              this.$button.prop('disabled', false)
                  .removeClass('disabled');
          },
  
          /**
           * Disable the multiselect.
           */
          disable: function() {
              this.$select.prop('disabled', true);
              this.$button.prop('disabled', true)
                  .addClass('disabled');
          },
  
          /**
           * Set the options.
           *
           * @param {Array} options
           */
          setOptions: function(options) {
              this.options = this.mergeOptions(options);
          },
  
          /**
           * Merges the given options with the default options.
           *
           * @param {Array} options
           * @returns {Array}
           */
          mergeOptions: function(options) {
              return $.extend(true, {}, this.defaults, options);
          },
  
          /**
           * Checks whether a select all checkbox is present.
           *
           * @returns {Boolean}
           */
          hasSelectAll: function() {
              return $('li.' + this.options.selectAllValue, this.$ul).length > 0;
          },
  
          /**
           * Updates the select all checkbox based on the currently displayed and selected checkboxes.
           */
          updateSelectAll: function() {
              if (this.hasSelectAll()) {
                  var allBoxes = $("li:not(.multiselect-item):not(.filter-hidden) input:enabled", this.$ul),
                      allBoxesLength = allBoxes.length,
                      checkedBoxesLength = allBoxes.filter(":checked").length,
                      selectAllLi  = $("li." + this.options.selectAllValue, this.$ul),
                      selectAllInput = selectAllLi.find("input");
  
                  if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) {
                      selectAllInput.prop("checked", true);
                      selectAllLi.addClass(this.options.selectedClass);
                  }
                  else {
                      selectAllInput.prop("checked", false);
                      selectAllLi.removeClass(this.options.selectedClass);
                  }
              }
          },
  
          /**
           * Update the button text and its title based on the currently selected options.
           */
          updateButtonText: function() {
              var options = this.getSelected();
  
              // First update the displayed button text.
              $('button', this.$container).html(this.options.buttonText(options, this.$select));
  
              // Now update the title attribute of the button.
              $('button', this.$container).attr('title', this.options.buttonTitle(options, this.$select));
  
          },
  
          /**
           * Get all selected options.
           *
           * @returns {jQUery}
           */
          getSelected: function() {
              return $('option', this.$select).filter(":selected");
          },
  
          /**
           * Gets a select option by its value.
           *
           * @param {String} value
           * @returns {jQuery}
           */
          getOptionByValue: function (value) {
  
              var options = $('option', this.$select);
              var valueToCompare = value.toString();
  
              for (var i = 0; i < options.length; i = i + 1) {
                  var option = options[i];
                  if (option.value === valueToCompare) {
                      return $(option);
                  }
              }
          },
  
          /**
           * Get the input (radio/checkbox) by its value.
           *
           * @param {String} value
           * @returns {jQuery}
           */
          getInputByValue: function (value) {
  
              var checkboxes = $('li input', this.$ul);
              var valueToCompare = value.toString();
  
              for (var i = 0; i < checkboxes.length; i = i + 1) {
                  var checkbox = checkboxes[i];
                  if (checkbox.value === valueToCompare) {
                      return $(checkbox);
                  }
              }
          },
  
          /**
           * Used for knockout integration.
           */
          updateOriginalOptions: function() {
              this.originalOptions = this.$select.clone()[0].options;
          },
  
          asyncFunction: function(callback, timeout, self) {
              var args = Array.prototype.slice.call(arguments, 3);
              return setTimeout(function() {
                  callback.apply(self || window, args);
              }, timeout);
          }
      };
  
      $.fn.multiselect = function(option, parameter) {
          return this.each(function() {
              var data = $(this).data('multiselect');
              var options = typeof option === 'object' && option;
  
              // Initialize the multiselect.
              if (!data) {
                  data = new Multiselect(this, options);
                  $(this).data('multiselect', data);
              }
  
              // Call multiselect method.
              if (typeof option === 'string') {
                  data[option](parameter);
  
                  if (option === 'destroy') {
                      $(this).data('multiselect', false);
                  }
              }
          });
      };
  
      $.fn.multiselect.Constructor = Multiselect;
  
      $(function() {
          $("select[data-role=multiselect]").multiselect();
      });
  
  }(window.jQuery);
  