angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

draggable.js (35343B)


      1 /*!
      2  * jQuery UI Draggable 1.12.1
      3  * http://jqueryui.com
      4  *
      5  * Copyright jQuery Foundation and other contributors
      6  * Released under the MIT license.
      7  * http://jquery.org/license
      8  */
      9 
     10 //>>label: Draggable
     11 //>>group: Interactions
     12 //>>description: Enables dragging functionality for any element.
     13 //>>docs: http://api.jqueryui.com/draggable/
     14 //>>demos: http://jqueryui.com/draggable/
     15 //>>css.structure: ../../themes/base/draggable.css
     16 
     17 ( function( factory ) {
     18 	if ( typeof define === "function" && define.amd ) {
     19 
     20 		// AMD. Register as an anonymous module.
     21 		define( [
     22 			"jquery",
     23 			"./mouse",
     24 			"./core"
     25 		], factory );
     26 	} else {
     27 
     28 		// Browser globals
     29 		factory( jQuery );
     30 	}
     31 }( function( $ ) {
     32 
     33 $.widget( "ui.draggable", $.ui.mouse, {
     34 	version: "1.12.1",
     35 	widgetEventPrefix: "drag",
     36 	options: {
     37 		addClasses: true,
     38 		appendTo: "parent",
     39 		axis: false,
     40 		connectToSortable: false,
     41 		containment: false,
     42 		cursor: "auto",
     43 		cursorAt: false,
     44 		grid: false,
     45 		handle: false,
     46 		helper: "original",
     47 		iframeFix: false,
     48 		opacity: false,
     49 		refreshPositions: false,
     50 		revert: false,
     51 		revertDuration: 500,
     52 		scope: "default",
     53 		scroll: true,
     54 		scrollSensitivity: 20,
     55 		scrollSpeed: 20,
     56 		snap: false,
     57 		snapMode: "both",
     58 		snapTolerance: 20,
     59 		stack: false,
     60 		zIndex: false,
     61 
     62 		// Callbacks
     63 		drag: null,
     64 		start: null,
     65 		stop: null
     66 	},
     67 	_create: function() {
     68 
     69 		if ( this.options.helper === "original" ) {
     70 			this._setPositionRelative();
     71 		}
     72 		if ( this.options.addClasses ) {
     73 			this._addClass( "ui-draggable" );
     74 		}
     75 		this._setHandleClassName();
     76 
     77 		this._mouseInit();
     78 	},
     79 
     80 	_setOption: function( key, value ) {
     81 		this._super( key, value );
     82 		if ( key === "handle" ) {
     83 			this._removeHandleClassName();
     84 			this._setHandleClassName();
     85 		}
     86 	},
     87 
     88 	_destroy: function() {
     89 		if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
     90 			this.destroyOnClear = true;
     91 			return;
     92 		}
     93 		this._removeHandleClassName();
     94 		this._mouseDestroy();
     95 	},
     96 
     97 	_mouseCapture: function( event ) {
     98 		var o = this.options;
     99 
    100 		// Among others, prevent a drag on a resizable-handle
    101 		if ( this.helper || o.disabled ||
    102 				$( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
    103 			return false;
    104 		}
    105 
    106 		//Quit if we're not on a valid handle
    107 		this.handle = this._getHandle( event );
    108 		if ( !this.handle ) {
    109 			return false;
    110 		}
    111 
    112 		this._blurActiveElement( event );
    113 
    114 		this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
    115 
    116 		return true;
    117 
    118 	},
    119 
    120 	_blockFrames: function( selector ) {
    121 		this.iframeBlocks = this.document.find( selector ).map( function() {
    122 			var iframe = $( this );
    123 
    124 			return $( "<div>" )
    125 				.css( "position", "absolute" )
    126 				.appendTo( iframe.parent() )
    127 				.outerWidth( iframe.outerWidth() )
    128 				.outerHeight( iframe.outerHeight() )
    129 				.offset( iframe.offset() )[ 0 ];
    130 		} );
    131 	},
    132 
    133 	_unblockFrames: function() {
    134 		if ( this.iframeBlocks ) {
    135 			this.iframeBlocks.remove();
    136 			delete this.iframeBlocks;
    137 		}
    138 	},
    139 
    140 	_blurActiveElement: function( event ) {
    141 		var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
    142 			target = $( event.target );
    143 
    144 		// Don't blur if the event occurred on an element that is within
    145 		// the currently focused element
    146 		// See #10527, #12472
    147 		if ( target.closest( activeElement ).length ) {
    148 			return;
    149 		}
    150 
    151 		// Blur any element that currently has focus, see #4261
    152 		$.ui.safeBlur( activeElement );
    153 	},
    154 
    155 	_mouseStart: function( event ) {
    156 
    157 		var o = this.options;
    158 
    159 		//Create and append the visible helper
    160 		this.helper = this._createHelper( event );
    161 
    162 		this._addClass( this.helper, "ui-draggable-dragging" );
    163 
    164 		//Cache the helper size
    165 		this._cacheHelperProportions();
    166 
    167 		//If ddmanager is used for droppables, set the global draggable
    168 		if ( $.ui.ddmanager ) {
    169 			$.ui.ddmanager.current = this;
    170 		}
    171 
    172 		/*
    173 		 * - Position generation -
    174 		 * This block generates everything position related - it's the core of draggables.
    175 		 */
    176 
    177 		//Cache the margins of the original element
    178 		this._cacheMargins();
    179 
    180 		//Store the helper's css position
    181 		this.cssPosition = this.helper.css( "position" );
    182 		this.scrollParent = this.helper.scrollParent( true );
    183 		this.offsetParent = this.helper.offsetParent();
    184 		this.hasFixedAncestor = this.helper.parents().filter( function() {
    185 				return $( this ).css( "position" ) === "fixed";
    186 			} ).length > 0;
    187 
    188 		//The element's absolute position on the page minus margins
    189 		this.positionAbs = this.element.offset();
    190 		this._refreshOffsets( event );
    191 
    192 		//Generate the original position
    193 		this.originalPosition = this.position = this._generatePosition( event, false );
    194 		this.originalPageX = event.pageX;
    195 		this.originalPageY = event.pageY;
    196 
    197 		//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
    198 		( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
    199 
    200 		//Set a containment if given in the options
    201 		this._setContainment();
    202 
    203 		//Trigger event + callbacks
    204 		if ( this._trigger( "start", event ) === false ) {
    205 			this._clear();
    206 			return false;
    207 		}
    208 
    209 		//Recache the helper size
    210 		this._cacheHelperProportions();
    211 
    212 		//Prepare the droppable offsets
    213 		if ( $.ui.ddmanager && !o.dropBehaviour ) {
    214 			$.ui.ddmanager.prepareOffsets( this, event );
    215 		}
    216 
    217 		// Execute the drag once - this causes the helper not to be visible before getting its
    218 		// correct position
    219 		this._mouseDrag( event, true );
    220 
    221 		// If the ddmanager is used for droppables, inform the manager that dragging has started
    222 		// (see #5003)
    223 		if ( $.ui.ddmanager ) {
    224 			$.ui.ddmanager.dragStart( this, event );
    225 		}
    226 
    227 		return true;
    228 	},
    229 
    230 	_refreshOffsets: function( event ) {
    231 		this.offset = {
    232 			top: this.positionAbs.top - this.margins.top,
    233 			left: this.positionAbs.left - this.margins.left,
    234 			scroll: false,
    235 			parent: this._getParentOffset(),
    236 			relative: this._getRelativeOffset()
    237 		};
    238 
    239 		this.offset.click = {
    240 			left: event.pageX - this.offset.left,
    241 			top: event.pageY - this.offset.top
    242 		};
    243 	},
    244 
    245 	_mouseDrag: function( event, noPropagation ) {
    246 
    247 		// reset any necessary cached properties (see #5009)
    248 		if ( this.hasFixedAncestor ) {
    249 			this.offset.parent = this._getParentOffset();
    250 		}
    251 
    252 		//Compute the helpers position
    253 		this.position = this._generatePosition( event, true );
    254 		this.positionAbs = this._convertPositionTo( "absolute" );
    255 
    256 		//Call plugins and callbacks and use the resulting position if something is returned
    257 		if ( !noPropagation ) {
    258 			var ui = this._uiHash();
    259 			if ( this._trigger( "drag", event, ui ) === false ) {
    260 				this._mouseUp( new $.Event( "mouseup", event ) );
    261 				return false;
    262 			}
    263 			this.position = ui.position;
    264 		}
    265 
    266 		this.helper[ 0 ].style.left = this.position.left + "px";
    267 		this.helper[ 0 ].style.top = this.position.top + "px";
    268 
    269 		if ( $.ui.ddmanager ) {
    270 			$.ui.ddmanager.drag( this, event );
    271 		}
    272 
    273 		return false;
    274 	},
    275 
    276 	_mouseStop: function( event ) {
    277 
    278 		//If we are using droppables, inform the manager about the drop
    279 		var that = this,
    280 			dropped = false;
    281 		if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
    282 			dropped = $.ui.ddmanager.drop( this, event );
    283 		}
    284 
    285 		//if a drop comes from outside (a sortable)
    286 		if ( this.dropped ) {
    287 			dropped = this.dropped;
    288 			this.dropped = false;
    289 		}
    290 
    291 		if ( ( this.options.revert === "invalid" && !dropped ) ||
    292 				( this.options.revert === "valid" && dropped ) ||
    293 				this.options.revert === true || ( $.isFunction( this.options.revert ) &&
    294 				this.options.revert.call( this.element, dropped ) )
    295 		) {
    296 			$( this.helper ).animate(
    297 				this.originalPosition,
    298 				parseInt( this.options.revertDuration, 10 ),
    299 				function() {
    300 					if ( that._trigger( "stop", event ) !== false ) {
    301 						that._clear();
    302 					}
    303 				}
    304 			);
    305 		} else {
    306 			if ( this._trigger( "stop", event ) !== false ) {
    307 				this._clear();
    308 			}
    309 		}
    310 
    311 		return false;
    312 	},
    313 
    314 	_mouseUp: function( event ) {
    315 		this._unblockFrames();
    316 
    317 		// If the ddmanager is used for droppables, inform the manager that dragging has stopped
    318 		// (see #5003)
    319 		if ( $.ui.ddmanager ) {
    320 			$.ui.ddmanager.dragStop( this, event );
    321 		}
    322 
    323 		// Only need to focus if the event occurred on the draggable itself, see #10527
    324 		if ( this.handleElement.is( event.target ) ) {
    325 
    326 			// The interaction is over; whether or not the click resulted in a drag,
    327 			// focus the element
    328 			this.element.trigger( "focus" );
    329 		}
    330 
    331 		return $.ui.mouse.prototype._mouseUp.call( this, event );
    332 	},
    333 
    334 	cancel: function() {
    335 
    336 		if ( this.helper.is( ".ui-draggable-dragging" ) ) {
    337 			this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
    338 		} else {
    339 			this._clear();
    340 		}
    341 
    342 		return this;
    343 
    344 	},
    345 
    346 	_getHandle: function( event ) {
    347 		return this.options.handle ?
    348 			!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
    349 			true;
    350 	},
    351 
    352 	_setHandleClassName: function() {
    353 		this.handleElement = this.options.handle ?
    354 			this.element.find( this.options.handle ) : this.element;
    355 		this._addClass( this.handleElement, "ui-draggable-handle" );
    356 	},
    357 
    358 	_removeHandleClassName: function() {
    359 		this._removeClass( this.handleElement, "ui-draggable-handle" );
    360 	},
    361 
    362 	_createHelper: function( event ) {
    363 
    364 		var o = this.options,
    365 			helperIsFunction = $.isFunction( o.helper ),
    366 			helper = helperIsFunction ?
    367 				$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
    368 				( o.helper === "clone" ?
    369 					this.element.clone().removeAttr( "id" ) :
    370 					this.element );
    371 
    372 		if ( !helper.parents( "body" ).length ) {
    373 			helper.appendTo( ( o.appendTo === "parent" ?
    374 				this.element[ 0 ].parentNode :
    375 				o.appendTo ) );
    376 		}
    377 
    378 		// Http://bugs.jqueryui.com/ticket/9446
    379 		// a helper function can return the original element
    380 		// which wouldn't have been set to relative in _create
    381 		if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
    382 			this._setPositionRelative();
    383 		}
    384 
    385 		if ( helper[ 0 ] !== this.element[ 0 ] &&
    386 				!( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
    387 			helper.css( "position", "absolute" );
    388 		}
    389 
    390 		return helper;
    391 
    392 	},
    393 
    394 	_setPositionRelative: function() {
    395 		if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
    396 			this.element[ 0 ].style.position = "relative";
    397 		}
    398 	},
    399 
    400 	_adjustOffsetFromHelper: function( obj ) {
    401 		if ( typeof obj === "string" ) {
    402 			obj = obj.split( " " );
    403 		}
    404 		if ( $.isArray( obj ) ) {
    405 			obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
    406 		}
    407 		if ( "left" in obj ) {
    408 			this.offset.click.left = obj.left + this.margins.left;
    409 		}
    410 		if ( "right" in obj ) {
    411 			this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
    412 		}
    413 		if ( "top" in obj ) {
    414 			this.offset.click.top = obj.top + this.margins.top;
    415 		}
    416 		if ( "bottom" in obj ) {
    417 			this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
    418 		}
    419 	},
    420 
    421 	_isRootNode: function( element ) {
    422 		return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
    423 	},
    424 
    425 	_getParentOffset: function() {
    426 
    427 		//Get the offsetParent and cache its position
    428 		var po = this.offsetParent.offset(),
    429 			document = this.document[ 0 ];
    430 
    431 		// This is a special case where we need to modify a offset calculated on start, since the
    432 		// following happened:
    433 		// 1. The position of the helper is absolute, so it's position is calculated based on the
    434 		// next positioned parent
    435 		// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
    436 		// the document, which means that the scroll is included in the initial calculation of the
    437 		// offset of the parent, and never recalculated upon drag
    438 		if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
    439 				$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
    440 			po.left += this.scrollParent.scrollLeft();
    441 			po.top += this.scrollParent.scrollTop();
    442 		}
    443 
    444 		if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
    445 			po = { top: 0, left: 0 };
    446 		}
    447 
    448 		return {
    449 			top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
    450 			left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
    451 		};
    452 
    453 	},
    454 
    455 	_getRelativeOffset: function() {
    456 		if ( this.cssPosition !== "relative" ) {
    457 			return { top: 0, left: 0 };
    458 		}
    459 
    460 		var p = this.element.position(),
    461 			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
    462 
    463 		return {
    464 			top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
    465 				( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
    466 			left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
    467 				( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
    468 		};
    469 
    470 	},
    471 
    472 	_cacheMargins: function() {
    473 		this.margins = {
    474 			left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
    475 			top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
    476 			right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
    477 			bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
    478 		};
    479 	},
    480 
    481 	_cacheHelperProportions: function() {
    482 		this.helperProportions = {
    483 			width: this.helper.outerWidth(),
    484 			height: this.helper.outerHeight()
    485 		};
    486 	},
    487 
    488 	_setContainment: function() {
    489 
    490 		var isUserScrollable, c, ce,
    491 			o = this.options,
    492 			document = this.document[ 0 ];
    493 
    494 		this.relativeContainer = null;
    495 
    496 		if ( !o.containment ) {
    497 			this.containment = null;
    498 			return;
    499 		}
    500 
    501 		if ( o.containment === "window" ) {
    502 			this.containment = [
    503 				$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
    504 				$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
    505 				$( window ).scrollLeft() + $( window ).width() -
    506 					this.helperProportions.width - this.margins.left,
    507 				$( window ).scrollTop() +
    508 					( $( window ).height() || document.body.parentNode.scrollHeight ) -
    509 					this.helperProportions.height - this.margins.top
    510 			];
    511 			return;
    512 		}
    513 
    514 		if ( o.containment === "document" ) {
    515 			this.containment = [
    516 				0,
    517 				0,
    518 				$( document ).width() - this.helperProportions.width - this.margins.left,
    519 				( $( document ).height() || document.body.parentNode.scrollHeight ) -
    520 					this.helperProportions.height - this.margins.top
    521 			];
    522 			return;
    523 		}
    524 
    525 		if ( o.containment.constructor === Array ) {
    526 			this.containment = o.containment;
    527 			return;
    528 		}
    529 
    530 		if ( o.containment === "parent" ) {
    531 			o.containment = this.helper[ 0 ].parentNode;
    532 		}
    533 
    534 		c = $( o.containment );
    535 		ce = c[ 0 ];
    536 
    537 		if ( !ce ) {
    538 			return;
    539 		}
    540 
    541 		isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
    542 
    543 		this.containment = [
    544 			( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
    545 				( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
    546 			( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
    547 				( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
    548 			( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
    549 				( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
    550 				( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
    551 				this.helperProportions.width -
    552 				this.margins.left -
    553 				this.margins.right,
    554 			( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
    555 				( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
    556 				( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
    557 				this.helperProportions.height -
    558 				this.margins.top -
    559 				this.margins.bottom
    560 		];
    561 		this.relativeContainer = c;
    562 	},
    563 
    564 	_convertPositionTo: function( d, pos ) {
    565 
    566 		if ( !pos ) {
    567 			pos = this.position;
    568 		}
    569 
    570 		var mod = d === "absolute" ? 1 : -1,
    571 			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
    572 
    573 		return {
    574 			top: (
    575 
    576 				// The absolute mouse position
    577 				pos.top	+
    578 
    579 				// Only for relative positioned nodes: Relative offset from element to offset parent
    580 				this.offset.relative.top * mod +
    581 
    582 				// The offsetParent's offset without borders (offset + border)
    583 				this.offset.parent.top * mod -
    584 				( ( this.cssPosition === "fixed" ?
    585 					-this.offset.scroll.top :
    586 					( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
    587 			),
    588 			left: (
    589 
    590 				// The absolute mouse position
    591 				pos.left +
    592 
    593 				// Only for relative positioned nodes: Relative offset from element to offset parent
    594 				this.offset.relative.left * mod +
    595 
    596 				// The offsetParent's offset without borders (offset + border)
    597 				this.offset.parent.left * mod	-
    598 				( ( this.cssPosition === "fixed" ?
    599 					-this.offset.scroll.left :
    600 					( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
    601 			)
    602 		};
    603 
    604 	},
    605 
    606 	_generatePosition: function( event, constrainPosition ) {
    607 
    608 		var containment, co, top, left,
    609 			o = this.options,
    610 			scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
    611 			pageX = event.pageX,
    612 			pageY = event.pageY;
    613 
    614 		// Cache the scroll
    615 		if ( !scrollIsRootNode || !this.offset.scroll ) {
    616 			this.offset.scroll = {
    617 				top: this.scrollParent.scrollTop(),
    618 				left: this.scrollParent.scrollLeft()
    619 			};
    620 		}
    621 
    622 		/*
    623 		 * - Position constraining -
    624 		 * Constrain the position to a mix of grid, containment.
    625 		 */
    626 
    627 		// If we are not dragging yet, we won't check for options
    628 		if ( constrainPosition ) {
    629 			if ( this.containment ) {
    630 				if ( this.relativeContainer ) {
    631 					co = this.relativeContainer.offset();
    632 					containment = [
    633 						this.containment[ 0 ] + co.left,
    634 						this.containment[ 1 ] + co.top,
    635 						this.containment[ 2 ] + co.left,
    636 						this.containment[ 3 ] + co.top
    637 					];
    638 				} else {
    639 					containment = this.containment;
    640 				}
    641 
    642 				if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
    643 					pageX = containment[ 0 ] + this.offset.click.left;
    644 				}
    645 				if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
    646 					pageY = containment[ 1 ] + this.offset.click.top;
    647 				}
    648 				if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
    649 					pageX = containment[ 2 ] + this.offset.click.left;
    650 				}
    651 				if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
    652 					pageY = containment[ 3 ] + this.offset.click.top;
    653 				}
    654 			}
    655 
    656 			if ( o.grid ) {
    657 
    658 				//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
    659 				// argument errors in IE (see ticket #6950)
    660 				top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
    661 					this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
    662 				pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
    663 					top - this.offset.click.top > containment[ 3 ] ) ?
    664 						top :
    665 						( ( top - this.offset.click.top >= containment[ 1 ] ) ?
    666 							top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
    667 
    668 				left = o.grid[ 0 ] ? this.originalPageX +
    669 					Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
    670 					this.originalPageX;
    671 				pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
    672 					left - this.offset.click.left > containment[ 2 ] ) ?
    673 						left :
    674 						( ( left - this.offset.click.left >= containment[ 0 ] ) ?
    675 							left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
    676 			}
    677 
    678 			if ( o.axis === "y" ) {
    679 				pageX = this.originalPageX;
    680 			}
    681 
    682 			if ( o.axis === "x" ) {
    683 				pageY = this.originalPageY;
    684 			}
    685 		}
    686 
    687 		return {
    688 			top: (
    689 
    690 				// The absolute mouse position
    691 				pageY -
    692 
    693 				// Click offset (relative to the element)
    694 				this.offset.click.top -
    695 
    696 				// Only for relative positioned nodes: Relative offset from element to offset parent
    697 				this.offset.relative.top -
    698 
    699 				// The offsetParent's offset without borders (offset + border)
    700 				this.offset.parent.top +
    701 				( this.cssPosition === "fixed" ?
    702 					-this.offset.scroll.top :
    703 					( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
    704 			),
    705 			left: (
    706 
    707 				// The absolute mouse position
    708 				pageX -
    709 
    710 				// Click offset (relative to the element)
    711 				this.offset.click.left -
    712 
    713 				// Only for relative positioned nodes: Relative offset from element to offset parent
    714 				this.offset.relative.left -
    715 
    716 				// The offsetParent's offset without borders (offset + border)
    717 				this.offset.parent.left +
    718 				( this.cssPosition === "fixed" ?
    719 					-this.offset.scroll.left :
    720 					( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
    721 			)
    722 		};
    723 
    724 	},
    725 
    726 	_clear: function() {
    727 		this._removeClass( this.helper, "ui-draggable-dragging" );
    728 		if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
    729 			this.helper.remove();
    730 		}
    731 		this.helper = null;
    732 		this.cancelHelperRemoval = false;
    733 		if ( this.destroyOnClear ) {
    734 			this.destroy();
    735 		}
    736 	},
    737 
    738 	// From now on bulk stuff - mainly helpers
    739 
    740 	_trigger: function( type, event, ui ) {
    741 		ui = ui || this._uiHash();
    742 		$.ui.plugin.call( this, type, [ event, ui, this ], true );
    743 
    744 		// Absolute position and offset (see #6884 ) have to be recalculated after plugins
    745 		if ( /^(drag|start|stop)/.test( type ) ) {
    746 			this.positionAbs = this._convertPositionTo( "absolute" );
    747 			ui.offset = this.positionAbs;
    748 		}
    749 		return $.Widget.prototype._trigger.call( this, type, event, ui );
    750 	},
    751 
    752 	plugins: {},
    753 
    754 	_uiHash: function() {
    755 		return {
    756 			helper: this.helper,
    757 			position: this.position,
    758 			originalPosition: this.originalPosition,
    759 			offset: this.positionAbs
    760 		};
    761 	}
    762 
    763 } );
    764 
    765 $.ui.plugin.add( "draggable", "connectToSortable", {
    766 	start: function( event, ui, draggable ) {
    767 		var uiSortable = $.extend( {}, ui, {
    768 			item: draggable.element
    769 		} );
    770 
    771 		draggable.sortables = [];
    772 		$( draggable.options.connectToSortable ).each( function() {
    773 			var sortable = $( this ).sortable( "instance" );
    774 
    775 			if ( sortable && !sortable.options.disabled ) {
    776 				draggable.sortables.push( sortable );
    777 
    778 				// RefreshPositions is called at drag start to refresh the containerCache
    779 				// which is used in drag. This ensures it's initialized and synchronized
    780 				// with any changes that might have happened on the page since initialization.
    781 				sortable.refreshPositions();
    782 				sortable._trigger( "activate", event, uiSortable );
    783 			}
    784 		} );
    785 	},
    786 	stop: function( event, ui, draggable ) {
    787 		var uiSortable = $.extend( {}, ui, {
    788 			item: draggable.element
    789 		} );
    790 
    791 		draggable.cancelHelperRemoval = false;
    792 
    793 		$.each( draggable.sortables, function() {
    794 			var sortable = this;
    795 
    796 			if ( sortable.isOver ) {
    797 				sortable.isOver = 0;
    798 
    799 				// Allow this sortable to handle removing the helper
    800 				draggable.cancelHelperRemoval = true;
    801 				sortable.cancelHelperRemoval = false;
    802 
    803 				// Use _storedCSS To restore properties in the sortable,
    804 				// as this also handles revert (#9675) since the draggable
    805 				// may have modified them in unexpected ways (#8809)
    806 				sortable._storedCSS = {
    807 					position: sortable.placeholder.css( "position" ),
    808 					top: sortable.placeholder.css( "top" ),
    809 					left: sortable.placeholder.css( "left" )
    810 				};
    811 
    812 				sortable._mouseStop( event );
    813 
    814 				// Once drag has ended, the sortable should return to using
    815 				// its original helper, not the shared helper from draggable
    816 				sortable.options.helper = sortable.options._helper;
    817 			} else {
    818 
    819 				// Prevent this Sortable from removing the helper.
    820 				// However, don't set the draggable to remove the helper
    821 				// either as another connected Sortable may yet handle the removal.
    822 				sortable.cancelHelperRemoval = true;
    823 
    824 				sortable._trigger( "deactivate", event, uiSortable );
    825 			}
    826 		} );
    827 	},
    828 	drag: function( event, ui, draggable ) {
    829 		$.each( draggable.sortables, function() {
    830 			var innermostIntersecting = false,
    831 				sortable = this;
    832 
    833 			// Copy over variables that sortable's _intersectsWith uses
    834 			sortable.positionAbs = draggable.positionAbs;
    835 			sortable.helperProportions = draggable.helperProportions;
    836 			sortable.offset.click = draggable.offset.click;
    837 
    838 			if ( sortable._intersectsWith( sortable.containerCache ) ) {
    839 				innermostIntersecting = true;
    840 
    841 				$.each( draggable.sortables, function() {
    842 
    843 					// Copy over variables that sortable's _intersectsWith uses
    844 					this.positionAbs = draggable.positionAbs;
    845 					this.helperProportions = draggable.helperProportions;
    846 					this.offset.click = draggable.offset.click;
    847 
    848 					if ( this !== sortable &&
    849 							this._intersectsWith( this.containerCache ) &&
    850 							$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
    851 						innermostIntersecting = false;
    852 					}
    853 
    854 					return innermostIntersecting;
    855 				} );
    856 			}
    857 
    858 			if ( innermostIntersecting ) {
    859 
    860 				// If it intersects, we use a little isOver variable and set it once,
    861 				// so that the move-in stuff gets fired only once.
    862 				if ( !sortable.isOver ) {
    863 					sortable.isOver = 1;
    864 
    865 					// Store draggable's parent in case we need to reappend to it later.
    866 					draggable._parent = ui.helper.parent();
    867 
    868 					sortable.currentItem = ui.helper
    869 						.appendTo( sortable.element )
    870 						.data( "ui-sortable-item", true );
    871 
    872 					// Store helper option to later restore it
    873 					sortable.options._helper = sortable.options.helper;
    874 
    875 					sortable.options.helper = function() {
    876 						return ui.helper[ 0 ];
    877 					};
    878 
    879 					// Fire the start events of the sortable with our passed browser event,
    880 					// and our own helper (so it doesn't create a new one)
    881 					event.target = sortable.currentItem[ 0 ];
    882 					sortable._mouseCapture( event, true );
    883 					sortable._mouseStart( event, true, true );
    884 
    885 					// Because the browser event is way off the new appended portlet,
    886 					// modify necessary variables to reflect the changes
    887 					sortable.offset.click.top = draggable.offset.click.top;
    888 					sortable.offset.click.left = draggable.offset.click.left;
    889 					sortable.offset.parent.left -= draggable.offset.parent.left -
    890 						sortable.offset.parent.left;
    891 					sortable.offset.parent.top -= draggable.offset.parent.top -
    892 						sortable.offset.parent.top;
    893 
    894 					draggable._trigger( "toSortable", event );
    895 
    896 					// Inform draggable that the helper is in a valid drop zone,
    897 					// used solely in the revert option to handle "valid/invalid".
    898 					draggable.dropped = sortable.element;
    899 
    900 					// Need to refreshPositions of all sortables in the case that
    901 					// adding to one sortable changes the location of the other sortables (#9675)
    902 					$.each( draggable.sortables, function() {
    903 						this.refreshPositions();
    904 					} );
    905 
    906 					// Hack so receive/update callbacks work (mostly)
    907 					draggable.currentItem = draggable.element;
    908 					sortable.fromOutside = draggable;
    909 				}
    910 
    911 				if ( sortable.currentItem ) {
    912 					sortable._mouseDrag( event );
    913 
    914 					// Copy the sortable's position because the draggable's can potentially reflect
    915 					// a relative position, while sortable is always absolute, which the dragged
    916 					// element has now become. (#8809)
    917 					ui.position = sortable.position;
    918 				}
    919 			} else {
    920 
    921 				// If it doesn't intersect with the sortable, and it intersected before,
    922 				// we fake the drag stop of the sortable, but make sure it doesn't remove
    923 				// the helper by using cancelHelperRemoval.
    924 				if ( sortable.isOver ) {
    925 
    926 					sortable.isOver = 0;
    927 					sortable.cancelHelperRemoval = true;
    928 
    929 					// Calling sortable's mouseStop would trigger a revert,
    930 					// so revert must be temporarily false until after mouseStop is called.
    931 					sortable.options._revert = sortable.options.revert;
    932 					sortable.options.revert = false;
    933 
    934 					sortable._trigger( "out", event, sortable._uiHash( sortable ) );
    935 					sortable._mouseStop( event, true );
    936 
    937 					// Restore sortable behaviors that were modfied
    938 					// when the draggable entered the sortable area (#9481)
    939 					sortable.options.revert = sortable.options._revert;
    940 					sortable.options.helper = sortable.options._helper;
    941 
    942 					if ( sortable.placeholder ) {
    943 						sortable.placeholder.remove();
    944 					}
    945 
    946 					// Restore and recalculate the draggable's offset considering the sortable
    947 					// may have modified them in unexpected ways. (#8809, #10669)
    948 					ui.helper.appendTo( draggable._parent );
    949 					draggable._refreshOffsets( event );
    950 					ui.position = draggable._generatePosition( event, true );
    951 
    952 					draggable._trigger( "fromSortable", event );
    953 
    954 					// Inform draggable that the helper is no longer in a valid drop zone
    955 					draggable.dropped = false;
    956 
    957 					// Need to refreshPositions of all sortables just in case removing
    958 					// from one sortable changes the location of other sortables (#9675)
    959 					$.each( draggable.sortables, function() {
    960 						this.refreshPositions();
    961 					} );
    962 				}
    963 			}
    964 		} );
    965 	}
    966 } );
    967 
    968 $.ui.plugin.add( "draggable", "cursor", {
    969 	start: function( event, ui, instance ) {
    970 		var t = $( "body" ),
    971 			o = instance.options;
    972 
    973 		if ( t.css( "cursor" ) ) {
    974 			o._cursor = t.css( "cursor" );
    975 		}
    976 		t.css( "cursor", o.cursor );
    977 	},
    978 	stop: function( event, ui, instance ) {
    979 		var o = instance.options;
    980 		if ( o._cursor ) {
    981 			$( "body" ).css( "cursor", o._cursor );
    982 		}
    983 	}
    984 } );
    985 
    986 $.ui.plugin.add( "draggable", "opacity", {
    987 	start: function( event, ui, instance ) {
    988 		var t = $( ui.helper ),
    989 			o = instance.options;
    990 		if ( t.css( "opacity" ) ) {
    991 			o._opacity = t.css( "opacity" );
    992 		}
    993 		t.css( "opacity", o.opacity );
    994 	},
    995 	stop: function( event, ui, instance ) {
    996 		var o = instance.options;
    997 		if ( o._opacity ) {
    998 			$( ui.helper ).css( "opacity", o._opacity );
    999 		}
   1000 	}
   1001 } );
   1002 
   1003 $.ui.plugin.add( "draggable", "scroll", {
   1004 	start: function( event, ui, i ) {
   1005 		if ( !i.scrollParentNotHidden ) {
   1006 			i.scrollParentNotHidden = i.helper.scrollParent( false );
   1007 		}
   1008 
   1009 		if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
   1010 				i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
   1011 			i.overflowOffset = i.scrollParentNotHidden.offset();
   1012 		}
   1013 	},
   1014 	drag: function( event, ui, i  ) {
   1015 
   1016 		var o = i.options,
   1017 			scrolled = false,
   1018 			scrollParent = i.scrollParentNotHidden[ 0 ],
   1019 			document = i.document[ 0 ];
   1020 
   1021 		if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
   1022 			if ( !o.axis || o.axis !== "x" ) {
   1023 				if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
   1024 						o.scrollSensitivity ) {
   1025 					scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
   1026 				} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
   1027 					scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
   1028 				}
   1029 			}
   1030 
   1031 			if ( !o.axis || o.axis !== "y" ) {
   1032 				if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
   1033 						o.scrollSensitivity ) {
   1034 					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
   1035 				} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
   1036 					scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
   1037 				}
   1038 			}
   1039 
   1040 		} else {
   1041 
   1042 			if ( !o.axis || o.axis !== "x" ) {
   1043 				if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
   1044 					scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
   1045 				} else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
   1046 						o.scrollSensitivity ) {
   1047 					scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
   1048 				}
   1049 			}
   1050 
   1051 			if ( !o.axis || o.axis !== "y" ) {
   1052 				if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
   1053 					scrolled = $( document ).scrollLeft(
   1054 						$( document ).scrollLeft() - o.scrollSpeed
   1055 					);
   1056 				} else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
   1057 						o.scrollSensitivity ) {
   1058 					scrolled = $( document ).scrollLeft(
   1059 						$( document ).scrollLeft() + o.scrollSpeed
   1060 					);
   1061 				}
   1062 			}
   1063 
   1064 		}
   1065 
   1066 		if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
   1067 			$.ui.ddmanager.prepareOffsets( i, event );
   1068 		}
   1069 
   1070 	}
   1071 } );
   1072 
   1073 $.ui.plugin.add( "draggable", "snap", {
   1074 	start: function( event, ui, i ) {
   1075 
   1076 		var o = i.options;
   1077 
   1078 		i.snapElements = [];
   1079 
   1080 		$( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
   1081 			.each( function() {
   1082 				var $t = $( this ),
   1083 					$o = $t.offset();
   1084 				if ( this !== i.element[ 0 ] ) {
   1085 					i.snapElements.push( {
   1086 						item: this,
   1087 						width: $t.outerWidth(), height: $t.outerHeight(),
   1088 						top: $o.top, left: $o.left
   1089 					} );
   1090 				}
   1091 			} );
   1092 
   1093 	},
   1094 	drag: function( event, ui, inst ) {
   1095 
   1096 		var ts, bs, ls, rs, l, r, t, b, i, first,
   1097 			o = inst.options,
   1098 			d = o.snapTolerance,
   1099 			x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
   1100 			y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
   1101 
   1102 		for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {
   1103 
   1104 			l = inst.snapElements[ i ].left - inst.margins.left;
   1105 			r = l + inst.snapElements[ i ].width;
   1106 			t = inst.snapElements[ i ].top - inst.margins.top;
   1107 			b = t + inst.snapElements[ i ].height;
   1108 
   1109 			if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
   1110 					!$.contains( inst.snapElements[ i ].item.ownerDocument,
   1111 					inst.snapElements[ i ].item ) ) {
   1112 				if ( inst.snapElements[ i ].snapping ) {
   1113 					( inst.options.snap.release &&
   1114 						inst.options.snap.release.call(
   1115 							inst.element,
   1116 							event,
   1117 							$.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
   1118 						) );
   1119 				}
   1120 				inst.snapElements[ i ].snapping = false;
   1121 				continue;
   1122 			}
   1123 
   1124 			if ( o.snapMode !== "inner" ) {
   1125 				ts = Math.abs( t - y2 ) <= d;
   1126 				bs = Math.abs( b - y1 ) <= d;
   1127 				ls = Math.abs( l - x2 ) <= d;
   1128 				rs = Math.abs( r - x1 ) <= d;
   1129 				if ( ts ) {
   1130 					ui.position.top = inst._convertPositionTo( "relative", {
   1131 						top: t - inst.helperProportions.height,
   1132 						left: 0
   1133 					} ).top;
   1134 				}
   1135 				if ( bs ) {
   1136 					ui.position.top = inst._convertPositionTo( "relative", {
   1137 						top: b,
   1138 						left: 0
   1139 					} ).top;
   1140 				}
   1141 				if ( ls ) {
   1142 					ui.position.left = inst._convertPositionTo( "relative", {
   1143 						top: 0,
   1144 						left: l - inst.helperProportions.width
   1145 					} ).left;
   1146 				}
   1147 				if ( rs ) {
   1148 					ui.position.left = inst._convertPositionTo( "relative", {
   1149 						top: 0,
   1150 						left: r
   1151 					} ).left;
   1152 				}
   1153 			}
   1154 
   1155 			first = ( ts || bs || ls || rs );
   1156 
   1157 			if ( o.snapMode !== "outer" ) {
   1158 				ts = Math.abs( t - y1 ) <= d;
   1159 				bs = Math.abs( b - y2 ) <= d;
   1160 				ls = Math.abs( l - x1 ) <= d;
   1161 				rs = Math.abs( r - x2 ) <= d;
   1162 				if ( ts ) {
   1163 					ui.position.top = inst._convertPositionTo( "relative", {
   1164 						top: t,
   1165 						left: 0
   1166 					} ).top;
   1167 				}
   1168 				if ( bs ) {
   1169 					ui.position.top = inst._convertPositionTo( "relative", {
   1170 						top: b - inst.helperProportions.height,
   1171 						left: 0
   1172 					} ).top;
   1173 				}
   1174 				if ( ls ) {
   1175 					ui.position.left = inst._convertPositionTo( "relative", {
   1176 						top: 0,
   1177 						left: l
   1178 					} ).left;
   1179 				}
   1180 				if ( rs ) {
   1181 					ui.position.left = inst._convertPositionTo( "relative", {
   1182 						top: 0,
   1183 						left: r - inst.helperProportions.width
   1184 					} ).left;
   1185 				}
   1186 			}
   1187 
   1188 			if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
   1189 				( inst.options.snap.snap &&
   1190 					inst.options.snap.snap.call(
   1191 						inst.element,
   1192 						event,
   1193 						$.extend( inst._uiHash(), {
   1194 							snapItem: inst.snapElements[ i ].item
   1195 						} ) ) );
   1196 			}
   1197 			inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );
   1198 
   1199 		}
   1200 
   1201 	}
   1202 } );
   1203 
   1204 $.ui.plugin.add( "draggable", "stack", {
   1205 	start: function( event, ui, instance ) {
   1206 		var min,
   1207 			o = instance.options,
   1208 			group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
   1209 				return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
   1210 					( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
   1211 			} );
   1212 
   1213 		if ( !group.length ) { return; }
   1214 
   1215 		min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
   1216 		$( group ).each( function( i ) {
   1217 			$( this ).css( "zIndex", min + i );
   1218 		} );
   1219 		this.css( "zIndex", ( min + group.length ) );
   1220 	}
   1221 } );
   1222 
   1223 $.ui.plugin.add( "draggable", "zIndex", {
   1224 	start: function( event, ui, instance ) {
   1225 		var t = $( ui.helper ),
   1226 			o = instance.options;
   1227 
   1228 		if ( t.css( "zIndex" ) ) {
   1229 			o._zIndex = t.css( "zIndex" );
   1230 		}
   1231 		t.css( "zIndex", o.zIndex );
   1232 	},
   1233 	stop: function( event, ui, instance ) {
   1234 		var o = instance.options;
   1235 
   1236 		if ( o._zIndex ) {
   1237 			$( ui.helper ).css( "zIndex", o._zIndex );
   1238 		}
   1239 	}
   1240 } );
   1241 
   1242 return $.ui.draggable;
   1243 
   1244 } ) );