From 0ec0ea6ef7e9314f434113f7443d468629492a21 Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Wed, 13 Jun 2018 09:20:22 -0400
Subject: [PATCH 1/2] Add reorderTarget API to VM

---
 src/virtual-machine.js       | 20 ++++++++++++++++++++
 test/unit/virtual-machine.js | 24 ++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/src/virtual-machine.js b/src/virtual-machine.js
index 893f411fc..5e77adebf 100644
--- a/src/virtual-machine.js
+++ b/src/virtual-machine.js
@@ -6,6 +6,7 @@ const Buffer = require('buffer').Buffer;
 const centralDispatch = require('./dispatch/central-dispatch');
 const ExtensionManager = require('./extension-support/extension-manager');
 const log = require('./util/log');
+const MathUtil = require('./util/math-util');
 const Runtime = require('./engine/runtime');
 const sb2 = require('./serialization/sb2');
 const sb3 = require('./serialization/sb3');
@@ -1033,6 +1034,25 @@ class VirtualMachine extends EventEmitter {
         return null;
     }
 
+    /**
+     * Reorder target by index. Return whether a change was made.
+     * @param {!string} targetIndex Index of the target.
+     * @param {!number} newIndex index that the target should be moved to.
+     * @returns {boolean} Whether a target was reordered.
+     */
+    reorderTarget (targetIndex, newIndex) {
+        targetIndex = MathUtil.clamp(targetIndex, 0, this.runtime.targets.length - 1);
+        newIndex = MathUtil.clamp(newIndex, 0, this.runtime.targets.length - 1);
+        if (targetIndex === newIndex) return false;
+        const target = this.runtime.targets[targetIndex];
+        let targets = this.runtime.targets;
+        targets = targets.slice(0, targetIndex).concat(targets.slice(targetIndex + 1));
+        targets.splice(newIndex, 0, target);
+        this.runtime.targets = targets;
+        this.emitTargetsUpdate();
+        return true;
+    }
+
     /**
      * Reorder the costumes of a target if it exists. Return whether it succeeded.
      * @param {!string} targetId ID of the target which owns the costumes.
diff --git a/test/unit/virtual-machine.js b/test/unit/virtual-machine.js
index 22a69235f..1404f8df0 100644
--- a/test/unit/virtual-machine.js
+++ b/test/unit/virtual-machine.js
@@ -368,6 +368,30 @@ test('reorderSound', t => {
     t.end();
 });
 
+test('reorderTarget', t => {
+    const vm = new VirtualMachine();
+    vm.emitTargetsUpdate = () => {};
+
+    vm.runtime.targets = ['a', 'b', 'c', 'd'];
+
+    t.equal(vm.reorderTarget(2, 2), false);
+    t.deepEqual(vm.runtime.targets, ['a', 'b', 'c', 'd']);
+
+    // Make sure clamping works
+    t.equal(vm.reorderTarget(-100, -5), false);
+    t.deepEqual(vm.runtime.targets, ['a', 'b', 'c', 'd']);
+
+    // Reorder upwards
+    t.equal(vm.reorderTarget(0, 2), true);
+    t.deepEqual(vm.runtime.targets, ['b', 'c', 'a', 'd']);
+
+    // Reorder downwards
+    t.equal(vm.reorderTarget(3, 1), true);
+    t.deepEqual(vm.runtime.targets, ['b', 'd', 'c', 'a']);
+
+    t.end();
+});
+
 test('emitWorkspaceUpdate', t => {
     const vm = new VirtualMachine();
     const blocksToXML = comments => {

From 4b64df222a1a4228a73e9dfd5abc8c82a7662677 Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Wed, 13 Jun 2018 10:09:22 -0400
Subject: [PATCH 2/2] Hoist target list assignment to simplify code.

---
 src/virtual-machine.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/virtual-machine.js b/src/virtual-machine.js
index 5e77adebf..4ff368faa 100644
--- a/src/virtual-machine.js
+++ b/src/virtual-machine.js
@@ -1041,11 +1041,11 @@ class VirtualMachine extends EventEmitter {
      * @returns {boolean} Whether a target was reordered.
      */
     reorderTarget (targetIndex, newIndex) {
-        targetIndex = MathUtil.clamp(targetIndex, 0, this.runtime.targets.length - 1);
-        newIndex = MathUtil.clamp(newIndex, 0, this.runtime.targets.length - 1);
-        if (targetIndex === newIndex) return false;
-        const target = this.runtime.targets[targetIndex];
         let targets = this.runtime.targets;
+        targetIndex = MathUtil.clamp(targetIndex, 0, targets.length - 1);
+        newIndex = MathUtil.clamp(newIndex, 0, targets.length - 1);
+        if (targetIndex === newIndex) return false;
+        const target = targets[targetIndex];
         targets = targets.slice(0, targetIndex).concat(targets.slice(targetIndex + 1));
         targets.splice(newIndex, 0, target);
         this.runtime.targets = targets;