text: /*
  * MultiSelect v0.9.12
  * Copyright (c) 2012 Louis Cuny
  *
  * This program is free software. It comes without any warranty, to
  * the extent permitted by applicable law. You can redistribute it
  * and/or modify it under the terms of the Do What The Fuck You Want
  * To Public License, Version 2, as published by Sam Hocevar. See
  * http://sam.zoy.org/wtfpl/COPYING for more details.
  */
  
  !function ($) {
  
    "use strict";
  
  
   /* MULTISELECT CLASS DEFINITION
    * ====================== */
  
    var MultiSelect = function (element, options) {
      this.options = options;
      this.$element = $(element);
      this.$container = $('<div/>', { 'class': "ms-container" });
      this.$selectableContainer = $('<div/>', { 'class': 'ms-selectable' });
      this.$selectionContainer = $('<div/>', { 'class': 'ms-selection' });
      this.$selectableUl = $('<ul/>', { 'class': "ms-list", 'tabindex' : '-1' });
      this.$selectionUl = $('<ul/>', { 'class': "ms-list", 'tabindex' : '-1' });
      this.scrollTo = 0;
      this.elemsSelector = 'li:visible:not(.ms-optgroup-label,.ms-optgroup-container,.'+options.disabledClass+')';
    };
  
    MultiSelect.prototype = {
      constructor: MultiSelect,
  
      init: function(){
        var that = this,
            ms = this.$element;
  
        if (ms.next('.ms-container').length === 0){
          ms.css({ position: 'absolute', left: '-9999px' });
          ms.attr('id', ms.attr('id') ? ms.attr('id') : Math.ceil(Math.random()*1000)+'multiselect');
          this.$container.attr('id', 'ms-'+ms.attr('id'));
          this.$container.addClass(that.options.cssClass);
          ms.find('option').each(function(){
            that.generateLisFromOption(this);
          });
  
          this.$selectionUl.find('.ms-optgroup-label').hide();
  
          if (that.options.selectableHeader){
            that.$selectableContainer.append(that.options.selectableHeader);
          }
          that.$selectableContainer.append(that.$selectableUl);
          if (that.options.selectableFooter){
            that.$selectableContainer.append(that.options.selectableFooter);
          }
  
          if (that.options.selectionHeader){
            that.$selectionContainer.append(that.options.selectionHeader);
          }
          that.$selectionContainer.append(that.$selectionUl);
          if (that.options.selectionFooter){
            that.$selectionContainer.append(that.options.selectionFooter);
          }
  
          that.$container.append(that.$selectableContainer);
          that.$container.append(that.$selectionContainer);
          ms.after(that.$container);
  
          that.activeMouse(that.$selectableUl);
          that.activeKeyboard(that.$selectableUl);
  
          var action = that.options.dblClick ? 'dblclick' : 'click';
  
          that.$selectableUl.on(action, '.ms-elem-selectable', function(){
            that.select($(this).data('ms-value'));
          });
          that.$selectionUl.on(action, '.ms-elem-selection', function(){
  	        that.deselect($(this).data('ms-value'));         
          });
          
          that.activeMouse(that.$selectionUl);
          that.activeKeyboard(that.$selectionUl);
  
          ms.on('focus', function(){
            that.$selectableUl.focus();
          });
        }
  
        var selectedValues = ms.find('option:selected').map(function(){ return $(this).val(); }).get();
        that.select(selectedValues, 'init');
  
        if (typeof that.options.afterInit === 'function') {
          that.options.afterInit.call(this, this.$container);
        }
      },
  
      'generateLisFromOption' : function(option, index, $container){
        var that = this,
            ms = that.$element,
            attributes = "",
            $option = $(option);
  
        for (var cpt = 0; cpt < option.attributes.length; cpt++){
          var attr = option.attributes[cpt];
  
          if(attr.name !== 'value' && attr.name !== 'disabled'){
            attributes += attr.name+'="'+attr.value+'" ';
          }
        }
        var selectableLi = $('<li '+attributes+'><span>'+that.escapeHTML($option.text())+'</span></li>'),
            selectedLi = selectableLi.clone(),
            value = $option.val(),
            elementId = that.sanitize(value);
  
        selectableLi
          .data('ms-value', value)
          .addClass('ms-elem-selectable')
          .attr('id', elementId+'-selectable');
  
        selectedLi
          .data('ms-value', value)
          .addClass('ms-elem-selection')
          .attr('id', elementId+'-selection')
          .hide();
  
        if ($option.prop('disabled') || ms.prop('disabled')){
          selectedLi.addClass(that.options.disabledClass);
          selectableLi.addClass(that.options.disabledClass);
        }
  
        var $optgroup = $option.parent('optgroup');
  
        if ($optgroup.length > 0){
          var optgroupLabel = $optgroup.attr('label'),
              optgroupId = that.sanitize(optgroupLabel),
              $selectableOptgroup = that.$selectableUl.find('#optgroup-selectable-'+optgroupId),
              $selectionOptgroup = that.$selectionUl.find('#optgroup-selection-'+optgroupId);
  
          if ($selectableOptgroup.length === 0){
            var optgroupContainerTpl = '<li class="ms-optgroup-container"></li>',
                optgroupTpl = '<ul class="ms-optgroup"><li class="ms-optgroup-label"><span>'+optgroupLabel+'</span></li></ul>';
  
            $selectableOptgroup = $(optgroupContainerTpl);
            $selectionOptgroup = $(optgroupContainerTpl);
            $selectableOptgroup.attr('id', 'optgroup-selectable-'+optgroupId);
            $selectionOptgroup.attr('id', 'optgroup-selection-'+optgroupId);
            $selectableOptgroup.append($(optgroupTpl));
            $selectionOptgroup.append($(optgroupTpl));
            if (that.options.selectableOptgroup){
              $selectableOptgroup.find('.ms-optgroup-label').on('click', function(){
                var values = $optgroup.children(':not(:selected, :disabled)').map(function(){ return $(this).val();}).get();
                that.select(values);
              });
              $selectionOptgroup.find('.ms-optgroup-label').on('click', function(){
                var values = $optgroup.children(':selected:not(:disabled)').map(function(){ return $(this).val();}).get();
                that.deselect(values);
              });
            }
            that.$selectableUl.append($selectableOptgroup);
            that.$selectionUl.append($selectionOptgroup);
          }
          index = index === undefined ? $selectableOptgroup.find('ul').children().length : index + 1;
          selectableLi.insertAt(index, $selectableOptgroup.children());
          selectedLi.insertAt(index, $selectionOptgroup.children());
        } else {
          index = index === undefined ? that.$selectableUl.children().length : index;
  
          selectableLi.insertAt(index, that.$selectableUl);
          selectedLi.insertAt(index, that.$selectionUl);
        }
      },
  
      'addOption' : function(options){
        var that = this;
  
        if (options.value !== undefined && options.value !== null){
          options = [options];
        } 
        $.each(options, function(index, option){
          if (option.value !== undefined && option.value !== null &&
              that.$element.find("option[value='"+option.value+"']").length === 0){
            var $option = $('<option value="'+option.value+'">'+option.text+'</option>'),
                $container = option.nested === undefined ? that.$element : $("optgroup[label='"+option.nested+"']"),
                index = parseInt((typeof option.index === 'undefined' ? $container.children().length : option.index));
  
            if (option.optionClass) {
              $option.addClass(option.optionClass);
            }
  
            if (option.disabled) {
              $option.prop('disabled', true);
            }
  
            $option.insertAt(index, $container);
            that.generateLisFromOption($option.get(0), index, option.nested);
          }
        });
      },
  
      'escapeHTML' : function(text){
        return $("<div>").text(text).html();
      },
  
      'activeKeyboard' : function($list){
        var that = this;
  
        $list.on('focus', function(){
          $(this).addClass('ms-focus');
        })
        .on('blur', function(){
          $(this).removeClass('ms-focus');
        })
        .on('keydown', function(e){
          switch (e.which) {
            case 40:
            case 38:
              e.preventDefault();
              e.stopPropagation();
              that.moveHighlight($(this), (e.which === 38) ? -1 : 1);
              return;
            case 37:
            case 39:
              e.preventDefault();
              e.stopPropagation();
              that.switchList($list);
              return;
            case 9:
              if(that.$element.is('[tabindex]')){
                e.preventDefault();
                var tabindex = parseInt(that.$element.attr('tabindex'), 10);
                tabindex = (e.shiftKey) ? tabindex-1 : tabindex+1;
                $('[tabindex="'+(tabindex)+'"]').focus();
                return;
              }else{
                if(e.shiftKey){
                  that.$element.trigger('focus');
                }
              }
          }
          if($.inArray(e.which, that.options.keySelect) > -1){
            e.preventDefault();
            e.stopPropagation();
            that.selectHighlighted($list);
            return;
          }
        });
      },
  
      'moveHighlight': function($list, direction){
        var $elems = $list.find(this.elemsSelector),
            $currElem = $elems.filter('.ms-hover'),
            $nextElem = null,
            elemHeight = $elems.first().outerHeight(),
            containerHeight = $list.height(),
            containerSelector = '#'+this.$container.prop('id');
  
        $elems.removeClass('ms-hover');
        if (direction === 1){ // DOWN
  
          $nextElem = $currElem.nextAll(this.elemsSelector).first();
          if ($nextElem.length === 0){
            var $optgroupUl = $currElem.parent();
  
            if ($optgroupUl.hasClass('ms-optgroup')){
              var $optgroupLi = $optgroupUl.parent(),
                  $nextOptgroupLi = $optgroupLi.next(':visible');
  
              if ($nextOptgroupLi.length > 0){
                $nextElem = $nextOptgroupLi.find(this.elemsSelector).first();
              } else {
                $nextElem = $elems.first();
              }
            } else {
              $nextElem = $elems.first();
            }
          }
        } else if (direction === -1){ // UP
  
          $nextElem = $currElem.prevAll(this.elemsSelector).first();
          if ($nextElem.length === 0){
            var $optgroupUl = $currElem.parent();
  
            if ($optgroupUl.hasClass('ms-optgroup')){
              var $optgroupLi = $optgroupUl.parent(),
                  $prevOptgroupLi = $optgroupLi.prev(':visible');
  
              if ($prevOptgroupLi.length > 0){
                $nextElem = $prevOptgroupLi.find(this.elemsSelector).last();
              } else {
                $nextElem = $elems.last();
              }
            } else {
              $nextElem = $elems.last();
            }
          }
        }
        if ($nextElem.length > 0){
          $nextElem.addClass('ms-hover');
          var scrollTo = $list.scrollTop() + $nextElem.position().top - 
                         containerHeight / 2 + elemHeight / 2;
  
          $list.scrollTop(scrollTo);
        }
      },
  
      'selectHighlighted' : function($list){
        var $elems = $list.find(this.elemsSelector),
            $highlightedElem = $elems.filter('.ms-hover').first();
  
        if ($highlightedElem.length > 0){
          if ($list.parent().hasClass('ms-selectable')){
            this.select($highlightedElem.data('ms-value'));
          } else {
            this.deselect($highlightedElem.data('ms-value'));
          }
          $elems.removeClass('ms-hover');
        }
      },
  
      'switchList' : function($list){
        $list.blur();
        this.$container.find(this.elemsSelector).removeClass('ms-hover');
        if ($list.parent().hasClass('ms-selectable')){
          this.$selectionUl.focus();
        } else {
          this.$selectableUl.focus();
        }
      },
  
      'activeMouse' : function($list){
        var that = this;
  
        this.$container.on('mouseenter', that.elemsSelector, function(){
          $(this).parents('.ms-container').find(that.elemsSelector).removeClass('ms-hover');
          $(this).addClass('ms-hover');
        });
  
        this.$container.on('mouseleave', that.elemsSelector, function () {
          $(this).parents('.ms-container').find(that.elemsSelector).removeClass('ms-hover');
        });
      },
      
      'refresh' : function() {
        this.destroy();
        this.$element.multiSelect(this.options);
      },
  
      'destroy' : function(){
        $("#ms-"+this.$element.attr("id")).remove();
        this.$element.off('focus');
        this.$element.css('position', '').css('left', '');
        this.$element.removeData('multiselect');
      },
  
      'select' : function(value, method){
        if (typeof value === 'string'){ value = [value]; }
  
        var that = this,
            ms = this.$element,
            msIds = $.map(value, function(val){ return(that.sanitize(val)); }),
            selectables = this.$selectableUl.find('#' + msIds.join('-selectable, #')+'-selectable').filter(':not(.'+that.options.disabledClass+')'),
            selections = this.$selectionUl.find('#' + msIds.join('-selection, #') + '-selection').filter(':not(.'+that.options.disabledClass+')'),
            options = ms.find('option:not(:disabled)').filter(function(){ return($.inArray(this.value, value) > -1); });
  
        if (method === 'init'){
          selectables = this.$selectableUl.find('#' + msIds.join('-selectable, #')+'-selectable'),
          selections = this.$selectionUl.find('#' + msIds.join('-selection, #') + '-selection');
        }
  
        if (selectables.length > 0){
          selectables.addClass('ms-selected').hide();
          selections.addClass('ms-selected').show();
  
          options.prop('selected', true);
  
          that.$container.find(that.elemsSelector).removeClass('ms-hover');
  
          var selectableOptgroups = that.$selectableUl.children('.ms-optgroup-container');
          if (selectableOptgroups.length > 0){
            selectableOptgroups.each(function(){
              var selectablesLi = $(this).find('.ms-elem-selectable');
              if (selectablesLi.length === selectablesLi.filter('.ms-selected').length){
                $(this).find('.ms-optgroup-label').hide();
              }
            });
  
            var selectionOptgroups = that.$selectionUl.children('.ms-optgroup-container');
            selectionOptgroups.each(function(){
              var selectionsLi = $(this).find('.ms-elem-selection');
              if (selectionsLi.filter('.ms-selected').length > 0){
                $(this).find('.ms-optgroup-label').show();
              }
            });
          } else {
            if (that.options.keepOrder && method !== 'init'){
              var selectionLiLast = that.$selectionUl.find('.ms-selected');
              if((selectionLiLast.length > 1) && (selectionLiLast.last().get(0) != selections.get(0))) {
                selections.insertAfter(selectionLiLast.last());
              }
            }
          }
          if (method !== 'init'){
            ms.trigger('change');
            if (typeof that.options.afterSelect === 'function') {
              that.options.afterSelect.call(this, value);
            }
          }
        }
      },
  
      'deselect' : function(value){
        if (typeof value === 'string'){ value = [value]; }
  
        var that = this,
            ms = this.$element,
            msIds = $.map(value, function(val){ return(that.sanitize(val)); }),
            selectables = this.$selectableUl.find('#' + msIds.join('-selectable, #')+'-selectable'),
            selections = this.$selectionUl.find('#' + msIds.join('-selection, #')+'-selection').filter('.ms-selected').filter(':not(.'+that.options.disabledClass+')'),
            options = ms.find('option').filter(function(){ return($.inArray(this.value, value) > -1); });
  
        if (selections.length > 0){
          selectables.removeClass('ms-selected').show();
          selections.removeClass('ms-selected').hide();
          options.prop('selected', false);
  
          that.$container.find(that.elemsSelector).removeClass('ms-hover');
  
          var selectableOptgroups = that.$selectableUl.children('.ms-optgroup-container');
          if (selectableOptgroups.length > 0){
            selectableOptgroups.each(function(){
              var selectablesLi = $(this).find('.ms-elem-selectable');
              if (selectablesLi.filter(':not(.ms-selected)').length > 0){
                $(this).find('.ms-optgroup-label').show();
              }
            });
  
            var selectionOptgroups = that.$selectionUl.children('.ms-optgroup-container');
            selectionOptgroups.each(function(){
              var selectionsLi = $(this).find('.ms-elem-selection');
              if (selectionsLi.filter('.ms-selected').length === 0){
                $(this).find('.ms-optgroup-label').hide();
              }
            });
          }
          ms.trigger('change');
          if (typeof that.options.afterDeselect === 'function') {
            that.options.afterDeselect.call(this, value);
          }
        }
      },
  
      'select_all' : function(){
        var ms = this.$element,
            values = ms.val();
  
        ms.find('option:not(":disabled")').prop('selected', true);
        this.$selectableUl.find('.ms-elem-selectable').filter(':not(.'+this.options.disabledClass+')').addClass('ms-selected').hide();
        this.$selectionUl.find('.ms-optgroup-label').show();
        this.$selectableUl.find('.ms-optgroup-label').hide();
        this.$selectionUl.find('.ms-elem-selection').filter(':not(.'+this.options.disabledClass+')').addClass('ms-selected').show();
        this.$selectionUl.focus();
        ms.trigger('change');
        if (typeof this.options.afterSelect === 'function') {
          var selectedValues = $.grep(ms.val(), function(item){
            return $.inArray(item, values) < 0;
          });
          this.options.afterSelect.call(this, selectedValues);
        }
      },
  
      'deselect_all' : function(){
        var ms = this.$element,
            values = ms.val();
  
        ms.find('option').prop('selected', false);
        this.$selectableUl.find('.ms-elem-selectable').removeClass('ms-selected').show();
        this.$selectionUl.find('.ms-optgroup-label').hide();
        this.$selectableUl.find('.ms-optgroup-label').show();
        this.$selectionUl.find('.ms-elem-selection').removeClass('ms-selected').hide();
        this.$selectableUl.focus();
        ms.trigger('change');
        if (typeof this.options.afterDeselect === 'function') {
          this.options.afterDeselect.call(this, values);
        }
      },
  
      sanitize: function(value){
        var hash = 0, i, character;
        if (value.length == 0) return hash;
        var ls = 0;
        for (i = 0, ls = value.length; i < ls; i++) {
          character  = value.charCodeAt(i);
          hash  = ((hash<<5)-hash)+character;
          hash |= 0; // Convert to 32bit integer
        }
        return hash;
      }
    };
  
    /* MULTISELECT PLUGIN DEFINITION
     * ======================= */
  
    $.fn.multiSelect = function () {
      var option = arguments[0],
          args = arguments;
  
      return this.each(function () {
        var $this = $(this),
            data = $this.data('multiselect'),
            options = $.extend({}, $.fn.multiSelect.defaults, $this.data(), typeof option === 'object' && option);
  
        if (!data){ $this.data('multiselect', (data = new MultiSelect(this, options))); }
  
        if (typeof option === 'string'){
          data[option](args[1]);
        } else {
          data.init();
        }
      });
    };
  
    $.fn.multiSelect.defaults = {
      keySelect: [32],
      selectableOptgroup: false,
      disabledClass : 'disabled',
      dblClick : false,
      keepOrder: false,
      cssClass: ''
    };
  
    $.fn.multiSelect.Constructor = MultiSelect;
  
    $.fn.insertAt = function(index, $parent) {
      return this.each(function() {
        if (index === 0) {
          $parent.prepend(this);
        } else {
          $parent.children().eq(index - 1).after(this);
        }
      });
  };
  
  }(window.jQuery);
  