diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 370dacd6..ebfe80a6 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -534,12 +534,19 @@ class RenderedTarget extends Target { /** * Delete a costume by index. * @param {number} index Costume index to be deleted + * @return {?object} The costume that was deleted or null + * if the index was out of bounds of the costumes list or + * this target only has one costume. */ deleteCostume (index) { const originalCostumeCount = this.sprite.costumes.length; - if (originalCostumeCount === 1) return; + if (originalCostumeCount === 1) return null; - this.sprite.deleteCostumeAt(index); + if (index < 0 || index >= originalCostumeCount) { + return null; + } + + const deletedCostume = this.sprite.deleteCostumeAt(index); if (index === this.currentCostume && index === originalCostumeCount - 1) { this.setCostume(index - 1); @@ -550,6 +557,7 @@ class RenderedTarget extends Target { } this.runtime.requestTargetsUpdate(this); + return deletedCostume; } /** diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index fa24f8d7..f02f41a0 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -93,11 +93,10 @@ class Sprite { /** * Delete a costume by index. * @param {number} index Costume index to be deleted + * @return {?object} The deleted costume */ deleteCostumeAt (index) { - this.costumes_ = this.costumes_ - .slice(0, index) - .concat(this.costumes_.slice(index + 1)); + return this.costumes.splice(index, 1)[0]; } /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index d23c8577..61871112 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -589,9 +589,19 @@ class VirtualMachine extends EventEmitter { /** * Delete a costume from the current editing target. * @param {int} costumeIndex - the index of the costume to be removed. + * @return {?function} A function to restore the deleted costume, or null, + * if no costume was deleted. */ deleteCostume (costumeIndex) { - this.editingTarget.deleteCostume(costumeIndex); + const deletedCostume = this.editingTarget.deleteCostume(costumeIndex); + if (deletedCostume) { + const target = this.editingTarget; + return () => { + target.addCostume(deletedCostume); + this.emitTargetsUpdate(); + }; + } + return null; } /** diff --git a/test/unit/sprites_rendered-target.js b/test/unit/sprites_rendered-target.js index cbcc9e50..6c92f1f0 100644 --- a/test/unit/sprites_rendered-target.js +++ b/test/unit/sprites_rendered-target.js @@ -116,41 +116,45 @@ test('deleteCostume', t => { // Costume 2 => Costume 3 // Costume 3 a.setCostume(0); - a.deleteCostume(0); + const deletedCostume = a.deleteCostume(0); t.equals(a.sprite.costumes.length, 2); t.equals(a.sprite.costumes[0].id, 2); t.equals(a.sprite.costumes[1].id, 3); t.equals(a.currentCostume, 0); + t.deepEqual(deletedCostume, o1); // Costume 1 Costume 1 // x* Costume 2 => * Costume 3 // Costume 3 a.sprite.costumes = [o1, o2, o3]; a.setCostume(1); - a.deleteCostume(1); + const deletedCostume2 = a.deleteCostume(1); t.equals(a.sprite.costumes.length, 2); t.equals(a.sprite.costumes[0].id, 1); t.equals(a.sprite.costumes[1].id, 3); t.equals(a.currentCostume, 1); + t.deepEqual(deletedCostume2, o2); // Costume 1 Costume 1 // Costume 2 => * Costume 2 // x* Costume 3 a.sprite.costumes = [o1, o2, o3]; a.setCostume(2); - a.deleteCostume(2); + const deletedCostume3 = a.deleteCostume(2); t.equals(a.sprite.costumes.length, 2); t.equals(a.sprite.costumes[0].id, 1); t.equals(a.sprite.costumes[1].id, 2); t.equals(a.currentCostume, 1); + t.deepEqual(deletedCostume3, o3); // Refuses to delete only costume a.sprite.costumes = [o1]; a.setCostume(0); - a.deleteCostume(0); + const noDeletedCostume = a.deleteCostume(0); t.equals(a.sprite.costumes.length, 1); t.equals(a.sprite.costumes[0].id, 1); t.equals(a.currentCostume, 0); + t.equal(noDeletedCostume, null); // Costume 1 Costume 1 // x Costume 2 Costume 3 diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js index 1bd56f50..81c9c043 100644 --- a/test/unit/virtual-machine.js +++ b/test/unit/virtual-machine.js @@ -30,6 +30,30 @@ test('deleteSound returns function after deleting or null if nothing was deleted t.end(); }); +test('deleteCostume returns function after deleting or null if nothing was deleted', t => { + const vm = new VirtualMachine(); + const sprite = new Sprite(); + sprite.costumes = [{id: 1}, {id: 2}, {id: 3}]; + sprite.currentCostume = 0; + const rt = new Runtime(); + const target = new RenderedTarget(sprite, rt); + vm.editingTarget = target; + + const addFun = vm.deleteCostume(1); + t.equal(sprite.costumes.length, 2); + t.equal(sprite.costumes[0].id, 1); + t.equal(sprite.costumes[1].id, 3); + t.type(addFun, 'function'); + + const noAddFun = vm.deleteCostume(2); + t.equal(sprite.costumes.length, 2); + t.equal(sprite.costumes[0].id, 1); + t.equal(sprite.costumes[1].id, 3); + t.equal(noAddFun, null); + + t.end(); +}); + test('addSprite throws on invalid string', t => { const vm = new VirtualMachine();