<html>

  <head>
    <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>

    <link data-require="jqueryui@*" data-semver="1.10.0" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/css/smoothness/jquery-ui-1.10.0.custom.min.css" />
    <script data-require="jqueryui@*" data-semver="1.10.0" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/jquery-ui.js"></script>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
    <script src="https://rawgit.com/angular-ui/ui-sortable/master/src/sortable.js"></script>
    <script src="angular-ui-sortable-multiselect.js"></script>
    <script src="script.js"></script>
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <div ng-app="sortableApp" ng-controller="sortableController" class="container">
      <h2>angular-ui-sortable-multiselection</h2>
      
      <div class="floatleft">
        <div ui-sortable="sortableOptions" ng-model="trash" class="list">
          <div ui-sortable-selectable="" ng-repeat="item in trash" class="item">
            {{item.text}}
          </div>
        </div>
      </div>
      
      <div class="floatleft" style="margin-left: 20px;">
        <div ui-sortable="sortableOptions" ng-model="notTrash" class="list">
          <div ui-sortable-selectable="" ng-repeat="item in notTrash" class="item">
            {{item.text}}
          </div>
        </div>
      </div>
      
      <div class="clear"></div>
    </div>
  </body>

</html>
var myapp = angular.module('sortableApp', ['ui.sortable', 'ui.sortable.multiselection']);


myapp.controller('sortableController', function ($scope, uiSortableMultiSelectionMethods, uiSortableMulitSelectionCollapse) {
  var tmpList = [];
  
  for (var i = 1; i <= 10; i++){
    tmpList.push({
      text: 'Item ' + i,
      value: i
    });
  }
  
  $scope.trash = tmpList.slice(0, 5);
  $scope.notTrash = tmpList.slice(5);
  
  $scope.sortableOptions = {
    helper: uiSortableMultiSelectionMethods.helper,
    connectWith: ".list",
    stop: function(e, ui) {
      uiSortableMultiSelectionMethods.stop(e, ui);
    }
  };
});
.list {
	list-style: none outside none;
	margin: 10px 0 30px;
	border: 1px solid #000;
	width: 250px;
	height: 500px;
}

.item {
	width: 200px;
	padding: 5px 10px;
	margin: 5px 0;
	border: 2px solid #444;
	border-radius: 5px;
	background-color: #EA8A8A;

	font-size: 1.1em;
	font-weight: bold;
	text-align: center;
	cursor: move;
}

.item.ui-sortable-selected {
  background-color: yellow;
}


/***  Extra ***/

body {
	font-family: Verdana, 'Trebuchet ms', Tahoma;
}

.logList {
	margin-top: 20px;
	width: 250px;
	min-height: 200px;
	padding: 5px 15px;
	border: 5px solid #000;
	border-radius: 15px;
}

.logList:before {
	content: 'log';
	padding: 0 5px;
	position: relative;
	top: -1.1em;
	background-color: #FFF;
}

.container {
	width: 600px;
	margin: auto;
}

h2 {
	text-align: center;
}

.floatleft {
  float: left;
}

.clear {
  clear: both;
}
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) {
            
            var $this = angular.element(this);
            var lastIndex = scope.$parent._lastSelectedIndex;
            
            if(!e.ctrlKey && !e.metaKey && !e.shiftKey) {
              $this.siblings().removeClass(selectedItemClass);
              $this.addClass(selectedItemClass);
              
            } else if(event.shiftKey && !event.ctrlKey && lastIndex > -1) {
              var curIndex = $this.index();
              
              if(curIndex > lastIndex) {
                $this.parent().children().slice(lastIndex, curIndex+1).addClass(selectedItemClass);
                
              } else if(curIndex < lastIndex) {
                $this.parent().children().slice(curIndex, lastIndex).addClass(selectedItemClass);
              }
              
            } else if(event.ctrlKey && !event.shiftKey) {
              if($this.hasClass(selectedItemClass)) {
                $this.removeClass(selectedItemClass);
              } else {
                $this.addClass(selectedItemClass);
              }
            }
            
            scope.$parent._lastSelectedIndex = $this.index();
          });
        }
      };
    }
  ])
  .factory('uiSortableMulitSelectionCollapse', [
    'uiSortableMultiSelectionClass',
    function(selectedItemClass) {
      return {
        helper: function(e, item) {
          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
          };
          
          var helperTag = item[0].tagName;
          var helper = null;
          if(indexes.length > 1) {
            helper = item.clone();
            helper.text('You have '+indexes.length+' items selected.');
          } else {
            helper = item.clone();
          }
          return helper;
        },
        stop: function(e, item) {
          //TODO implement this
        }
      };
    }
  ])
  .factory('uiSortableMultiSelectionMethods', [
    'uiSortableMultiSelectionClass',
    function (selectedItemClass) {
      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);
        },
        stop: function (e, ui) {
          console.log(
          ui,
          ui.item.attr('ng-model'), 
          ui.item.parent().attr('ng-model'),
          ui.item.scope(),
          ui.item.parent(),
          ui.item.parent().scope(),
         // ui.item.sortable.droptarget.scope(),
          ui.item.sortable.droptarget.scope().$eval(ui.item.parent().attr('ng-model'))
          );
          
          var isSelfList = ui.item.sortable.droptarget.scope();
          
          var ngModel = ui.item.parent().scope() ? 
            ui.item.parent().scope().$eval(ui.item.parent().attr('ng-model')) :
            ui.item.sortable.droptarget.scope().$eval(ui.item.parent().attr('ng-model'));
            
          var otherModel = ui.item.sortable.droptarget.scope().$eval(ui.item.sortable.droptarget.attr('ng-model'));
          
          var oldPosition = ui.item.sortable.index;
          var newPosition = ui.item.sortable.dropindex;
              
          function fixIndex (x) {
            if (oldPosition < newPosition && oldPosition < x && x <= newPosition) {
              return x - 1;
            } else if (newPosition < oldPosition && newPosition <= x && x < oldPosition) {
              return x + 1;
            }
            return x;
          }

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

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

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

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

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

          var indexes = groupIndexes(draggedElementIndexes);
          
          console.log(ngModel, otherModel);

          // 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];
          
          // the code should run in reverse order,
          // so that the indexes will not break
          var models = {
            below: getModelsFromIndexes(indexes.below),
            above: getModelsFromIndexes(indexes.above)
          };
        
          Array.prototype.splice.apply(
            otherModel,
            [otherModel.indexOf(draggedModel) + 1, 0]
            .concat(models.below));

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

          ui.item.parent().find('> .' + selectedItemClass).removeClass('' + selectedItemClass).show();
        }
      };
    }]);