Merge "Use local context to get messages"
[mediawiki.git] / resources / jquery.ui / jquery.ui.draggable.js
blob6da1aafef47d097dd26c5a4ad1c1d5897b976ba6
1 /*!
2  * jQuery UI Draggable 1.8.23
3  *
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
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;
82                 
83                 if ( o.iframeFix ) {
84                         $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
85                                 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
86                                 .css({
87                                         width: this.offsetWidth+"px", height: this.offsetHeight+"px",
88                                         position: "absolute", opacity: "0.001", zIndex: 1000
89                                 })
90                                 .css($(this).offset())
91                                 .appendTo("body");
92                         });
93                 }
95                 return true;
97         },
99         _mouseStart: function(event) {
101                 var o = this.options;
103                 //Create and append the visible helper
104                 this.helper = this._createHelper(event);
106                 this.helper.addClass("ui-draggable-dragging");
108                 //Cache the helper size
109                 this._cacheHelperProportions();
111                 //If ddmanager is used for droppables, set the global draggable
112                 if($.ui.ddmanager)
113                         $.ui.ddmanager.current = this;
115                 /*
116                  * - Position generation -
117                  * This block generates everything position related - it's the core of draggables.
118                  */
120                 //Cache the margins of the original element
121                 this._cacheMargins();
123                 //Store the helper's css position
124                 this.cssPosition = this.helper.css("position");
125                 this.scrollParent = this.helper.scrollParent();
127                 //The element's absolute position on the page minus margins
128                 this.offset = this.positionAbs = this.element.offset();
129                 this.offset = {
130                         top: this.offset.top - this.margins.top,
131                         left: this.offset.left - this.margins.left
132                 };
134                 $.extend(this.offset, {
135                         click: { //Where the click happened, relative to the element
136                                 left: event.pageX - this.offset.left,
137                                 top: event.pageY - this.offset.top
138                         },
139                         parent: this._getParentOffset(),
140                         relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
141                 });
143                 //Generate the original position
144                 this.originalPosition = this.position = this._generatePosition(event);
145                 this.originalPageX = event.pageX;
146                 this.originalPageY = event.pageY;
148                 //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
149                 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
151                 //Set a containment if given in the options
152                 if(o.containment)
153                         this._setContainment();
155                 //Trigger event + callbacks
156                 if(this._trigger("start", event) === false) {
157                         this._clear();
158                         return false;
159                 }
161                 //Recache the helper size
162                 this._cacheHelperProportions();
164                 //Prepare the droppable offsets
165                 if ($.ui.ddmanager && !o.dropBehaviour)
166                         $.ui.ddmanager.prepareOffsets(this, event);
168                 
169                 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
170                 
171                 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
172                 if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
173                 
174                 return true;
175         },
177         _mouseDrag: function(event, noPropagation) {
179                 //Compute the helpers position
180                 this.position = this._generatePosition(event);
181                 this.positionAbs = this._convertPositionTo("absolute");
183                 //Call plugins and callbacks and use the resulting position if something is returned
184                 if (!noPropagation) {
185                         var ui = this._uiHash();
186                         if(this._trigger('drag', event, ui) === false) {
187                                 this._mouseUp({});
188                                 return false;
189                         }
190                         this.position = ui.position;
191                 }
193                 if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
194                 if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
195                 if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
197                 return false;
198         },
200         _mouseStop: function(event) {
202                 //If we are using droppables, inform the manager about the drop
203                 var dropped = false;
204                 if ($.ui.ddmanager && !this.options.dropBehaviour)
205                         dropped = $.ui.ddmanager.drop(this, event);
207                 //if a drop comes from outside (a sortable)
208                 if(this.dropped) {
209                         dropped = this.dropped;
210                         this.dropped = false;
211                 }
212                 
213                 //if the original element is no longer in the DOM don't bother to continue (see #8269)
214                 var element = this.element[0], elementInDom = false;
215                 while ( element && (element = element.parentNode) ) {
216                         if (element == document ) {
217                                 elementInDom = true;
218                         }
219                 }
220                 if ( !elementInDom && this.options.helper === "original" )
221                         return false;
223                 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))) {
224                         var self = this;
225                         $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
226                                 if(self._trigger("stop", event) !== false) {
227                                         self._clear();
228                                 }
229                         });
230                 } else {
231                         if(this._trigger("stop", event) !== false) {
232                                 this._clear();
233                         }
234                 }
236                 return false;
237         },
238         
239         _mouseUp: function(event) {
240                 if (this.options.iframeFix === true) {
241                         $("div.ui-draggable-iframeFix").each(function() { 
242                                 this.parentNode.removeChild(this); 
243                         }); //Remove frame helpers
244                 }
245                 
246                 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
247                 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
248                 
249                 return $.ui.mouse.prototype._mouseUp.call(this, event);
250         },
251         
252         cancel: function() {
253                 
254                 if(this.helper.is(".ui-draggable-dragging")) {
255                         this._mouseUp({});
256                 } else {
257                         this._clear();
258                 }
259                 
260                 return this;
261                 
262         },
264         _getHandle: function(event) {
266                 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
267                 $(this.options.handle, this.element)
268                         .find("*")
269                         .andSelf()
270                         .each(function() {
271                                 if(this == event.target) handle = true;
272                         });
274                 return handle;
276         },
278         _createHelper: function(event) {
280                 var o = this.options;
281                 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
283                 if(!helper.parents('body').length)
284                         helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
286                 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
287                         helper.css("position", "absolute");
289                 return helper;
291         },
293         _adjustOffsetFromHelper: function(obj) {
294                 if (typeof obj == 'string') {
295                         obj = obj.split(' ');
296                 }
297                 if ($.isArray(obj)) {
298                         obj = {left: +obj[0], top: +obj[1] || 0};
299                 }
300                 if ('left' in obj) {
301                         this.offset.click.left = obj.left + this.margins.left;
302                 }
303                 if ('right' in obj) {
304                         this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
305                 }
306                 if ('top' in obj) {
307                         this.offset.click.top = obj.top + this.margins.top;
308                 }
309                 if ('bottom' in obj) {
310                         this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
311                 }
312         },
314         _getParentOffset: function() {
316                 //Get the offsetParent and cache its position
317                 this.offsetParent = this.helper.offsetParent();
318                 var po = this.offsetParent.offset();
320                 // This is a special case where we need to modify a offset calculated on start, since the following happened:
321                 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
322                 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
323                 //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
324                 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
325                         po.left += this.scrollParent.scrollLeft();
326                         po.top += this.scrollParent.scrollTop();
327                 }
329                 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
330                 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
331                         po = { top: 0, left: 0 };
333                 return {
334                         top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
335                         left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
336                 };
338         },
340         _getRelativeOffset: function() {
342                 if(this.cssPosition == "relative") {
343                         var p = this.element.position();
344                         return {
345                                 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
346                                 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
347                         };
348                 } else {
349                         return { top: 0, left: 0 };
350                 }
352         },
354         _cacheMargins: function() {
355                 this.margins = {
356                         left: (parseInt(this.element.css("marginLeft"),10) || 0),
357                         top: (parseInt(this.element.css("marginTop"),10) || 0),
358                         right: (parseInt(this.element.css("marginRight"),10) || 0),
359                         bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
360                 };
361         },
363         _cacheHelperProportions: function() {
364                 this.helperProportions = {
365                         width: this.helper.outerWidth(),
366                         height: this.helper.outerHeight()
367                 };
368         },
370         _setContainment: function() {
372                 var o = this.options;
373                 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
374                 if(o.containment == 'document' || o.containment == 'window') this.containment = [
375                         o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
376                         o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
377                         (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
378                         (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
379                 ];
381                 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
382                         var c = $(o.containment);
383                         var ce = c[0]; if(!ce) return;
384                         var co = c.offset();
385                         var over = ($(ce).css("overflow") != 'hidden');
387                         this.containment = [
388                                 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
389                                 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
390                                 (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,
391                                 (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
392                         ];
393                         this.relative_container = c;
395                 } else if(o.containment.constructor == Array) {
396                         this.containment = o.containment;
397                 }
399         },
401         _convertPositionTo: function(d, pos) {
403                 if(!pos) pos = this.position;
404                 var mod = d == "absolute" ? 1 : -1;
405                 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                 return {
408                         top: (
409                                 pos.top                                                                                                                                 // The absolute mouse position
410                                 + this.offset.relative.top * mod                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
411                                 + this.offset.parent.top * mod                                                                                  // The offsetParent's offset without borders (offset + border)
412                                 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
413                         ),
414                         left: (
415                                 pos.left                                                                                                                                // The absolute mouse position
416                                 + this.offset.relative.left * mod                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
417                                 + this.offset.parent.left * mod                                                                                 // The offsetParent's offset without borders (offset + border)
418                                 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
419                         )
420                 };
422         },
424         _generatePosition: function(event) {
426                 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);
427                 var pageX = event.pageX;
428                 var pageY = event.pageY;
430                 /*
431                  * - Position constraining -
432                  * Constrain the position to a mix of grid, containment.
433                  */
435                 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
436                          var containment;
437                          if(this.containment) {
438                                  if (this.relative_container){
439                                      var co = this.relative_container.offset();
440                                      containment = [ this.containment[0] + co.left,
441                                                      this.containment[1] + co.top,
442                                                      this.containment[2] + co.left,
443                                                      this.containment[3] + co.top ];
444                                  }
445                                  else {
446                                      containment = this.containment;
447                                  }
449                                 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
450                                 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
451                                 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
452                                 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
453                         }
455                         if(o.grid) {
456                                 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
457                                 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
458                                 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;
460                                 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
461                                 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;
462                         }
464                 }
466                 return {
467                         top: (
468                                 pageY                                                                                                                           // The absolute mouse position
469                                 - this.offset.click.top                                                                                                 // Click offset (relative to the element)
470                                 - this.offset.relative.top                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
471                                 - this.offset.parent.top                                                                                                // The offsetParent's offset without borders (offset + border)
472                                 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
473                         ),
474                         left: (
475                                 pageX                                                                                                                           // The absolute mouse position
476                                 - this.offset.click.left                                                                                                // Click offset (relative to the element)
477                                 - this.offset.relative.left                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
478                                 - this.offset.parent.left                                                                                               // The offsetParent's offset without borders (offset + border)
479                                 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
480                         )
481                 };
483         },
485         _clear: function() {
486                 this.helper.removeClass("ui-draggable-dragging");
487                 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
488                 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
489                 this.helper = null;
490                 this.cancelHelperRemoval = false;
491         },
493         // From now on bulk stuff - mainly helpers
495         _trigger: function(type, event, ui) {
496                 ui = ui || this._uiHash();
497                 $.ui.plugin.call(this, type, [event, ui]);
498                 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
499                 return $.Widget.prototype._trigger.call(this, type, event, ui);
500         },
502         plugins: {},
504         _uiHash: function(event) {
505                 return {
506                         helper: this.helper,
507                         position: this.position,
508                         originalPosition: this.originalPosition,
509                         offset: this.positionAbs
510                 };
511         }
515 $.extend($.ui.draggable, {
516         version: "1.8.23"
519 $.ui.plugin.add("draggable", "connectToSortable", {
520         start: function(event, ui) {
522                 var inst = $(this).data("draggable"), o = inst.options,
523                         uiSortable = $.extend({}, ui, { item: inst.element });
524                 inst.sortables = [];
525                 $(o.connectToSortable).each(function() {
526                         var sortable = $.data(this, 'sortable');
527                         if (sortable && !sortable.options.disabled) {
528                                 inst.sortables.push({
529                                         instance: sortable,
530                                         shouldRevert: sortable.options.revert
531                                 });
532                                 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).
533                                 sortable._trigger("activate", event, uiSortable);
534                         }
535                 });
537         },
538         stop: function(event, ui) {
540                 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
541                 var inst = $(this).data("draggable"),
542                         uiSortable = $.extend({}, ui, { item: inst.element });
544                 $.each(inst.sortables, function() {
545                         if(this.instance.isOver) {
547                                 this.instance.isOver = 0;
549                                 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
550                                 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
552                                 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
553                                 if(this.shouldRevert) this.instance.options.revert = true;
555                                 //Trigger the stop of the sortable
556                                 this.instance._mouseStop(event);
558                                 this.instance.options.helper = this.instance.options._helper;
560                                 //If the helper has been the original item, restore properties in the sortable
561                                 if(inst.options.helper == 'original')
562                                         this.instance.currentItem.css({ top: 'auto', left: 'auto' });
564                         } else {
565                                 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
566                                 this.instance._trigger("deactivate", event, uiSortable);
567                         }
569                 });
571         },
572         drag: function(event, ui) {
574                 var inst = $(this).data("draggable"), self = this;
576                 var checkPos = function(o) {
577                         var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
578                         var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
579                         var itemHeight = o.height, itemWidth = o.width;
580                         var itemTop = o.top, itemLeft = o.left;
582                         return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
583                 };
585                 $.each(inst.sortables, function(i) {
586                         
587                         //Copy over some variables to allow calling the sortable's native _intersectsWith
588                         this.instance.positionAbs = inst.positionAbs;
589                         this.instance.helperProportions = inst.helperProportions;
590                         this.instance.offset.click = inst.offset.click;
591                         
592                         if(this.instance._intersectsWith(this.instance.containerCache)) {
594                                 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
595                                 if(!this.instance.isOver) {
597                                         this.instance.isOver = 1;
598                                         //Now we fake the start of dragging for the sortable instance,
599                                         //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
600                                         //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)
601                                         this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
602                                         this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
603                                         this.instance.options.helper = function() { return ui.helper[0]; };
605                                         event.target = this.instance.currentItem[0];
606                                         this.instance._mouseCapture(event, true);
607                                         this.instance._mouseStart(event, true, true);
609                                         //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
610                                         this.instance.offset.click.top = inst.offset.click.top;
611                                         this.instance.offset.click.left = inst.offset.click.left;
612                                         this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
613                                         this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
615                                         inst._trigger("toSortable", event);
616                                         inst.dropped = this.instance.element; //draggable revert needs that
617                                         //hack so receive/update callbacks work (mostly)
618                                         inst.currentItem = inst.element;
619                                         this.instance.fromOutside = inst;
621                                 }
623                                 //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
624                                 if(this.instance.currentItem) this.instance._mouseDrag(event);
626                         } else {
628                                 //If it doesn't intersect with the sortable, and it intersected before,
629                                 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
630                                 if(this.instance.isOver) {
632                                         this.instance.isOver = 0;
633                                         this.instance.cancelHelperRemoval = true;
634                                         
635                                         //Prevent reverting on this forced stop
636                                         this.instance.options.revert = false;
637                                         
638                                         // The out event needs to be triggered independently
639                                         this.instance._trigger('out', event, this.instance._uiHash(this.instance));
640                                         
641                                         this.instance._mouseStop(event, true);
642                                         this.instance.options.helper = this.instance.options._helper;
644                                         //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
645                                         this.instance.currentItem.remove();
646                                         if(this.instance.placeholder) this.instance.placeholder.remove();
648                                         inst._trigger("fromSortable", event);
649                                         inst.dropped = false; //draggable revert needs that
650                                 }
652                         };
654                 });
656         }
659 $.ui.plugin.add("draggable", "cursor", {
660         start: function(event, ui) {
661                 var t = $('body'), o = $(this).data('draggable').options;
662                 if (t.css("cursor")) o._cursor = t.css("cursor");
663                 t.css("cursor", o.cursor);
664         },
665         stop: function(event, ui) {
666                 var o = $(this).data('draggable').options;
667                 if (o._cursor) $('body').css("cursor", o._cursor);
668         }
671 $.ui.plugin.add("draggable", "opacity", {
672         start: function(event, ui) {
673                 var t = $(ui.helper), o = $(this).data('draggable').options;
674                 if(t.css("opacity")) o._opacity = t.css("opacity");
675                 t.css('opacity', o.opacity);
676         },
677         stop: function(event, ui) {
678                 var o = $(this).data('draggable').options;
679                 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
680         }
683 $.ui.plugin.add("draggable", "scroll", {
684         start: function(event, ui) {
685                 var i = $(this).data("draggable");
686                 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
687         },
688         drag: function(event, ui) {
690                 var i = $(this).data("draggable"), o = i.options, scrolled = false;
692                 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
694                         if(!o.axis || o.axis != 'x') {
695                                 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
696                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
697                                 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
698                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
699                         }
701                         if(!o.axis || o.axis != 'y') {
702                                 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
703                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
704                                 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
705                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
706                         }
708                 } else {
710                         if(!o.axis || o.axis != 'x') {
711                                 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
712                                         scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
713                                 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
714                                         scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
715                         }
717                         if(!o.axis || o.axis != 'y') {
718                                 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
719                                         scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
720                                 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
721                                         scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
722                         }
724                 }
726                 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
727                         $.ui.ddmanager.prepareOffsets(i, event);
729         }
732 $.ui.plugin.add("draggable", "snap", {
733         start: function(event, ui) {
735                 var i = $(this).data("draggable"), o = i.options;
736                 i.snapElements = [];
738                 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
739                         var $t = $(this); var $o = $t.offset();
740                         if(this != i.element[0]) i.snapElements.push({
741                                 item: this,
742                                 width: $t.outerWidth(), height: $t.outerHeight(),
743                                 top: $o.top, left: $o.left
744                         });
745                 });
747         },
748         drag: function(event, ui) {
750                 var inst = $(this).data("draggable"), o = inst.options;
751                 var d = o.snapTolerance;
753                 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
754                         y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
756                 for (var i = inst.snapElements.length - 1; i >= 0; i--){
758                         var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
759                                 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
761                         //Yes, I know, this is insane ;)
762                         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))) {
763                                 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 })));
764                                 inst.snapElements[i].snapping = false;
765                                 continue;
766                         }
768                         if(o.snapMode != 'inner') {
769                                 var ts = Math.abs(t - y2) <= d;
770                                 var bs = Math.abs(b - y1) <= d;
771                                 var ls = Math.abs(l - x2) <= d;
772                                 var rs = Math.abs(r - x1) <= d;
773                                 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
774                                 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
775                                 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
776                                 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
777                         }
779                         var first = (ts || bs || ls || rs);
781                         if(o.snapMode != 'outer') {
782                                 var ts = Math.abs(t - y1) <= d;
783                                 var bs = Math.abs(b - y2) <= d;
784                                 var ls = Math.abs(l - x1) <= d;
785                                 var rs = Math.abs(r - x2) <= d;
786                                 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
787                                 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
788                                 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
789                                 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
790                         }
792                         if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
793                                 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
794                         inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
796                 };
798         }
801 $.ui.plugin.add("draggable", "stack", {
802         start: function(event, ui) {
804                 var o = $(this).data("draggable").options;
806                 var group = $.makeArray($(o.stack)).sort(function(a,b) {
807                         return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
808                 });
809                 if (!group.length) { return; }
810                 
811                 var min = parseInt(group[0].style.zIndex) || 0;
812                 $(group).each(function(i) {
813                         this.style.zIndex = min + i;
814                 });
816                 this[0].style.zIndex = min + group.length;
818         }
821 $.ui.plugin.add("draggable", "zIndex", {
822         start: function(event, ui) {
823                 var t = $(ui.helper), o = $(this).data("draggable").options;
824                 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
825                 t.css('zIndex', o.zIndex);
826         },
827         stop: function(event, ui) {
828                 var o = $(this).data("draggable").options;
829                 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
830         }
833 })(jQuery);