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]);