2 * jQuery UI Draggable 1.8.20
4 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
8 * http://docs.jquery.com/UI/Draggables
15 (function( $, undefined ) {
17 $.widget("ui.draggable", $.ui
.mouse
, {
18 widgetEventPrefix
: "drag",
23 connectToSortable
: false,
32 refreshPositions
: false,
37 scrollSensitivity
: 20,
47 if (this.options
.helper
== 'original' && !(/^(?:r|a|f)/).test(this.element
.css("position")))
48 this.element
[0].style
.position
= 'relative';
50 (this.options
.addClasses
&& this.element
.addClass("ui-draggable"));
51 (this.options
.disabled
&& this.element
.addClass("ui-draggable-disabled"));
58 if(!this.element
.data('draggable')) return;
60 .removeData("draggable")
62 .removeClass("ui-draggable"
63 + " ui-draggable-dragging"
64 + " ui-draggable-disabled");
70 _mouseCapture: function(event
) {
74 // among others, prevent a drag on a resizable-handle
75 if (this.helper
|| o
.disabled
|| $(event
.target
).is('.ui-resizable-handle'))
78 //Quit if we're not on a valid handle
79 this.handle
= this._getHandle(event
);
84 $(o
.iframeFix
=== true ? "iframe" : o
.iframeFix
).each(function() {
85 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
87 width
: this.offsetWidth
+"px", height
: this.offsetHeight
+"px",
88 position
: "absolute", opacity
: "0.001", zIndex
: 1000
90 .css($(this).offset())
99 _mouseStart: function(event
) {
101 var o
= this.options
;
103 //Create and append the visible helper
104 this.helper
= this._createHelper(event
);
106 //Cache the helper size
107 this._cacheHelperProportions();
109 //If ddmanager is used for droppables, set the global draggable
111 $.ui
.ddmanager
.current
= this;
114 * - Position generation -
115 * This block generates everything position related - it's the core of draggables.
118 //Cache the margins of the original element
119 this._cacheMargins();
121 //Store the helper's css position
122 this.cssPosition
= this.helper
.css("position");
123 this.scrollParent
= this.helper
.scrollParent();
125 //The element's absolute position on the page minus margins
126 this.offset
= this.positionAbs
= this.element
.offset();
128 top
: this.offset
.top
- this.margins
.top
,
129 left
: this.offset
.left
- this.margins
.left
132 $.extend(this.offset
, {
133 click
: { //Where the click happened, relative to the element
134 left
: event
.pageX
- this.offset
.left
,
135 top
: event
.pageY
- this.offset
.top
137 parent
: this._getParentOffset(),
138 relative
: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
141 //Generate the original position
142 this.originalPosition
= this.position
= this._generatePosition(event
);
143 this.originalPageX
= event
.pageX
;
144 this.originalPageY
= event
.pageY
;
146 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
147 (o
.cursorAt
&& this._adjustOffsetFromHelper(o
.cursorAt
));
149 //Set a containment if given in the options
151 this._setContainment();
153 //Trigger event + callbacks
154 if(this._trigger("start", event
) === false) {
159 //Recache the helper size
160 this._cacheHelperProportions();
162 //Prepare the droppable offsets
163 if ($.ui
.ddmanager
&& !o
.dropBehaviour
)
164 $.ui
.ddmanager
.prepareOffsets(this, event
);
166 this.helper
.addClass("ui-draggable-dragging");
167 this._mouseDrag(event
, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
169 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
170 if ( $.ui
.ddmanager
) $.ui
.ddmanager
.dragStart(this, event
);
175 _mouseDrag: function(event
, noPropagation
) {
177 //Compute the helpers position
178 this.position
= this._generatePosition(event
);
179 this.positionAbs
= this._convertPositionTo("absolute");
181 //Call plugins and callbacks and use the resulting position if something is returned
182 if (!noPropagation
) {
183 var ui
= this._uiHash();
184 if(this._trigger('drag', event
, ui
) === false) {
188 this.position
= ui
.position
;
191 if(!this.options
.axis
|| this.options
.axis
!= "y") this.helper
[0].style
.left
= this.position
.left
+'px';
192 if(!this.options
.axis
|| this.options
.axis
!= "x") this.helper
[0].style
.top
= this.position
.top
+'px';
193 if($.ui
.ddmanager
) $.ui
.ddmanager
.drag(this, event
);
198 _mouseStop: function(event
) {
200 //If we are using droppables, inform the manager about the drop
202 if ($.ui
.ddmanager
&& !this.options
.dropBehaviour
)
203 dropped
= $.ui
.ddmanager
.drop(this, event
);
205 //if a drop comes from outside (a sortable)
207 dropped
= this.dropped
;
208 this.dropped
= false;
211 //if the original element is no longer in the DOM don't bother to continue (see #8269)
212 var element
= this.element
[0], elementInDom
= false;
213 while ( element
&& (element
= element
.parentNode
) ) {
214 if (element
== document
) {
218 if ( !elementInDom
&& this.options
.helper
=== "original" )
221 if((this.options
.revert
== "invalid" && !dropped
) || (this.options
.revert
== "valid" && dropped
) || this.options
.revert
=== true || ($.isFunction(this.options
.revert
) && this.options
.revert
.call(this.element
, dropped
))) {
223 $(this.helper
).animate(this.originalPosition
, parseInt(this.options
.revertDuration
, 10), function() {
224 if(self
._trigger("stop", event
) !== false) {
229 if(this._trigger("stop", event
) !== false) {
237 _mouseUp: function(event
) {
238 if (this.options
.iframeFix
=== true) {
239 $("div.ui-draggable-iframeFix").each(function() {
240 this.parentNode
.removeChild(this);
241 }); //Remove frame helpers
244 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
245 if( $.ui
.ddmanager
) $.ui
.ddmanager
.dragStop(this, event
);
247 return $.ui
.mouse
.prototype._mouseUp
.call(this, event
);
252 if(this.helper
.is(".ui-draggable-dragging")) {
262 _getHandle: function(event
) {
264 var handle
= !this.options
.handle
|| !$(this.options
.handle
, this.element
).length
? true : false;
265 $(this.options
.handle
, this.element
)
269 if(this == event
.target
) handle
= true;
276 _createHelper: function(event
) {
278 var o
= this.options
;
279 var helper
= $.isFunction(o
.helper
) ? $(o
.helper
.apply(this.element
[0], [event
])) : (o
.helper
== 'clone' ? this.element
.clone().removeAttr('id') : this.element
);
281 if(!helper
.parents('body').length
)
282 helper
.appendTo((o
.appendTo
== 'parent' ? this.element
[0].parentNode
: o
.appendTo
));
284 if(helper
[0] != this.element
[0] && !(/(fixed|absolute)/).test(helper
.css("position")))
285 helper
.css("position", "absolute");
291 _adjustOffsetFromHelper: function(obj
) {
292 if (typeof obj
== 'string') {
293 obj
= obj
.split(' ');
295 if ($.isArray(obj
)) {
296 obj
= {left
: +obj
[0], top
: +obj
[1] || 0};
299 this.offset
.click
.left
= obj
.left
+ this.margins
.left
;
301 if ('right' in obj
) {
302 this.offset
.click
.left
= this.helperProportions
.width
- obj
.right
+ this.margins
.left
;
305 this.offset
.click
.top
= obj
.top
+ this.margins
.top
;
307 if ('bottom' in obj
) {
308 this.offset
.click
.top
= this.helperProportions
.height
- obj
.bottom
+ this.margins
.top
;
312 _getParentOffset: function() {
314 //Get the offsetParent and cache its position
315 this.offsetParent
= this.helper
.offsetParent();
316 var po
= this.offsetParent
.offset();
318 // This is a special case where we need to modify a offset calculated on start, since the following happened:
319 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
320 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
321 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
322 if(this.cssPosition
== 'absolute' && this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) {
323 po
.left
+= this.scrollParent
.scrollLeft();
324 po
.top
+= this.scrollParent
.scrollTop();
327 if((this.offsetParent
[0] == document
.body
) //This needs to be actually done for all browsers, since pageX/pageY includes this information
328 || (this.offsetParent
[0].tagName
&& this.offsetParent
[0].tagName
.toLowerCase() == 'html' && $.browser
.msie
)) //Ugly IE fix
329 po
= { top
: 0, left
: 0 };
332 top
: po
.top
+ (parseInt(this.offsetParent
.css("borderTopWidth"),10) || 0),
333 left
: po
.left
+ (parseInt(this.offsetParent
.css("borderLeftWidth"),10) || 0)
338 _getRelativeOffset: function() {
340 if(this.cssPosition
== "relative") {
341 var p
= this.element
.position();
343 top
: p
.top
- (parseInt(this.helper
.css("top"),10) || 0) + this.scrollParent
.scrollTop(),
344 left
: p
.left
- (parseInt(this.helper
.css("left"),10) || 0) + this.scrollParent
.scrollLeft()
347 return { top
: 0, left
: 0 };
352 _cacheMargins: function() {
354 left
: (parseInt(this.element
.css("marginLeft"),10) || 0),
355 top
: (parseInt(this.element
.css("marginTop"),10) || 0),
356 right
: (parseInt(this.element
.css("marginRight"),10) || 0),
357 bottom
: (parseInt(this.element
.css("marginBottom"),10) || 0)
361 _cacheHelperProportions: function() {
362 this.helperProportions
= {
363 width
: this.helper
.outerWidth(),
364 height
: this.helper
.outerHeight()
368 _setContainment: function() {
370 var o
= this.options
;
371 if(o
.containment
== 'parent') o
.containment
= this.helper
[0].parentNode
;
372 if(o
.containment
== 'document' || o
.containment
== 'window') this.containment
= [
373 o
.containment
== 'document' ? 0 : $(window
).scrollLeft() - this.offset
.relative
.left
- this.offset
.parent
.left
,
374 o
.containment
== 'document' ? 0 : $(window
).scrollTop() - this.offset
.relative
.top
- this.offset
.parent
.top
,
375 (o
.containment
== 'document' ? 0 : $(window
).scrollLeft()) + $(o
.containment
== 'document' ? document
: window
).width() - this.helperProportions
.width
- this.margins
.left
,
376 (o
.containment
== 'document' ? 0 : $(window
).scrollTop()) + ($(o
.containment
== 'document' ? document
: window
).height() || document
.body
.parentNode
.scrollHeight
) - this.helperProportions
.height
- this.margins
.top
379 if(!(/^(document|window|parent)$/).test(o
.containment
) && o
.containment
.constructor != Array
) {
380 var c
= $(o
.containment
);
381 var ce
= c
[0]; if(!ce
) return;
383 var over
= ($(ce
).css("overflow") != 'hidden');
386 (parseInt($(ce
).css("borderLeftWidth"),10) || 0) + (parseInt($(ce
).css("paddingLeft"),10) || 0),
387 (parseInt($(ce
).css("borderTopWidth"),10) || 0) + (parseInt($(ce
).css("paddingTop"),10) || 0),
388 (over
? Math
.max(ce
.scrollWidth
,ce
.offsetWidth
) : ce
.offsetWidth
) - (parseInt($(ce
).css("borderLeftWidth"),10) || 0) - (parseInt($(ce
).css("paddingRight"),10) || 0) - this.helperProportions
.width
- this.margins
.left
- this.margins
.right
,
389 (over
? Math
.max(ce
.scrollHeight
,ce
.offsetHeight
) : ce
.offsetHeight
) - (parseInt($(ce
).css("borderTopWidth"),10) || 0) - (parseInt($(ce
).css("paddingBottom"),10) || 0) - this.helperProportions
.height
- this.margins
.top
- this.margins
.bottom
391 this.relative_container
= c
;
393 } else if(o
.containment
.constructor == Array
) {
394 this.containment
= o
.containment
;
399 _convertPositionTo: function(d
, pos
) {
401 if(!pos
) pos
= this.position
;
402 var mod
= d
== "absolute" ? 1 : -1;
403 var o
= this.options
, scroll
= this.cssPosition
== 'absolute' && !(this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) ? this.offsetParent
: this.scrollParent
, scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
407 pos
.top
// The absolute mouse position
408 + this.offset
.relative
.top
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
409 + this.offset
.parent
.top
* mod
// The offsetParent's offset without borders (offset + border)
410 - ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ) * mod
)
413 pos
.left
// The absolute mouse position
414 + this.offset
.relative
.left
* mod
// Only for relative positioned nodes: Relative offset from element to offset parent
415 + this.offset
.parent
.left
* mod
// The offsetParent's offset without borders (offset + border)
416 - ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() ) * mod
)
422 _generatePosition: function(event
) {
424 var o
= this.options
, scroll
= this.cssPosition
== 'absolute' && !(this.scrollParent
[0] != document
&& $.ui
.contains(this.scrollParent
[0], this.offsetParent
[0])) ? this.offsetParent
: this.scrollParent
, scrollIsRootNode
= (/(html|body)/i).test(scroll
[0].tagName
);
425 var pageX
= event
.pageX
;
426 var pageY
= event
.pageY
;
429 * - Position constraining -
430 * Constrain the position to a mix of grid, containment.
433 if(this.originalPosition
) { //If we are not dragging yet, we won't check for options
435 if(this.containment
) {
436 if (this.relative_container
){
437 var co
= this.relative_container
.offset();
438 containment
= [ this.containment
[0] + co
.left
,
439 this.containment
[1] + co
.top
,
440 this.containment
[2] + co
.left
,
441 this.containment
[3] + co
.top
];
444 containment
= this.containment
;
447 if(event
.pageX
- this.offset
.click
.left
< containment
[0]) pageX
= containment
[0] + this.offset
.click
.left
;
448 if(event
.pageY
- this.offset
.click
.top
< containment
[1]) pageY
= containment
[1] + this.offset
.click
.top
;
449 if(event
.pageX
- this.offset
.click
.left
> containment
[2]) pageX
= containment
[2] + this.offset
.click
.left
;
450 if(event
.pageY
- this.offset
.click
.top
> containment
[3]) pageY
= containment
[3] + this.offset
.click
.top
;
454 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
455 var top
= o
.grid
[1] ? this.originalPageY
+ Math
.round((pageY
- this.originalPageY
) / o
.grid
[1]) * o
.grid
[1] : this.originalPageY
;
456 pageY
= containment
? (!(top
- this.offset
.click
.top
< containment
[1] || top
- this.offset
.click
.top
> containment
[3]) ? top
: (!(top
- this.offset
.click
.top
< containment
[1]) ? top
- o
.grid
[1] : top
+ o
.grid
[1])) : top
;
458 var left
= o
.grid
[0] ? this.originalPageX
+ Math
.round((pageX
- this.originalPageX
) / o
.grid
[0]) * o
.grid
[0] : this.originalPageX
;
459 pageX
= containment
? (!(left
- this.offset
.click
.left
< containment
[0] || left
- this.offset
.click
.left
> containment
[2]) ? left
: (!(left
- this.offset
.click
.left
< containment
[0]) ? left
- o
.grid
[0] : left
+ o
.grid
[0])) : left
;
466 pageY
// The absolute mouse position
467 - this.offset
.click
.top
// Click offset (relative to the element)
468 - this.offset
.relative
.top
// Only for relative positioned nodes: Relative offset from element to offset parent
469 - this.offset
.parent
.top
// The offsetParent's offset without borders (offset + border)
470 + ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollTop() : ( scrollIsRootNode
? 0 : scroll
.scrollTop() ) ))
473 pageX
// The absolute mouse position
474 - this.offset
.click
.left
// Click offset (relative to the element)
475 - this.offset
.relative
.left
// Only for relative positioned nodes: Relative offset from element to offset parent
476 - this.offset
.parent
.left
// The offsetParent's offset without borders (offset + border)
477 + ($.browser
.safari
&& $.browser
.version
< 526 && this.cssPosition
== 'fixed' ? 0 : ( this.cssPosition
== 'fixed' ? -this.scrollParent
.scrollLeft() : scrollIsRootNode
? 0 : scroll
.scrollLeft() ))
484 this.helper
.removeClass("ui-draggable-dragging");
485 if(this.helper
[0] != this.element
[0] && !this.cancelHelperRemoval
) this.helper
.remove();
486 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
488 this.cancelHelperRemoval
= false;
491 // From now on bulk stuff - mainly helpers
493 _trigger: function(type
, event
, ui
) {
494 ui
= ui
|| this._uiHash();
495 $.ui
.plugin
.call(this, type
, [event
, ui
]);
496 if(type
== "drag") this.positionAbs
= this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
497 return $.Widget
.prototype._trigger
.call(this, type
, event
, ui
);
502 _uiHash: function(event
) {
505 position
: this.position
,
506 originalPosition
: this.originalPosition
,
507 offset
: this.positionAbs
513 $.extend($.ui
.draggable
, {
517 $.ui
.plugin
.add("draggable", "connectToSortable", {
518 start: function(event
, ui
) {
520 var inst
= $(this).data("draggable"), o
= inst
.options
,
521 uiSortable
= $.extend({}, ui
, { item
: inst
.element
});
523 $(o
.connectToSortable
).each(function() {
524 var sortable
= $.data(this, 'sortable');
525 if (sortable
&& !sortable
.options
.disabled
) {
526 inst
.sortables
.push({
528 shouldRevert
: sortable
.options
.revert
530 sortable
.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
531 sortable
._trigger("activate", event
, uiSortable
);
536 stop: function(event
, ui
) {
538 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
539 var inst
= $(this).data("draggable"),
540 uiSortable
= $.extend({}, ui
, { item
: inst
.element
});
542 $.each(inst
.sortables
, function() {
543 if(this.instance
.isOver
) {
545 this.instance
.isOver
= 0;
547 inst
.cancelHelperRemoval
= true; //Don't remove the helper in the draggable instance
548 this.instance
.cancelHelperRemoval
= false; //Remove it in the sortable instance (so sortable plugins like revert still work)
550 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
551 if(this.shouldRevert
) this.instance
.options
.revert
= true;
553 //Trigger the stop of the sortable
554 this.instance
._mouseStop(event
);
556 this.instance
.options
.helper
= this.instance
.options
._helper
;
558 //If the helper has been the original item, restore properties in the sortable
559 if(inst
.options
.helper
== 'original')
560 this.instance
.currentItem
.css({ top
: 'auto', left
: 'auto' });
563 this.instance
.cancelHelperRemoval
= false; //Remove the helper in the sortable instance
564 this.instance
._trigger("deactivate", event
, uiSortable
);
570 drag: function(event
, ui
) {
572 var inst
= $(this).data("draggable"), self
= this;
574 var checkPos = function(o
) {
575 var dyClick
= this.offset
.click
.top
, dxClick
= this.offset
.click
.left
;
576 var helperTop
= this.positionAbs
.top
, helperLeft
= this.positionAbs
.left
;
577 var itemHeight
= o
.height
, itemWidth
= o
.width
;
578 var itemTop
= o
.top
, itemLeft
= o
.left
;
580 return $.ui
.isOver(helperTop
+ dyClick
, helperLeft
+ dxClick
, itemTop
, itemLeft
, itemHeight
, itemWidth
);
583 $.each(inst
.sortables
, function(i
) {
585 //Copy over some variables to allow calling the sortable's native _intersectsWith
586 this.instance
.positionAbs
= inst
.positionAbs
;
587 this.instance
.helperProportions
= inst
.helperProportions
;
588 this.instance
.offset
.click
= inst
.offset
.click
;
590 if(this.instance
._intersectsWith(this.instance
.containerCache
)) {
592 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
593 if(!this.instance
.isOver
) {
595 this.instance
.isOver
= 1;
596 //Now we fake the start of dragging for the sortable instance,
597 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
598 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
599 this.instance
.currentItem
= $(self
).clone().removeAttr('id').appendTo(this.instance
.element
).data("sortable-item", true);
600 this.instance
.options
._helper
= this.instance
.options
.helper
; //Store helper option to later restore it
601 this.instance
.options
.helper = function() { return ui
.helper
[0]; };
603 event
.target
= this.instance
.currentItem
[0];
604 this.instance
._mouseCapture(event
, true);
605 this.instance
._mouseStart(event
, true, true);
607 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
608 this.instance
.offset
.click
.top
= inst
.offset
.click
.top
;
609 this.instance
.offset
.click
.left
= inst
.offset
.click
.left
;
610 this.instance
.offset
.parent
.left
-= inst
.offset
.parent
.left
- this.instance
.offset
.parent
.left
;
611 this.instance
.offset
.parent
.top
-= inst
.offset
.parent
.top
- this.instance
.offset
.parent
.top
;
613 inst
._trigger("toSortable", event
);
614 inst
.dropped
= this.instance
.element
; //draggable revert needs that
615 //hack so receive/update callbacks work (mostly)
616 inst
.currentItem
= inst
.element
;
617 this.instance
.fromOutside
= inst
;
621 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
622 if(this.instance
.currentItem
) this.instance
._mouseDrag(event
);
626 //If it doesn't intersect with the sortable, and it intersected before,
627 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
628 if(this.instance
.isOver
) {
630 this.instance
.isOver
= 0;
631 this.instance
.cancelHelperRemoval
= true;
633 //Prevent reverting on this forced stop
634 this.instance
.options
.revert
= false;
636 // The out event needs to be triggered independently
637 this.instance
._trigger('out', event
, this.instance
._uiHash(this.instance
));
639 this.instance
._mouseStop(event
, true);
640 this.instance
.options
.helper
= this.instance
.options
._helper
;
642 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
643 this.instance
.currentItem
.remove();
644 if(this.instance
.placeholder
) this.instance
.placeholder
.remove();
646 inst
._trigger("fromSortable", event
);
647 inst
.dropped
= false; //draggable revert needs that
657 $.ui
.plugin
.add("draggable", "cursor", {
658 start: function(event
, ui
) {
659 var t
= $('body'), o
= $(this).data('draggable').options
;
660 if (t
.css("cursor")) o
._cursor
= t
.css("cursor");
661 t
.css("cursor", o
.cursor
);
663 stop: function(event
, ui
) {
664 var o
= $(this).data('draggable').options
;
665 if (o
._cursor
) $('body').css("cursor", o
._cursor
);
669 $.ui
.plugin
.add("draggable", "opacity", {
670 start: function(event
, ui
) {
671 var t
= $(ui
.helper
), o
= $(this).data('draggable').options
;
672 if(t
.css("opacity")) o
._opacity
= t
.css("opacity");
673 t
.css('opacity', o
.opacity
);
675 stop: function(event
, ui
) {
676 var o
= $(this).data('draggable').options
;
677 if(o
._opacity
) $(ui
.helper
).css('opacity', o
._opacity
);
681 $.ui
.plugin
.add("draggable", "scroll", {
682 start: function(event
, ui
) {
683 var i
= $(this).data("draggable");
684 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') i
.overflowOffset
= i
.scrollParent
.offset();
686 drag: function(event
, ui
) {
688 var i
= $(this).data("draggable"), o
= i
.options
, scrolled
= false;
690 if(i
.scrollParent
[0] != document
&& i
.scrollParent
[0].tagName
!= 'HTML') {
692 if(!o
.axis
|| o
.axis
!= 'x') {
693 if((i
.overflowOffset
.top
+ i
.scrollParent
[0].offsetHeight
) - event
.pageY
< o
.scrollSensitivity
)
694 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
+ o
.scrollSpeed
;
695 else if(event
.pageY
- i
.overflowOffset
.top
< o
.scrollSensitivity
)
696 i
.scrollParent
[0].scrollTop
= scrolled
= i
.scrollParent
[0].scrollTop
- o
.scrollSpeed
;
699 if(!o
.axis
|| o
.axis
!= 'y') {
700 if((i
.overflowOffset
.left
+ i
.scrollParent
[0].offsetWidth
) - event
.pageX
< o
.scrollSensitivity
)
701 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
+ o
.scrollSpeed
;
702 else if(event
.pageX
- i
.overflowOffset
.left
< o
.scrollSensitivity
)
703 i
.scrollParent
[0].scrollLeft
= scrolled
= i
.scrollParent
[0].scrollLeft
- o
.scrollSpeed
;
708 if(!o
.axis
|| o
.axis
!= 'x') {
709 if(event
.pageY
- $(document
).scrollTop() < o
.scrollSensitivity
)
710 scrolled
= $(document
).scrollTop($(document
).scrollTop() - o
.scrollSpeed
);
711 else if($(window
).height() - (event
.pageY
- $(document
).scrollTop()) < o
.scrollSensitivity
)
712 scrolled
= $(document
).scrollTop($(document
).scrollTop() + o
.scrollSpeed
);
715 if(!o
.axis
|| o
.axis
!= 'y') {
716 if(event
.pageX
- $(document
).scrollLeft() < o
.scrollSensitivity
)
717 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() - o
.scrollSpeed
);
718 else if($(window
).width() - (event
.pageX
- $(document
).scrollLeft()) < o
.scrollSensitivity
)
719 scrolled
= $(document
).scrollLeft($(document
).scrollLeft() + o
.scrollSpeed
);
724 if(scrolled
!== false && $.ui
.ddmanager
&& !o
.dropBehaviour
)
725 $.ui
.ddmanager
.prepareOffsets(i
, event
);
730 $.ui
.plugin
.add("draggable", "snap", {
731 start: function(event
, ui
) {
733 var i
= $(this).data("draggable"), o
= i
.options
;
736 $(o
.snap
.constructor != String
? ( o
.snap
.items
|| ':data(draggable)' ) : o
.snap
).each(function() {
737 var $t
= $(this); var $o
= $t
.offset();
738 if(this != i
.element
[0]) i
.snapElements
.push({
740 width
: $t
.outerWidth(), height
: $t
.outerHeight(),
741 top
: $o
.top
, left
: $o
.left
746 drag: function(event
, ui
) {
748 var inst
= $(this).data("draggable"), o
= inst
.options
;
749 var d
= o
.snapTolerance
;
751 var x1
= ui
.offset
.left
, x2
= x1
+ inst
.helperProportions
.width
,
752 y1
= ui
.offset
.top
, y2
= y1
+ inst
.helperProportions
.height
;
754 for (var i
= inst
.snapElements
.length
- 1; i
>= 0; i
--){
756 var l
= inst
.snapElements
[i
].left
, r
= l
+ inst
.snapElements
[i
].width
,
757 t
= inst
.snapElements
[i
].top
, b
= t
+ inst
.snapElements
[i
].height
;
759 //Yes, I know, this is insane ;)
760 if(!((l
-d
< x1
&& x1
< r
+d
&& t
-d
< y1
&& y1
< b
+d
) || (l
-d
< x1
&& x1
< r
+d
&& t
-d
< y2
&& y2
< b
+d
) || (l
-d
< x2
&& x2
< r
+d
&& t
-d
< y1
&& y1
< b
+d
) || (l
-d
< x2
&& x2
< r
+d
&& t
-d
< y2
&& y2
< b
+d
))) {
761 if(inst
.snapElements
[i
].snapping
) (inst
.options
.snap
.release
&& inst
.options
.snap
.release
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
762 inst
.snapElements
[i
].snapping
= false;
766 if(o
.snapMode
!= 'inner') {
767 var ts
= Math
.abs(t
- y2
) <= d
;
768 var bs
= Math
.abs(b
- y1
) <= d
;
769 var ls
= Math
.abs(l
- x2
) <= d
;
770 var rs
= Math
.abs(r
- x1
) <= d
;
771 if(ts
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
- inst
.helperProportions
.height
, left
: 0 }).top
- inst
.margins
.top
;
772 if(bs
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
, left
: 0 }).top
- inst
.margins
.top
;
773 if(ls
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
- inst
.helperProportions
.width
}).left
- inst
.margins
.left
;
774 if(rs
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
}).left
- inst
.margins
.left
;
777 var first
= (ts
|| bs
|| ls
|| rs
);
779 if(o
.snapMode
!= 'outer') {
780 var ts
= Math
.abs(t
- y1
) <= d
;
781 var bs
= Math
.abs(b
- y2
) <= d
;
782 var ls
= Math
.abs(l
- x1
) <= d
;
783 var rs
= Math
.abs(r
- x2
) <= d
;
784 if(ts
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: t
, left
: 0 }).top
- inst
.margins
.top
;
785 if(bs
) ui
.position
.top
= inst
._convertPositionTo("relative", { top
: b
- inst
.helperProportions
.height
, left
: 0 }).top
- inst
.margins
.top
;
786 if(ls
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: l
}).left
- inst
.margins
.left
;
787 if(rs
) ui
.position
.left
= inst
._convertPositionTo("relative", { top
: 0, left
: r
- inst
.helperProportions
.width
}).left
- inst
.margins
.left
;
790 if(!inst
.snapElements
[i
].snapping
&& (ts
|| bs
|| ls
|| rs
|| first
))
791 (inst
.options
.snap
.snap
&& inst
.options
.snap
.snap
.call(inst
.element
, event
, $.extend(inst
._uiHash(), { snapItem
: inst
.snapElements
[i
].item
})));
792 inst
.snapElements
[i
].snapping
= (ts
|| bs
|| ls
|| rs
|| first
);
799 $.ui
.plugin
.add("draggable", "stack", {
800 start: function(event
, ui
) {
802 var o
= $(this).data("draggable").options
;
804 var group
= $.makeArray($(o
.stack
)).sort(function(a
,b
) {
805 return (parseInt($(a
).css("zIndex"),10) || 0) - (parseInt($(b
).css("zIndex"),10) || 0);
807 if (!group
.length
) { return; }
809 var min
= parseInt(group
[0].style
.zIndex
) || 0;
810 $(group
).each(function(i
) {
811 this.style
.zIndex
= min
+ i
;
814 this[0].style
.zIndex
= min
+ group
.length
;
819 $.ui
.plugin
.add("draggable", "zIndex", {
820 start: function(event
, ui
) {
821 var t
= $(ui
.helper
), o
= $(this).data("draggable").options
;
822 if(t
.css("zIndex")) o
._zIndex
= t
.css("zIndex");
823 t
.css('zIndex', o
.zIndex
);
825 stop: function(event
, ui
) {
826 var o
= $(this).data("draggable").options
;
827 if(o
._zIndex
) $(ui
.helper
).css('zIndex', o
._zIndex
);