2 * jQuery UI Draggable 1.8.21
4 * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about)
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 * http://jquery.org/license
8 * http://docs.jquery.com/UI/Draggables
15 (function( $, undefined ) {
17 $.widget("ui.draggable", $.ui.mouse, {
18 widgetEventPrefix: "drag",
23 connectToSortable: false,
32 refreshPositions: false,
37 scrollSensitivity: 20,
47 if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
48 this.element[0].style.position = 'relative';
50 (this.options.addClasses && this.element.addClass("ui-draggable"));
51 (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
58 if(!this.element.data('draggable')) return;
60 .removeData("draggable")
62 .removeClass("ui-draggable"
63 + " ui-draggable-dragging"
64 + " ui-draggable-disabled");
70 _mouseCapture: function(event) {
74 // among others, prevent a drag on a resizable-handle
75 if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
78 //Quit if we're not on a valid handle
79 this.handle = this._getHandle(event);
84 $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
85 $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
87 width: this.offsetWidth+"px", height: this.offsetHeight+"px",
88 position: "absolute", opacity: "0.001", zIndex: 1000
90 .css($(this).offset())
99 _mouseStart: function(event) {
101 var o = this.options;
103 //Create and append the visible helper
104 this.helper = this._createHelper(event);
106 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
113 $.ui.ddmanager.current = this;
116 * - Position generation -
117 * This block generates everything position related - it's the core of draggables.
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();
130 top: this.offset.top - this.margins.top,
131 left: this.offset.left - this.margins.left
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
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
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
153 this._setContainment();
155 //Trigger event + callbacks
156 if(this._trigger("start", event) === false) {
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);
169 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
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);
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) {
190 this.position = ui.position;
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);
200 _mouseStop: function(event) {
202 //If we are using droppables, inform the manager about the drop
204 if ($.ui.ddmanager && !this.options.dropBehaviour)
205 dropped = $.ui.ddmanager.drop(this, event);
207 //if a drop comes from outside (a sortable)
209 dropped = this.dropped;
210 this.dropped = false;
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 ) {
220 if ( !elementInDom && this.options.helper === "original" )
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))) {
225 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
226 if(self._trigger("stop", event) !== false) {
231 if(this._trigger("stop", event) !== false) {
239 _mouseUp: function(event) {
240 if (this.options.iframeFix === true) {
241 $("div.ui-draggable-iframeFix").each(function() {
242 this.parentNode.removeChild(this);
243 }); //Remove frame helpers
246 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
247 if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
249 return $.ui.mouse.prototype._mouseUp.call(this, event);
254 if(this.helper.is(".ui-draggable-dragging")) {
264 _getHandle: function(event) {
266 var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
267 $(this.options.handle, this.element)
271 if(this == event.target) handle = true;
278 _createHelper: function(event) {
280 var o = this.options;
281 var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
283 if(!helper.parents('body').length)
284 helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
286 if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
287 helper.css("position", "absolute");
293 _adjustOffsetFromHelper: function(obj) {
294 if (typeof obj == 'string') {
295 obj = obj.split(' ');
297 if ($.isArray(obj)) {
298 obj = {left: +obj[0], top: +obj[1] || 0};
301 this.offset.click.left = obj.left + this.margins.left;
303 if ('right' in obj) {
304 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
307 this.offset.click.top = obj.top + this.margins.top;
309 if ('bottom' in obj) {
310 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
314 _getParentOffset: function() {
316 //Get the offsetParent and cache its position
317 this.offsetParent = this.helper.offsetParent();
318 var po = this.offsetParent.offset();
320 // This is a special case where we need to modify a offset calculated on start, since the following happened:
321 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
322 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
323 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
324 if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
325 po.left += this.scrollParent.scrollLeft();
326 po.top += this.scrollParent.scrollTop();
329 if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
330 || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
331 po = { top: 0, left: 0 };
334 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
335 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
340 _getRelativeOffset: function() {
342 if(this.cssPosition == "relative") {
343 var p = this.element.position();
345 top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
346 left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
349 return { top: 0, left: 0 };
354 _cacheMargins: function() {
356 left: (parseInt(this.element.css("marginLeft"),10) || 0),
357 top: (parseInt(this.element.css("marginTop"),10) || 0),
358 right: (parseInt(this.element.css("marginRight"),10) || 0),
359 bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
363 _cacheHelperProportions: function() {
364 this.helperProportions = {
365 width: this.helper.outerWidth(),
366 height: this.helper.outerHeight()
370 _setContainment: function() {
372 var o = this.options;
373 if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
374 if(o.containment == 'document' || o.containment == 'window') this.containment = [
375 o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
376 o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
377 (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
378 (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
381 if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
382 var c = $(o.containment);
383 var ce = c[0]; if(!ce) return;
385 var over = ($(ce).css("overflow") != 'hidden');
388 (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
389 (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
390 (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
391 (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
393 this.relative_container = c;
395 } else if(o.containment.constructor == Array) {
396 this.containment = o.containment;
401 _convertPositionTo: function(d, pos) {
403 if(!pos) pos = this.position;
404 var mod = d == "absolute" ? 1 : -1;
405 var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
409 pos.top // The absolute mouse position
410 + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
411 + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
412 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
415 pos.left // The absolute mouse position
416 + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
417 + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
418 - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
424 _generatePosition: function(event) {
426 var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
427 var pageX = event.pageX;
428 var pageY = event.pageY;
431 * - Position constraining -
432 * Constrain the position to a mix of grid, containment.
435 if(this.originalPosition) { //If we are not dragging yet, we won't check for options
437 if(this.containment) {
438 if (this.relative_container){
439 var co = this.relative_container.offset();
440 containment = [ this.containment[0] + co.left,
441 this.containment[1] + co.top,
442 this.containment[2] + co.left,
443 this.containment[3] + co.top ];
446 containment = this.containment;
449 if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
450 if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
451 if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
452 if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
456 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
457 var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
458 pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
460 var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
461 pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
468 pageY // The absolute mouse position
469 - this.offset.click.top // Click offset (relative to the element)
470 - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
471 - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
472 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
475 pageX // The absolute mouse position
476 - this.offset.click.left // Click offset (relative to the element)
477 - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
478 - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
479 + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
486 this.helper.removeClass("ui-draggable-dragging");
487 if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
488 //if($.ui.ddmanager) $.ui.ddmanager.current = null;
490 this.cancelHelperRemoval = false;
493 // From now on bulk stuff - mainly helpers
495 _trigger: function(type, event, ui) {
496 ui = ui || this._uiHash();
497 $.ui.plugin.call(this, type, [event, ui]);
498 if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
499 return $.Widget.prototype._trigger.call(this, type, event, ui);
504 _uiHash: function(event) {
507 position: this.position,
508 originalPosition: this.originalPosition,
509 offset: this.positionAbs
515 $.extend($.ui.draggable, {
519 $.ui.plugin.add("draggable", "connectToSortable", {
520 start: function(event, ui) {
522 var inst = $(this).data("draggable"), o = inst.options,
523 uiSortable = $.extend({}, ui, { item: inst.element });
525 $(o.connectToSortable).each(function() {
526 var sortable = $.data(this, 'sortable');
527 if (sortable && !sortable.options.disabled) {
528 inst.sortables.push({
530 shouldRevert: sortable.options.revert
532 sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
533 sortable._trigger("activate", event, uiSortable);
538 stop: function(event, ui) {
540 //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
541 var inst = $(this).data("draggable"),
542 uiSortable = $.extend({}, ui, { item: inst.element });
544 $.each(inst.sortables, function() {
545 if(this.instance.isOver) {
547 this.instance.isOver = 0;
549 inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
550 this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
552 //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
553 if(this.shouldRevert) this.instance.options.revert = true;
555 //Trigger the stop of the sortable
556 this.instance._mouseStop(event);
558 this.instance.options.helper = this.instance.options._helper;
560 //If the helper has been the original item, restore properties in the sortable
561 if(inst.options.helper == 'original')
562 this.instance.currentItem.css({ top: 'auto', left: 'auto' });
565 this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
566 this.instance._trigger("deactivate", event, uiSortable);
572 drag: function(event, ui) {
574 var inst = $(this).data("draggable"), self = this;
576 var checkPos = function(o) {
577 var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
578 var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
579 var itemHeight = o.height, itemWidth = o.width;
580 var itemTop = o.top, itemLeft = o.left;
582 return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
585 $.each(inst.sortables, function(i) {
587 //Copy over some variables to allow calling the sortable's native _intersectsWith
588 this.instance.positionAbs = inst.positionAbs;
589 this.instance.helperProportions = inst.helperProportions;
590 this.instance.offset.click = inst.offset.click;
592 if(this.instance._intersectsWith(this.instance.containerCache)) {
594 //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
595 if(!this.instance.isOver) {
597 this.instance.isOver = 1;
598 //Now we fake the start of dragging for the sortable instance,
599 //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
600 //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
601 this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
602 this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
603 this.instance.options.helper = function() { return ui.helper[0]; };
605 event.target = this.instance.currentItem[0];
606 this.instance._mouseCapture(event, true);
607 this.instance._mouseStart(event, true, true);
609 //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
610 this.instance.offset.click.top = inst.offset.click.top;
611 this.instance.offset.click.left = inst.offset.click.left;
612 this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
613 this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
615 inst._trigger("toSortable", event);
616 inst.dropped = this.instance.element; //draggable revert needs that
617 //hack so receive/update callbacks work (mostly)
618 inst.currentItem = inst.element;
619 this.instance.fromOutside = inst;
623 //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
624 if(this.instance.currentItem) this.instance._mouseDrag(event);
628 //If it doesn't intersect with the sortable, and it intersected before,
629 //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
630 if(this.instance.isOver) {
632 this.instance.isOver = 0;
633 this.instance.cancelHelperRemoval = true;
635 //Prevent reverting on this forced stop
636 this.instance.options.revert = false;
638 // The out event needs to be triggered independently
639 this.instance._trigger('out', event, this.instance._uiHash(this.instance));
641 this.instance._mouseStop(event, true);
642 this.instance.options.helper = this.instance.options._helper;
644 //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
645 this.instance.currentItem.remove();
646 if(this.instance.placeholder) this.instance.placeholder.remove();
648 inst._trigger("fromSortable", event);
649 inst.dropped = false; //draggable revert needs that
659 $.ui.plugin.add("draggable", "cursor", {
660 start: function(event, ui) {
661 var t = $('body'), o = $(this).data('draggable').options;
662 if (t.css("cursor")) o._cursor = t.css("cursor");
663 t.css("cursor", o.cursor);
665 stop: function(event, ui) {
666 var o = $(this).data('draggable').options;
667 if (o._cursor) $('body').css("cursor", o._cursor);
671 $.ui.plugin.add("draggable", "opacity", {
672 start: function(event, ui) {
673 var t = $(ui.helper), o = $(this).data('draggable').options;
674 if(t.css("opacity")) o._opacity = t.css("opacity");
675 t.css('opacity', o.opacity);
677 stop: function(event, ui) {
678 var o = $(this).data('draggable').options;
679 if(o._opacity) $(ui.helper).css('opacity', o._opacity);
683 $.ui.plugin.add("draggable", "scroll", {
684 start: function(event, ui) {
685 var i = $(this).data("draggable");
686 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
688 drag: function(event, ui) {
690 var i = $(this).data("draggable"), o = i.options, scrolled = false;
692 if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
694 if(!o.axis || o.axis != 'x') {
695 if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
696 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
697 else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
698 i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
701 if(!o.axis || o.axis != 'y') {
702 if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
703 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
704 else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
705 i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
710 if(!o.axis || o.axis != 'x') {
711 if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
712 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
713 else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
714 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
717 if(!o.axis || o.axis != 'y') {
718 if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
719 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
720 else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
721 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
726 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
727 $.ui.ddmanager.prepareOffsets(i, event);
732 $.ui.plugin.add("draggable", "snap", {
733 start: function(event, ui) {
735 var i = $(this).data("draggable"), o = i.options;
738 $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
739 var $t = $(this); var $o = $t.offset();
740 if(this != i.element[0]) i.snapElements.push({
742 width: $t.outerWidth(), height: $t.outerHeight(),
743 top: $o.top, left: $o.left
748 drag: function(event, ui) {
750 var inst = $(this).data("draggable"), o = inst.options;
751 var d = o.snapTolerance;
753 var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
754 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
756 for (var i = inst.snapElements.length - 1; i >= 0; i--){
758 var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
759 t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
761 //Yes, I know, this is insane ;)
762 if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
763 if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
764 inst.snapElements[i].snapping = false;
768 if(o.snapMode != 'inner') {
769 var ts = Math.abs(t - y2) <= d;
770 var bs = Math.abs(b - y1) <= d;
771 var ls = Math.abs(l - x2) <= d;
772 var rs = Math.abs(r - x1) <= d;
773 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
774 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
775 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
776 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
779 var first = (ts || bs || ls || rs);
781 if(o.snapMode != 'outer') {
782 var ts = Math.abs(t - y1) <= d;
783 var bs = Math.abs(b - y2) <= d;
784 var ls = Math.abs(l - x1) <= d;
785 var rs = Math.abs(r - x2) <= d;
786 if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
787 if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
788 if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
789 if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
792 if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
793 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
794 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
801 $.ui.plugin.add("draggable", "stack", {
802 start: function(event, ui) {
804 var o = $(this).data("draggable").options;
806 var group = $.makeArray($(o.stack)).sort(function(a,b) {
807 return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
809 if (!group.length) { return; }
811 var min = parseInt(group[0].style.zIndex) || 0;
812 $(group).each(function(i) {
813 this.style.zIndex = min + i;
816 this[0].style.zIndex = min + group.length;
821 $.ui.plugin.add("draggable", "zIndex", {
822 start: function(event, ui) {
823 var t = $(ui.helper), o = $(this).data("draggable").options;
824 if(t.css("zIndex")) o._zIndex = t.css("zIndex");
825 t.css('zIndex', o.zIndex);
827 stop: function(event, ui) {
828 var o = $(this).data("draggable").options;
829 if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);