text: /**
   * jquery.multisortable.js - v0.2
   * https://github.com/shvetsgroup/jquery.multisortable
   *
   * Author: Ethan Atlakson, Jay Hayes, Gabriel Such, Alexander Shvets
   * Last Revision 3/16/2012
   * multi-selectable, multi-sortable jQuery plugin
   */
  
  !function($) {
  
    $.fn.multiselectable = function(options) {
      if (!options) {
        options = {}
      }
      options = $.extend({}, $.fn.multiselectable.defaults, options);
  
      function mouseDown(e) {
        var item = $(this),
          parent = item.parent(),
          myIndex = item.index();
  
        var prev = parent.find('.multiselectable-previous');
        // If no previous selection found, start selecting from first selected item.
        prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous');
        var prevIndex = prev.index();
  
        if (e.ctrlKey || e.metaKey) {
          if (item.hasClass(options.selectedClass)) {
            item.removeClass(options.selectedClass).removeClass('multiselectable-previous')
            if (item.not('.child').length) {
              item.nextUntil(':not(.child)').removeClass(options.selectedClass);
            }
          }
          else {
            parent.find('.multiselectable-previous').removeClass('multiselectable-previous');
            item.addClass(options.selectedClass).addClass('multiselectable-previous')
            if (item.not('.child').length) {
              item.nextUntil(':not(.child)').addClass(options.selectedClass);
            }
          }
        }
  
        if (e.shiftKey) {
          var last_shift_range = parent.find('.multiselectable-shift');
          last_shift_range.removeClass(options.selectedClass).removeClass('multiselectable-shift');
  
          var shift_range;
          if (prevIndex < myIndex) {
            shift_range = item.prevUntil('.multiselectable-previous').add(prev).add(item);
          }
          else if (prevIndex > myIndex) {
            shift_range = item.nextUntil('.multiselectable-previous').add(prev).add(item);
          }
          shift_range.addClass(options.selectedClass).addClass('multiselectable-shift');
        }
        else {
          parent.find('.multiselectable-shift').removeClass('multiselectable-shift');
        }
  
        if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
          parent.find('.multiselectable-previous').removeClass('multiselectable-previous');
          if (!item.hasClass(options.selectedClass)) {
            parent.find('.' + options.selectedClass).removeClass(options.selectedClass);
            item.addClass(options.selectedClass).addClass('multiselectable-previous');
            if (item.not('.child').length) {
              item.nextUntil(':not(.child)').addClass(options.selectedClass);
            }
          }
        }
  
        options.mousedown(e, item);
      }
  
      function click(e) {
        if ( $(this).is('.ui-draggable-dragging') ) {
          return;
        }
  
        var item = $(this), parent = item.parent();
  
        // If item wasn't draged and is not multiselected, it should reset selection for other items.
        if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
          parent.find('.multiselectable-previous').removeClass('multiselectable-previous');
          parent.find('.' + options.selectedClass).removeClass(options.selectedClass);
          item.addClass(options.selectedClass).addClass('multiselectable-previous');
          if (item.not('.child').length) {
            item.nextUntil(':not(.child)').addClass(options.selectedClass);
          }
        }
  
        options.click(e, item);
      }
  
      return this.each(function() {
        var list = $(this);
  
        if (!list.data('multiselectable')) {
          list.data('multiselectable', true)
            .delegate(options.items, 'mousedown', mouseDown)
            .delegate(options.items, 'click', click)
            .disableSelection();
        }
      })
    };
  
    $.fn.multiselectable.defaults = {
      click: function(event, elem) {},
      mousedown: function(event, elem) {},
      selectedClass: 'selected',
      items: 'li'
    };
  
  
    $.fn.multisortable = function(options) {
      if (!options) {
        options = {}
      }
      var settings = $.extend({}, $.fn.multisortable.defaults, options);
  
      function regroup(item, list) {
        if (list.find('.' + settings.selectedClass).length > 0) {
          var myIndex = item.data('i');
  
          var itemsBefore = list.find('.' + settings.selectedClass).filter(function() {
            return $(this).data('i') < myIndex
          }).css({
              position: '',
              width: '',
              left: '',
              top: '',
              zIndex: ''
            });
  
          item.before(itemsBefore);
  
          var itemsAfter = list.find('.' + settings.selectedClass).filter(function() {
            return $(this).data('i') > myIndex
          }).css({
              position: '',
              width: '',
              left: '',
              top: '',
              zIndex: ''
            });
  
          item.after(itemsAfter);
  
          setTimeout(function() {
            itemsAfter.add(itemsBefore).addClass(settings.selectedClass);
          }, 0);
        }
      }
  
      return this.each(function() {
        var list = $(this);
  
        //enable multi-selection
        list.multiselectable({
          selectedClass: settings.selectedClass,
          click: settings.click,
          items: settings.items,
          mousedown: settings.mousedown
        });
  
        //enable sorting
        options.cancel = settings.items + ':not(.' + settings.selectedClass + ')';
        options.placeholder = settings.placeholder;
        options.start = function(event, ui) {
          if (ui.item.hasClass(settings.selectedClass)) {
            var parent = ui.item.parent();
  
            //assign indexes to all selected items
            parent.find('.' + settings.selectedClass).each(function(i) {
              $(this).data('i', i);
            });
  
            // adjust placeholder size to be size of items
            var height = parent.find('.' + settings.selectedClass).length * ui.item.outerHeight();
            ui.placeholder.height(height);
          }
  
          settings.start(event, ui);
        };
  
        options.stop = function(event, ui) {
          regroup(ui.item, ui.item.parent());
          settings.stop(event, ui);
        };
  
        options.sort = function(event, ui) {
          var parent = ui.item.parent(),
            myIndex = ui.item.data('i'),
            top = parseInt(ui.item.css('top').replace('px', '')),
            left = parseInt(ui.item.css('left').replace('px', ''));
  
          // fix to keep compatibility using prototype.js and jquery together
          $.fn.reverse = Array.prototype._reverse || Array.prototype.reverse
  
          var height = 0;
          $('.' + settings.selectedClass, parent).filter(function() {
            return $(this).data('i') < myIndex;
          }).reverse().each(function() {
              height += $(this).outerHeight();
              $(this).css({
                left: left,
                top: top - height,
                position: 'absolute',
                zIndex: 1000,
                width: ui.item.width()
              })
            });
  
          height = ui.item.outerHeight();
          $('.' + settings.selectedClass, parent).filter(function() {
            return $(this).data('i') > myIndex;
          }).each(function() {
              var item = $(this);
              item.css({
                left: left,
                top: top + height,
                position: 'absolute',
                zIndex: 1000,
                width: ui.item.width()
              });
  
              height += item.outerHeight();
            });
  
          settings.sort(event, ui);
        };
  
        options.receive = function(event, ui) {
          regroup(ui.item, ui.sender);
          settings.receive(event, ui);
        };
  
        list.sortable(options).disableSelection();
      })
    };
  
    $.fn.multisortable.defaults = {
      start: function(event, ui) {},
      stop: function(event, ui) {},
      sort: function(event, ui) {},
      receive: function(event, ui) {},
      click: function(event, elem) {},
      mousedown: function(event, elem) {},
      selectedClass: 'selected',
      placeholder: 'placeholder',
      items: 'li'
    };
  
  }(jQuery);