text: /**
   * Created with IntelliJ IDEA.
   * User: Ganaraj.Pr
   * Date: 11/10/13
   * Time: 11:27
   * To change this template use File | Settings | File Templates.
   */
  
  (function (angular) {
  
  	function isDnDsSupported() {
  		return 'ondrag' in document.createElement("a");
  	}
  
  	if (!isDnDsSupported()) {
  		angular.module("ang-drag-drop", []);
  		return;
  	}
  
  	if (window.jQuery && (-1 == window.jQuery.event.props.indexOf("dataTransfer"))) {
  		window.jQuery.event.props.push("dataTransfer");
  	}
  
  	var currentData;
  
  	angular.module("ang-drag-drop", [])
  		.directive("uiDraggable", [
  			'$parse',
  			'$rootScope',
  			'$dragImage',
  			function ($parse, $rootScope, $dragImage) {
  				return function (scope, element, attrs) {
  					var dragData = "",
  						isDragHandleUsed = false,
  						dragHandleClass,
  						draggingClass = attrs.draggingClass || "on-dragging",
  						dragTarget;
  
  					element.attr("draggable", false);
  
  					attrs.$observe("uiDraggable", function (newValue) {
  						if (newValue) {
  							element.attr("draggable", newValue);
  						}
  						else {
  							element.removeAttr("draggable");
  						}
  
  					});
  
  					if (attrs.drag) {
  						scope.$watch(attrs.drag, function (newValue) {
  							dragData = newValue || "";
  						});
  					}
  
  					if (angular.isString(attrs.dragHandleClass)) {
  						isDragHandleUsed = true;
  						dragHandleClass = attrs.dragHandleClass.trim() || "drag-handle";
  
  						element.bind("mousedown", function (e) {
  							dragTarget = e.target;
  						});
  					}
  
  					function dragendHandler(e) {
  						setTimeout(function () {
  							element.unbind('$destroy', dragendHandler);
  						}, 0);
  						var sendChannel = attrs.dragChannel || "defaultchannel";
  						$rootScope.$broadcast("ANGULAR_DRAG_END", sendChannel);
  						if (e.dataTransfer && e.dataTransfer.dropEffect !== "none") {
  							if (attrs.onDropSuccess) {
  								var fn = $parse(attrs.onDropSuccess);
  								scope.$evalAsync(function () {
  									fn(scope, {$event: e});
  								});
  							} else {
  								if (attrs.onDropFailure) {
  									var fn = $parse(attrs.onDropFailure);
  									scope.$evalAsync(function () {
  										fn(scope, {$event: e});
  									});
  								}
  							}
  						}
  						element.removeClass(draggingClass);
  					}
  
  					element.bind("dragend", dragendHandler);
  
  					element.bind("dragstart", function (e) {
  						var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);
  
  						if (isDragAllowed) {
  							var sendChannel = attrs.dragChannel || "defaultchannel";
  							var sendData = angular.toJson({
  								data: dragData,
  								channel: sendChannel
  							});
  							var dragImage = attrs.dragImage || null;
  
  							element.addClass(draggingClass);
  							element.bind('$destroy', dragendHandler);
  
  							if (dragImage) {
  								var dragImageFn = $parse(attrs.dragImage);
  								scope.$evalAsync(function () {
  									var dragImageParameters = dragImageFn(scope, {$event: e});
  									if (dragImageParameters) {
  										if (angular.isString(dragImageParameters)) {
  											dragImageParameters = $dragImage.generate(dragImageParameters);
  										}
  										if (dragImageParameters.image) {
  											var xOffset = dragImageParameters.xOffset || 0,
  												yOffset = dragImageParameters.yOffset || 0;
  											e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
  										}
  									}
  								});
  							}
  
  							e.dataTransfer.setData("dataToSend", sendData);
  							currentData = angular.fromJson(sendData);
  							e.dataTransfer.effectAllowed = "copyMove";
  							$rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel, currentData.data);
  						}
  						else {
  							e.preventDefault();
  						}
  					});
  				};
  			}
  		])
  		.directive("uiOnDrop", [
  			'$parse',
  			'$rootScope',
  			function ($parse, $rootScope) {
  				return function (scope, element, attr) {
  					var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
  					var dropChannel = attr.dropChannel || "defaultchannel";
  					var dragChannel = "";
  					var dragEnterClass = attr.dragEnterClass || "on-drag-enter";
  					var dragHoverClass = attr.dragHoverClass || "on-drag-hover";
  					var customDragEnterEvent = $parse(attr.onDragEnter);
  					var customDragLeaveEvent = $parse(attr.onDragLeave);
  
  					function onDragOver(e) {
  						if (e.preventDefault) {
  							e.preventDefault(); // Necessary. Allows us to drop.
  						}
  
  						if (e.stopPropagation) {
  							e.stopPropagation();
  						}
  
  						var fn = $parse(attr.uiOnDragOver);
  						scope.$evalAsync(function () {
  							fn(scope, {$event: e, $channel: dropChannel});
  						});
  
  						e.dataTransfer.dropEffect = e.shiftKey ? 'copy' : 'move';
  						return false;
  					}
  
  					function onDragLeave(e) {
  						if (e.preventDefault) {
  							e.preventDefault();
  						}
  
  						if (e.stopPropagation) {
  							e.stopPropagation();
  						}
  						dragging--;
  
  						if (dragging == 0) {
  							scope.$evalAsync(function () {
  								customDragEnterEvent(scope, {$event: e});
  							});
  							element.removeClass(dragHoverClass);
  						}
  
  						var fn = $parse(attr.uiOnDragLeave);
  						scope.$evalAsync(function () {
  							fn(scope, {$event: e, $channel: dropChannel});
  						});
  					}
  
  					function onDragEnter(e) {
  						if (e.preventDefault) {
  							e.preventDefault();
  						}
  
  						if (e.stopPropagation) {
  							e.stopPropagation();
  						}
  						dragging++;
  
  						var fn = $parse(attr.uiOnDragEnter);
  						scope.$evalAsync(function () {
  							fn(scope, {$event: e, $channel: dropChannel});
  						});
  
  						$rootScope.$broadcast("ANGULAR_HOVER", dragChannel);
  						scope.$evalAsync(function () {
  							customDragLeaveEvent(scope, {$event: e});
  						});
  						element.addClass(dragHoverClass);
  					}
  
  					function onDrop(e) {
  						if (e.preventDefault) {
  							e.preventDefault(); // Necessary. Allows us to drop.
  						}
  						if (e.stopPropagation) {
  							e.stopPropagation(); // Necessary. Allows us to drop.
  						}
  
  						var sendData = e.dataTransfer.getData("dataToSend");
  						sendData = angular.fromJson(sendData);
  
  						var fn = $parse(attr.uiOnDrop);
  						scope.$evalAsync(function () {
  							fn(scope, {
  								$data: sendData.data,
  								$event: e,
  								$channel: sendData.channel
  							});
  						});
  						element.removeClass(dragEnterClass);
  						dragging = 0;
  					}
  
  					function isDragChannelAccepted(dragChannel, dropChannel) {
  						if (dropChannel === "*") {
  							return true;
  						}
  
  						var channelMatchPattern = new RegExp("(\\s|[,])+(" + dragChannel + ")(\\s|[,])+", "i");
  
  						return channelMatchPattern.test("," + dropChannel + ",");
  					}
  
  					function preventNativeDnD(e) {
  						if (e.preventDefault) {
  							e.preventDefault();
  						}
  						if (e.stopPropagation) {
  							e.stopPropagation();
  						}
  						e.dataTransfer.dropEffect = "none";
  						return false;
  					}
  
  					var deregisterDragStart = $rootScope.$on("ANGULAR_DRAG_START", function (event, channel) {
  						dragChannel = channel;
  						if (isDragChannelAccepted(channel, dropChannel)) {
  							if (attr.dropValidate) {
  								var validateFn = $parse(attr.dropValidate);
  								var valid = validateFn(scope, {
  									$data: currentData.data,
  									$channel: currentData.channel
  								});
  								if (!valid) {
  									element.bind("dragover", preventNativeDnD);
  									element.bind("dragenter", preventNativeDnD);
  									element.bind("dragleave", preventNativeDnD);
  									element.bind("drop", preventNativeDnD);
  									return;
  								}
  							}
  
  							element.bind("dragover", onDragOver);
  							element.bind("dragenter", onDragEnter);
  							element.bind("dragleave", onDragLeave);
  
  							element.bind("drop", onDrop);
  							element.addClass(dragEnterClass);
  						}
  						else {
  							element.bind("dragover", preventNativeDnD);
  							element.bind("dragenter", preventNativeDnD);
  							element.bind("dragleave", preventNativeDnD);
  							element.bind("drop", preventNativeDnD);
  						}
  
  					});
  
  
  					var deregisterDragEnd = $rootScope.$on("ANGULAR_DRAG_END", function (e, channel) {
  						dragChannel = "";
  						if (isDragChannelAccepted(channel, dropChannel)) {
  
  							element.unbind("dragover", onDragOver);
  							element.unbind("dragenter", onDragEnter);
  							element.unbind("dragleave", onDragLeave);
  
  							element.unbind("drop", onDrop);
  							element.removeClass(dragHoverClass);
  							element.removeClass(dragEnterClass);
  						}
  
  						element.unbind("dragover", preventNativeDnD);
  						element.unbind("dragenter", preventNativeDnD);
  						element.unbind("dragleave", preventNativeDnD);
  						element.unbind("drop", preventNativeDnD);
  					});
  
  
  					var deregisterDragHover = $rootScope.$on("ANGULAR_HOVER", function (e, channel) {
  						if (isDragChannelAccepted(channel, dropChannel)) {
  							element.removeClass(dragHoverClass);
  						}
  					});
  
  
  					scope.$on('$destroy', function () {
  						deregisterDragStart();
  						deregisterDragEnd();
  						deregisterDragHover();
  					});
  
  
  					attr.$observe('dropChannel', function (value) {
  						if (value) {
  							dropChannel = value;
  						}
  					});
  
  
  				};
  			}
  		])
  		.constant("$dragImageConfig", {
  			height: 20,
  			width: 200,
  			padding: 10,
  			font: 'bold 11px Arial',
  			fontColor: '#eee8d5',
  			backgroundColor: '#93a1a1',
  			xOffset: 0,
  			yOffset: 0
  		})
  		.service("$dragImage", [
  			'$dragImageConfig',
  			function (defaultConfig) {
  				var ELLIPSIS = '…';
  
  				function fitString(canvas, text, config) {
  					var width = canvas.measureText(text).width;
  					if (width < config.width) {
  						return text;
  					}
  					while (width + config.padding > config.width) {
  						text = text.substring(0, text.length - 1);
  						width = canvas.measureText(text + ELLIPSIS).width;
  					}
  					return text + ELLIPSIS;
  				};
  
  				this.generate = function (text, options) {
  					var config = angular.extend({}, defaultConfig, options || {});
  					var el = document.createElement('canvas');
  
  					el.height = config.height;
  					el.width = config.width;
  
  					var canvas = el.getContext('2d');
  
  					canvas.fillStyle = config.backgroundColor;
  					canvas.fillRect(0, 0, config.width, config.height);
  					canvas.font = config.font;
  					canvas.fillStyle = config.fontColor;
  
  					var title = fitString(canvas, text, config);
  					canvas.fillText(title, 4, config.padding + 4);
  
  					var image = new Image();
  					image.src = el.toDataURL();
  
  					return {
  						image: image,
  						xOffset: config.xOffset,
  						yOffset: config.yOffset
  					};
  				}
  			}
  		]);
  
  }(angular));
  