angular-dragdrop.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /**
  2. * Created with IntelliJ IDEA.
  3. * User: Ganaraj.Pr
  4. * Date: 11/10/13
  5. * Time: 11:27
  6. * To change this template use File | Settings | File Templates.
  7. */
  8. (function(angular){
  9. function isDnDsSupported(){
  10. return 'ondrag' in document.createElement("a");
  11. }
  12. if(!isDnDsSupported()){
  13. angular.module("ang-drag-drop", []);
  14. return;
  15. }
  16. if (window.jQuery && (-1 == window.jQuery.event.props.indexOf("dataTransfer"))) {
  17. window.jQuery.event.props.push("dataTransfer");
  18. }
  19. var currentData;
  20. angular.module("ang-drag-drop",[])
  21. .directive("uiDraggable", [
  22. '$parse',
  23. '$rootScope',
  24. '$dragImage',
  25. function ($parse, $rootScope, $dragImage) {
  26. return function (scope, element, attrs) {
  27. var dragData = "",
  28. isDragHandleUsed = false,
  29. dragHandleClass,
  30. draggingClass = attrs.draggingClass || "on-dragging",
  31. dragTarget;
  32. element.attr("draggable", false);
  33. attrs.$observe("uiDraggable", function (newValue) {
  34. console.log(newValue);
  35. if(newValue){
  36. element.attr("draggable", newValue);
  37. }
  38. else{
  39. element.removeAttr("draggable");
  40. }
  41. });
  42. if (attrs.drag) {
  43. scope.$watch(attrs.drag, function (newValue) {
  44. dragData = newValue || "";
  45. });
  46. }
  47. if (angular.isString(attrs.dragHandleClass)) {
  48. isDragHandleUsed = true;
  49. dragHandleClass = attrs.dragHandleClass.trim() || "drag-handle";
  50. element.bind("mousedown", function (e) {
  51. dragTarget = e.target;
  52. });
  53. }
  54. function dragendHandler(e) {
  55. setTimeout(function() {
  56. element.unbind('$destroy', dragendHandler);
  57. }, 0);
  58. var sendChannel = attrs.dragChannel || "defaultchannel";
  59. $rootScope.$broadcast("ANGULAR_DRAG_END", sendChannel);
  60. if (e.dataTransfer && e.dataTransfer.dropEffect !== "none") {
  61. if (attrs.onDropSuccess) {
  62. var fn = $parse(attrs.onDropSuccess);
  63. scope.$evalAsync(function () {
  64. fn(scope, {$event: e});
  65. });
  66. } else {
  67. if (attrs.onDropFailure) {
  68. var fn = $parse(attrs.onDropFailure);
  69. scope.$evalAsync(function () {
  70. fn(scope, {$event: e});
  71. });
  72. }
  73. }
  74. }
  75. element.removeClass(draggingClass);
  76. }
  77. element.bind("dragend", dragendHandler);
  78. element.bind("dragstart", function (e) {
  79. var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);
  80. if (isDragAllowed) {
  81. var sendChannel = attrs.dragChannel || "defaultchannel";
  82. var sendData = angular.toJson({ data: dragData, channel: sendChannel });
  83. var dragImage = attrs.dragImage || null;
  84. element.addClass(draggingClass);
  85. element.bind('$destroy', dragendHandler);
  86. if (dragImage) {
  87. var dragImageFn = $parse(attrs.dragImage);
  88. scope.$evalAsync(function() {
  89. var dragImageParameters = dragImageFn(scope, {$event: e});
  90. if (dragImageParameters) {
  91. if (angular.isString(dragImageParameters)) {
  92. dragImageParameters = $dragImage.generate(dragImageParameters);
  93. }
  94. if (dragImageParameters.image) {
  95. var xOffset = dragImageParameters.xOffset || 0,
  96. yOffset = dragImageParameters.yOffset || 0;
  97. e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
  98. }
  99. }
  100. });
  101. }
  102. e.dataTransfer.setData("dataToSend", sendData);
  103. currentData = angular.fromJson(sendData);
  104. e.dataTransfer.effectAllowed = "copyMove";
  105. $rootScope.$broadcast("ANGULAR_DRAG_START", sendChannel, currentData.data);
  106. }
  107. else {
  108. e.preventDefault();
  109. }
  110. });
  111. };
  112. }
  113. ])
  114. .directive("uiOnDrop", [
  115. '$parse',
  116. '$rootScope',
  117. function ($parse, $rootScope) {
  118. return function (scope, element, attr) {
  119. var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
  120. var dropChannel = attr.dropChannel || "defaultchannel" ;
  121. var dragChannel = "";
  122. var dragEnterClass = attr.dragEnterClass || "on-drag-enter";
  123. var dragHoverClass = attr.dragHoverClass || "on-drag-hover";
  124. var customDragEnterEvent = $parse(attr.onDragEnter);
  125. var customDragLeaveEvent = $parse(attr.onDragLeave);
  126. function onDragOver(e) {
  127. if (e.preventDefault) {
  128. e.preventDefault(); // Necessary. Allows us to drop.
  129. }
  130. if (e.stopPropagation) {
  131. e.stopPropagation();
  132. }
  133. var fn = $parse(attr.uiOnDragOver);
  134. scope.$evalAsync(function () {
  135. fn(scope, {$event: e, $channel: dropChannel});
  136. });
  137. e.dataTransfer.dropEffect = e.shiftKey ? 'copy' : 'move';
  138. return false;
  139. }
  140. function onDragLeave(e) {
  141. if (e.preventDefault) {
  142. e.preventDefault();
  143. }
  144. if (e.stopPropagation) {
  145. e.stopPropagation();
  146. }
  147. dragging--;
  148. if (dragging == 0) {
  149. scope.$evalAsync(function () {
  150. customDragEnterEvent(scope, {$event: e});
  151. });
  152. element.removeClass(dragHoverClass);
  153. }
  154. var fn = $parse(attr.uiOnDragLeave);
  155. scope.$evalAsync(function () {
  156. fn(scope, {$event: e, $channel: dropChannel});
  157. });
  158. }
  159. function onDragEnter(e) {
  160. if (e.preventDefault) {
  161. e.preventDefault();
  162. }
  163. if (e.stopPropagation) {
  164. e.stopPropagation();
  165. }
  166. dragging++;
  167. var fn = $parse(attr.uiOnDragEnter);
  168. scope.$evalAsync(function () {
  169. fn(scope, {$event: e, $channel: dropChannel});
  170. });
  171. $rootScope.$broadcast("ANGULAR_HOVER", dragChannel);
  172. scope.$evalAsync(function () {
  173. customDragLeaveEvent(scope, {$event: e});
  174. });
  175. element.addClass(dragHoverClass);
  176. }
  177. function onDrop(e) {
  178. if (e.preventDefault) {
  179. e.preventDefault(); // Necessary. Allows us to drop.
  180. }
  181. if (e.stopPropagation) {
  182. e.stopPropagation(); // Necessary. Allows us to drop.
  183. }
  184. var sendData = e.dataTransfer.getData("dataToSend");
  185. sendData = angular.fromJson(sendData);
  186. var fn = $parse(attr.uiOnDrop);
  187. scope.$evalAsync(function () {
  188. fn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel});
  189. });
  190. element.removeClass(dragEnterClass);
  191. dragging = 0;
  192. }
  193. function isDragChannelAccepted(dragChannel, dropChannel) {
  194. if (dropChannel === "*") {
  195. return true;
  196. }
  197. var channelMatchPattern = new RegExp("(\\s|[,])+(" + dragChannel + ")(\\s|[,])+", "i");
  198. return channelMatchPattern.test("," + dropChannel + ",");
  199. }
  200. function preventNativeDnD(e) {
  201. if (e.preventDefault) {
  202. e.preventDefault();
  203. }
  204. if (e.stopPropagation) {
  205. e.stopPropagation();
  206. }
  207. e.dataTransfer.dropEffect = "none";
  208. return false;
  209. }
  210. var deregisterDragStart = $rootScope.$on("ANGULAR_DRAG_START", function (event, channel) {
  211. dragChannel = channel;
  212. if (isDragChannelAccepted(channel, dropChannel)) {
  213. if (attr.dropValidate) {
  214. var validateFn = $parse(attr.dropValidate);
  215. var valid = validateFn(scope, {$data: currentData.data, $channel: currentData.channel});
  216. if (!valid) {
  217. element.bind("dragover", preventNativeDnD);
  218. element.bind("dragenter", preventNativeDnD);
  219. element.bind("dragleave", preventNativeDnD);
  220. element.bind("drop", preventNativeDnD);
  221. return;
  222. }
  223. }
  224. element.bind("dragover", onDragOver);
  225. element.bind("dragenter", onDragEnter);
  226. element.bind("dragleave", onDragLeave);
  227. element.bind("drop", onDrop);
  228. element.addClass(dragEnterClass);
  229. }
  230. else {
  231. element.bind("dragover", preventNativeDnD);
  232. element.bind("dragenter", preventNativeDnD);
  233. element.bind("dragleave", preventNativeDnD);
  234. element.bind("drop", preventNativeDnD);
  235. }
  236. });
  237. var deregisterDragEnd = $rootScope.$on("ANGULAR_DRAG_END", function (e, channel) {
  238. dragChannel = "";
  239. if (isDragChannelAccepted(channel, dropChannel)) {
  240. element.unbind("dragover", onDragOver);
  241. element.unbind("dragenter", onDragEnter);
  242. element.unbind("dragleave", onDragLeave);
  243. element.unbind("drop", onDrop);
  244. element.removeClass(dragHoverClass);
  245. element.removeClass(dragEnterClass);
  246. }
  247. element.unbind("dragover", preventNativeDnD);
  248. element.unbind("dragenter", preventNativeDnD);
  249. element.unbind("dragleave", preventNativeDnD);
  250. element.unbind("drop", preventNativeDnD);
  251. });
  252. var deregisterDragHover = $rootScope.$on("ANGULAR_HOVER", function (e, channel) {
  253. if (isDragChannelAccepted(channel, dropChannel)) {
  254. element.removeClass(dragHoverClass);
  255. }
  256. });
  257. scope.$on('$destroy', function () {
  258. deregisterDragStart();
  259. deregisterDragEnd();
  260. deregisterDragHover();
  261. });
  262. attr.$observe('dropChannel', function (value) {
  263. if (value) {
  264. dropChannel = value;
  265. }
  266. });
  267. };
  268. }
  269. ])
  270. .constant("$dragImageConfig", {
  271. height: 20,
  272. width: 200,
  273. padding: 10,
  274. font: 'bold 11px Arial',
  275. fontColor: '#eee8d5',
  276. backgroundColor: '#93a1a1',
  277. xOffset: 0,
  278. yOffset: 0
  279. })
  280. .service("$dragImage", [
  281. '$dragImageConfig',
  282. function (defaultConfig) {
  283. var ELLIPSIS = '…';
  284. function fitString(canvas, text, config) {
  285. var width = canvas.measureText(text).width;
  286. if (width < config.width) {
  287. return text;
  288. }
  289. while (width + config.padding > config.width) {
  290. text = text.substring(0, text.length - 1);
  291. width = canvas.measureText(text + ELLIPSIS).width;
  292. }
  293. return text + ELLIPSIS;
  294. };
  295. this.generate = function (text, options) {
  296. var config = angular.extend({}, defaultConfig, options || {});
  297. var el = document.createElement('canvas');
  298. el.height = config.height;
  299. el.width = config.width;
  300. var canvas = el.getContext('2d');
  301. canvas.fillStyle = config.backgroundColor;
  302. canvas.fillRect(0, 0, config.width, config.height);
  303. canvas.font = config.font;
  304. canvas.fillStyle = config.fontColor;
  305. var title = fitString(canvas, text, config);
  306. canvas.fillText(title, 4, config.padding + 4);
  307. var image = new Image();
  308. image.src = el.toDataURL();
  309. return {
  310. image: image,
  311. xOffset: config.xOffset,
  312. yOffset: config.yOffset
  313. };
  314. }
  315. }
  316. ]);
  317. }(angular));