ApiBase::dieUsageMsg() now also accept being given a single element as a string.
[mediawiki.git] / resources / jquery.ui / jquery.ui.draggable.js
blob5f36761607134e58445c63d0d4fa4eeaac0aa632
1 /*
2  * jQuery UI Draggable 1.8.11
3  *
4  * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5  * Dual licensed under the MIT or GPL Version 2 licenses.
6  * http://jquery.org/license
7  *
8  * http://docs.jquery.com/UI/Draggables
9  *
10  * Depends:
11  *      jquery.ui.core.js
12  *      jquery.ui.mouse.js
13  *      jquery.ui.widget.js
14  */
15 (function( $, undefined ) {
17 $.widget("ui.draggable", $.ui.mouse, {
18         widgetEventPrefix: "drag",
19         options: {
20                 addClasses: true,
21                 appendTo: "parent",
22                 axis: false,
23                 connectToSortable: false,
24                 containment: false,
25                 cursor: "auto",
26                 cursorAt: false,
27                 grid: false,
28                 handle: false,
29                 helper: "original",
30                 iframeFix: false,
31                 opacity: false,
32                 refreshPositions: false,
33                 revert: false,
34                 revertDuration: 500,
35                 scope: "default",
36                 scroll: true,
37                 scrollSensitivity: 20,
38                 scrollSpeed: 20,
39                 snap: false,
40                 snapMode: "both",
41                 snapTolerance: 20,
42                 stack: false,
43                 zIndex: false
44         },
45         _create: function() {
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"));
53                 this._mouseInit();
55         },
57         destroy: function() {
58                 if(!this.element.data('draggable')) return;
59                 this.element
60                         .removeData("draggable")
61                         .unbind(".draggable")
62                         .removeClass("ui-draggable"
63                                 + " ui-draggable-dragging"
64                                 + " ui-draggable-disabled");
65                 this._mouseDestroy();
67                 return this;
68         },
70         _mouseCapture: function(event) {
72                 var o = this.options;
74                 // among others, prevent a drag on a resizable-handle
75                 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
76                         return false;
78                 //Quit if we're not on a valid handle
79                 this.handle = this._getHandle(event);
80                 if (!this.handle)
81                         return false;
83                 return true;
85         },
87         _mouseStart: function(event) {
89                 var o = this.options;
91                 //Create and append the visible helper
92                 this.helper = this._createHelper(event);
94                 //Cache the helper size
95                 this._cacheHelperProportions();
97                 //If ddmanager is used for droppables, set the global draggable
98                 if($.ui.ddmanager)
99                         $.ui.ddmanager.current = this;
101                 /*
102                  * - Position generation -
103                  * This block generates everything position related - it's the core of draggables.
104                  */
106                 //Cache the margins of the original element
107                 this._cacheMargins();
109                 //Store the helper's css position
110                 this.cssPosition = this.helper.css("position");
111                 this.scrollParent = this.helper.scrollParent();
113                 //The element's absolute position on the page minus margins
114                 this.offset = this.positionAbs = this.element.offset();
115                 this.offset = {
116                         top: this.offset.top - this.margins.top,
117                         left: this.offset.left - this.margins.left
118                 };
120                 $.extend(this.offset, {
121                         click: { //Where the click happened, relative to the element
122                                 left: event.pageX - this.offset.left,
123                                 top: event.pageY - this.offset.top
124                         },
125                         parent: this._getParentOffset(),
126                         relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
127                 });
129                 //Generate the original position
130                 this.originalPosition = this.position = this._generatePosition(event);
131                 this.originalPageX = event.pageX;
132                 this.originalPageY = event.pageY;
134                 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
135                 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
137                 //Set a containment if given in the options
138                 if(o.containment)
139                         this._setContainment();
141                 //Trigger event + callbacks
142                 if(this._trigger("start", event) === false) {
143                         this._clear();
144                         return false;
145                 }
147                 //Recache the helper size
148                 this._cacheHelperProportions();
150                 //Prepare the droppable offsets
151                 if ($.ui.ddmanager && !o.dropBehaviour)
152                         $.ui.ddmanager.prepareOffsets(this, event);
154                 this.helper.addClass("ui-draggable-dragging");
155                 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
156                 return true;
157         },
159         _mouseDrag: function(event, noPropagation) {
161                 //Compute the helpers position
162                 this.position = this._generatePosition(event);
163                 this.positionAbs = this._convertPositionTo("absolute");
165                 //Call plugins and callbacks and use the resulting position if something is returned
166                 if (!noPropagation) {
167                         var ui = this._uiHash();
168                         if(this._trigger('drag', event, ui) === false) {
169                                 this._mouseUp({});
170                                 return false;
171                         }
172                         this.position = ui.position;
173                 }
175                 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
176                 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
177                 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
179                 return false;
180         },
182         _mouseStop: function(event) {
184                 //If we are using droppables, inform the manager about the drop
185                 var dropped = false;
186                 if ($.ui.ddmanager && !this.options.dropBehaviour)
187                         dropped = $.ui.ddmanager.drop(this, event);
189                 //if a drop comes from outside (a sortable)
190                 if(this.dropped) {
191                         dropped = this.dropped;
192                         this.dropped = false;
193                 }
194                 
195                 //if the original element is removed, don't bother to continue if helper is set to "original"
196                 if((!this.element[0] || !this.element[0].parentNode) && this.options.helper == "original")
197                         return false;
199                 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))) {
200                         var self = this;
201                         $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
202                                 if(self._trigger("stop", event) !== false) {
203                                         self._clear();
204                                 }
205                         });
206                 } else {
207                         if(this._trigger("stop", event) !== false) {
208                                 this._clear();
209                         }
210                 }
212                 return false;
213         },
214         
215         cancel: function() {
216                 
217                 if(this.helper.is(".ui-draggable-dragging")) {
218                         this._mouseUp({});
219                 } else {
220                         this._clear();
221                 }
222                 
223                 return this;
224                 
225         },
227         _getHandle: function(event) {
229                 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
230                 $(this.options.handle, this.element)
231                         .find("*")
232                         .andSelf()
233                         .each(function() {
234                                 if(this == event.target) handle = true;
235                         });
237                 return handle;
239         },
241         _createHelper: function(event) {
243                 var o = this.options;
244                 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
246                 if(!helper.parents('body').length)
247                         helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
249                 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
250                         helper.css("position", "absolute");
252                 return helper;
254         },
256         _adjustOffsetFromHelper: function(obj) {
257                 if (typeof obj == 'string') {
258                         obj = obj.split(' ');
259                 }
260                 if ($.isArray(obj)) {
261                         obj = {left: +obj[0], top: +obj[1] || 0};
262                 }
263                 if ('left' in obj) {
264                         this.offset.click.left = obj.left + this.margins.left;
265                 }
266                 if ('right' in obj) {
267                         this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
268                 }
269                 if ('top' in obj) {
270                         this.offset.click.top = obj.top + this.margins.top;
271                 }
272                 if ('bottom' in obj) {
273                         this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
274                 }
275         },
277         _getParentOffset: function() {
279                 //Get the offsetParent and cache its position
280                 this.offsetParent = this.helper.offsetParent();
281                 var po = this.offsetParent.offset();
283                 // This is a special case where we need to modify a offset calculated on start, since the following happened:
284                 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
285                 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
286                 //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
287                 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
288                         po.left += this.scrollParent.scrollLeft();
289                         po.top += this.scrollParent.scrollTop();
290                 }
292                 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
293                 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
294                         po = { top: 0, left: 0 };
296                 return {
297                         top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
298                         left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
299                 };
301         },
303         _getRelativeOffset: function() {
305                 if(this.cssPosition == "relative") {
306                         var p = this.element.position();
307                         return {
308                                 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
309                                 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
310                         };
311                 } else {
312                         return { top: 0, left: 0 };
313                 }
315         },
317         _cacheMargins: function() {
318                 this.margins = {
319                         left: (parseInt(this.element.css("marginLeft"),10) || 0),
320                         top: (parseInt(this.element.css("marginTop"),10) || 0),
321                         right: (parseInt(this.element.css("marginRight"),10) || 0),
322                         bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
323                 };
324         },
326         _cacheHelperProportions: function() {
327                 this.helperProportions = {
328                         width: this.helper.outerWidth(),
329                         height: this.helper.outerHeight()
330                 };
331         },
333         _setContainment: function() {
335                 var o = this.options;
336                 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
337                 if(o.containment == 'document' || o.containment == 'window') this.containment = [
338                         (o.containment == 'document' ? 0 : $(window).scrollLeft()) - this.offset.relative.left - this.offset.parent.left,
339                         (o.containment == 'document' ? 0 : $(window).scrollTop()) - this.offset.relative.top - this.offset.parent.top,
340                         (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
341                         (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
342                 ];
344                 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
345                         var ce = $(o.containment)[0]; if(!ce) return;
346                         var co = $(o.containment).offset();
347                         var over = ($(ce).css("overflow") != 'hidden');
349                         this.containment = [
350                                 co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
351                                 co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
352                                 co.left+(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,
353                                 co.top+(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
354                         ];
355                 } else if(o.containment.constructor == Array) {
356                         this.containment = o.containment;
357                 }
359         },
361         _convertPositionTo: function(d, pos) {
363                 if(!pos) pos = this.position;
364                 var mod = d == "absolute" ? 1 : -1;
365                 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);
367                 return {
368                         top: (
369                                 pos.top                                                                                                                                 // The absolute mouse position
370                                 + this.offset.relative.top * mod                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
371                                 + this.offset.parent.top * mod                                                                                  // The offsetParent's offset without borders (offset + border)
372                                 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
373                         ),
374                         left: (
375                                 pos.left                                                                                                                                // The absolute mouse position
376                                 + this.offset.relative.left * mod                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
377                                 + this.offset.parent.left * mod                                                                                 // The offsetParent's offset without borders (offset + border)
378                                 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
379                         )
380                 };
382         },
384         _generatePosition: function(event) {
386                 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);
387                 var pageX = event.pageX;
388                 var pageY = event.pageY;
390                 /*
391                  * - Position constraining -
392                  * Constrain the position to a mix of grid, containment.
393                  */
395                 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
397                         if(this.containment) {
398                                 if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
399                                 if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
400                                 if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
401                                 if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
402                         }
404                         if(o.grid) {
405                                 var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
406                                 pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
408                                 var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
409                                 pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
410                         }
412                 }
414                 return {
415                         top: (
416                                 pageY                                                                                                                           // The absolute mouse position
417                                 - this.offset.click.top                                                                                                 // Click offset (relative to the element)
418                                 - this.offset.relative.top                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
419                                 - this.offset.parent.top                                                                                                // The offsetParent's offset without borders (offset + border)
420                                 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
421                         ),
422                         left: (
423                                 pageX                                                                                                                           // The absolute mouse position
424                                 - this.offset.click.left                                                                                                // Click offset (relative to the element)
425                                 - this.offset.relative.left                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
426                                 - this.offset.parent.left                                                                                               // The offsetParent's offset without borders (offset + border)
427                                 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
428                         )
429                 };
431         },
433         _clear: function() {
434                 this.helper.removeClass("ui-draggable-dragging");
435                 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
436                 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
437                 this.helper = null;
438                 this.cancelHelperRemoval = false;
439         },
441         // From now on bulk stuff - mainly helpers
443         _trigger: function(type, event, ui) {
444                 ui = ui || this._uiHash();
445                 $.ui.plugin.call(this, type, [event, ui]);
446                 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
447                 return $.Widget.prototype._trigger.call(this, type, event, ui);
448         },
450         plugins: {},
452         _uiHash: function(event) {
453                 return {
454                         helper: this.helper,
455                         position: this.position,
456                         originalPosition: this.originalPosition,
457                         offset: this.positionAbs
458                 };
459         }
463 $.extend($.ui.draggable, {
464         version: "1.8.11"
467 $.ui.plugin.add("draggable", "connectToSortable", {
468         start: function(event, ui) {
470                 var inst = $(this).data("draggable"), o = inst.options,
471                         uiSortable = $.extend({}, ui, { item: inst.element });
472                 inst.sortables = [];
473                 $(o.connectToSortable).each(function() {
474                         var sortable = $.data(this, 'sortable');
475                         if (sortable && !sortable.options.disabled) {
476                                 inst.sortables.push({
477                                         instance: sortable,
478                                         shouldRevert: sortable.options.revert
479                                 });
480                                 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).
481                                 sortable._trigger("activate", event, uiSortable);
482                         }
483                 });
485         },
486         stop: function(event, ui) {
488                 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
489                 var inst = $(this).data("draggable"),
490                         uiSortable = $.extend({}, ui, { item: inst.element });
492                 $.each(inst.sortables, function() {
493                         if(this.instance.isOver) {
495                                 this.instance.isOver = 0;
497                                 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
498                                 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
500                                 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
501                                 if(this.shouldRevert) this.instance.options.revert = true;
503                                 //Trigger the stop of the sortable
504                                 this.instance._mouseStop(event);
506                                 this.instance.options.helper = this.instance.options._helper;
508                                 //If the helper has been the original item, restore properties in the sortable
509                                 if(inst.options.helper == 'original')
510                                         this.instance.currentItem.css({ top: 'auto', left: 'auto' });
512                         } else {
513                                 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
514                                 this.instance._trigger("deactivate", event, uiSortable);
515                         }
517                 });
519         },
520         drag: function(event, ui) {
522                 var inst = $(this).data("draggable"), self = this;
524                 var checkPos = function(o) {
525                         var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
526                         var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
527                         var itemHeight = o.height, itemWidth = o.width;
528                         var itemTop = o.top, itemLeft = o.left;
530                         return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
531                 };
533                 $.each(inst.sortables, function(i) {
534                         
535                         //Copy over some variables to allow calling the sortable's native _intersectsWith
536                         this.instance.positionAbs = inst.positionAbs;
537                         this.instance.helperProportions = inst.helperProportions;
538                         this.instance.offset.click = inst.offset.click;
539                         
540                         if(this.instance._intersectsWith(this.instance.containerCache)) {
542                                 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
543                                 if(!this.instance.isOver) {
545                                         this.instance.isOver = 1;
546                                         //Now we fake the start of dragging for the sortable instance,
547                                         //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
548                                         //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)
549                                         this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
550                                         this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
551                                         this.instance.options.helper = function() { return ui.helper[0]; };
553                                         event.target = this.instance.currentItem[0];
554                                         this.instance._mouseCapture(event, true);
555                                         this.instance._mouseStart(event, true, true);
557                                         //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
558                                         this.instance.offset.click.top = inst.offset.click.top;
559                                         this.instance.offset.click.left = inst.offset.click.left;
560                                         this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
561                                         this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
563                                         inst._trigger("toSortable", event);
564                                         inst.dropped = this.instance.element; //draggable revert needs that
565                                         //hack so receive/update callbacks work (mostly)
566                                         inst.currentItem = inst.element;
567                                         this.instance.fromOutside = inst;
569                                 }
571                                 //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
572                                 if(this.instance.currentItem) this.instance._mouseDrag(event);
574                         } else {
576                                 //If it doesn't intersect with the sortable, and it intersected before,
577                                 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
578                                 if(this.instance.isOver) {
580                                         this.instance.isOver = 0;
581                                         this.instance.cancelHelperRemoval = true;
582                                         
583                                         //Prevent reverting on this forced stop
584                                         this.instance.options.revert = false;
585                                         
586                                         // The out event needs to be triggered independently
587                                         this.instance._trigger('out', event, this.instance._uiHash(this.instance));
588                                         
589                                         this.instance._mouseStop(event, true);
590                                         this.instance.options.helper = this.instance.options._helper;
592                                         //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
593                                         this.instance.currentItem.remove();
594                                         if(this.instance.placeholder) this.instance.placeholder.remove();
596                                         inst._trigger("fromSortable", event);
597                                         inst.dropped = false; //draggable revert needs that
598                                 }
600                         };
602                 });
604         }
607 $.ui.plugin.add("draggable", "cursor", {
608         start: function(event, ui) {
609                 var t = $('body'), o = $(this).data('draggable').options;
610                 if (t.css("cursor")) o._cursor = t.css("cursor");
611                 t.css("cursor", o.cursor);
612         },
613         stop: function(event, ui) {
614                 var o = $(this).data('draggable').options;
615                 if (o._cursor) $('body').css("cursor", o._cursor);
616         }
619 $.ui.plugin.add("draggable", "iframeFix", {
620         start: function(event, ui) {
621                 var o = $(this).data('draggable').options;
622                 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
623                         $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
624                         .css({
625                                 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
626                                 position: "absolute", opacity: "0.001", zIndex: 1000
627                         })
628                         .css($(this).offset())
629                         .appendTo("body");
630                 });
631         },
632         stop: function(event, ui) {
633                 $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
634         }
637 $.ui.plugin.add("draggable", "opacity", {
638         start: function(event, ui) {
639                 var t = $(ui.helper), o = $(this).data('draggable').options;
640                 if(t.css("opacity")) o._opacity = t.css("opacity");
641                 t.css('opacity', o.opacity);
642         },
643         stop: function(event, ui) {
644                 var o = $(this).data('draggable').options;
645                 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
646         }
649 $.ui.plugin.add("draggable", "scroll", {
650         start: function(event, ui) {
651                 var i = $(this).data("draggable");
652                 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
653         },
654         drag: function(event, ui) {
656                 var i = $(this).data("draggable"), o = i.options, scrolled = false;
658                 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
660                         if(!o.axis || o.axis != 'x') {
661                                 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
662                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
663                                 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
664                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
665                         }
667                         if(!o.axis || o.axis != 'y') {
668                                 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
669                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
670                                 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
671                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
672                         }
674                 } else {
676                         if(!o.axis || o.axis != 'x') {
677                                 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
678                                         scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
679                                 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
680                                         scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
681                         }
683                         if(!o.axis || o.axis != 'y') {
684                                 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
685                                         scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
686                                 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
687                                         scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
688                         }
690                 }
692                 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
693                         $.ui.ddmanager.prepareOffsets(i, event);
695         }
698 $.ui.plugin.add("draggable", "snap", {
699         start: function(event, ui) {
701                 var i = $(this).data("draggable"), o = i.options;
702                 i.snapElements = [];
704                 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
705                         var $t = $(this); var $o = $t.offset();
706                         if(this != i.element[0]) i.snapElements.push({
707                                 item: this,
708                                 width: $t.outerWidth(), height: $t.outerHeight(),
709                                 top: $o.top, left: $o.left
710                         });
711                 });
713         },
714         drag: function(event, ui) {
716                 var inst = $(this).data("draggable"), o = inst.options;
717                 var d = o.snapTolerance;
719                 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
720                         y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
722                 for (var i = inst.snapElements.length - 1; i >= 0; i--){
724                         var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
725                                 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
727                         //Yes, I know, this is insane ;)
728                         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))) {
729                                 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 })));
730                                 inst.snapElements[i].snapping = false;
731                                 continue;
732                         }
734                         if(o.snapMode != 'inner') {
735                                 var ts = Math.abs(t - y2) <= d;
736                                 var bs = Math.abs(b - y1) <= d;
737                                 var ls = Math.abs(l - x2) <= d;
738                                 var rs = Math.abs(r - x1) <= d;
739                                 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
740                                 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
741                                 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
742                                 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
743                         }
745                         var first = (ts || bs || ls || rs);
747                         if(o.snapMode != 'outer') {
748                                 var ts = Math.abs(t - y1) <= d;
749                                 var bs = Math.abs(b - y2) <= d;
750                                 var ls = Math.abs(l - x1) <= d;
751                                 var rs = Math.abs(r - x2) <= d;
752                                 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
753                                 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
754                                 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
755                                 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
756                         }
758                         if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
759                                 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
760                         inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
762                 };
764         }
767 $.ui.plugin.add("draggable", "stack", {
768         start: function(event, ui) {
770                 var o = $(this).data("draggable").options;
772                 var group = $.makeArray($(o.stack)).sort(function(a,b) {
773                         return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
774                 });
775                 if (!group.length) { return; }
776                 
777                 var min = parseInt(group[0].style.zIndex) || 0;
778                 $(group).each(function(i) {
779                         this.style.zIndex = min + i;
780                 });
782                 this[0].style.zIndex = min + group.length;
784         }
787 $.ui.plugin.add("draggable", "zIndex", {
788         start: function(event, ui) {
789                 var t = $(ui.helper), o = $(this).data("draggable").options;
790                 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
791                 t.css('zIndex', o.zIndex);
792         },
793         stop: function(event, ui) {
794                 var o = $(this).data("draggable").options;
795                 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
796         }
799 })(jQuery);