jquery-ui/ui/jquery.ui.draggable.js

302 lines
8.1 KiB
JavaScript
Raw Normal View History

2008-06-04 02:34:33 +00:00
/*
2011-10-14 18:19:04 +00:00
* jQuery UI Draggable @VERSION
2008-06-04 02:34:33 +00:00
*
2012-01-07 17:18:08 +00:00
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
2011-10-14 18:19:04 +00:00
* http://docs.jquery.com/UI/Draggable
2008-06-04 02:34:33 +00:00
*
* Depends:
* jquery.ui.core.js
* jquery.ui.interaction.js
* jquery.ui.widget.js
2008-06-04 02:34:33 +00:00
*/
(function( $, undefined ) {
2008-06-04 02:34:33 +00:00
$.widget( "ui.draggable", $.ui.interaction, {
2011-10-14 18:19:04 +00:00
version: "@VERSION",
widgetEventPrefix: "drag",
options: {
helper: null
},
2011-10-14 18:19:04 +00:00
// dragEl: element being dragged (original or helper)
// position: final CSS position of dragEl
2011-10-14 18:19:04 +00:00
// offset: offset of dragEl
// startCoords: pageX/Y of the mousedown (offset of pointer)
2011-10-14 18:19:04 +00:00
// startPosition: CSS position prior to drag start
// startOffset: offset prior to drag start
// tempPosition: overridable CSS position of dragEl
2011-10-14 18:19:04 +00:00
// overflowOffset: offset of scroll parent
// overflow: object containing width and height keys of scroll parent
_create: function() {
this._super();
2011-10-14 18:19:04 +00:00
// Static position elements can't be moved with top/left
if ( this.element.css( "position" ) === "static" ) {
this.element.css( "position", "relative" );
}
},
2011-10-14 18:19:04 +00:00
_getPosition: function() {
var left, top, position, offset,
2011-10-14 18:19:04 +00:00
scrollTop = this.scrollParent.scrollTop(),
scrollLeft = this.scrollParent.scrollLeft();
2008-11-28 15:43:32 +00:00
// If fixed or absolute
if ( this.cssPosition !== "relative" ) {
position = this.dragEl.position();
2008-11-28 15:43:32 +00:00
// Take into account scrollbar
2011-10-14 18:19:04 +00:00
position.top -= scrollTop;
position.left -= scrollLeft;
return position;
}
2011-10-14 18:19:04 +00:00
// When using relative, css values are checked
2011-11-23 14:03:02 +00:00
// Otherwise the position wouldn't account for padding on ancestors
left = this.dragEl.css( "left" );
top = this.dragEl.css( "top" );
2011-11-23 14:03:02 +00:00
// Webkit will give back auto if there is no explicit value
left = ( left === "auto" ) ? 0: parseInt( left, 10 );
top = ( top === "auto" ) ? 0: parseInt( top, 10 );
return {
left: left - scrollLeft,
top: top - scrollTop
};
},
_handleScrolling: function( event ) {
2012-01-07 17:18:08 +00:00
var scrollTop = this.scrollParent.scrollTop(),
scrollLeft = this.scrollParent.scrollLeft(),
scrollSensitivity = 20,
baseSpeed = 5,
speed = function( distance ) {
return baseSpeed + Math.round( distance / 2 );
2012-01-07 17:18:08 +00:00
},
// overflowOffset is only set when scrollParent is not doc/html
overflowLeft = this.overflowOffset ?
this.overflowOffset.left :
scrollLeft,
overflowTop = this.overflowOffset ?
this.overflowOffset.top :
scrollTop,
xRight = this.overflow.width + overflowLeft - event.pageX,
xLeft = event.pageX - overflowLeft,
yBottom = this.overflow.height + overflowTop - event.pageY,
yTop = event.pageY - overflowTop;
// Handle vertical scrolling
2012-01-07 17:18:08 +00:00
if ( yBottom < scrollSensitivity ) {
this.scrollParent.scrollTop( scrollTop +
speed( scrollSensitivity - yBottom ) );
} else if ( yTop < scrollSensitivity ) {
this.scrollParent.scrollTop( scrollTop -
speed( scrollSensitivity - yTop ) );
}
// Handle horizontal scrolling
2012-01-07 17:18:08 +00:00
if ( xRight < scrollSensitivity ) {
this.scrollParent.scrollLeft( scrollLeft +
speed( scrollSensitivity - xRight ) );
} else if ( xLeft < scrollSensitivity ) {
this.scrollParent.scrollLeft( scrollLeft -
speed( scrollSensitivity - xLeft ) );
}
},
_start: function( event ) {
// The actual dragging element, should always be a jQuery object
this.dragEl = this.element;
2011-10-14 18:19:04 +00:00
// Helper required
if ( this.options.helper ) {
// clone
if ( this.options.helper === true ) {
this.dragEl = this.element.clone()
.removeAttr( "id" )
.find( "[id]" )
.removeAttr( "id" )
.end();
} else {
2011-10-14 18:19:04 +00:00
// TODO: figure out the signature for this; see #4957
this.dragEl = $( this.options.helper() );
}
this.dragEl
// Helper must be absolute to function properly
2011-10-14 18:19:04 +00:00
.css( "position", "absolute" )
.appendTo( this.document[0].body )
2011-10-14 18:19:04 +00:00
.offset( this.element.offset() );
}
2011-11-06 22:42:35 +00:00
this.cssPosition = this.dragEl.css( "position" );
2011-11-23 14:03:02 +00:00
this.scrollParent = this.element.scrollParent();
2008-06-04 02:34:33 +00:00
// Cache starting absolute and relative positions
2011-10-14 18:19:04 +00:00
this.startPosition = this._getPosition();
this.startOffset = this.dragEl.offset();
// Cache current position and offset
this.position = $.extend( {}, this.startPosition );
this.offset = $.extend( {}, this.startOffset );
2009-01-22 13:10:18 +00:00
this.startCoords = {
left: event.pageX,
top: event.pageY
};
2009-01-22 13:10:18 +00:00
// Cache the offset of scrollParent, if required for _handleScrolling
2012-01-07 17:18:08 +00:00
if ( this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML" ) {
this.overflowOffset = this.scrollParent.offset();
}
2012-01-07 17:18:08 +00:00
this.overflow = {
height: this.scrollParent[0] === this.document[0] ?
this.window.height() : this.scrollParent.height(),
width: this.scrollParent[0] === this.document[0] ?
this.window.width() : this.scrollParent.width()
};
2011-10-14 18:19:04 +00:00
this._preparePosition( event );
// If user cancels start, don't allow dragging
2011-11-18 00:48:04 +00:00
if ( this._trigger( "start", event, this._uiHash() ) === false ) {
return false;
}
2011-11-18 00:48:04 +00:00
this._blockFrames();
this._setCss( event );
},
_move: function( event ) {
this._preparePosition( event );
// If user cancels drag, don't move the element
2011-11-18 00:48:04 +00:00
if ( this._trigger( "drag", event, this._uiHash() ) === false ) {
return;
}
this._setCss( event );
// Scroll the scrollParent, if needed
this._handleScrolling( event );
},
_stop: function( event ) {
this._preparePosition( event );
// If user cancels stop, leave helper there, disallow any CSS changes
2011-11-18 00:48:04 +00:00
if ( this._trigger( "stop", event, this._uiHash() ) === false ) {
this._setCss( event );
if ( this.options.helper ) {
this.dragEl.remove();
}
}
2011-11-18 00:48:04 +00:00
this._unblockFrames();
},
// Uses event to determine new position of draggable, before any override from callbacks
// TODO: handle absolute element inside relative parent like a relative element
_preparePosition: function( event ) {
var leftDiff = event.pageX - this.startCoords.left,
topDiff = event.pageY - this.startCoords.top,
2011-10-14 18:19:04 +00:00
newLeft = leftDiff + this.startPosition.left,
newTop = topDiff + this.startPosition.top;
// Save off new values for .css() in various callbacks using this function
this.position = {
left: newLeft,
top: newTop
};
// Save off values to compare user override against automatic coordinates
this.tempPosition = {
left: newLeft,
top: newTop
};
2011-11-18 00:48:04 +00:00
// Refresh offset cache with new positions
this.offset.left = this.startOffset.left + leftDiff;
this.offset.top = this.startOffset.top + topDiff;
},
2008-06-04 02:34:33 +00:00
// Places draggable where mouse or user from callback indicates
_setCss: function( event ) {
2012-01-07 17:18:08 +00:00
var newLeft, newTop;
2008-06-04 02:34:33 +00:00
// User overriding left/top so shortcut math is no longer valid
if ( this.tempPosition.left !== this.position.left || this.tempPosition.top !== this.position.top ) {
2012-01-07 17:18:08 +00:00
// Reset offset based on difference of expected and overridden values
this.offset.top = this.offset.top +
this.position.top - this.tempPosition.top;
this.offset.left = this.offset.left +
this.position.left - this.tempPosition.left;
this.offset = this.dragEl.offset();
}
2011-10-14 18:19:04 +00:00
newLeft = this.position.left;
newTop = this.position.top;
2011-10-14 18:19:04 +00:00
// TODO: does this work with nested scrollable parents?
if ( this.cssPosition !== "fixed" ) {
newLeft = newLeft + this.scrollParent.scrollLeft();
newTop = newTop + this.scrollParent.scrollTop();
}
this.dragEl.css({
2011-11-18 00:48:04 +00:00
left: newLeft,
top: newTop
});
},
2011-10-14 18:19:04 +00:00
_uiHash: function( event ) {
var ret = {
position: this.position,
offset: this.offset
2008-06-04 02:34:33 +00:00
};
2011-11-06 22:42:35 +00:00
2011-11-18 00:48:04 +00:00
// TODO: should we always set the helper?
if ( this.options.helper ) {
ret.helper = this.dragEl;
}
2011-11-06 22:42:35 +00:00
return ret;
2011-11-06 22:42:35 +00:00
},
2011-11-23 14:03:02 +00:00
_blockFrames: function() {
var body = this.document[0].body;
this.iframeBlocks = this.document.find( "iframe" ).map(function() {
var iframe = $( this ),
iframeOffset = iframe.offset();
return $( "<div>" )
.css({
position: "absolute",
width: iframe.outerWidth(),
height: iframe.outerHeight(),
top: iframeOffset.top,
left: iframeOffset.left
})
.appendTo( body )[0];
});
},
2011-11-06 22:42:35 +00:00
_unblockFrames: function() {
if ( this.iframeBlocks ) {
2011-11-18 00:48:04 +00:00
this.iframeBlocks.remove();
delete this.iframeBlocks;
2011-11-06 22:42:35 +00:00
}
}
2008-06-04 02:34:33 +00:00
});
2011-10-14 18:19:04 +00:00
})( jQuery );