Merge pull request #2 from scottgonzalez/interactions

Draggable: Cleanup.
This commit is contained in:
Dave Stein 2011-10-14 11:54:59 -07:00
commit 6ea6c47855

View File

@ -1,11 +1,11 @@
/* /*
* jQuery UI Draggable 2.0.0 * jQuery UI Draggable @VERSION
* *
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses. * Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license * http://jquery.org/license
* *
* http://docs.jquery.com/UI/Draggables * http://docs.jquery.com/UI/Draggable
* *
* Depends: * Depends:
* jquery.ui.core.js * jquery.ui.core.js
@ -14,90 +14,59 @@
(function( $, undefined ) { (function( $, undefined ) {
$.widget( "ui.draggable", { $.widget( "ui.draggable", {
version: "@VERSION",
widgetEventPrefix: "drag", widgetEventPrefix: "drag",
options: { options: {
helper: false,
scrollSpeed: 20,
scrollSensitivity: 20, scrollSensitivity: 20,
helper: false scrollSpeed: 20
}, },
// TODO: actually remove data // dragEl: element being dragged (original or helper)
destroy: function() { // position: CSS position of dragEl
return this; // offset: offset of dragEl
}, // startCoords: clientX/Y of the mousedown (offset of pointer)
// startPosition: CSS position prior to drag start
// startOffset: offset prior to drag start
// overflowOffset: offset of scroll parent
// overflowHeight: height of scroll parent
// overflowWidth: width of scroll parent
_create: function() { _create: function() {
// TODO: add these to the base widget
// Either initialized element or the helper this.doc = $( this.element[0].ownerDocument );
this.dragEl = false, this.win = $( this.doc[0].defaultView );
this.position = {},
this.offset = {},
// Start X/Y coords of mouse before drag
this.startCoords = {},
// Start position of element before drag
this.startPosition = {},
// Start offset of element before drag
this.startOffset = {},
this.scrollParent = this.element.scrollParent(); this.scrollParent = this.element.scrollParent();
// Offset of scrollParent, used for auto-scrolling // Static position elements can't be moved with top/left
this.overflowOffset = {};
// Height of scrollParent, used for auto-scrolling
this.overflowHeight = 0;
// Width of scrollParent, used for auto-scrolling
this.overflowWidth = 0;
// Static position elements can"t be moved with top/left
if ( this.element.css( "position" ) === "static" ) { if ( this.element.css( "position" ) === "static" ) {
this.element.css( "position", "relative" ); this.element.css( "position", "relative" );
} }
// Using proxy to avoid anon functions using self to pass "this" along // TODO: use _bind()
this.element.bind( "mousedown." + this.widgetName, $.proxy( this._mouseDown, this ) ); this.element.bind( "mousedown." + this.widgetName, $.proxy( this, "_mouseDown" ) );
}, },
_usingHelper : function() { // TODO: why is relative handled differently than fixed/absolute?
return ( this.options.helper === true || typeof this.options.helper === 'function' ); _getPosition: function() {
},
_setPosition: function() {
var left, top, position, var left, top, position,
scrollTop = this.scrollParent.scrollTop(), scrollTop = this.scrollParent.scrollTop(),
scrollLeft = this.scrollParent.scrollLeft(); scrollLeft = this.scrollParent.scrollLeft();
// Helper is appended to body so offset of element is all that's needed
if ( this._usingHelper() ) {
return this.element.offset();
}
// If fixed or absolute // If fixed or absolute
if ( this.cssPosition !== "relative" ) { if ( this.cssPosition !== "relative" ) {
position = this.dragEl.position(); position = this.dragEl.position();
// Take into account scrollbar // Take into account scrollbar
position.top = position.top - scrollTop; position.top -= scrollTop;
position.left = position.left - scrollLeft position.left -= scrollLeft
return position; return position;
} }
/** When using relative, css values are checked **/ // When using relative, css values are checked
left = this.dragEl.css( "left" ); left = this.dragEl.css( "left" );
top = this.dragEl.css( "top" ); top = this.dragEl.css( "top" );
@ -106,64 +75,42 @@ $.widget( "ui.draggable", {
top = ( top === "auto" ) ? 0: parseInt( top, 10 ); top = ( top === "auto" ) ? 0: parseInt( top, 10 );
return { return {
left: left - scrollLeft, left: left - scrollLeft,
top: top - scrollTop top: top - scrollTop
}; };
}, },
_mouseDown: function( event ) { _mouseDown: function( event ) {
// Prevent text selection, among other things
// Stop browser from highlighting, among other things
event.preventDefault(); event.preventDefault();
// The actual dragging element, should always be a jQuery object // The actual dragging element, should always be a jQuery object
this.dragEl = this.element; this.dragEl = this.element;
this.cssPosition = this.dragEl.css( "position" ); this.cssPosition = this.dragEl.css( "position" );
// Helper required, so clone, hide, and set reference // Helper required
if ( this._usingHelper() ) { if ( this.options.helper ) {
// clone
// If getting a cloned helper
if ( this.options.helper === true ) { if ( this.options.helper === true ) {
this.dragEl = this.element.clone();
// If source element has an ID, change ID of helper to avoid overlap // If source element has an ID, change ID of helper to avoid overlap
this.dragEl = this.element.clone();
if ( this.element.attr( "id" ) ) { if ( this.element.attr( "id" ) ) {
this.dragEl.attr( "id", this.element.attr( "id" ) + "-" + this.widgetName ); this.dragEl.attr( "id", this.element.attr( "id" ) + "-" + this.widgetName );
} }
} else { } else {
// TODO: figure out the signature for this; see #4957
this.dragEl = this.options.helper(); this.dragEl = $( this.options.helper() );
// If function was passed, it should return a DOMElement
if ( typeof this.dragEl.nodeType !== 'number' ) {
throw "Helper function must return a DOMElement";
} }
this.dragEl = $( this.dragEl );
}
// Automatically make helper absolute
this.dragEl this.dragEl
.css({ // TODO: should we move this to the stylesheet and use a class?
position: "absolute" .css( "position", "absolute" )
}); .appendTo( this.doc[0].body )
.offset( this.element.offset() );
$( "body" ).append( this.dragEl );
} }
// Cache starting absolute and relative positions // Cache starting absolute and relative positions
this.startPosition = this._setPosition(); this.startPosition = this._getPosition();
this.startOffset = this.dragEl.offset(); this.startOffset = this.dragEl.offset();
// Cache current position and offset // Cache current position and offset
@ -176,34 +123,24 @@ $.widget( "ui.draggable", {
}; };
// Cache the offset of scrollParent // Cache the offset of scrollParent
// TODO: store overflow height/width in a hash instead of separate properties
this.overflowOffset = this.scrollParent.offset(); this.overflowOffset = this.scrollParent.offset();
this.overflowHeight = ( this.scrollParent[0] === document ) ? $(window).height() : this.scrollParent.height(); this.overflowHeight = ( this.scrollParent[0] === this.doc[0] ) ?
this.overflowWidth = ( this.scrollParent[0] === document ) ? $(window).width() : this.scrollParent.width(); this.win.height() : this.scrollParent.height();
this.overflowWidth = ( this.scrollParent[0] === this.doc[0] ) ?
this.win.width() : this.scrollParent.width();
this._trigger( "start", event ); // TODO: allow modifying position, just like during drag
this._trigger( "start", event, this._uiHash() );
$(document).bind( "mousemove." + this.widgetName, $.proxy( this._mouseMove, this ) )
.bind( "mouseup." + this.widgetName, $.proxy( this._mouseUp, this ) );
// Set the helper up by actual element
if ( this._usingHelper() ) {
// get the absolute position of element so that helper will know where to go
elOffset = this.element.offset();
this.dragEl.css({
display: "block",
top: elOffset.top + "px",
left: elOffset.left + "px"
});
}
// TODO: use ._bind()
// TODO: rename _bind() to _on(); add _off()
this.doc
.bind( "mousemove." + this.widgetName, $.proxy( this, "_mouseMove" ) )
.bind( "mouseup." + this.widgetName, $.proxy( this, "_mouseUp" ) );
}, },
_mouseMove: function( event ) { _mouseMove: function( event ) {
var leftDiff = event.clientX - this.startCoords.left, var leftDiff = event.clientX - this.startCoords.left,
topDiff = event.clientY - this.startCoords.top, topDiff = event.clientY - this.startCoords.top,
newLeft = leftDiff + this.startPosition.left, newLeft = leftDiff + this.startPosition.left,
@ -218,87 +155,67 @@ $.widget( "ui.draggable", {
this.offset.left = this.startOffset.left + newLeft; this.offset.left = this.startOffset.left + newLeft;
this.offset.top = this.startOffset.top + newTop; this.offset.top = this.startOffset.top + newTop;
this._trigger( "drag", event ); this._trigger( "drag", event, this._uiHash() );
// User overriding left/top so shortcut math is no longer valid // User overriding left/top so shortcut math is no longer valid
if ( newLeft !== this.position.left || newTop !== this.position.top ) { if ( newLeft !== this.position.left || newTop !== this.position.top ) {
// TODO: can we just store the previous offset values
// and not go through .offset()?
// refresh offset using slower functions // refresh offset using slower functions
this.offset = this.dragEl.offset(); this.offset = this.dragEl.offset();
} }
newLeft = this.position.left; newLeft = this.position.left;
newTop = this.position.top; newTop = this.position.top;
if ( this.cssPosition !== 'fixed' ) { // TODO: does this work with nested scrollable parents?
if ( this.cssPosition !== "fixed" ) {
newLeft = newLeft + this.scrollParent.scrollLeft(); newLeft = newLeft + this.scrollParent.scrollLeft();
newTop = newTop + this.scrollParent.scrollTop(); newTop = newTop + this.scrollParent.scrollTop();
} }
this.dragEl.css({ this.dragEl.css({
left: newLeft + "px", left: newLeft + "px",
top: newTop + "px" top: newTop + "px"
}); });
// Scroll the scrollParent, if needed // Scroll the scrollParent, if needed
this._handleScrolling( event ); this._handleScrolling( event );
}, },
_handleScrolling: function( event ) { _handleScrolling: function( event ) {
var scrollTop = this.doc.scrollTop(),
var doc = $(document), scrollLeft = this.doc.scrollLeft();
scrollTop = doc.scrollTop(),
scrollLeft = doc.scrollLeft();
// Handle vertical scrolling // Handle vertical scrolling
if ( ( ( this.overflowHeight + scrollTop ) - event.pageY ) < this.options.scrollSensitivity ) { if ( ( ( this.overflowHeight + scrollTop ) - event.pageY ) < this.options.scrollSensitivity ) {
doc.scrollTop( scrollTop + this.options.scrollSpeed ); this.doc.scrollTop( scrollTop + this.options.scrollSpeed );
} }
// Handle horizontal scrolling // Handle horizontal scrolling
if ( ( ( this.overflowWidth + scrollLeft ) - event.pageX ) < this.options.scrollSensitivity ) { if ( ( ( this.overflowWidth + scrollLeft ) - event.pageX ) < this.options.scrollSensitivity ) {
doc.scrollLeft( scrollLeft + this.options.scrollSpeed ); this.doc.scrollLeft( scrollLeft + this.options.scrollSpeed );
} }
}, },
_mouseUp: function( event ) { _mouseUp: function( event ) {
this._trigger( "stop", event, this._uiHash() );
var doc = $(document); if ( this.options.helper ) {
this._trigger( "stop", event );
this.startCoords = {};
if ( this._usingHelper() ) {
this.dragEl.remove(); this.dragEl.remove();
} }
doc.unbind( "mousemove." + this.widgetName ); this.doc
doc.unbind( "mouseup." + this.widgetName ); .unbind( "mousemove." + this.widgetName )
.unbind( "mouseup." + this.widgetName );
}, },
_uiHash: function( event ) { _uiHash: function( event ) {
return { return {
position: this.position, position: this.position,
offset: this.offset offset: this.offset
}; };
} }
}); });
$.extend($.ui.draggable, {
version: "2.0.0"
});
})( jQuery ); })( jQuery );