angular.module('ui.sortable.multiselection', [])
  .constant('uiSortableMultiSelectionClass', 'ui-sortable-selected')
  .directive('uiSortableSelectable', [
    'uiSortableMultiSelectionClass',
    function(selectedItemClass) {
      return {
        link: function(scope, element/*, attrs*/) {
          element.on('click', function (e) {
            if (e.ctrlKey || e.metaKey) {
              var $this = angular.element(this);
              $this.toggleClass(selectedItemClass);
            }
          });
        }
      };
    }
  ])
  .factory('uiSortableMultiSelectionMethods', [
    'uiSortableMultiSelectionClass',
    function (selectedItemClass) {
      function fixIndex (oldPosition, newPosition, x) {
        if (oldPosition < x && (newPosition === undefined || (oldPosition < newPosition && x <= newPosition))) {
          return x - 1;
        } else if (x < oldPosition && (newPosition === undefined || (newPosition < oldPosition && newPosition <= x))) {
          return x + 1;
        }
        return x;
      }

      function groupIndexes (indexes, oldPosition, newPosition) {
        var above = [],
            below = [];

        for (var i = 0; i < indexes.length; i++) {
          var x = indexes[i];
          if (x < oldPosition) {
            above.push(fixIndex(oldPosition, newPosition, x));
          } else if (oldPosition < x) {
            below.push(fixIndex(oldPosition, newPosition, x));
          }
        }

        return {
          above: above,
          below: below
        };
      }

      function getModelsFromIndexes (ngModel, indexes) {
        var result = [];
        for (var i = indexes.length - 1; i >= 0; i--) {
          result.push(ngModel.splice(indexes[i], 1)[0]);
        }
        result.reverse();
        return result;
      }

      return {
        helper: function (e, item) {
          // when starting to sort an unhighlighted item ,
          // deselect any existing highlighted items
          if (!item.hasClass(selectedItemClass)) {
              item.addClass(selectedItemClass)
                .siblings()
                .removeClass(selectedItemClass);
          }

          var selectedElements = item.parent().children('.' + selectedItemClass);
          var selectedSiblings = item.siblings('.' + selectedItemClass);

          // indexes of the selected siblings
          var indexes = angular.element.map(selectedSiblings, function (element) {
            return angular.element(element).index();
          });

          item.sortableMultiSelect = {
            indexes: indexes
          };

          // Clone the selected items and to put them inside the helper
          var elements = selectedElements.clone();

          // like `helper: 'clone'` does, hide the dragged elements
          selectedSiblings.hide();

          // Create the helper to act as a bucket for the cloned elements
          var helperTag = item[0].tagName;
          var helper = angular.element('<' + helperTag + '/>');
          return helper.append(elements);
        },
        start: function(e, ui) {
          ui.item.sortableMultiSelect.sourceElement = ui.item.parent();
        },
        update: function(e, ui) {
          if(!ui.item.sortable.received) {

          }
          if (ui.item.sortable.received && !ui.item.sortable.isCanceled()) {
            var scope = ui.item.sortable.droptarget.scope();

            scope.$apply(function () {
              var ngModel = scope.$eval(ui.item.sortable.droptarget.attr('ng-model')),
                  newPosition = ui.item.sortable.dropindex,
                  models = ui.item.sortableMultiSelect.moved;

              // add the models to the target list
              Array.prototype.splice.apply(
                ngModel,
                [newPosition+ 1, 0]
                .concat(models.below));

              Array.prototype.splice.apply(
                ngModel,
                [newPosition, 0]
                .concat(models.above));
            });
          }
        },
        remove: function(e, ui) {
          if (!ui.item.sortable.isCanceled()) {
            var scope = ui.item.sortableMultiSelect.sourceElement.scope();

            scope.$apply(function () {
              var ngModel = scope.$eval(ui.item.sortableMultiSelect.sourceElement.attr('ng-model')),
                  oldPosition = ui.item.sortable.index;

              var indexes = groupIndexes(ui.item.sortableMultiSelect.indexes, oldPosition);

              // get the models and remove them from the original list
              // the code should run in reverse order,
              // so that the indexes will not break
              ui.item.sortableMultiSelect.moved = {
                below: getModelsFromIndexes(ngModel, indexes.below),
                above: getModelsFromIndexes(ngModel, indexes.above)
              };
            });
          }
        },
        stop: function (e, ui) {
          if (!ui.item.sortable.received &&
             ('dropindex' in ui.item.sortable) &&
             !ui.item.sortable.isCanceled()) {
            var sourceElement = ui.item.sortableMultiSelect.sourceElement || ui.item.parent(),
                ngModel = sourceElement.scope().$eval(sourceElement.attr('ng-model')),
                oldPosition = ui.item.sortable.index,
                newPosition = ui.item.sortable.dropindex;

            var draggedElementIndexes = ui.item.sortableMultiSelect.indexes;
            if (!draggedElementIndexes.length) {
              return;
            }

            var indexes = groupIndexes(draggedElementIndexes, oldPosition, newPosition);

            // get the model of the dragged item
            // so that we can locate its position
            // after we remove the co-dragged elements
            var draggedModel = ngModel[newPosition];
            
            // get the models and remove them from the list
            // the code should run in reverse order,
            // so that the indexes will not break
            var models = {
              below: getModelsFromIndexes(ngModel, indexes.below),
              above: getModelsFromIndexes(ngModel, indexes.above)
            };
          
            // add the models to the list
            Array.prototype.splice.apply(
              ngModel,
              [ngModel.indexOf(draggedModel) + 1, 0]
              .concat(models.below));

            Array.prototype.splice.apply(
              ngModel,
              [ngModel.indexOf(draggedModel), 0]
              .concat(models.above));

            ui.item.parent().find('> .' + selectedItemClass).removeClass('' + selectedItemClass).show();
          }
        }
      };
    }]);
@@ -1795,32 +1795,737 @@
    %7D%0A%0A      
-return %7B
+function combineCallbacks(first,second)%7B%0A        if(second && (typeof second === 'function')) %7B%0A          return function(e, ui) %7B%0A            first(e, ui);%0A            second(e, ui);%0A          %7D;%0A        %7D%0A        return first;%0A      %7D%0A%0A      return %7B%0A        extendOptions: function (sortableOptions) %7B%0A          sortableOptions = sortableOptions %7C%7C %7B%7D;%0A          var result = $.extend(%7B%7D, this, sortableOptions);%0A%0A          for (var prop in sortableOptions) %7B%0A            if (sortableOptions.hasOwnProperty(prop)) %7B%0A              if (this%5Bprop%5D) %7B%0A                result%5Bprop%5D = combineCallbacks(this%5Bprop%5D, sortableOptions%5Bprop%5D);%0A              %7D%0A            %7D%0A          %7D%0A%0A          return result;%0A        %7D,
 %0A        hel

add extendOptions method