text: /*
   jQuery UI Sortable plugin wrapper
  
   @param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
  */
  angular.module('ui.sortable', [])
    .value('uiSortableConfig',{})
    .directive('uiSortable', [ 'uiSortableConfig',
          function(uiSortableConfig) {
          return {
            require: '?ngModel',
            link: function(scope, element, attrs, ngModel) {
  
                function combineCallbacks(first,second){
                    if( second && (typeof second === "function") ){
                        return function(e,ui){
                            first(e,ui);
                            second(e,ui);
                        };
                    }
                    return first;
                }
  
              var opts = {};
  
              var callbacks = {
                  receive: null,
                  remove:null,
                  start:null,
                  stop:null,
                  update:null
              };
  
              angular.extend(opts, uiSortableConfig);
  
              if (ngModel) {
  
                ngModel.$render = function() {
                  element.sortable( "refresh" );
                };
  
                callbacks.start = function(e, ui) {
                  // Save position of dragged item
                  ui.item.sortable = { index: ui.item.index() };
                };
  
                callbacks.update = function(e, ui) {
                  // For some reason the reference to ngModel in stop() is wrong
                  ui.item.sortable.resort = ngModel;
                };
  
                callbacks.receive = function(e, ui) {
                  ui.item.sortable.relocate = true;
                  // added item to array into correct position and set up flag
                  ngModel.$modelValue.splice(ui.item.index(), 0, ui.item.sortable.moved);
                };
  
                callbacks.remove = function(e, ui) {
                  // copy data into item
                  if (ngModel.$modelValue.length === 1) {
                    ui.item.sortable.moved = ngModel.$modelValue.splice(0, 1)[0];
                  } else {
                    ui.item.sortable.moved =  ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0];
                  }
                };
  
                callbacks.stop = function(e, ui) {
                  // digest all prepared changes
                  if (ui.item.sortable.resort && !ui.item.sortable.relocate) {
  
                    // Fetch saved and current position of dropped element
                    var end, start;
                    start = ui.item.sortable.index;
                    end = ui.item.index();
  
                    // Reorder array and apply change to scope
                    ui.item.sortable.resort.$modelValue.splice(end, 0, ui.item.sortable.resort.$modelValue.splice(start, 1)[0]);
  
                  }
                  if (ui.item.sortable.resort || ui.item.sortable.relocate) {
                    scope.$apply();
                  }
                };
  
              }
  
  
                scope.$watch(attrs.uiSortable, function(newVal, oldVal){
                    angular.forEach(newVal, function(value, key){
  
                        if( callbacks[key] ){
                            // wrap the callback
                            value = combineCallbacks( callbacks[key], value );
                        }
  
                        element.sortable('option', key, value);
                    });
                }, true);
  
                angular.forEach(callbacks, function(value, key ){
  
                      opts[key] = combineCallbacks(value, opts[key]);
                });
  
                // Create sortable
  
              element.sortable(opts);
            }
          };
        }
  ]); 
  