2 * jQuery UI Droppable 1.7.1
4 * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT (MIT-LICENSE.txt)
6 * and GPL (GPL-LICENSE.txt) licenses.
8 * http://docs.jquery.com/UI/Droppables
16 $.widget("ui.droppable", {
20 var o
= this.options
, accept
= o
.accept
;
21 this.isover
= 0; this.isout
= 1;
23 this.options
.accept
= this.options
.accept
&& $.isFunction(this.options
.accept
) ? this.options
.accept : function(d
) {
27 //Store the droppable's proportions
28 this.proportions
= { width
: this.element
[0].offsetWidth
, height
: this.element
[0].offsetHeight
};
30 // Add the reference and positions to the manager
31 $.ui
.ddmanager
.droppables
[this.options
.scope
] = $.ui
.ddmanager
.droppables
[this.options
.scope
] || [];
32 $.ui
.ddmanager
.droppables
[this.options
.scope
].push(this);
34 (this.options
.addClasses
&& this.element
.addClass("ui-droppable"));
39 var drop
= $.ui
.ddmanager
.droppables
[this.options
.scope
];
40 for ( var i
= 0; i
< drop
.length
; i
++ )
41 if ( drop
[i
] == this )
45 .removeClass("ui-droppable ui-droppable-disabled")
46 .removeData("droppable")
47 .unbind(".droppable");
50 _setData: function(key
, value
) {
53 this.options
.accept
= value
&& $.isFunction(value
) ? value : function(d
) {
57 $.widget
.prototype._setData
.apply(this, arguments
);
62 _activate: function(event
) {
63 var draggable
= $.ui
.ddmanager
.current
;
64 if(this.options
.activeClass
) this.element
.addClass(this.options
.activeClass
);
65 (draggable
&& this._trigger('activate', event
, this.ui(draggable
)));
68 _deactivate: function(event
) {
69 var draggable
= $.ui
.ddmanager
.current
;
70 if(this.options
.activeClass
) this.element
.removeClass(this.options
.activeClass
);
71 (draggable
&& this._trigger('deactivate', event
, this.ui(draggable
)));
74 _over: function(event
) {
76 var draggable
= $.ui
.ddmanager
.current
;
77 if (!draggable
|| (draggable
.currentItem
|| draggable
.element
)[0] == this.element
[0]) return; // Bail if draggable and droppable are same element
79 if (this.options
.accept
.call(this.element
[0],(draggable
.currentItem
|| draggable
.element
))) {
80 if(this.options
.hoverClass
) this.element
.addClass(this.options
.hoverClass
);
81 this._trigger('over', event
, this.ui(draggable
));
86 _out: function(event
) {
88 var draggable
= $.ui
.ddmanager
.current
;
89 if (!draggable
|| (draggable
.currentItem
|| draggable
.element
)[0] == this.element
[0]) return; // Bail if draggable and droppable are same element
91 if (this.options
.accept
.call(this.element
[0],(draggable
.currentItem
|| draggable
.element
))) {
92 if(this.options
.hoverClass
) this.element
.removeClass(this.options
.hoverClass
);
93 this._trigger('out', event
, this.ui(draggable
));
98 _drop: function(event
,custom
) {
100 var draggable
= custom
|| $.ui
.ddmanager
.current
;
101 if (!draggable
|| (draggable
.currentItem
|| draggable
.element
)[0] == this.element
[0]) return false; // Bail if draggable and droppable are same element
103 var childrenIntersection
= false;
104 this.element
.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
105 var inst
= $.data(this, 'droppable');
106 if(inst
.options
.greedy
&& $.ui
.intersect(draggable
, $.extend(inst
, { offset
: inst
.element
.offset() }), inst
.options
.tolerance
)) {
107 childrenIntersection
= true; return false;
110 if(childrenIntersection
) return false;
112 if(this.options
.accept
.call(this.element
[0],(draggable
.currentItem
|| draggable
.element
))) {
113 if(this.options
.activeClass
) this.element
.removeClass(this.options
.activeClass
);
114 if(this.options
.hoverClass
) this.element
.removeClass(this.options
.hoverClass
);
115 this._trigger('drop', event
, this.ui(draggable
));
125 draggable
: (c
.currentItem
|| c
.element
),
127 position
: c
.position
,
128 absolutePosition
: c
.positionAbs
, //deprecated
129 offset
: c
.positionAbs
135 $.extend($.ui
.droppable
, {
145 tolerance
: 'intersect'
149 $.ui
.intersect = function(draggable
, droppable
, toleranceMode
) {
151 if (!droppable
.offset
) return false;
153 var x1
= (draggable
.positionAbs
|| draggable
.position
.absolute
).left
, x2
= x1
+ draggable
.helperProportions
.width
,
154 y1
= (draggable
.positionAbs
|| draggable
.position
.absolute
).top
, y2
= y1
+ draggable
.helperProportions
.height
;
155 var l
= droppable
.offset
.left
, r
= l
+ droppable
.proportions
.width
,
156 t
= droppable
.offset
.top
, b
= t
+ droppable
.proportions
.height
;
158 switch (toleranceMode
) {
160 return (l
< x1
&& x2
< r
161 && t
< y1
&& y2
< b
);
164 return (l
< x1
+ (draggable
.helperProportions
.width
/ 2) // Right Half
165 && x2
- (draggable
.helperProportions
.width
/ 2) < r
// Left Half
166 && t
< y1
+ (draggable
.helperProportions
.height
/ 2) // Bottom Half
167 && y2
- (draggable
.helperProportions
.height
/ 2) < b
); // Top Half
170 var draggableLeft
= ((draggable
.positionAbs
|| draggable
.position
.absolute
).left
+ (draggable
.clickOffset
|| draggable
.offset
.click
).left
),
171 draggableTop
= ((draggable
.positionAbs
|| draggable
.position
.absolute
).top
+ (draggable
.clickOffset
|| draggable
.offset
.click
).top
),
172 isOver
= $.ui
.isOver(draggableTop
, draggableLeft
, t
, l
, droppable
.proportions
.height
, droppable
.proportions
.width
);
177 (y1
>= t
&& y1
<= b
) || // Top edge touching
178 (y2
>= t
&& y2
<= b
) || // Bottom edge touching
179 (y1
< t
&& y2
> b
) // Surrounded vertically
181 (x1
>= l
&& x1
<= r
) || // Left edge touching
182 (x2
>= l
&& x2
<= r
) || // Right edge touching
183 (x1
< l
&& x2
> r
) // Surrounded horizontally
194 This manager tracks offsets of draggables and droppables
198 droppables
: { 'default': [] },
199 prepareOffsets: function(t
, event
) {
201 var m
= $.ui
.ddmanager
.droppables
[t
.options
.scope
];
202 var type
= event
? event
.type
: null; // workaround for #2317
203 var list
= (t
.currentItem
|| t
.element
).find(":data(droppable)").andSelf();
205 droppablesLoop
: for (var i
= 0; i
< m
.length
; i
++) {
207 if(m
[i
].options
.disabled
|| (t
&& !m
[i
].options
.accept
.call(m
[i
].element
[0],(t
.currentItem
|| t
.element
)))) continue; //No disabled and non-accepted
208 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
209 m
[i
].visible
= m
[i
].element
.css("display") != "none"; if(!m
[i
].visible
) continue; //If the element is not visible, continue
211 m
[i
].offset
= m
[i
].element
.offset();
212 m
[i
].proportions
= { width
: m
[i
].element
[0].offsetWidth
, height
: m
[i
].element
[0].offsetHeight
};
214 if(type
== "mousedown") m
[i
]._activate
.call(m
[i
], event
); //Activate the droppable if used directly from draggables
219 drop: function(draggable
, event
) {
222 $.each($.ui
.ddmanager
.droppables
[draggable
.options
.scope
], function() {
224 if(!this.options
) return;
225 if (!this.options
.disabled
&& this.visible
&& $.ui
.intersect(draggable
, this, this.options
.tolerance
))
226 dropped
= this._drop
.call(this, event
);
228 if (!this.options
.disabled
&& this.visible
&& this.options
.accept
.call(this.element
[0],(draggable
.currentItem
|| draggable
.element
))) {
229 this.isout
= 1; this.isover
= 0;
230 this._deactivate
.call(this, event
);
237 drag: function(draggable
, event
) {
239 //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
240 if(draggable
.options
.refreshPositions
) $.ui
.ddmanager
.prepareOffsets(draggable
, event
);
242 //Run through all droppables and check their positions based on specific tolerance options
244 $.each($.ui
.ddmanager
.droppables
[draggable
.options
.scope
], function() {
246 if(this.options
.disabled
|| this.greedyChild
|| !this.visible
) return;
247 var intersects
= $.ui
.intersect(draggable
, this, this.options
.tolerance
);
249 var c
= !intersects
&& this.isover
== 1 ? 'isout' : (intersects
&& this.isover
== 0 ? 'isover' : null);
253 if (this.options
.greedy
) {
254 var parent
= this.element
.parents(':data(droppable):eq(0)');
256 parentInstance
= $.data(parent
[0], 'droppable');
257 parentInstance
.greedyChild
= (c
== 'isover' ? 1 : 0);
261 // we just moved into a greedy child
262 if (parentInstance
&& c
== 'isover') {
263 parentInstance
['isover'] = 0;
264 parentInstance
['isout'] = 1;
265 parentInstance
._out
.call(parentInstance
, event
);
268 this[c
] = 1; this[c
== 'isout' ? 'isover' : 'isout'] = 0;
269 this[c
== "isover" ? "_over" : "_out"].call(this, event
);
271 // we just moved out of a greedy child
272 if (parentInstance
&& c
== 'isout') {
273 parentInstance
['isout'] = 0;
274 parentInstance
['isover'] = 1;
275 parentInstance
._over
.call(parentInstance
, event
);