diff --git a/tests/unit/sortable/sortable.html b/tests/unit/sortable/sortable.html
index 3edc999b76e..30977ee9a3e 100644
--- a/tests/unit/sortable/sortable.html
+++ b/tests/unit/sortable/sortable.html
@@ -45,6 +45,29 @@
border-width: 0;
height:19px;
}
+ #sortable-horizontal {
+ width: 142px;
+ height: 22px;
+ }
+ #sortable-vertical {
+ width: 22px;
+ height: 142px;
+ }
+ #sortable-grid {
+ width: 62px;
+ height: 62px;
+ }
+ .sortable-axis span {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ }
+ .sortable-axis span.wide {
+ width: 60px;
+ }
+ .sortable-axis span.tall {
+ height: 60px;
+ }
#sortable-table {
width: 100%;
}
@@ -71,6 +94,34 @@
Item 5
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+
+
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+
+
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+ Item 6
+ Item 7
+ Item 8
+ Item 9
+
+
diff --git a/tests/unit/sortable/sortable_options.js b/tests/unit/sortable/sortable_options.js
index f2beb4dbcd6..6b96bf1e5eb 100644
--- a/tests/unit/sortable/sortable_options.js
+++ b/tests/unit/sortable/sortable_options.js
@@ -212,9 +212,127 @@ test("{ containment: 'window' }", function() {
test("{ containment: Selector }", function() {
ok(false, "missing test - untested code is broken code.");
+});*/
+
+test("#5772: wide element intersect sorting to first and last position on x-axis", function() {
+ expect(2);
+
+ var element = $("#sortable-horizontal").sortable({
+ axis: "x",
+ containment: "parent",
+ scroll: false,
+ tolerance: "intersect"
+ }),
+ item = element.find(".wide").eq(0);
+
+ item.simulate("drag", { dx: -150 });
+
+ equal(item.index(), 0, "Item is sorted to first position");
+
+ item.simulate("drag", { dx: 150 });
+
+ equal(item.index(), 4, "Item is sorted to last position");
+});
+
+test("#5772: wide element pointer sorting to first and last position on x-axis", function() {
+ expect(2);
+
+ var element = $("#sortable-horizontal").sortable({
+ axis: "x",
+ containment: "parent",
+ scroll: false,
+ tolerance: "pointer"
+ }),
+ item = element.find(".wide").eq(0);
+
+ item.simulate("drag", { dx: -150 });
+
+ equal(item.index(), 0, "Item is sorted to first position");
+
+ item.simulate("drag", { dx: 150 });
+
+ equal(item.index(), 4, "Item is sorted to last position");
+});
+
+test("#5772: tall element intersect sorting to first and last position on y-axis", function() {
+ expect(2);
+
+ var element = $("#sortable-vertical").sortable({
+ axis: "y",
+ containment: "parent",
+ scroll: false,
+ tolerance: "intersect"
+ }),
+ item = element.find(".tall").eq(0);
+
+ item.simulate("drag", { dy: -150 });
+
+ equal(item.index(), 0, "Item is sorted to first position");
+
+ item.simulate("drag", { dy: 150 });
+
+ equal(item.index(), 4, "Item is sorted to last position");
+});
+
+test("#5772: tall element pointer sorting to first and last position on y-axis", function() {
+ expect(2);
+
+ var element = $("#sortable-vertical").sortable({
+ axis: "y",
+ containment: "parent",
+ scroll: false,
+ tolerance: "pointer"
+ }),
+ item = element.find(".tall").eq(0);
+
+ item.simulate("drag", { dy: -150 });
+
+ equal(item.index(), 0, "Item is sorted to first position");
+
+ item.simulate("drag", { dy: 150 });
+
+ equal(item.index(), 4, "Item is sorted to last position");
+});
+
+test("#5772: element intersect sorting to first and last position on grid", function() {
+ expect(2);
+
+ var element = $("#sortable-grid").sortable({
+ containment: "parent",
+ scroll: false,
+ tolerance: "intersect"
+ }),
+ item = element.find("span").eq(5);
+
+ item.simulate("drag", { dy: -150, dx: -150 });
+
+ equal(item.index(), 0, "Item is sorted to first position");
+
+ item.simulate("drag", { dx: 150, dy: 150 });
+
+ equal(item.index(), 8, "Item is sorted to last position");
+});
+
+test("#5772: element pointer sorting to first and last position on grid", function() {
+ expect(2);
+
+ var element = $("#sortable-grid").sortable({
+ containment: "parent",
+ scroll: false,
+ tolerance: "pointer"
+ }),
+ item = element.find("span").eq(5);
+
+ item.simulate("drag", { dy: -150, dx: -150 });
+
+ equal(item.index(), 0, "Item is sorted to first position");
+
+ item.simulate("drag", { dx: 150, dy: 150 });
+
+ equal(item.index(), 8, "Item is sorted to last position");
});
-test("{ cursor: 'auto' }, default", function() {
+/*test("{ cursor: 'auto' }, default", function() {
ok(false, "missing test - untested code is broken code.");
});
diff --git a/ui/sortable.js b/ui/sortable.js
index 09f8aa10e97..fd1151c4b91 100644
--- a/ui/sortable.js
+++ b/ui/sortable.js
@@ -288,7 +288,6 @@ return $.widget("ui.sortable", $.ui.mouse, {
this._cacheHelperProportions();
}
-
//Post "activate" events to possible containers
if( !noActivation ) {
for ( i = this.containers.length - 1; i >= 0; i-- ) {
@@ -316,7 +315,8 @@ return $.widget("ui.sortable", $.ui.mouse, {
_mouseDrag: function(event) {
var i, item, itemElement, intersection,
o = this.options,
- scrolled = false;
+ scrolled = false,
+ touchingContainmentEdge = false;
//Compute the helpers position
this.position = this._generatePosition(event);
@@ -374,47 +374,68 @@ return $.widget("ui.sortable", $.ui.mouse, {
this.helper[0].style.top = this.position.top+"px";
}
- //Rearrange
- for (i = this.items.length - 1; i >= 0; i--) {
-
- //Cache variables and intersection, continue if no intersection
- item = this.items[i];
- itemElement = item.item[0];
- intersection = this._intersectsWithPointer(item);
- if (!intersection) {
- continue;
+ // Check if the helper is touching the edges of the containment.
+ if(this.containment) {
+ if((this.positionAbs.left === this.containment[0] || this.options.axis === "y") &&
+ (this.positionAbs.top === this.containment[1] || this.options.axis === "x")) {
+ touchingContainmentEdge = 0;
+ this.direction = "down";
}
-
- // Only put the placeholder inside the current Container, skip all
- // items from other containers. This works because when moving
- // an item from one container to another the
- // currentContainer is switched before the placeholder is moved.
- //
- // Without this, moving items in "sub-sortables" can cause
- // the placeholder to jitter between the outer and inner container.
- if (item.instance !== this.currentContainer) {
- continue;
+ else if((this.positionAbs.left === this.containment[2] || this.options.axis === "y") &&
+ (this.positionAbs.top === this.containment[3] || this.options.axis === "x")) {
+ touchingContainmentEdge = this.items.length - 1;
+ this.direction = "up";
}
+ }
- // cannot intersect with itself
- // no useless actions that have been done before
- // no action if the item moved is the parent of the item checked
- if (itemElement !== this.currentItem[0] &&
- this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
- !$.contains(this.placeholder[0], itemElement) &&
- (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
- ) {
+ if(touchingContainmentEdge !== false &&
+ this.placeholder[0] !== this.items[touchingContainmentEdge].item[0]) {
+ this._rearrange(event, this.items[touchingContainmentEdge]);
+ this._trigger("change", event, this._uiHash());
+ } else {
- this.direction = intersection === 1 ? "down" : "up";
+ //Rearrange
+ for (i = this.items.length - 1; i >= 0; i--) {
- if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
- this._rearrange(event, item);
- } else {
- break;
+ //Cache variables and intersection, continue if no intersection
+ item = this.items[i];
+ itemElement = item.item[0];
+ intersection = this._intersectsWithPointer(item);
+ if (!intersection) {
+ continue;
}
- this._trigger("change", event, this._uiHash());
- break;
+ // Only put the placeholder inside the current Container, skip all
+ // items from other containers. This works because when moving
+ // an item from one container to another the
+ // currentContainer is switched before the placeholder is moved.
+ //
+ // Without this, moving items in "sub-sortables" can cause
+ // the placeholder to jitter between the outer and inner container.
+ if (item.instance !== this.currentContainer) {
+ continue;
+ }
+
+ // cannot intersect with itself
+ // no useless actions that have been done before
+ // no action if the item moved is the parent of the item checked
+ if (itemElement !== this.currentItem[0] &&
+ this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
+ !$.contains(this.placeholder[0], itemElement) &&
+ (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
+ ) {
+
+ this.direction = intersection === 1 ? "down" : "up";
+
+ if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
+ this._rearrange(event, item);
+ } else {
+ break;
+ }
+
+ this._trigger("change", event, this._uiHash());
+ break;
+ }
}
}
@@ -493,6 +514,7 @@ return $.widget("ui.sortable", $.ui.mouse, {
}
if (this.placeholder) {
+
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
if(this.placeholder[0].parentNode) {
this.placeholder[0].parentNode.removeChild(this.placeholder[0]);