1 /* |
|
2 * jQuery UI Droppable 1.8.1 |
|
3 * |
|
4 * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) |
|
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 * jquery.ui.core.js |
|
12 * jquery.ui.widget.js |
|
13 * jquery.ui.mouse.js |
|
14 * jquery.ui.draggable.js |
|
15 */ |
|
16 (function($) { |
|
17 |
|
18 $.widget("ui.droppable", { |
|
19 widgetEventPrefix: "drop", |
|
20 options: { |
|
21 accept: '*', |
|
22 activeClass: false, |
|
23 addClasses: true, |
|
24 greedy: false, |
|
25 hoverClass: false, |
|
26 scope: 'default', |
|
27 tolerance: 'intersect' |
|
28 }, |
|
29 _create: function() { |
|
30 |
|
31 var o = this.options, accept = o.accept; |
|
32 this.isover = 0; this.isout = 1; |
|
33 |
|
34 this.accept = $.isFunction(accept) ? accept : function(d) { |
|
35 return d.is(accept); |
|
36 }; |
|
37 |
|
38 //Store the droppable's proportions |
|
39 this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight }; |
|
40 |
|
41 // Add the reference and positions to the manager |
|
42 $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || []; |
|
43 $.ui.ddmanager.droppables[o.scope].push(this); |
|
44 |
|
45 (o.addClasses && this.element.addClass("ui-droppable")); |
|
46 |
|
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 |
|
55 this.element |
|
56 .removeClass("ui-droppable ui-droppable-disabled") |
|
57 .removeData("droppable") |
|
58 .unbind(".droppable"); |
|
59 |
|
60 return this; |
|
61 }, |
|
62 |
|
63 _setOption: function(key, value) { |
|
64 |
|
65 if(key == 'accept') { |
|
66 this.accept = $.isFunction(value) ? value : function(d) { |
|
67 return d.is(value); |
|
68 }; |
|
69 } |
|
70 $.Widget.prototype._setOption.apply(this, arguments); |
|
71 }, |
|
72 |
|
73 _activate: function(event) { |
|
74 var draggable = $.ui.ddmanager.current; |
|
75 if(this.options.activeClass) this.element.addClass(this.options.activeClass); |
|
76 (draggable && this._trigger('activate', event, this.ui(draggable))); |
|
77 }, |
|
78 |
|
79 _deactivate: function(event) { |
|
80 var draggable = $.ui.ddmanager.current; |
|
81 if(this.options.activeClass) this.element.removeClass(this.options.activeClass); |
|
82 (draggable && this._trigger('deactivate', event, this.ui(draggable))); |
|
83 }, |
|
84 |
|
85 _over: function(event) { |
|
86 |
|
87 var draggable = $.ui.ddmanager.current; |
|
88 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element |
|
89 |
|
90 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { |
|
91 if(this.options.hoverClass) this.element.addClass(this.options.hoverClass); |
|
92 this._trigger('over', event, this.ui(draggable)); |
|
93 } |
|
94 |
|
95 }, |
|
96 |
|
97 _out: function(event) { |
|
98 |
|
99 var draggable = $.ui.ddmanager.current; |
|
100 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element |
|
101 |
|
102 if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { |
|
103 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass); |
|
104 this._trigger('out', event, this.ui(draggable)); |
|
105 } |
|
106 |
|
107 }, |
|
108 |
|
109 _drop: function(event,custom) { |
|
110 |
|
111 var draggable = custom || $.ui.ddmanager.current; |
|
112 if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element |
|
113 |
|
114 var childrenIntersection = false; |
|
115 this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() { |
|
116 var inst = $.data(this, 'droppable'); |
|
117 if( |
|
118 inst.options.greedy |
|
119 && !inst.options.disabled |
|
120 && inst.options.scope == draggable.options.scope |
|
121 && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) |
|
122 && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance) |
|
123 ) { childrenIntersection = true; return false; } |
|
124 }); |
|
125 if(childrenIntersection) return false; |
|
126 |
|
127 if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { |
|
128 if(this.options.activeClass) this.element.removeClass(this.options.activeClass); |
|
129 if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass); |
|
130 this._trigger('drop', event, this.ui(draggable)); |
|
131 return this.element; |
|
132 } |
|
133 |
|
134 return false; |
|
135 |
|
136 }, |
|
137 |
|
138 ui: function(c) { |
|
139 return { |
|
140 draggable: (c.currentItem || c.element), |
|
141 helper: c.helper, |
|
142 position: c.position, |
|
143 offset: c.positionAbs |
|
144 }; |
|
145 } |
|
146 |
|
147 }); |
|
148 |
|
149 $.extend($.ui.droppable, { |
|
150 version: "1.8.1" |
|
151 }); |
|
152 |
|
153 $.ui.intersect = function(draggable, droppable, toleranceMode) { |
|
154 |
|
155 if (!droppable.offset) return false; |
|
156 |
|
157 var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width, |
|
158 y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height; |
|
159 var l = droppable.offset.left, r = l + droppable.proportions.width, |
|
160 t = droppable.offset.top, b = t + droppable.proportions.height; |
|
161 |
|
162 switch (toleranceMode) { |
|
163 case 'fit': |
|
164 return (l < x1 && x2 < r |
|
165 && t < y1 && y2 < b); |
|
166 break; |
|
167 case 'intersect': |
|
168 return (l < x1 + (draggable.helperProportions.width / 2) // Right Half |
|
169 && x2 - (draggable.helperProportions.width / 2) < r // Left Half |
|
170 && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half |
|
171 && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half |
|
172 break; |
|
173 case 'pointer': |
|
174 var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left), |
|
175 draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top), |
|
176 isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width); |
|
177 return isOver; |
|
178 break; |
|
179 case 'touch': |
|
180 return ( |
|
181 (y1 >= t && y1 <= b) || // Top edge touching |
|
182 (y2 >= t && y2 <= b) || // Bottom edge touching |
|
183 (y1 < t && y2 > b) // Surrounded vertically |
|
184 ) && ( |
|
185 (x1 >= l && x1 <= r) || // Left edge touching |
|
186 (x2 >= l && x2 <= r) || // Right edge touching |
|
187 (x1 < l && x2 > r) // Surrounded horizontally |
|
188 ); |
|
189 break; |
|
190 default: |
|
191 return false; |
|
192 break; |
|
193 } |
|
194 |
|
195 }; |
|
196 |
|
197 /* |
|
198 This manager tracks offsets of draggables and droppables |
|
199 */ |
|
200 $.ui.ddmanager = { |
|
201 current: null, |
|
202 droppables: { 'default': [] }, |
|
203 prepareOffsets: function(t, event) { |
|
204 |
|
205 var m = $.ui.ddmanager.droppables[t.options.scope] || []; |
|
206 var type = event ? event.type : null; // workaround for #2317 |
|
207 var list = (t.currentItem || t.element).find(":data(droppable)").andSelf(); |
|
208 |
|
209 droppablesLoop: for (var i = 0; i < m.length; i++) { |
|
210 |
|
211 if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted |
|
212 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 |
|
213 m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue |
|
214 |
|
215 m[i].offset = m[i].element.offset(); |
|
216 m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight }; |
|
217 |
|
218 if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables |
|
219 |
|
220 } |
|
221 |
|
222 }, |
|
223 drop: function(draggable, event) { |
|
224 |
|
225 var dropped = false; |
|
226 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { |
|
227 |
|
228 if(!this.options) return; |
|
229 if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) |
|
230 dropped = dropped || this._drop.call(this, event); |
|
231 |
|
232 if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { |
|
233 this.isout = 1; this.isover = 0; |
|
234 this._deactivate.call(this, event); |
|
235 } |
|
236 |
|
237 }); |
|
238 return dropped; |
|
239 |
|
240 }, |
|
241 drag: function(draggable, event) { |
|
242 |
|
243 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse. |
|
244 if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event); |
|
245 |
|
246 //Run through all droppables and check their positions based on specific tolerance options |
|
247 $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() { |
|
248 |
|
249 if(this.options.disabled || this.greedyChild || !this.visible) return; |
|
250 var intersects = $.ui.intersect(draggable, this, this.options.tolerance); |
|
251 |
|
252 var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null); |
|
253 if(!c) return; |
|
254 |
|
255 var parentInstance; |
|
256 if (this.options.greedy) { |
|
257 var parent = this.element.parents(':data(droppable):eq(0)'); |
|
258 if (parent.length) { |
|
259 parentInstance = $.data(parent[0], 'droppable'); |
|
260 parentInstance.greedyChild = (c == 'isover' ? 1 : 0); |
|
261 } |
|
262 } |
|
263 |
|
264 // we just moved into a greedy child |
|
265 if (parentInstance && c == 'isover') { |
|
266 parentInstance['isover'] = 0; |
|
267 parentInstance['isout'] = 1; |
|
268 parentInstance._out.call(parentInstance, event); |
|
269 } |
|
270 |
|
271 this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0; |
|
272 this[c == "isover" ? "_over" : "_out"].call(this, event); |
|
273 |
|
274 // we just moved out of a greedy child |
|
275 if (parentInstance && c == 'isout') { |
|
276 parentInstance['isout'] = 0; |
|
277 parentInstance['isover'] = 1; |
|
278 parentInstance._over.call(parentInstance, event); |
|
279 } |
|
280 }); |
|
281 |
|
282 } |
|
283 }; |
|
284 |
|
285 })(jQuery); |
|