Merge "Add ss_active_users in SiteStats::isSane"
[mediawiki.git] / resources / jquery.ui / jquery.ui.draggable.js
blob149035c247c36a0d57386a96ef6771999a479015
1 /*!
2  * jQuery UI Draggable 1.8.24
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                 //Remove frame helpers
241                 $("div.ui-draggable-iframeFix").each(function() { 
242                         this.parentNode.removeChild(this); 
243                 });
244                 
245                 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
246                 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
247                 
248                 return $.ui.mouse.prototype._mouseUp.call(this, event);
249         },
250         
251         cancel: function() {
252                 
253                 if(this.helper.is(".ui-draggable-dragging")) {
254                         this._mouseUp({});
255                 } else {
256                         this._clear();
257                 }
258                 
259                 return this;
260                 
261         },
263         _getHandle: function(event) {
265                 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
266                 $(this.options.handle, this.element)
267                         .find("*")
268                         .andSelf()
269                         .each(function() {
270                                 if(this == event.target) handle = true;
271                         });
273                 return handle;
275         },
277         _createHelper: function(event) {
279                 var o = this.options;
280                 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
282                 if(!helper.parents('body').length)
283                         helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
285                 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
286                         helper.css("position", "absolute");
288                 return helper;
290         },
292         _adjustOffsetFromHelper: function(obj) {
293                 if (typeof obj == 'string') {
294                         obj = obj.split(' ');
295                 }
296                 if ($.isArray(obj)) {
297                         obj = {left: +obj[0], top: +obj[1] || 0};
298                 }
299                 if ('left' in obj) {
300                         this.offset.click.left = obj.left + this.margins.left;
301                 }
302                 if ('right' in obj) {
303                         this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
304                 }
305                 if ('top' in obj) {
306                         this.offset.click.top = obj.top + this.margins.top;
307                 }
308                 if ('bottom' in obj) {
309                         this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
310                 }
311         },
313         _getParentOffset: function() {
315                 //Get the offsetParent and cache its position
316                 this.offsetParent = this.helper.offsetParent();
317                 var po = this.offsetParent.offset();
319                 // This is a special case where we need to modify a offset calculated on start, since the following happened:
320                 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
321                 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
322                 //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
323                 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
324                         po.left += this.scrollParent.scrollLeft();
325                         po.top += this.scrollParent.scrollTop();
326                 }
328                 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
329                 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
330                         po = { top: 0, left: 0 };
332                 return {
333                         top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
334                         left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
335                 };
337         },
339         _getRelativeOffset: function() {
341                 if(this.cssPosition == "relative") {
342                         var p = this.element.position();
343                         return {
344                                 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
345                                 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
346                         };
347                 } else {
348                         return { top: 0, left: 0 };
349                 }
351         },
353         _cacheMargins: function() {
354                 this.margins = {
355                         left: (parseInt(this.element.css("marginLeft"),10) || 0),
356                         top: (parseInt(this.element.css("marginTop"),10) || 0),
357                         right: (parseInt(this.element.css("marginRight"),10) || 0),
358                         bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
359                 };
360         },
362         _cacheHelperProportions: function() {
363                 this.helperProportions = {
364                         width: this.helper.outerWidth(),
365                         height: this.helper.outerHeight()
366                 };
367         },
369         _setContainment: function() {
371                 var o = this.options;
372                 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
373                 if(o.containment == 'document' || o.containment == 'window') this.containment = [
374                         o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
375                         o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
376                         (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
377                         (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
378                 ];
380                 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
381                         var c = $(o.containment);
382                         var ce = c[0]; if(!ce) return;
383                         var co = c.offset();
384                         var over = ($(ce).css("overflow") != 'hidden');
386                         this.containment = [
387                                 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
388                                 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
389                                 (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,
390                                 (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                         ];
392                         this.relative_container = c;
394                 } else if(o.containment.constructor == Array) {
395                         this.containment = o.containment;
396                 }
398         },
400         _convertPositionTo: function(d, pos) {
402                 if(!pos) pos = this.position;
403                 var mod = d == "absolute" ? 1 : -1;
404                 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);
406                 return {
407                         top: (
408                                 pos.top                                                                                                                                 // The absolute mouse position
409                                 + this.offset.relative.top * mod                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
410                                 + this.offset.parent.top * mod                                                                                  // The offsetParent's offset without borders (offset + border)
411                                 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
412                         ),
413                         left: (
414                                 pos.left                                                                                                                                // The absolute mouse position
415                                 + this.offset.relative.left * mod                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
416                                 + this.offset.parent.left * mod                                                                                 // The offsetParent's offset without borders (offset + border)
417                                 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
418                         )
419                 };
421         },
423         _generatePosition: function(event) {
425                 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);
426                 var pageX = event.pageX;
427                 var pageY = event.pageY;
429                 /*
430                  * - Position constraining -
431                  * Constrain the position to a mix of grid, containment.
432                  */
434                 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
435                          var containment;
436                          if(this.containment) {
437                                  if (this.relative_container){
438                                      var co = this.relative_container.offset();
439                                      containment = [ this.containment[0] + co.left,
440                                                      this.containment[1] + co.top,
441                                                      this.containment[2] + co.left,
442                                                      this.containment[3] + co.top ];
443                                  }
444                                  else {
445                                      containment = this.containment;
446                                  }
448                                 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
449                                 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
450                                 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
451                                 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
452                         }
454                         if(o.grid) {
455                                 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
456                                 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
457                                 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;
459                                 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
460                                 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;
461                         }
463                 }
465                 return {
466                         top: (
467                                 pageY                                                                                                                           // The absolute mouse position
468                                 - this.offset.click.top                                                                                                 // Click offset (relative to the element)
469                                 - this.offset.relative.top                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
470                                 - this.offset.parent.top                                                                                                // The offsetParent's offset without borders (offset + border)
471                                 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
472                         ),
473                         left: (
474                                 pageX                                                                                                                           // The absolute mouse position
475                                 - this.offset.click.left                                                                                                // Click offset (relative to the element)
476                                 - this.offset.relative.left                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
477                                 - this.offset.parent.left                                                                                               // The offsetParent's offset without borders (offset + border)
478                                 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
479                         )
480                 };
482         },
484         _clear: function() {
485                 this.helper.removeClass("ui-draggable-dragging");
486                 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
487                 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
488                 this.helper = null;
489                 this.cancelHelperRemoval = false;
490         },
492         // From now on bulk stuff - mainly helpers
494         _trigger: function(type, event, ui) {
495                 ui = ui || this._uiHash();
496                 $.ui.plugin.call(this, type, [event, ui]);
497                 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
498                 return $.Widget.prototype._trigger.call(this, type, event, ui);
499         },
501         plugins: {},
503         _uiHash: function(event) {
504                 return {
505                         helper: this.helper,
506                         position: this.position,
507                         originalPosition: this.originalPosition,
508                         offset: this.positionAbs
509                 };
510         }
514 $.extend($.ui.draggable, {
515         version: "1.8.24"
518 $.ui.plugin.add("draggable", "connectToSortable", {
519         start: function(event, ui) {
521                 var inst = $(this).data("draggable"), o = inst.options,
522                         uiSortable = $.extend({}, ui, { item: inst.element });
523                 inst.sortables = [];
524                 $(o.connectToSortable).each(function() {
525                         var sortable = $.data(this, 'sortable');
526                         if (sortable && !sortable.options.disabled) {
527                                 inst.sortables.push({
528                                         instance: sortable,
529                                         shouldRevert: sortable.options.revert
530                                 });
531                                 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).
532                                 sortable._trigger("activate", event, uiSortable);
533                         }
534                 });
536         },
537         stop: function(event, ui) {
539                 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
540                 var inst = $(this).data("draggable"),
541                         uiSortable = $.extend({}, ui, { item: inst.element });
543                 $.each(inst.sortables, function() {
544                         if(this.instance.isOver) {
546                                 this.instance.isOver = 0;
548                                 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
549                                 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
551                                 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
552                                 if(this.shouldRevert) this.instance.options.revert = true;
554                                 //Trigger the stop of the sortable
555                                 this.instance._mouseStop(event);
557                                 this.instance.options.helper = this.instance.options._helper;
559                                 //If the helper has been the original item, restore properties in the sortable
560                                 if(inst.options.helper == 'original')
561                                         this.instance.currentItem.css({ top: 'auto', left: 'auto' });
563                         } else {
564                                 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
565                                 this.instance._trigger("deactivate", event, uiSortable);
566                         }
568                 });
570         },
571         drag: function(event, ui) {
573                 var inst = $(this).data("draggable"), self = this;
575                 var checkPos = function(o) {
576                         var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
577                         var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
578                         var itemHeight = o.height, itemWidth = o.width;
579                         var itemTop = o.top, itemLeft = o.left;
581                         return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
582                 };
584                 $.each(inst.sortables, function(i) {
585                         
586                         //Copy over some variables to allow calling the sortable's native _intersectsWith
587                         this.instance.positionAbs = inst.positionAbs;
588                         this.instance.helperProportions = inst.helperProportions;
589                         this.instance.offset.click = inst.offset.click;
590                         
591                         if(this.instance._intersectsWith(this.instance.containerCache)) {
593                                 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
594                                 if(!this.instance.isOver) {
596                                         this.instance.isOver = 1;
597                                         //Now we fake the start of dragging for the sortable instance,
598                                         //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
599                                         //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)
600                                         this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
601                                         this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
602                                         this.instance.options.helper = function() { return ui.helper[0]; };
604                                         event.target = this.instance.currentItem[0];
605                                         this.instance._mouseCapture(event, true);
606                                         this.instance._mouseStart(event, true, true);
608                                         //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
609                                         this.instance.offset.click.top = inst.offset.click.top;
610                                         this.instance.offset.click.left = inst.offset.click.left;
611                                         this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
612                                         this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
614                                         inst._trigger("toSortable", event);
615                                         inst.dropped = this.instance.element; //draggable revert needs that
616                                         //hack so receive/update callbacks work (mostly)
617                                         inst.currentItem = inst.element;
618                                         this.instance.fromOutside = inst;
620                                 }
622                                 //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
623                                 if(this.instance.currentItem) this.instance._mouseDrag(event);
625                         } else {
627                                 //If it doesn't intersect with the sortable, and it intersected before,
628                                 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
629                                 if(this.instance.isOver) {
631                                         this.instance.isOver = 0;
632                                         this.instance.cancelHelperRemoval = true;
633                                         
634                                         //Prevent reverting on this forced stop
635                                         this.instance.options.revert = false;
636                                         
637                                         // The out event needs to be triggered independently
638                                         this.instance._trigger('out', event, this.instance._uiHash(this.instance));
639                                         
640                                         this.instance._mouseStop(event, true);
641                                         this.instance.options.helper = this.instance.options._helper;
643                                         //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
644                                         this.instance.currentItem.remove();
645                                         if(this.instance.placeholder) this.instance.placeholder.remove();
647                                         inst._trigger("fromSortable", event);
648                                         inst.dropped = false; //draggable revert needs that
649                                 }
651                         };
653                 });
655         }
658 $.ui.plugin.add("draggable", "cursor", {
659         start: function(event, ui) {
660                 var t = $('body'), o = $(this).data('draggable').options;
661                 if (t.css("cursor")) o._cursor = t.css("cursor");
662                 t.css("cursor", o.cursor);
663         },
664         stop: function(event, ui) {
665                 var o = $(this).data('draggable').options;
666                 if (o._cursor) $('body').css("cursor", o._cursor);
667         }
670 $.ui.plugin.add("draggable", "opacity", {
671         start: function(event, ui) {
672                 var t = $(ui.helper), o = $(this).data('draggable').options;
673                 if(t.css("opacity")) o._opacity = t.css("opacity");
674                 t.css('opacity', o.opacity);
675         },
676         stop: function(event, ui) {
677                 var o = $(this).data('draggable').options;
678                 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
679         }
682 $.ui.plugin.add("draggable", "scroll", {
683         start: function(event, ui) {
684                 var i = $(this).data("draggable");
685                 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
686         },
687         drag: function(event, ui) {
689                 var i = $(this).data("draggable"), o = i.options, scrolled = false;
691                 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
693                         if(!o.axis || o.axis != 'x') {
694                                 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
695                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
696                                 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
697                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
698                         }
700                         if(!o.axis || o.axis != 'y') {
701                                 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
702                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
703                                 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
704                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
705                         }
707                 } else {
709                         if(!o.axis || o.axis != 'x') {
710                                 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
711                                         scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
712                                 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
713                                         scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
714                         }
716                         if(!o.axis || o.axis != 'y') {
717                                 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
718                                         scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
719                                 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
720                                         scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
721                         }
723                 }
725                 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
726                         $.ui.ddmanager.prepareOffsets(i, event);
728         }
731 $.ui.plugin.add("draggable", "snap", {
732         start: function(event, ui) {
734                 var i = $(this).data("draggable"), o = i.options;
735                 i.snapElements = [];
737                 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
738                         var $t = $(this); var $o = $t.offset();
739                         if(this != i.element[0]) i.snapElements.push({
740                                 item: this,
741                                 width: $t.outerWidth(), height: $t.outerHeight(),
742                                 top: $o.top, left: $o.left
743                         });
744                 });
746         },
747         drag: function(event, ui) {
749                 var inst = $(this).data("draggable"), o = inst.options;
750                 var d = o.snapTolerance;
752                 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
753                         y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
755                 for (var i = inst.snapElements.length - 1; i >= 0; i--){
757                         var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
758                                 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
760                         //Yes, I know, this is insane ;)
761                         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))) {
762                                 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 })));
763                                 inst.snapElements[i].snapping = false;
764                                 continue;
765                         }
767                         if(o.snapMode != 'inner') {
768                                 var ts = Math.abs(t - y2) <= d;
769                                 var bs = Math.abs(b - y1) <= d;
770                                 var ls = Math.abs(l - x2) <= d;
771                                 var rs = Math.abs(r - x1) <= d;
772                                 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
773                                 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
774                                 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
775                                 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
776                         }
778                         var first = (ts || bs || ls || rs);
780                         if(o.snapMode != 'outer') {
781                                 var ts = Math.abs(t - y1) <= d;
782                                 var bs = Math.abs(b - y2) <= d;
783                                 var ls = Math.abs(l - x1) <= d;
784                                 var rs = Math.abs(r - x2) <= d;
785                                 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
786                                 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
787                                 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
788                                 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
789                         }
791                         if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
792                                 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
793                         inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
795                 };
797         }
800 $.ui.plugin.add("draggable", "stack", {
801         start: function(event, ui) {
803                 var o = $(this).data("draggable").options;
805                 var group = $.makeArray($(o.stack)).sort(function(a,b) {
806                         return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
807                 });
808                 if (!group.length) { return; }
809                 
810                 var min = parseInt(group[0].style.zIndex) || 0;
811                 $(group).each(function(i) {
812                         this.style.zIndex = min + i;
813                 });
815                 this[0].style.zIndex = min + group.length;
817         }
820 $.ui.plugin.add("draggable", "zIndex", {
821         start: function(event, ui) {
822                 var t = $(ui.helper), o = $(this).data("draggable").options;
823                 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
824                 t.css('zIndex', o.zIndex);
825         },
826         stop: function(event, ui) {
827                 var o = $(this).data("draggable").options;
828                 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
829         }
832 })(jQuery);