text: /**
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to
   * deal in the Software without restriction, including without limitation the
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   * sell copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   */
  
  /**
   * Implementing Drag and Drop functionality in AngularJS is easier than ever.
   * Demo: http://codef0rmer.github.com/angular-dragdrop/
   *
   * @version 1.0.1
   *
   * (c) 2013 Amit Gharat a.k.a codef0rmer <amit.2006.it@gmail.com> - amitgharat.wordpress.com
   */
  
  var jqyoui = angular.module('ngDragDrop', []).service('ngDragDropService', ['$timeout', '$parse', function($timeout, $parse) {
  	this.callEventCallback = function (scope, callbackName, event, ui) {
  	  if (!callbackName) {
  		return;
  	  }
  	  var args = [event, ui];
  	  var match = callbackName.match(/^(.+)\((.+)\)$/);
  	  if (match !== null) {
  		callbackName = match[1];
  		values = eval('[' + match[0].replace(/^(.+)\(/, '').replace(/\)/, '') + ']');
  		args.push.apply(args, values);
  	  }
  	  scope[callbackName].apply(scope, args);
  	};
  
  	this.invokeDrop = function ($draggable, $droppable, event, ui) {
  	  var dragModel = '',
  		dropModel = '',
  		dragSettings = {},
  		dropSettings = {},
  		jqyoui_pos = null,
  		dragItem = {},
  		dropItem = {},
  		dragModelValue,
  		dropModelValue,
  		$droppableDraggable = null,
  		droppableScope = $droppable.scope(),
  		draggableScope = $draggable.scope();
  
  	  dragModel = $draggable.attr('ng-model');
  	  dropModel = $droppable.attr('ng-model');
  	  dragModelValue = draggableScope.$eval(dragModel);
  	  dropModelValue = droppableScope.$eval(dropModel);
  
  	  $droppableDraggable = $droppable.find('[jqyoui-draggable]:last');
  	  dropSettings = droppableScope.$eval($droppable.attr('jqyoui-droppable')) || [];
  	  dragSettings = draggableScope.$eval($draggable.attr('jqyoui-draggable')) || [];
  
  	  jqyoui_pos = angular.isArray(dragModelValue) ? dragSettings.index : null;
  	  dragItem = angular.isArray(dragModelValue) ? dragModelValue[jqyoui_pos] : dragModelValue;
  
  	  if (angular.isArray(dropModelValue) && dropSettings && dropSettings.index !== undefined) {
  		dropItem = dropModelValue[dropSettings.index];
  	  } else if (!angular.isArray(dropModelValue)) {
  		dropItem = dropModelValue;
  	  } else {
  		dropItem = {};
  	  }
  
  	  if (dragSettings.animate === true) {
  		this.move($draggable, $droppableDraggable.length > 0 ? $droppableDraggable : $droppable, null, 'fast', dropSettings, null);
  		this.move($droppableDraggable.length > 0 && !dropSettings.multiple ? $droppableDraggable : [], $draggable.parent('[jqyoui-droppable]'), jqyoui.startXY, 'fast', dropSettings, function() {
  		  $timeout(function() {
  			// Do not move this into move() to avoid flickering issue
  			$draggable.css({'position': 'relative', 'left': '', 'top': ''});
  			$droppableDraggable.css({'position': 'relative', 'left': '', 'top': ''});
  
  			this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
  			this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
  			this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
  		  }.bind(this));
  		}.bind(this));
  	  } else {
  		$timeout(function() {
  		  this.mutateDraggable(draggableScope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable);
  		  this.mutateDroppable(droppableScope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
  		  this.callEventCallback(droppableScope, dropSettings.onDrop, event, ui);
  		}.bind(this));
  	  }
  	};
  
  	this.move = function($fromEl, $toEl, toPos, duration, dropSettings, callback) {
  	  if ($fromEl.length === 0) {
  		if (callback) {
  		  window.setTimeout(function() {
  			callback();
  		  }, 300);
  		}
  		return false;
  	  }
  
  	  var zIndex = 9999,
  		fromPos = $fromEl.offset(),
  		wasVisible = $toEl && $toEl.is(':visible');
  
  	  if (toPos === null && $toEl.length > 0) {
  		if ($toEl.attr('jqyoui-draggable') !== undefined && $toEl.attr('ng-model') !== undefined && $toEl.is(':visible') && dropSettings && dropSettings.multiple) {
  		  toPos = $toEl.offset();
  		  if (dropSettings.stack === false) {
  			toPos.left+= $toEl.outerWidth(true);
  		  } else {
  			toPos.top+= $toEl.outerHeight(true);
  		  }
  		} else {
  		  toPos = $toEl.css({'visibility': 'hidden', 'display': 'block'}).offset();
  		  $toEl.css({'visibility': '','display': wasVisible ? '' : 'none'});
  		}
  	  }
  
  	  $fromEl.css({'position': 'absolute', 'z-index': zIndex})
  		.css(fromPos)
  		.animate(toPos, duration, function() {
  		  if (callback) callback();
  		});
  	};
  
  	this.mutateDroppable = function(scope, dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos) {
  		var dropModelValue = scope.$eval(dropModel);
  		var notRepeating = true;
  
  		scope.__dragItem = dragItem;
  	  
  	  	//console.log(dragItem);
  
  	  	//console.log(dropSettings, dragSettings, dropModel, dragItem, jqyoui_pos);
  
  	  	if (angular.isArray(dropModelValue)) {
  			if (dropSettings && dropSettings.index >= 0) {
  				dropModelValue[dropSettings.index] = dragItem;
  			} else if (dragSettings.element && dragSettings.element == "array") {
  				dragItem = dragItem.fields;
  				angular.forEach(dragItem, function(value){
  					if (dropSettings.element && dropSettings.element == "object") {
  						var Object = {"field":"","variable":""};
  						Object.field = Object.variable = value;
  						angular.forEach(dropModelValue, function(valueA){
  							if(valueA.field==value) notRepeating = false;
  						});
  						if(notRepeating){
  							dropModelValue.push(Object);
  						}
  						else{
  							notRepeating = true;
  						}
  					} else {
  						angular.forEach(dropModelValue, function(valueA){
  							if(valueA==value) notRepeating = false;
  						});
  						if(notRepeating){
  							dropModelValue.push(value);
  						}
  						else{
  							notRepeating = true;
  						}
  					}
  				});
  			} else {
  				if (dropSettings.element && dropSettings.element == "object") {
  					var Object = {"field":"","variable":""};
  					Object.field = Object.variable = dragItem;
  					angular.forEach(dropModelValue, function(value){
  						if(value.field==dragItem) notRepeating = false;
  					});
  					if(notRepeating){
  						dropModelValue.push(Object);
  					}
  				} else {
  					angular.forEach(dropModelValue, function(value){
  						if(value==dragItem) notRepeating = false;
  					});
  					if(notRepeating){
  						dropModelValue.push(dragItem);
  					}
  				}
  			}
  			if (dragSettings && dragSettings.placeholder === true) {
  				dropModelValue[dropModelValue.length - 1]['jqyoui_pos'] = jqyoui_pos;
  			}
  		} else {
  			$parse(dropModel + ' = __dragItem')(scope);
  			if (dragSettings && dragSettings.placeholder === true) {
  				dropModelValue['jqyoui_pos'] = jqyoui_pos;
  			}
  		}
  	};
  
  	this.mutateDraggable = function(scope, dropSettings, dragSettings, dragModel, dropModel, dropItem, $draggable) {
  	  var isEmpty = $.isEmptyObject(angular.copy(dropItem)),
  		dragModelValue = scope.$eval(dragModel);
  
  	  scope.__dropItem = dropItem;
  
  	  if (dragSettings && dragSettings.placeholder) {
  		if (dragSettings.placeholder != 'keep'){
  		  if (angular.isArray(dragModelValue) && dragSettings.index !== undefined) {
  			dragModelValue[dragSettings.index] = dropItem;
  		  } else {
  			$parse(dragModel + ' = __dropItem')(scope);
  		  }
  		}
  	  } else {
  		if (angular.isArray(dragModelValue)) {
  		  if (isEmpty) {
  			if (dragSettings && ( dragSettings.placeholder !== true && dragSettings.placeholder !== 'keep' )) {
  			  dragModelValue.splice(dragSettings.index, 1);
  			}
  		  } else {
  			dragModelValue[dragSettings.index] = dropItem;
  		  }
  		} else {
  		  // Fix: LIST(object) to LIST(array) - model does not get updated using just scope[dragModel] = {...}
  		  // P.S.: Could not figure out why it happened
  		  $parse(dragModel + ' = __dropItem')(scope);
  		  if (scope.$parent) {
  			$parse(dragModel + ' = __dropItem')(scope.$parent);
  		  }
  		}
  	  }
  
  	  $draggable.css({'z-index': '', 'left': '', 'top': ''});
  	};
    }]).directive('jqyouiDraggable', ['ngDragDropService', function(ngDragDropService) {
  	return {
  	  require: '?jqyouiDroppable',
  	  restrict: 'A',
  	  link: function(scope, element, attrs) {
  		var dragSettings, zIndex;
  		var updateDraggable = function(newValue, oldValue) {
  		  if (newValue) {
  			dragSettings = scope.$eval(element.attr('jqyoui-draggable')) || [];
  			element
  			  .draggable({disabled: false})
  			  .draggable(scope.$eval(attrs.jqyouiOptions) || {})
  			  .draggable({
  				start: function(event, ui) {
  				  zIndex = $(this).css('z-index', 99999);
  				  $(this).css('z-index', 99999);
  				  jqyoui.startXY = $(this).offset();
  				  ngDragDropService.callEventCallback(scope, dragSettings.onStart, event, ui);
  				},
  				stop: function(event, ui) {
  				  $(this).css('z-index', zIndex);
  				  ngDragDropService.callEventCallback(scope, dragSettings.onStop, event, ui);
  				},
  				drag: function(event, ui) {
  				  ngDragDropService.callEventCallback(scope, dragSettings.onDrag, event, ui);
  				}
  			  });
  		  } else {
  			element.draggable({disabled: true});
  		  }
  		};
  		scope.$watch(function() { return scope.$eval(attrs.drag); }, updateDraggable);
  		updateDraggable();
  	  }
  	};
    }]).directive('jqyouiDroppable', ['ngDragDropService', function(ngDragDropService) {
  	return {
  	  restrict: 'A',
  	  priority: 1,
  	  link: function(scope, element, attrs) {
  		var updateDroppable = function(newValue, oldValue) {
  		  if (newValue) {
  			element
  			  .droppable({disabled: false})
  			  .droppable(scope.$eval(attrs.jqyouiOptions) || {})
  			  .droppable({
  				over: function(event, ui) {
  				  var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || [];
  				  ngDragDropService.callEventCallback(scope, dropSettings.onOver, event, ui);
  				},
  				out: function(event, ui) {
  				  var dropSettings = scope.$eval(angular.element(this).attr('jqyoui-droppable')) || [];
  				  ngDragDropService.callEventCallback(scope, dropSettings.onOut, event, ui);
  				},
  				drop: function(event, ui) {
  				  ngDragDropService.invokeDrop(angular.element(ui.draggable), angular.element(this), event, ui);
  				}
  			  });
  		  } else {
  			element.droppable({disabled: true});
  		  }
  		};
  
  		scope.$watch(function() { return scope.$eval(attrs.drop); }, updateDroppable);
  		updateDroppable();
  	  }
  	};
    }]);