diff --git a/src/item/Item.js b/src/item/Item.js index 390d095c..d790f64a 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -2350,14 +2350,9 @@ new function() { // Injection scope for hit-test functions shared with project if (_proto && !(item instanceof _proto)) { items.splice(i, 1); } else { - // If the item is removed and inserted it again further - // above, the index needs to be adjusted accordingly. - var owner = item._getOwner(), - shift = owner === this && item._index < index; // Notify parent of change. Don't notify item itself yet, // as we're doing so when adding it to the new owner below. - if (owner && item._remove(false, true) && shift) - index--; + item._remove(false, true); } } Base.splice(children, items, index, 0); @@ -2387,6 +2382,32 @@ new function() { // Injection scope for hit-test functions shared with project // through _getOwner() in the various Item#insert*() methods. _insertItem: '#insertChild', + /** + * Private helper method used by {@link #insertAbove(item)} and + * {@link #insertBelow(item)}, to insert this item in relation to a + * specified other item. + * + * @param {Item} item the item in relation to which which it should be + * inserted + * @param {Number} offset the offset at which the item should be inserted + * @return {Item} the inserted item, or `null` if inserting was not possible + */ + _insertAt: function(item, offset, _preserve) { + var res = this; + if (res !== item) { + var owner = item && item._getOwner(); + if (owner) { + // Notify parent of change. Don't notify item itself yet, + // as we're doing so when adding it to the new owner below. + res._remove(false, true); + owner._insertItem(item._index + offset, res, _preserve); + } else { + res = null; + } + } + return res; + }, + /** * Inserts this item above the specified item. * @@ -2394,9 +2415,7 @@ new function() { // Injection scope for hit-test functions shared with project * @return {Item} the inserted item, or `null` if inserting was not possible */ insertAbove: function(item, _preserve) { - var owner = item && item._getOwner(); - return owner ? owner._insertItem(item._index + 1, this, _preserve) - : null; + return this._insertAt(item, 1, _preserve); }, /** @@ -2406,8 +2425,7 @@ new function() { // Injection scope for hit-test functions shared with project * @return {Item} the inserted item, or `null` if inserting was not possible */ insertBelow: function(item, _preserve) { - var owner = item && item._getOwner(); - return owner ? owner._insertItem(item._index, this, _preserve) : null; + return this._insertAt(item, 0, _preserve); }, /** diff --git a/src/item/Project.js b/src/item/Project.js index 79dd9b26..c84791f6 100644 --- a/src/item/Project.js +++ b/src/item/Project.js @@ -352,6 +352,8 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{ */ insertLayer: function(index, layer) { if (layer instanceof Layer) { + // Notify parent of change. Don't notify item itself yet, + // as we're doing so when adding it to the new owner below. layer._remove(false, true); Base.splice(this._children, [layer], index, 0); layer._setProject(this, true); diff --git a/test/tests/Item_Order.js b/test/tests/Item_Order.js index 94b06eed..b99814ef 100644 --- a/test/tests/Item_Order.js +++ b/test/tests/Item_Order.js @@ -59,37 +59,57 @@ test('Item Order', function() { test('Item#insertAbove(item) / Item#insertBelow(item)', function() { var item0, item1, item2; - function testMove(command, indexes) { - paper.project.clear(); - new Layer(); - item0 = new Group(); - item1 = new Group(); - item2 = new Group(); - command(); - var str = getFunctionMessage(command); - equals(item0.index, indexes[0], str + ': item0.index'); - equals(item1.index, indexes[1], str + ': item1.index'); - equals(item2.index, indexes[2], str + ': item2.index'); + function testType(ctor) { + function testMove(command, indexes) { + paper.project.clear(); + if (ctor !== Layer) + new Layer(); + item0 = new ctor(); + item1 = new ctor(); + item2 = new ctor(); + command(); + var str = getFunctionMessage(command); + var name = item0.className.toLowerCase(); + equals(item0.index, indexes[0], str + ': ' + name + '0.index'); + equals(item1.index, indexes[1], str + ': ' + name + '1.index'); + equals(item2.index, indexes[2], str + ': ' + name + '2.index'); + } + + testMove(function() { item0.insertBelow(item0); }, [0,1,2]); + testMove(function() { item0.insertBelow(item1); }, [0,1,2]); + testMove(function() { item0.insertBelow(item2); }, [1,0,2]); + testMove(function() { item1.insertBelow(item0); }, [1,0,2]); + testMove(function() { item1.insertBelow(item1); }, [0,1,2]); + testMove(function() { item1.insertBelow(item2); }, [0,1,2]); + + testMove(function() { item2.insertBelow(item0); }, [1,2,0]); + testMove(function() { item2.insertBelow(item1); }, [0,2,1]); + testMove(function() { item2.insertBelow(item2); }, [0,1,2]); + + testMove(function() { item0.insertAbove(item0); }, [0,1,2]); + testMove(function() { item0.insertAbove(item1); }, [1,0,2]); + testMove(function() { item0.insertAbove(item2); }, [2,0,1]); + testMove(function() { item1.insertAbove(item0); }, [0,1,2]); + testMove(function() { item1.insertAbove(item1); }, [0,1,2]); + testMove(function() { item1.insertAbove(item2); }, [0,2,1]); + testMove(function() { item2.insertAbove(item0); }, [0,2,1]); + testMove(function() { item2.insertAbove(item1); }, [0,1,2]); + testMove(function() { item2.insertAbove(item2); }, [0,1,2]); } - testMove(function() { item0.insertBelow(item0); }, [0,1,2]); - testMove(function() { item0.insertBelow(item1); }, [0,1,2]); - testMove(function() { item0.insertBelow(item2); }, [1,0,2]); - testMove(function() { item1.insertBelow(item0); }, [1,0,2]); - testMove(function() { item1.insertBelow(item1); }, [0,1,2]); - testMove(function() { item1.insertBelow(item2); }, [0,1,2]); - - testMove(function() { item2.insertBelow(item0); }, [1,2,0]); - testMove(function() { item2.insertBelow(item1); }, [0,2,1]); - testMove(function() { item2.insertBelow(item2); }, [0,1,2]); - - testMove(function() { item0.insertAbove(item0); }, [0,1,2]); - testMove(function() { item0.insertAbove(item1); }, [1,0,2]); - testMove(function() { item0.insertAbove(item2); }, [2,0,1]); - testMove(function() { item1.insertAbove(item0); }, [0,1,2]); - testMove(function() { item1.insertAbove(item1); }, [0,1,2]); - testMove(function() { item1.insertAbove(item2); }, [0,2,1]); - testMove(function() { item2.insertAbove(item0); }, [0,2,1]); - testMove(function() { item2.insertAbove(item1); }, [0,1,2]); - testMove(function() { item2.insertAbove(item2); }, [0,1,2]); + testType(Group); + testType(Layer); +}); + +test('Item#insertChild() with already inserted children', function() { + var item1 = new Group(), + item2 = new Group(), + item3 = new Group(), + item4 = new Group(), + newIndex = 1, + oldIndex = item4.index; + item4.parent.insertChild(1, item4); + equals(function() { return item4.index; }, newIndex); + item4.parent.insertChild(oldIndex, item4); + equals(function() { return item4.index; }, oldIndex); });