and a note on manual changes in dataTables.bootstrap.css
[myslice.git] / third-party / jquery-ui-1.10.2 / ui / jquery.ui.draggable.js
1 /*!
2  * jQuery UI Draggable 1.10.2
3  * http://jqueryui.com
4  *
5  * Copyright 2013 jQuery Foundation and other contributors
6  * Released under the MIT license.
7  * http://jquery.org/license
8  *
9  * http://api.jqueryui.com/draggable/
10  *
11  * Depends:
12  *      jquery.ui.core.js
13  *      jquery.ui.mouse.js
14  *      jquery.ui.widget.js
15  */
16 (function( $, undefined ) {
17
18 $.widget("ui.draggable", $.ui.mouse, {
19         version: "1.10.2",
20         widgetEventPrefix: "drag",
21         options: {
22                 addClasses: true,
23                 appendTo: "parent",
24                 axis: false,
25                 connectToSortable: false,
26                 containment: false,
27                 cursor: "auto",
28                 cursorAt: false,
29                 grid: false,
30                 handle: false,
31                 helper: "original",
32                 iframeFix: false,
33                 opacity: false,
34                 refreshPositions: false,
35                 revert: false,
36                 revertDuration: 500,
37                 scope: "default",
38                 scroll: true,
39                 scrollSensitivity: 20,
40                 scrollSpeed: 20,
41                 snap: false,
42                 snapMode: "both",
43                 snapTolerance: 20,
44                 stack: false,
45                 zIndex: false,
46
47                 // callbacks
48                 drag: null,
49                 start: null,
50                 stop: null
51         },
52         _create: function() {
53
54                 if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
55                         this.element[0].style.position = "relative";
56                 }
57                 if (this.options.addClasses){
58                         this.element.addClass("ui-draggable");
59                 }
60                 if (this.options.disabled){
61                         this.element.addClass("ui-draggable-disabled");
62                 }
63
64                 this._mouseInit();
65
66         },
67
68         _destroy: function() {
69                 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
70                 this._mouseDestroy();
71         },
72
73         _mouseCapture: function(event) {
74
75                 var o = this.options;
76
77                 // among others, prevent a drag on a resizable-handle
78                 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
79                         return false;
80                 }
81
82                 //Quit if we're not on a valid handle
83                 this.handle = this._getHandle(event);
84                 if (!this.handle) {
85                         return false;
86                 }
87
88                 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
89                         $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
90                         .css({
91                                 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
92                                 position: "absolute", opacity: "0.001", zIndex: 1000
93                         })
94                         .css($(this).offset())
95                         .appendTo("body");
96                 });
97
98                 return true;
99
100         },
101
102         _mouseStart: function(event) {
103
104                 var o = this.options;
105
106                 //Create and append the visible helper
107                 this.helper = this._createHelper(event);
108
109                 this.helper.addClass("ui-draggable-dragging");
110
111                 //Cache the helper size
112                 this._cacheHelperProportions();
113
114                 //If ddmanager is used for droppables, set the global draggable
115                 if($.ui.ddmanager) {
116                         $.ui.ddmanager.current = this;
117                 }
118
119                 /*
120                  * - Position generation -
121                  * This block generates everything position related - it's the core of draggables.
122                  */
123
124                 //Cache the margins of the original element
125                 this._cacheMargins();
126
127                 //Store the helper's css position
128                 this.cssPosition = this.helper.css("position");
129                 this.scrollParent = this.helper.scrollParent();
130
131                 //The element's absolute position on the page minus margins
132                 this.offset = this.positionAbs = this.element.offset();
133                 this.offset = {
134                         top: this.offset.top - this.margins.top,
135                         left: this.offset.left - this.margins.left
136                 };
137
138                 $.extend(this.offset, {
139                         click: { //Where the click happened, relative to the element
140                                 left: event.pageX - this.offset.left,
141                                 top: event.pageY - this.offset.top
142                         },
143                         parent: this._getParentOffset(),
144                         relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
145                 });
146
147                 //Generate the original position
148                 this.originalPosition = this.position = this._generatePosition(event);
149                 this.originalPageX = event.pageX;
150                 this.originalPageY = event.pageY;
151
152                 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
153                 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
154
155                 //Set a containment if given in the options
156                 if(o.containment) {
157                         this._setContainment();
158                 }
159
160                 //Trigger event + callbacks
161                 if(this._trigger("start", event) === false) {
162                         this._clear();
163                         return false;
164                 }
165
166                 //Recache the helper size
167                 this._cacheHelperProportions();
168
169                 //Prepare the droppable offsets
170                 if ($.ui.ddmanager && !o.dropBehaviour) {
171                         $.ui.ddmanager.prepareOffsets(this, event);
172                 }
173
174
175                 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
176
177                 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
178                 if ( $.ui.ddmanager ) {
179                         $.ui.ddmanager.dragStart(this, event);
180                 }
181
182                 return true;
183         },
184
185         _mouseDrag: function(event, noPropagation) {
186
187                 //Compute the helpers position
188                 this.position = this._generatePosition(event);
189                 this.positionAbs = this._convertPositionTo("absolute");
190
191                 //Call plugins and callbacks and use the resulting position if something is returned
192                 if (!noPropagation) {
193                         var ui = this._uiHash();
194                         if(this._trigger("drag", event, ui) === false) {
195                                 this._mouseUp({});
196                                 return false;
197                         }
198                         this.position = ui.position;
199                 }
200
201                 if(!this.options.axis || this.options.axis !== "y") {
202                         this.helper[0].style.left = this.position.left+"px";
203                 }
204                 if(!this.options.axis || this.options.axis !== "x") {
205                         this.helper[0].style.top = this.position.top+"px";
206                 }
207                 if($.ui.ddmanager) {
208                         $.ui.ddmanager.drag(this, event);
209                 }
210
211                 return false;
212         },
213
214         _mouseStop: function(event) {
215
216                 //If we are using droppables, inform the manager about the drop
217                 var element,
218                         that = this,
219                         elementInDom = false,
220                         dropped = false;
221                 if ($.ui.ddmanager && !this.options.dropBehaviour) {
222                         dropped = $.ui.ddmanager.drop(this, event);
223                 }
224
225                 //if a drop comes from outside (a sortable)
226                 if(this.dropped) {
227                         dropped = this.dropped;
228                         this.dropped = false;
229                 }
230
231                 //if the original element is no longer in the DOM don't bother to continue (see #8269)
232                 element = this.element[0];
233                 while ( element && (element = element.parentNode) ) {
234                         if (element === document ) {
235                                 elementInDom = true;
236                         }
237                 }
238                 if ( !elementInDom && this.options.helper === "original" ) {
239                         return false;
240                 }
241
242                 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))) {
243                         $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
244                                 if(that._trigger("stop", event) !== false) {
245                                         that._clear();
246                                 }
247                         });
248                 } else {
249                         if(this._trigger("stop", event) !== false) {
250                                 this._clear();
251                         }
252                 }
253
254                 return false;
255         },
256
257         _mouseUp: function(event) {
258                 //Remove frame helpers
259                 $("div.ui-draggable-iframeFix").each(function() {
260                         this.parentNode.removeChild(this);
261                 });
262
263                 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
264                 if( $.ui.ddmanager ) {
265                         $.ui.ddmanager.dragStop(this, event);
266                 }
267
268                 return $.ui.mouse.prototype._mouseUp.call(this, event);
269         },
270
271         cancel: function() {
272
273                 if(this.helper.is(".ui-draggable-dragging")) {
274                         this._mouseUp({});
275                 } else {
276                         this._clear();
277                 }
278
279                 return this;
280
281         },
282
283         _getHandle: function(event) {
284                 return this.options.handle ?
285                         !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
286                         true;
287         },
288
289         _createHelper: function(event) {
290
291                 var o = this.options,
292                         helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
293
294                 if(!helper.parents("body").length) {
295                         helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
296                 }
297
298                 if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
299                         helper.css("position", "absolute");
300                 }
301
302                 return helper;
303
304         },
305
306         _adjustOffsetFromHelper: function(obj) {
307                 if (typeof obj === "string") {
308                         obj = obj.split(" ");
309                 }
310                 if ($.isArray(obj)) {
311                         obj = {left: +obj[0], top: +obj[1] || 0};
312                 }
313                 if ("left" in obj) {
314                         this.offset.click.left = obj.left + this.margins.left;
315                 }
316                 if ("right" in obj) {
317                         this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
318                 }
319                 if ("top" in obj) {
320                         this.offset.click.top = obj.top + this.margins.top;
321                 }
322                 if ("bottom" in obj) {
323                         this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
324                 }
325         },
326
327         _getParentOffset: function() {
328
329                 //Get the offsetParent and cache its position
330                 this.offsetParent = this.helper.offsetParent();
331                 var po = this.offsetParent.offset();
332
333                 // This is a special case where we need to modify a offset calculated on start, since the following happened:
334                 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
335                 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
336                 //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
337                 if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
338                         po.left += this.scrollParent.scrollLeft();
339                         po.top += this.scrollParent.scrollTop();
340                 }
341
342                 //This needs to be actually done for all browsers, since pageX/pageY includes this information
343                 //Ugly IE fix
344                 if((this.offsetParent[0] === document.body) ||
345                         (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
346                         po = { top: 0, left: 0 };
347                 }
348
349                 return {
350                         top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
351                         left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
352                 };
353
354         },
355
356         _getRelativeOffset: function() {
357
358                 if(this.cssPosition === "relative") {
359                         var p = this.element.position();
360                         return {
361                                 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
362                                 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
363                         };
364                 } else {
365                         return { top: 0, left: 0 };
366                 }
367
368         },
369
370         _cacheMargins: function() {
371                 this.margins = {
372                         left: (parseInt(this.element.css("marginLeft"),10) || 0),
373                         top: (parseInt(this.element.css("marginTop"),10) || 0),
374                         right: (parseInt(this.element.css("marginRight"),10) || 0),
375                         bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
376                 };
377         },
378
379         _cacheHelperProportions: function() {
380                 this.helperProportions = {
381                         width: this.helper.outerWidth(),
382                         height: this.helper.outerHeight()
383                 };
384         },
385
386         _setContainment: function() {
387
388                 var over, c, ce,
389                         o = this.options;
390
391                 if(o.containment === "parent") {
392                         o.containment = this.helper[0].parentNode;
393                 }
394                 if(o.containment === "document" || o.containment === "window") {
395                         this.containment = [
396                                 o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
397                                 o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
398                                 (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
399                                 (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
400                         ];
401                 }
402
403                 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) {
404                         c = $(o.containment);
405                         ce = c[0];
406
407                         if(!ce) {
408                                 return;
409                         }
410
411                         over = ($(ce).css("overflow") !== "hidden");
412
413                         this.containment = [
414                                 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
415                                 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
416                                 (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderRightWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
417                                 (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderBottomWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top  - this.margins.bottom
418                         ];
419                         this.relative_container = c;
420
421                 } else if(o.containment.constructor === Array) {
422                         this.containment = o.containment;
423                 }
424
425         },
426
427         _convertPositionTo: function(d, pos) {
428
429                 if(!pos) {
430                         pos = this.position;
431                 }
432
433                 var mod = d === "absolute" ? 1 : -1,
434                         scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
435
436                 return {
437                         top: (
438                                 pos.top +                                                                                                                               // The absolute mouse position
439                                 this.offset.relative.top * mod +                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
440                                 this.offset.parent.top * mod -                                                                          // The offsetParent's offset without borders (offset + border)
441                                 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
442                         ),
443                         left: (
444                                 pos.left +                                                                                                                              // The absolute mouse position
445                                 this.offset.relative.left * mod +                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
446                                 this.offset.parent.left * mod   -                                                                               // The offsetParent's offset without borders (offset + border)
447                                 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
448                         )
449                 };
450
451         },
452
453         _generatePosition: function(event) {
454
455                 var containment, co, top, left,
456                         o = this.options,
457                         scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
458                         scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName),
459                         pageX = event.pageX,
460                         pageY = event.pageY;
461
462                 /*
463                  * - Position constraining -
464                  * Constrain the position to a mix of grid, containment.
465                  */
466
467                 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
468                         if(this.containment) {
469                         if (this.relative_container){
470                                 co = this.relative_container.offset();
471                                 containment = [ this.containment[0] + co.left,
472                                         this.containment[1] + co.top,
473                                         this.containment[2] + co.left,
474                                         this.containment[3] + co.top ];
475                         }
476                         else {
477                                 containment = this.containment;
478                         }
479
480                                 if(event.pageX - this.offset.click.left < containment[0]) {
481                                         pageX = containment[0] + this.offset.click.left;
482                                 }
483                                 if(event.pageY - this.offset.click.top < containment[1]) {
484                                         pageY = containment[1] + this.offset.click.top;
485                                 }
486                                 if(event.pageX - this.offset.click.left > containment[2]) {
487                                         pageX = containment[2] + this.offset.click.left;
488                                 }
489                                 if(event.pageY - this.offset.click.top > containment[3]) {
490                                         pageY = containment[3] + this.offset.click.top;
491                                 }
492                         }
493
494                         if(o.grid) {
495                                 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
496                                 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
497                                 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;
498
499                                 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
500                                 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;
501                         }
502
503                 }
504
505                 return {
506                         top: (
507                                 pageY -                                                                                                                                 // The absolute mouse position
508                                 this.offset.click.top   -                                                                                               // Click offset (relative to the element)
509                                 this.offset.relative.top -                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
510                                 this.offset.parent.top +                                                                                                // The offsetParent's offset without borders (offset + border)
511                                 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
512                         ),
513                         left: (
514                                 pageX -                                                                                                                                 // The absolute mouse position
515                                 this.offset.click.left -                                                                                                // Click offset (relative to the element)
516                                 this.offset.relative.left -                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
517                                 this.offset.parent.left +                                                                                               // The offsetParent's offset without borders (offset + border)
518                                 ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
519                         )
520                 };
521
522         },
523
524         _clear: function() {
525                 this.helper.removeClass("ui-draggable-dragging");
526                 if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
527                         this.helper.remove();
528                 }
529                 this.helper = null;
530                 this.cancelHelperRemoval = false;
531         },
532
533         // From now on bulk stuff - mainly helpers
534
535         _trigger: function(type, event, ui) {
536                 ui = ui || this._uiHash();
537                 $.ui.plugin.call(this, type, [event, ui]);
538                 //The absolute position has to be recalculated after plugins
539                 if(type === "drag") {
540                         this.positionAbs = this._convertPositionTo("absolute");
541                 }
542                 return $.Widget.prototype._trigger.call(this, type, event, ui);
543         },
544
545         plugins: {},
546
547         _uiHash: function() {
548                 return {
549                         helper: this.helper,
550                         position: this.position,
551                         originalPosition: this.originalPosition,
552                         offset: this.positionAbs
553                 };
554         }
555
556 });
557
558 $.ui.plugin.add("draggable", "connectToSortable", {
559         start: function(event, ui) {
560
561                 var inst = $(this).data("ui-draggable"), o = inst.options,
562                         uiSortable = $.extend({}, ui, { item: inst.element });
563                 inst.sortables = [];
564                 $(o.connectToSortable).each(function() {
565                         var sortable = $.data(this, "ui-sortable");
566                         if (sortable && !sortable.options.disabled) {
567                                 inst.sortables.push({
568                                         instance: sortable,
569                                         shouldRevert: sortable.options.revert
570                                 });
571                                 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).
572                                 sortable._trigger("activate", event, uiSortable);
573                         }
574                 });
575
576         },
577         stop: function(event, ui) {
578
579                 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
580                 var inst = $(this).data("ui-draggable"),
581                         uiSortable = $.extend({}, ui, { item: inst.element });
582
583                 $.each(inst.sortables, function() {
584                         if(this.instance.isOver) {
585
586                                 this.instance.isOver = 0;
587
588                                 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
589                                 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
590
591                                 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
592                                 if(this.shouldRevert) {
593                                         this.instance.options.revert = this.shouldRevert;
594                                 }
595
596                                 //Trigger the stop of the sortable
597                                 this.instance._mouseStop(event);
598
599                                 this.instance.options.helper = this.instance.options._helper;
600
601                                 //If the helper has been the original item, restore properties in the sortable
602                                 if(inst.options.helper === "original") {
603                                         this.instance.currentItem.css({ top: "auto", left: "auto" });
604                                 }
605
606                         } else {
607                                 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
608                                 this.instance._trigger("deactivate", event, uiSortable);
609                         }
610
611                 });
612
613         },
614         drag: function(event, ui) {
615
616                 var inst = $(this).data("ui-draggable"), that = this;
617
618                 $.each(inst.sortables, function() {
619
620                         var innermostIntersecting = false,
621                                 thisSortable = this;
622
623                         //Copy over some variables to allow calling the sortable's native _intersectsWith
624                         this.instance.positionAbs = inst.positionAbs;
625                         this.instance.helperProportions = inst.helperProportions;
626                         this.instance.offset.click = inst.offset.click;
627
628                         if(this.instance._intersectsWith(this.instance.containerCache)) {
629                                 innermostIntersecting = true;
630                                 $.each(inst.sortables, function () {
631                                         this.instance.positionAbs = inst.positionAbs;
632                                         this.instance.helperProportions = inst.helperProportions;
633                                         this.instance.offset.click = inst.offset.click;
634                                         if (this !== thisSortable &&
635                                                 this.instance._intersectsWith(this.instance.containerCache) &&
636                                                 $.contains(thisSortable.instance.element[0], this.instance.element[0])
637                                         ) {
638                                                 innermostIntersecting = false;
639                                         }
640                                         return innermostIntersecting;
641                                 });
642                         }
643
644
645                         if(innermostIntersecting) {
646                                 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
647                                 if(!this.instance.isOver) {
648
649                                         this.instance.isOver = 1;
650                                         //Now we fake the start of dragging for the sortable instance,
651                                         //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
652                                         //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)
653                                         this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
654                                         this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
655                                         this.instance.options.helper = function() { return ui.helper[0]; };
656
657                                         event.target = this.instance.currentItem[0];
658                                         this.instance._mouseCapture(event, true);
659                                         this.instance._mouseStart(event, true, true);
660
661                                         //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
662                                         this.instance.offset.click.top = inst.offset.click.top;
663                                         this.instance.offset.click.left = inst.offset.click.left;
664                                         this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
665                                         this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
666
667                                         inst._trigger("toSortable", event);
668                                         inst.dropped = this.instance.element; //draggable revert needs that
669                                         //hack so receive/update callbacks work (mostly)
670                                         inst.currentItem = inst.element;
671                                         this.instance.fromOutside = inst;
672
673                                 }
674
675                                 //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
676                                 if(this.instance.currentItem) {
677                                         this.instance._mouseDrag(event);
678                                 }
679
680                         } else {
681
682                                 //If it doesn't intersect with the sortable, and it intersected before,
683                                 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
684                                 if(this.instance.isOver) {
685
686                                         this.instance.isOver = 0;
687                                         this.instance.cancelHelperRemoval = true;
688
689                                         //Prevent reverting on this forced stop
690                                         this.instance.options.revert = false;
691
692                                         // The out event needs to be triggered independently
693                                         this.instance._trigger("out", event, this.instance._uiHash(this.instance));
694
695                                         this.instance._mouseStop(event, true);
696                                         this.instance.options.helper = this.instance.options._helper;
697
698                                         //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
699                                         this.instance.currentItem.remove();
700                                         if(this.instance.placeholder) {
701                                                 this.instance.placeholder.remove();
702                                         }
703
704                                         inst._trigger("fromSortable", event);
705                                         inst.dropped = false; //draggable revert needs that
706                                 }
707
708                         }
709
710                 });
711
712         }
713 });
714
715 $.ui.plugin.add("draggable", "cursor", {
716         start: function() {
717                 var t = $("body"), o = $(this).data("ui-draggable").options;
718                 if (t.css("cursor")) {
719                         o._cursor = t.css("cursor");
720                 }
721                 t.css("cursor", o.cursor);
722         },
723         stop: function() {
724                 var o = $(this).data("ui-draggable").options;
725                 if (o._cursor) {
726                         $("body").css("cursor", o._cursor);
727                 }
728         }
729 });
730
731 $.ui.plugin.add("draggable", "opacity", {
732         start: function(event, ui) {
733                 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
734                 if(t.css("opacity")) {
735                         o._opacity = t.css("opacity");
736                 }
737                 t.css("opacity", o.opacity);
738         },
739         stop: function(event, ui) {
740                 var o = $(this).data("ui-draggable").options;
741                 if(o._opacity) {
742                         $(ui.helper).css("opacity", o._opacity);
743                 }
744         }
745 });
746
747 $.ui.plugin.add("draggable", "scroll", {
748         start: function() {
749                 var i = $(this).data("ui-draggable");
750                 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
751                         i.overflowOffset = i.scrollParent.offset();
752                 }
753         },
754         drag: function( event ) {
755
756                 var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
757
758                 if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
759
760                         if(!o.axis || o.axis !== "x") {
761                                 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
762                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
763                                 } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
764                                         i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
765                                 }
766                         }
767
768                         if(!o.axis || o.axis !== "y") {
769                                 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
770                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
771                                 } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
772                                         i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
773                                 }
774                         }
775
776                 } else {
777
778                         if(!o.axis || o.axis !== "x") {
779                                 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
780                                         scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
781                                 } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
782                                         scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
783                                 }
784                         }
785
786                         if(!o.axis || o.axis !== "y") {
787                                 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
788                                         scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
789                                 } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
790                                         scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
791                                 }
792                         }
793
794                 }
795
796                 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
797                         $.ui.ddmanager.prepareOffsets(i, event);
798                 }
799
800         }
801 });
802
803 $.ui.plugin.add("draggable", "snap", {
804         start: function() {
805
806                 var i = $(this).data("ui-draggable"),
807                         o = i.options;
808
809                 i.snapElements = [];
810
811                 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
812                         var $t = $(this),
813                                 $o = $t.offset();
814                         if(this !== i.element[0]) {
815                                 i.snapElements.push({
816                                         item: this,
817                                         width: $t.outerWidth(), height: $t.outerHeight(),
818                                         top: $o.top, left: $o.left
819                                 });
820                         }
821                 });
822
823         },
824         drag: function(event, ui) {
825
826                 var ts, bs, ls, rs, l, r, t, b, i, first,
827                         inst = $(this).data("ui-draggable"),
828                         o = inst.options,
829                         d = o.snapTolerance,
830                         x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
831                         y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
832
833                 for (i = inst.snapElements.length - 1; i >= 0; i--){
834
835                         l = inst.snapElements[i].left;
836                         r = l + inst.snapElements[i].width;
837                         t = inst.snapElements[i].top;
838                         b = t + inst.snapElements[i].height;
839
840                         //Yes, I know, this is insane ;)
841                         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))) {
842                                 if(inst.snapElements[i].snapping) {
843                                         (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
844                                 }
845                                 inst.snapElements[i].snapping = false;
846                                 continue;
847                         }
848
849                         if(o.snapMode !== "inner") {
850                                 ts = Math.abs(t - y2) <= d;
851                                 bs = Math.abs(b - y1) <= d;
852                                 ls = Math.abs(l - x2) <= d;
853                                 rs = Math.abs(r - x1) <= d;
854                                 if(ts) {
855                                         ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
856                                 }
857                                 if(bs) {
858                                         ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
859                                 }
860                                 if(ls) {
861                                         ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
862                                 }
863                                 if(rs) {
864                                         ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
865                                 }
866                         }
867
868                         first = (ts || bs || ls || rs);
869
870                         if(o.snapMode !== "outer") {
871                                 ts = Math.abs(t - y1) <= d;
872                                 bs = Math.abs(b - y2) <= d;
873                                 ls = Math.abs(l - x1) <= d;
874                                 rs = Math.abs(r - x2) <= d;
875                                 if(ts) {
876                                         ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
877                                 }
878                                 if(bs) {
879                                         ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
880                                 }
881                                 if(ls) {
882                                         ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
883                                 }
884                                 if(rs) {
885                                         ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
886                                 }
887                         }
888
889                         if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
890                                 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
891                         }
892                         inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
893
894                 }
895
896         }
897 });
898
899 $.ui.plugin.add("draggable", "stack", {
900         start: function() {
901                 var min,
902                         o = this.data("ui-draggable").options,
903                         group = $.makeArray($(o.stack)).sort(function(a,b) {
904                                 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
905                         });
906
907                 if (!group.length) { return; }
908
909                 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
910                 $(group).each(function(i) {
911                         $(this).css("zIndex", min + i);
912                 });
913                 this.css("zIndex", (min + group.length));
914         }
915 });
916
917 $.ui.plugin.add("draggable", "zIndex", {
918         start: function(event, ui) {
919                 var t = $(ui.helper), o = $(this).data("ui-draggable").options;
920                 if(t.css("zIndex")) {
921                         o._zIndex = t.css("zIndex");
922                 }
923                 t.css("zIndex", o.zIndex);
924         },
925         stop: function(event, ui) {
926                 var o = $(this).data("ui-draggable").options;
927                 if(o._zIndex) {
928                         $(ui.helper).css("zIndex", o._zIndex);
929                 }
930         }
931 });
932
933 })(jQuery);