From b662feeb9afccddf0742af8faad0514c9f1a5f67 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 19 Oct 2012 10:07:15 +1000 Subject: [PATCH 1/4] Draggable: Fixed snapping of bigger objects to smaller objects. Fixed #8165 - Problem with snap option when dragging big rect. --- ui/jquery.ui.draggable.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ui/jquery.ui.draggable.js b/ui/jquery.ui.draggable.js index cbf5e174701..7f146984233 100644 --- a/ui/jquery.ui.draggable.js +++ b/ui/jquery.ui.draggable.js @@ -847,8 +847,17 @@ $.ui.plugin.add("draggable", "snap", { t = inst.snapElements[i].top; b = t + inst.snapElements[i].height; - //Yes, I know, this is insane ;) - 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))) { + //This checks if any of the corners or sides are within snap distance. + if(!( + (l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || // Check the left top corner + (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || // Check the left bottom corner + (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || // Check the right top corner + (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d) || // Check the right bottom corner + (l > x1 && x2 > r && t-d < y1 && y1 < b+d) || // Check the top side + (l > x1 && x2 > r && t-d < y2 && y2 < b+d) || // Check the bottom side + (l-d < x1 && x1 < r+d && t > y1 && y2 > b) || // Check the right side + (l-d < x2 && x2 < r+d && t > y1 && y2 > b) // Check the left side + )) { 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 }))); } From c86319ed07e1dd840d9e72a0617eeccf8c48afcb Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 22 Oct 2012 11:47:12 +1000 Subject: [PATCH 2/4] Added andSelf option to Droppable to allow dragging and dropping a droppable onto itself. Added highest option to Draggable to only drop on the droppable with the highest z-index. --- ui/jquery.ui.draggable.js | 2 + ui/jquery.ui.droppable.js | 109 ++++++++++++++++++++++++++++++-------- 2 files changed, 89 insertions(+), 22 deletions(-) diff --git a/ui/jquery.ui.draggable.js b/ui/jquery.ui.draggable.js index 7f146984233..30c3c9458d4 100644 --- a/ui/jquery.ui.draggable.js +++ b/ui/jquery.ui.draggable.js @@ -43,6 +43,7 @@ $.widget("ui.draggable", $.ui.mouse, { snapTolerance: 20, stack: false, zIndex: false, + highest: false, // callbacks drag: null, @@ -168,6 +169,7 @@ $.widget("ui.draggable", $.ui.mouse, { //Prepare the droppable offsets if ($.ui.ddmanager && !o.dropBehaviour) { + $.ui.ddmanager.zStack[this.options.scope] = []; $.ui.ddmanager.prepareOffsets(this, event); } diff --git a/ui/jquery.ui.droppable.js b/ui/jquery.ui.droppable.js index 4fbfcde06cf..dcbdffafe85 100644 --- a/ui/jquery.ui.droppable.js +++ b/ui/jquery.ui.droppable.js @@ -31,6 +31,7 @@ $.widget("ui.droppable", { hoverClass: false, scope: "default", tolerance: "intersect", + andSelf: false, // callbacks activate: null, @@ -105,20 +106,47 @@ $.widget("ui.droppable", { } }, + _triggerOver: function(event, draggable) { + if(this.options.hoverClass) { + this.element.addClass(this.options.hoverClass); + } + this._trigger("over", event, this.ui(draggable)); + }, + + _triggerOut: function(event, draggable) { + if(this.options.hoverClass) { + this.element.removeClass(this.options.hoverClass); + } + this._trigger("out", event, this.ui(draggable)); + }, + _over: function(event) { var draggable = $.ui.ddmanager.current; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return; - } + if (!draggable || ((draggable.currentItem || draggable.element)[0] == this.element[0] && !this.options.andSelf)) return; // Bail if draggable and droppable are same element if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) { - this.element.addClass(this.options.hoverClass); + if(!draggable.options.highest) { + this._triggerOver(event, draggable); + } + else { + var zStack = $.ui.ddmanager.zStack[draggable.options.scope]; + + if(zStack.indexOf(this) == -1) { + var lastHighest = zStack[zStack.length - 1]; + + zStack.push(this); + + zStack.sort(this._sortHighest); + + var newHighest = zStack[zStack.length - 1]; + + if(lastHighest !== newHighest) { + if(lastHighest != null) lastHighest._triggerOut(event, draggable); + if(newHighest != null) newHighest._triggerOver(event, draggable); + } + } } - this._trigger("over", event, this.ui(draggable)); } }, @@ -126,31 +154,52 @@ $.widget("ui.droppable", { _out: function(event) { var draggable = $.ui.ddmanager.current; - - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return; - } + if (!draggable || ((draggable.currentItem || draggable.element)[0] == this.element[0] && !this.options.andSelf)) return; // Bail if draggable and droppable are same element if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) { - if(this.options.hoverClass) { - this.element.removeClass(this.options.hoverClass); + if(!draggable.options.highest) { + this._triggerOut(event, draggable); + } + else { + var zStack = $.ui.ddmanager.zStack[draggable.options.scope]; + + var lastHighest = zStack[zStack.length - 1]; + + zStack.splice(zStack.indexOf(this), 1); + + zStack.sort(this._sortHighest); + + var newHighest = zStack[zStack.length - 1]; + + if(lastHighest !== newHighest) { + if(lastHighest != null) lastHighest._triggerOut(event, draggable); + if(newHighest != null) newHighest._triggerOver(event, draggable); + } } - this._trigger("out", event, this.ui(draggable)); } }, _drop: function(event,custom) { - var draggable = custom || $.ui.ddmanager.current, - childrenIntersection = false; + var draggable = custom || $.ui.ddmanager.current; - // Bail if draggable and droppable are same element - if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) { - return false; + if (!draggable || ((draggable.currentItem || draggable.element)[0] == this.element[0] && !this.options.andSelf)) return false; // Bail if draggable and droppable are same element + + if(!draggable.options.highest) { + return this._triggerDrop(event, draggable); } + else { + var zStack = $.ui.ddmanager.zStack[draggable.options.scope]; + if(this == zStack[zStack.length - 1]) { + return this._triggerDrop(event, draggable); + } + } + }, + + _triggerDrop: function(event, draggable) { + var childrenIntersection = false; this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() { var inst = $.data(this, "ui-droppable"); if( @@ -180,6 +229,21 @@ $.widget("ui.droppable", { }, + _sortHighest: function(a, b) { + var zIndexA = parseInt(a.element.css("z-index"), 10); + var zIndexB = parseInt(b.element.css("z-index"), 10); + + if(zIndexA !== zIndexB) { + return zIndexA - zIndexB; + } + + // If elements share a z-index, the element that is later in the DOM is on top. + var indexA = a.element.index(); + var indexB = b.element.index(); + + return indexA - indexB; + }, + ui: function(c) { return { draggable: (c.currentItem || c.element), @@ -237,6 +301,7 @@ $.ui.intersect = function(draggable, droppable, toleranceMode) { $.ui.ddmanager = { current: null, droppables: { "default": [] }, + zStack: { 'default': [] }, prepareOffsets: function(t, event) { var i, j, @@ -253,7 +318,7 @@ $.ui.ddmanager = { // Filter out elements in the current dragged item for (j=0; j < list.length; j++) { - if(list[j] === m[i].element[0]) { + if(list[j] === m[i].element[0] && !m[i].options.andSelf) { m[i].proportions.height = 0; continue droppablesLoop; } From 73c13bf15de58afed6ef88e06c71c0dfa29e48ec Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 23 Jan 2013 16:54:11 +1000 Subject: [PATCH 3/4] Added snapping plugin to resizable. --- ui/jquery.ui.resizable.js | 119 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/ui/jquery.ui.resizable.js b/ui/jquery.ui.resizable.js index 2468dcaa65f..5370d4735f8 100644 --- a/ui/jquery.ui.resizable.js +++ b/ui/jquery.ui.resizable.js @@ -876,6 +876,125 @@ $.ui.plugin.add("resizable", "alsoResize", { } }); +$.ui.plugin.add("resizable", "snap", { + start: function(event, ui) { + var self = $(this).data("ui-resizable"), o = self.options; + self.snapElements = []; + var useOffset = self._helper != null; + + $(o.snap.constructor != String ? ( o.snap.items || ':data(ui-resizable)' ) : o.snap).each(function() { + if(this != self.element[0]){ + + var inst = $(this).data("ui-resizable"); + var el = null; + if(inst != null) { + el = inst.element; + } + else { + el = $(this); + } + + var curleft = useOffset ? el.offset().left : parseFloat(el.css('left'), 10) || 0; + var curtop = useOffset ? el.offset().top : parseFloat(el.css('top'), 10) || 0; + + var curwidth = el.width(); + var curheight = el.height(); + + self.snapElements.push({ + item: this, + width: curwidth, + height: curheight, + top: curtop, + left: curleft + }); + } + }); + + }, + resize: function(event, ui) { + var self = $(this).data("ui-resizable"), o = self.options; + + var d = (o.snapTolerance === undefined ? 20 : o.snapTolerance); + var mode = (o.snapMode === undefined ? 'both' : o.snapMode); + + var x1 = ui.position.left, x2 = ui.position.left + ui.size.width, + y1 = ui.position.top, y2 = ui.position.top + ui.size.height; + + var axis = {}; + axis.n = self.axis.indexOf("n") != -1; + axis.s = self.axis.indexOf("s") != -1; + axis.w = self.axis.indexOf("w") != -1; + axis.e = self.axis.indexOf("e") != -1; + + // Container to hold returned position and size. + var snapped = {}; + + for (var index = self.snapElements.length - 1; index >= 0; index--){ + + var l = self.snapElements[index].left, r = l + self.snapElements[index].width, + t = self.snapElements[index].top, b = t + self.snapElements[index].height; + + var o = {}; + var i = {}; + + // Check for snapping on the outside of the element. + if(mode != 'inner') { + o.ts = Math.abs(t - y2) <= d; + o.bs = Math.abs(b - y1) <= d; + o.ls = Math.abs(l - x2) <= d; + o.rs = Math.abs(r - x1) <= d; + } + + // Check for snapping on the inside of the element. + if(mode != 'outer') { + i.ts = Math.abs(t - y1) <= d; + i.bs = Math.abs(b - y2) <= d; + i.ls = Math.abs(l - x1) <= d; + i.rs = Math.abs(r - x2) <= d; + } + + // If the resizeable would touch the snapElement if they were snapped together. + var v = (y2 >= t && y2 <= b) || (y1 >= t && y1 <= b) || (y1 <= t && y2 >= b) || o.ts || o.bs || i.ts || i.bs; + var h = (x2 >= l && x2 <= r) || (x1 >= l && x1 <= r) || (x1 <= l && x2 >= r) || o.ls || o.rs || i.ls || i.rs; + + if(o.ts && axis.s && h) { + snapped.height = t - ui.position.top; + } + if(o.bs && axis.n && h) { + snapped.height = ui.size.height - (b - ui.position.top); + snapped.top = b; + } + if(o.ls && axis.e && v) { + snapped.width = l - ui.position.left; + } + if(o.rs && axis.w && v) { + snapped.width = ui.size.width - (r - ui.position.left); + snapped.left = r; + } + + if(i.ts && axis.n && h) { + snapped.height = ui.size.height - (t - ui.position.top); + snapped.top = t; + } + if(i.bs && axis.s && h) { + snapped.height = b - ui.position.top; + } + if(i.ls && axis.w && v) { + snapped.width = ui.size.width - (l - ui.position.left); + snapped.left = l; + } + if(i.rs && axis.e && v) { + snapped.width = r - ui.position.left; + } + } + + if(snapped.left != undefined) ui.position.left = snapped.left; + if(snapped.top != undefined) ui.position.top = snapped.top; + if(snapped.width != undefined) ui.size.width = snapped.width; + if(snapped.height != undefined) ui.size.height = snapped.height; + }, +}); + $.ui.plugin.add("resizable", "ghost", { start: function() { From cd715f7d2885d7186cb99c543903d538cba88005 Mon Sep 17 00:00:00 2001 From: Matt Way Date: Mon, 25 Feb 2013 16:42:05 +1000 Subject: [PATCH 4/4] Fix for browsers that don't support JS 1.6 --- ui/jquery.ui.droppable.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/jquery.ui.droppable.js b/ui/jquery.ui.droppable.js index dcbdffafe85..52273aa6a40 100644 --- a/ui/jquery.ui.droppable.js +++ b/ui/jquery.ui.droppable.js @@ -132,7 +132,7 @@ $.widget("ui.droppable", { else { var zStack = $.ui.ddmanager.zStack[draggable.options.scope]; - if(zStack.indexOf(this) == -1) { + if($.inArray(this, zStack) == -1) { var lastHighest = zStack[zStack.length - 1]; zStack.push(this); @@ -165,7 +165,7 @@ $.widget("ui.droppable", { var lastHighest = zStack[zStack.length - 1]; - zStack.splice(zStack.indexOf(this), 1); + zStack.splice($.inArray(this, zStack), 1); zStack.sort(this._sortHighest);