2 * jQuery udraggable plugin v0.3.0
3 * Copyright (c) 2013-2014 Grant McLean (grant@mclean.net.nz)
5 * Homepage: https://github.com/grantm/jquery-udraggable
7 * Dual licensed under the MIT and GPL (v2.0 or later) licenses:
8 * http://opensource.org/licenses/MIT
9 * http://opensource.org/licenses/GPL-2.0
11 * This library requires Michael S. Mikowski's unified mouse and touch
12 * event plugin: https://github.com/mmikowski/jquery.event.ue
19 var floor = Math.floor;
23 window.requestAnimationFrame = window.requestAnimationFrame || function(work) {
24 return setTimeout(work, 10);
27 window.cancelAnimationFrame = window.cancelAnimationFrame || function(id) {
28 return clearTimeout(id);
32 // Constructor function
34 var UDraggable = function (el, options) {
38 this.options = $.extend({}, $.fn.udraggable.defaults, options);
39 this.positionElement = this.options.positionElement || this.positionElement;
40 this.getStartPosition = this.options.getStartPosition || this.getStartPosition;
41 this.updatePositionFrameHandler = function() {
42 delete that.queuedUpdate;
43 var pos = that.ui.position;
44 that.positionElement(that.$el, that.started, pos.left, pos.top);
45 if (that.options.dragUpdate) {
46 that.options.dragUpdate.apply(that.el, [that.ui]);
49 this.queuePositionUpdate = function() {
50 if (!that.queuedUpdate) {
51 that.queuedUpdate = window.requestAnimationFrame(that.updatePositionFrameHandler);
57 UDraggable.prototype = {
59 constructor: UDraggable,
63 this.disabled = false;
65 this.normalisePosition();
66 var $target = this.options.handle ?
67 this.$el.find( this.options.handle ) :
69 if (this.options.longPress) {
71 .on('uheldstart.udraggable', function(e) { that.start(e); })
72 .on('uheldmove.udraggable', function(e) { that.move(e); })
73 .on('uheldend.udraggable', function(e) { that.end(e); });
77 .on('udragstart.udraggable', function(e) { that.start(e); })
78 .on('udragmove.udraggable', function(e) { that.move(e); })
79 .on('udragend.udraggable', function(e) { that.end(e); });
84 var $target = this.options.handle ?
85 this.$el.find( this.options.handle ) :
87 $target.off('.udraggable');
88 this.$el.removeData('udraggable');
96 this.disabled = false;
101 if (arguments.length === 0) {
104 if (arguments.length === 2) {
105 this.options[ arguments[0] ] = arguments[1];
108 if (arguments.length === 1) {
109 if (typeof arguments[0] === 'string') {
110 return this.options[ arguments[0] ];
112 if (typeof arguments[0] === 'object') {
113 for(name in arguments[0]) {
114 if (arguments[0].hasOwnProperty(name)) {
115 this.options[name] = arguments[0][name];
120 if (this.options.containment) {
121 this._initContainment();
125 normalisePosition: function() {
126 var pos = this.$el.position();
128 position: 'absolute',
140 var start = this.getStartPosition(this.$el);
141 this._initContainment();
144 offset: { top: start.y, left: start.x},
145 originalPosition: { top: start.y, left: start.x},
146 position: { top: start.y, left: start.x},
148 if (this.options.longPress) {
151 return this._stopPropagation(e);
155 if (this.disabled || (!this.started && !this._start(e))) {
158 var delta_x = e.px_current_x - e.px_start_x;
159 var delta_y = e.px_current_y - e.px_start_y;
160 var axis = this.options.axis;
161 if (axis && axis === "x") {
164 if (axis && axis === "y") {
168 left: this.ui.originalPosition.left,
169 top: this.ui.originalPosition.top
171 if (!axis || (axis === "x")) {
174 if (!axis || (axis === "y")) {
177 this._applyGrid(cur);
178 this._applyContainment(cur);
179 var pos = this.ui.position;
180 if ((cur.top !== pos.top) || (cur.left !== pos.left)) {
181 this.ui.position.left = cur.left;
182 this.ui.position.top = cur.top;
183 this.ui.offset.left = cur.left;
184 this.ui.offset.top = cur.top;
185 if (this.options.drag) {
186 this.options.drag.apply(this.el, [e, this.ui]);
188 this.queuePositionUpdate();
190 return this._stopPropagation(e);
194 if (this.started || this._start(e)) {
195 this.$el.removeClass("udraggable-dragging");
196 this.started = false;
197 if (this.queuedUpdate) {
198 window.cancelAnimationFrame(this.queuedUpdate);
200 this.updatePositionFrameHandler();
201 if (this.options.stop) {
202 this.options.stop.apply(this.el, [e, this.ui]);
205 return this._stopPropagation(e);
210 _stopPropagation: function(e) {
216 _start: function(e) {
217 if (!this._mouseDistanceMet(e) || !this._mouseDelayMet(e)) {
221 this.queuePositionUpdate();
222 if (this.options.start) {
223 this.options.start.apply(this.el, [e, this.ui]);
225 this.$el.addClass("udraggable-dragging");
229 _mouseDistanceMet: function(e) {
231 Math.abs(e.px_start_x - e.px_current_x),
232 Math.abs(e.px_start_y - e.px_current_y)
233 ) >= this.options.distance;
236 _mouseDelayMet: function(e) {
237 return e.ms_elapsed > this.options.delay;
240 _initContainment: function() {
241 var o = this.options;
244 if (!o.containment) {
245 this.containment = null;
249 if (o.containment.constructor === Array) {
250 this.containment = o.containment;
254 if (o.containment === "parent") {
255 o.containment = this.$el.offsetParent();
258 $c = $( o.containment );
267 $c.innerWidth() - this.$el.outerWidth(),
268 $c.innerHeight() - this.$el.outerHeight(),
272 _applyGrid: function(cur) {
273 if (this.options.grid) {
274 var gx = this.options.grid[0];
275 var gy = this.options.grid[1];
276 cur.left = floor( (cur.left + gx / 2) / gx ) * gx;
277 cur.top = floor( (cur.top + gy / 2) / gy ) * gy;
281 _applyContainment: function(cur) {
282 var cont = this.containment;
284 cur.left = min( max(cur.left, cont[0]), cont[2] );
285 cur.top = min( max(cur.top, cont[1]), cont[3] );
289 getStartPosition: function($el) {
291 x: parseInt($el.css('left'), 10) || 0,
292 y: parseInt($el.css('top'), 10) || 0
296 positionElement: function($el, dragging, left, top) {
297 $el.css({ left: left, top: top });
303 // jQuery plugin function
305 $.fn.udraggable = function(option) {
306 var args = Array.prototype.slice.call(arguments, 1);
308 this.each(function () {
310 var data = $this.data('udraggable');
312 data = new UDraggable(this, option);
313 $this.data('udraggable', data);
315 if (typeof option === 'string') { // option is a method - call it
316 if(typeof data[option] !== 'function') {
317 throw "jquery.udraggable has no '" + option + "' method";
319 var result = data[option].apply(data, args);
320 if (result !== undefined) {
321 results.push( result );
325 return results.length > 0 ? results[0] : this;
328 $.fn.udraggable.defaults = {