angular-dragdrop.js 12 KB

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