jquery-ui.droppable-1.6rc2.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * jQuery UI Droppable @VERSION
  3. *
  4. * Copyright (c) 2008 Paul Bakaus
  5. * Dual licensed under the MIT (MIT-LICENSE.txt)
  6. * and GPL (GPL-LICENSE.txt) licenses.
  7. *
  8. * http://docs.jquery.com/UI/Droppables
  9. *
  10. * Depends:
  11. * ui.core.js
  12. * ui.draggable.js
  13. */
  14. (function($) {
  15. $.widget("ui.droppable", {
  16. _setData: function(key, value) {
  17. if(key == 'accept') {
  18. this.options.accept = value && $.isFunction(value) ? value : function(d) {
  19. return d.is(accept);
  20. };
  21. } else {
  22. $.widget.prototype._setData.apply(this, arguments);
  23. }
  24. },
  25. _init: function() {
  26. var o = this.options, accept = o.accept;
  27. this.isover = 0; this.isout = 1;
  28. this.options.accept = this.options.accept && $.isFunction(this.options.accept) ? this.options.accept : function(d) {
  29. return d.is(accept);
  30. };
  31. //Store the droppable's proportions
  32. this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
  33. // Add the reference and positions to the manager
  34. $.ui.ddmanager.droppables[this.options.scope] = $.ui.ddmanager.droppables[this.options.scope] || [];
  35. $.ui.ddmanager.droppables[this.options.scope].push(this);
  36. (this.options.cssNamespace && this.element.addClass(this.options.cssNamespace+"-droppable"));
  37. },
  38. plugins: {},
  39. ui: function(c) {
  40. return {
  41. draggable: (c.currentItem || c.element),
  42. helper: c.helper,
  43. position: c.position,
  44. absolutePosition: c.positionAbs,
  45. options: this.options,
  46. element: this.element
  47. };
  48. },
  49. destroy: function() {
  50. var drop = $.ui.ddmanager.droppables[this.options.scope];
  51. for ( var i = 0; i < drop.length; i++ )
  52. if ( drop[i] == this )
  53. drop.splice(i, 1);
  54. this.element
  55. .removeClass("ui-droppable-disabled")
  56. .removeData("droppable")
  57. .unbind(".droppable");
  58. },
  59. _over: function(e) {
  60. var draggable = $.ui.ddmanager.current;
  61. if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
  62. if (this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
  63. $.ui.plugin.call(this, 'over', [e, this.ui(draggable)]);
  64. this.element.triggerHandler("dropover", [e, this.ui(draggable)], this.options.over);
  65. }
  66. },
  67. _out: function(e) {
  68. var draggable = $.ui.ddmanager.current;
  69. if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
  70. if (this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
  71. $.ui.plugin.call(this, 'out', [e, this.ui(draggable)]);
  72. this.element.triggerHandler("dropout", [e, this.ui(draggable)], this.options.out);
  73. }
  74. },
  75. _drop: function(e,custom) {
  76. var draggable = custom || $.ui.ddmanager.current;
  77. if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
  78. var childrenIntersection = false;
  79. this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
  80. var inst = $.data(this, 'droppable');
  81. if(inst.options.greedy && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)) {
  82. childrenIntersection = true; return false;
  83. }
  84. });
  85. if(childrenIntersection) return false;
  86. if(this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
  87. $.ui.plugin.call(this, 'drop', [e, this.ui(draggable)]);
  88. this.element.triggerHandler("drop", [e, this.ui(draggable)], this.options.drop);
  89. return this.element;
  90. }
  91. return false;
  92. },
  93. _activate: function(e) {
  94. var draggable = $.ui.ddmanager.current;
  95. $.ui.plugin.call(this, 'activate', [e, this.ui(draggable)]);
  96. if(draggable) this.element.triggerHandler("dropactivate", [e, this.ui(draggable)], this.options.activate);
  97. },
  98. _deactivate: function(e) {
  99. var draggable = $.ui.ddmanager.current;
  100. $.ui.plugin.call(this, 'deactivate', [e, this.ui(draggable)]);
  101. if(draggable) this.element.triggerHandler("dropdeactivate", [e, this.ui(draggable)], this.options.deactivate);
  102. }
  103. });
  104. $.extend($.ui.droppable, {
  105. defaults: {
  106. disabled: false,
  107. tolerance: 'intersect',
  108. scope: 'default',
  109. cssNamespace: 'ui'
  110. }
  111. });
  112. $.ui.intersect = function(draggable, droppable, toleranceMode) {
  113. if (!droppable.offset) return false;
  114. var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
  115. y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
  116. var l = droppable.offset.left, r = l + droppable.proportions.width,
  117. t = droppable.offset.top, b = t + droppable.proportions.height;
  118. switch (toleranceMode) {
  119. case 'fit':
  120. return (l < x1 && x2 < r
  121. && t < y1 && y2 < b);
  122. break;
  123. case 'intersect':
  124. return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
  125. && x2 - (draggable.helperProportions.width / 2) < r // Left Half
  126. && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
  127. && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
  128. break;
  129. case 'pointer':
  130. return (l < ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left) && ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left) < r
  131. && t < ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top) && ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top) < b);
  132. break;
  133. case 'touch':
  134. return (
  135. (y1 >= t && y1 <= b) || // Top edge touching
  136. (y2 >= t && y2 <= b) || // Bottom edge touching
  137. (y1 < t && y2 > b) // Surrounded vertically
  138. ) && (
  139. (x1 >= l && x1 <= r) || // Left edge touching
  140. (x2 >= l && x2 <= r) || // Right edge touching
  141. (x1 < l && x2 > r) // Surrounded horizontally
  142. );
  143. break;
  144. default:
  145. return false;
  146. break;
  147. }
  148. };
  149. /*
  150. This manager tracks offsets of draggables and droppables
  151. */
  152. $.ui.ddmanager = {
  153. current: null,
  154. droppables: { 'default': [] },
  155. prepareOffsets: function(t, e) {
  156. var m = $.ui.ddmanager.droppables[t.options.scope];
  157. var type = e ? e.type : null; // workaround for #2317
  158. var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
  159. droppablesLoop: for (var i = 0; i < m.length; i++) {
  160. if(m[i].options.disabled || (t && !m[i].options.accept.call(m[i].element,(t.currentItem || t.element)))) continue; //No disabled and non-accepted
  161. for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
  162. m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
  163. m[i].offset = m[i].element.offset();
  164. m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
  165. if(type == "dragstart" || type == "sortactivate") m[i]._activate.call(m[i], e); //Activate the droppable if used directly from draggables
  166. }
  167. },
  168. drop: function(draggable, e) {
  169. var dropped = false;
  170. $.each($.ui.ddmanager.droppables[draggable.options.scope], function() {
  171. if(!this.options) return;
  172. if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
  173. dropped = this._drop.call(this, e);
  174. if (!this.options.disabled && this.visible && this.options.accept.call(this.element,(draggable.currentItem || draggable.element))) {
  175. this.isout = 1; this.isover = 0;
  176. this._deactivate.call(this, e);
  177. }
  178. });
  179. return dropped;
  180. },
  181. drag: function(draggable, e) {
  182. //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
  183. if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, e);
  184. //Run through all droppables and check their positions based on specific tolerance options
  185. $.each($.ui.ddmanager.droppables[draggable.options.scope], function() {
  186. if(this.options.disabled || this.greedyChild || !this.visible) return;
  187. var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
  188. var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
  189. if(!c) return;
  190. var parentInstance;
  191. if (this.options.greedy) {
  192. var parent = this.element.parents(':data(droppable):eq(0)');
  193. if (parent.length) {
  194. parentInstance = $.data(parent[0], 'droppable');
  195. parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
  196. }
  197. }
  198. // we just moved into a greedy child
  199. if (parentInstance && c == 'isover') {
  200. parentInstance['isover'] = 0;
  201. parentInstance['isout'] = 1;
  202. parentInstance._out.call(parentInstance, e);
  203. }
  204. this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
  205. this[c == "isover" ? "_over" : "_out"].call(this, e);
  206. // we just moved out of a greedy child
  207. if (parentInstance && c == 'isout') {
  208. parentInstance['isout'] = 0;
  209. parentInstance['isover'] = 1;
  210. parentInstance._over.call(parentInstance, e);
  211. }
  212. });
  213. }
  214. };
  215. /*
  216. * Droppable Extensions
  217. */
  218. $.ui.plugin.add("droppable", "activeClass", {
  219. activate: function(e, ui) {
  220. $(this).addClass(ui.options.activeClass);
  221. },
  222. deactivate: function(e, ui) {
  223. $(this).removeClass(ui.options.activeClass);
  224. },
  225. drop: function(e, ui) {
  226. $(this).removeClass(ui.options.activeClass);
  227. }
  228. });
  229. $.ui.plugin.add("droppable", "hoverClass", {
  230. over: function(e, ui) {
  231. $(this).addClass(ui.options.hoverClass);
  232. },
  233. out: function(e, ui) {
  234. $(this).removeClass(ui.options.hoverClass);
  235. },
  236. drop: function(e, ui) {
  237. $(this).removeClass(ui.options.hoverClass);
  238. }
  239. });
  240. })(jQuery);