<!doctype html>
<html ng-app="demo">
  <head>
    <meta charset="utf-8">
    <title>AngularJS Drag Drop directive demo by Ajain Vivek</title>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.18/angular-sanitize.js"></script>
    <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.css">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <div class="container" ng-controller="mainCtrl">
      <h1 class="text-muted">Drag Drop Demo - https://github.com/ajainvivek/ngDragDrop</h1>

      <div class="usage row">
        <h4>Simple usage</h4>
        <p>This is a simple usage of the drag drop directive</p>
        <pre>&lt;ng-drag-drop dropTo="#drop-zone"&gt;&lt;/ng-drag-drop&gt;</pre>
        <h4>Results</h4>
        <div class="drag_container">
          <h4>Draggables</h4>
          <div ng-drag-drop dropTo="#drop-zone" styles="{{styles}}" on-drag="dragCallback(event)" on-drop="dropCallback(event)" on-over="overCallback(event)">
            <div class="draggable-item" dragIt="true">Drag Item A</div>
            <div class="draggable-item" dragIt="true">Drag Item B</div>
            <div class="draggable-item" dragIt="false">Fixed Item C</div>
          </div>
        </div>
        <div class="drop_container">
          <h4>Drop Zone</h4>
          <div id="drop-zone">
          </div>
        </div>
              </div>
    </div>

    <script src="ngDragDrop.js"></script>
    <script src="script.js"></script>
  </body>
</html>
var app = angular.module('demo', ['ngSanitize', 'ngDragDrop']);

app.controller("mainCtrl", function ($scope, ngDragDrop) {
	$scope.styles = {
		draggables : {
			onDragging : {border: "1px dashed #000", cursor : "move"},
			onStart : {opacity: 0.5}
		},
		droppables : {
			onEnter: {background: "red"},
			onLeave: {background: "yellow"}
		}
	};
	$scope.dragCallback = function (event) {
		console.log("Dragging", event);
	};
	$scope.dropCallback = function (event) {
		var currDragElem = ngDragDrop.getCurrentDragElement();
		var dropElem = angular.element(event.target);
		currDragElem.css({
			"pointer-events": "none"
		});
		dropElem.append(currDragElem);
	};
	$scope.overCallback = function (event) {
		console.log("Drag Over", event);
	};
});
.usage {
  padding: 10px;
  border: 1px solid rgba(0,0,0,0.09);
  border-radius: 5px;
}

#draggables {
	float: left;
	height: 100%;
}

.draggable-item {
	height: 100px;
	width: 100px;
	background: #ccc;
	margin: 10px auto;
	text-align: center;
	line-height: 100px;
}

.drag_container {
	float: left;
	width: 300px;
	margin: 10px;
	background: #ffe;
	text-align: center;
}

.drop_container {
	float: left;
	width: 300px;
	height: 100%;
	margin: 10px;
	background: #efe;
	text-align: center;
}


#drop-zone {
	height: 100%;
	min-height: 200px;
	width: 100%;
	padding: 20px;
}

/*
 * ngDragDrop - HTML5 Drag & Drop
 * http://github.com/ajainvivek/ngDragDrop
 * (c) 2015 MIT License, https://chaicode.com
 */

(function(window, angular, undefined) {
  'use strict';

  var module = angular.module('ngDragDrop', []);

  var $el = angular.element;
  var isDef = angular.isDefined;
  var style = (document.body || document.documentElement).style;
  var animationEndSupport = isDef(style.animation) || isDef(style.WebkitAnimation) || isDef(style.MozAnimation) || isDef(style.MsAnimation) || isDef(style.OAnimation);
  var animationEndEvent = 'animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend';

  module.provider('ngDragDrop', function() {
    var defaults = this.defaults = {
      styles : {
        draggables : {
          onDragging : {},
          onStart : {}
        },
        droppables : {
          onEnter: {},
          onLeave: {}
        }
      }
    };

    this.setDefaults = function (newDefaults) {
        angular.extend(defaults, newDefaults);
    };

    this.$get = ['$document', '$templateCache', '$compile', '$q', '$http', '$rootScope', '$timeout', '$window', '$controller',
      function($document, $templateCache, $compile, $q, $http, $rootScope, $timeout, $window, $controller) {
        var privateMethods = {
          currentDragElement: {},
          currentDropElement: {},
          resetStyles: function () {
            var defaults = publicMethods.getDefaults();
            var i;
            var onDraggingKeys = Object.keys(defaults.styles.draggables.onDragging);
            var onStartKeys = Object.keys(defaults.styles.draggables.onStart);
            var onEnterKeys = Object.keys(defaults.styles.droppables.onEnter);
            var onLeaveKeys = Object.keys(defaults.styles.droppables.onLeave);

            if (!angular.equals({}, this.currentDragElement)) {
              //Reset Draggables
              for (i = 0; i < onDraggingKeys.length; i++) {
                this.currentDragElement.css(onDraggingKeys[i], "");
              }
              for (i = 0; i < onStartKeys.length; i++) {
                this.currentDragElement.css(onStartKeys[i], "");
              }
            }
            
            if (!angular.equals({}, this.currentDropElement)) {
              //Reset Droppables
              for (i = 0; i < onEnterKeys.length; i++) {
                this.currentDropElement.css(onEnterKeys[i], "");
              }
              for (i = 0; i < onLeaveKeys.length; i++) {
                this.currentDropElement.css(onLeaveKeys[i], "");
              }
            }
            
          },
          bindDragEvents: function (scope, dragElems, listeners) {
            var self = this;
            var defaults = publicMethods.getDefaults();

            var onDragStart = function (event) {
              self.currentDragElement = angular.element(event.target);
              angular.element(event.target).css(defaults.styles.draggables.onStart);
            };

            var onDrag = function (event) {
              angular.element(event.target).css(defaults.styles.draggables.onDragging);
              listeners.onDrag({
                event : event
              });
              return true;
            };

            var onDragEnd = function (event) {
              self.resetStyles();
            };

            for (var i = 0; i < dragElems.length; i++) {
              dragElems[i].addEventListener('dragstart', onDragStart);
              dragElems[i].addEventListener('drag', onDrag);
              dragElems[i].addEventListener('dragend', onDragEnd);
            }
          },
          bindDropEvents: function (scope, dropZone, listeners) {
            var self = this;
            var defaults = publicMethods.getDefaults();
            
            var onDragEnter = function (event) {
              self.currentDropElement = angular.element(event.target);
              angular.element(event.target).css(defaults.styles.droppables.onEnter);
              event.preventDefault();
            };

            var onDragOver = function (event) {
              listeners.onOver({event: event});
              //Allow moves
              event.dataTransfer.dropEffect = "move";
              event.preventDefault();
              return false; 
            };

            var onDragLeave = function (event) {
              angular.element(event.target).css(defaults.styles.droppables.onLeave);
            };

            var onDrop = function (event) {
              event.preventDefault();
              listeners.onDrop({event: event});
              $timeout(function() { //Reset Drag/Drop Element
                self.currentDropElement = {}; 
                self.currentDragElement = {};
              }, 10);
             
            };

            dropZone.addEventListener('drop', onDrop);
            dropZone.addEventListener('dragenter', onDragEnter);
            dropZone.addEventListener('dragleave', onDragLeave);
            dropZone.addEventListener('dragover', onDragOver);
          },
          setDraggable: function(scope, elem, listeners) {
            var dragItems = angular.element(elem.querySelectorAll("[dragIt='true']"));
            dragItems.attr("draggable", true);
            this.bindDragEvents(scope, dragItems, listeners);
            return dragItems;
          },
          setDroppable: function (scope, elem, listeners) {
            var style = elem.attr("dropTo");
            this.bindDropEvents(scope, document.querySelectorAll(style)[0], listeners);
          }
        };

        var publicMethods = {
          setup: function(opts) {
            angular.extend(defaults, opts);
            var options = this.getDefaults();
            privateMethods.setDraggable(options.scope, options.elem[0], options.listeners);
            privateMethods.setDroppable(options.scope, options.elem, options.listeners);
          },
          getDefaults: function () {
              return defaults;
          },
          getCurrentDragElement : function () {
            return privateMethods.currentDragElement;
          }
        };

        return publicMethods;
      }
    ];
  });

  module.directive('ngDragDrop', ['ngDragDrop', '$parse', function(ngDragDrop, $parse) {
    return {
      restrict: 'EA',
      scope: {
        onDrag: '&',
        onDrop: '&',
        onOver: '&'
      },
      link: function(scope, elem, attrs) {
        var ngDragDropScope = angular.isDefined(scope.ngDragDropScope) ? scope.ngDragDropScope : 'noScope';
        var defaults = ngDragDrop.getDefaults();
        ngDragDrop.setup({
          elem : elem,
          attrs : attrs,
          scope: scope,
          listeners : {
            onDrag : scope.onDrag || function () {}, 
            onDrop : scope.onDrop || function () {},
            onOver : scope.onOver || function () {}
          },
          styles : JSON.parse(attrs.styles) || defaults.styles
        });
      }
    };
  }]);

})(window, window.angular);