From 809528abdc4c60bb941df03daa06798ddd767941 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 29 Jun 2016 13:48:30 -0400
Subject: [PATCH 01/40] Straw-man implementation of targets/sprites/clones

---
 src/engine/execute.js   |  5 ++--
 src/engine/runtime.js   | 63 +++++++++++++++++++----------------------
 src/engine/sequencer.js |  5 ++--
 src/engine/target.js    | 20 +++++++++++++
 src/index.js            | 18 +++++++-----
 src/sprites/clone.js    | 16 +++++++++++
 src/sprites/sprite.js   | 17 +++++++++++
 7 files changed, 99 insertions(+), 45 deletions(-)
 create mode 100644 src/engine/target.js
 create mode 100644 src/sprites/clone.js
 create mode 100644 src/sprites/sprite.js

diff --git a/src/engine/execute.js b/src/engine/execute.js
index 188f0bf52..001178d56 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -7,6 +7,7 @@ var Thread = require('./thread');
  */
 var execute = function (sequencer, thread) {
     var runtime = sequencer.runtime;
+    var target = runtime.targetForThread(thread);
 
     // Current block to execute is the one on the top of the stack.
     var currentBlockId = thread.peekStack();
@@ -29,13 +30,13 @@ var execute = function (sequencer, thread) {
     var argValues = {};
 
     // Add all fields on this block to the argValues.
-    var fields = runtime.blocks.getFields(currentBlockId);
+    var fields = target.blocks.getFields(currentBlockId);
     for (var fieldName in fields) {
         argValues[fieldName] = fields[fieldName].value;
     }
 
     // Recursively evaluate input blocks.
-    var inputs = runtime.blocks.getInputs(currentBlockId);
+    var inputs = target.blocks.getInputs(currentBlockId);
     for (var inputName in inputs) {
         var input = inputs[inputName];
         var inputBlockId = input.block;
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index 8c4c862e1..a0db919ae 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -10,19 +10,19 @@ var defaultBlockPackages = {
 };
 
 /**
- * Manages blocks, stacks, and the sequencer.
- * @param {!Blocks} blocks Blocks instance for this runtime.
+ * Manages targets, stacks, and the sequencer.
+ * @param {!Array.<Target>} targets List of targets for this runtime.
  */
-function Runtime (blocks) {
+function Runtime (targets) {
     // Bind event emitter
     EventEmitter.call(this);
 
     // State for the runtime
 
     /**
-     * Block management and storage
+     * Target management and storage.
      */
-    this.blocks = blocks;
+    this.targets = targets;
 
     /**
      * A list of threads that are currently running in the VM.
@@ -163,32 +163,13 @@ Runtime.prototype.greenFlag = function () {
         this._removeThread(this.threads[i]);
     }
     // Add all top stacks with green flag
-    var stacks = this.blocks.getStacks();
-    for (var j = 0; j < stacks.length; j++) {
-        var topBlock = stacks[j];
-        if (this.blocks.getBlock(topBlock).opcode === 'event_whenflagclicked') {
-            this._pushThread(stacks[j]);
-        }
-    }
-};
-
-/**
- * Distance sensor hack
- */
-Runtime.prototype.startDistanceSensors = function () {
-    // Add all top stacks with distance sensor
-    var stacks = this.blocks.getStacks();
-    for (var j = 0; j < stacks.length; j++) {
-        var topBlock = stacks[j];
-        if (this.blocks.getBlock(topBlock).opcode ===
-            'wedo_whendistanceclose') {
-            var alreadyRunning = false;
-            for (var k = 0; k < this.threads.length; k++) {
-                if (this.threads[k].topBlock === topBlock) {
-                    alreadyRunning = true;
-                }
-            }
-            if (!alreadyRunning) {
+    for (var t = 0; t < this.targets.length; t++) {
+        var target = this.targets[t];
+        var stacks = target.blocks.getStacks();
+        for (var j = 0; j < stacks.length; j++) {
+            var topBlock = stacks[j];
+            if (target.blocks.getBlock(topBlock).opcode ===
+                'event_whenflagclicked') {
                 this._pushThread(stacks[j]);
             }
         }
@@ -228,9 +209,6 @@ Runtime.prototype._step = function () {
  * @param {boolean} isGlowing True to turn on glow; false to turn off.
  */
 Runtime.prototype.glowBlock = function (blockId, isGlowing) {
-    if (!this.blocks.getBlock(blockId)) {
-        return;
-    }
     if (isGlowing) {
         this.emit(Runtime.BLOCK_GLOW_ON, blockId);
     } else {
@@ -238,6 +216,23 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) {
     }
 };
 
+/**
+ * Return the Target for a particular thread.
+ * @param {!Thread} thread Thread to determine target for.
+ * @return {?Target} Target object, if one exists.
+ */
+Runtime.prototype.targetForThread = function (thread) {
+    // @todo This is a messy solution,
+    // but prevents having circular data references.
+    // Have a map or some other way to associate target with threads.
+    for (var t = 0; t < this.targets.length; t++) {
+        var target = this.targets[t];
+        if (target.blocks.getBlock(thread.topBlock)) {
+            return target;
+        }
+    }
+};
+
 /**
  * setInterval implementation that works in a WebWorker or not.
  * @param {?Function} fcn Function to call.
diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index 1498d6320..e70955e69 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -106,7 +106,7 @@ Sequencer.prototype.stepToSubstack = function (thread, substackNum) {
         substackNum = 1;
     }
     var currentBlockId = thread.peekStack();
-    var substackId = this.runtime.blocks.getSubstack(
+    var substackId = this.runtime.targetForThread(thread).blocks.getSubstack(
         currentBlockId,
         substackNum
     );
@@ -153,7 +153,8 @@ Sequencer.prototype.proceedThread = function (thread) {
     // Pop from the stack - finished this level of execution.
     thread.popStack();
     // Push next connected block, if there is one.
-    var nextBlockId = this.runtime.blocks.getNextBlock(currentBlockId);
+    var nextBlockId = (this.runtime.targetForThread(thread).
+        blocks.getNextBlock(currentBlockId));
     if (nextBlockId) {
         thread.pushStack(nextBlockId);
     }
diff --git a/src/engine/target.js b/src/engine/target.js
new file mode 100644
index 000000000..ad7d192c6
--- /dev/null
+++ b/src/engine/target.js
@@ -0,0 +1,20 @@
+var Blocks = require('./blocks');
+
+/**
+ * @fileoverview
+ * A Target is an abstract "code-running" object for the Scratch VM.
+ * Examples include sprites/clones or potentially physical-world devices.
+ */
+
+/**
+ * @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
+ * @constructor
+ */
+function Target (blocks) {
+    if (!blocks) {
+        blocks = new Blocks(this);
+    }
+    this.blocks = blocks;
+}
+
+module.exports = Target;
diff --git a/src/index.js b/src/index.js
index 174f05b1c..08109c0a9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,7 +1,7 @@
 var EventEmitter = require('events');
 var util = require('util');
 
-var Blocks = require('./engine/blocks');
+var Sprite = require('./sprites/sprite');
 var Runtime = require('./engine/runtime');
 
 /**
@@ -21,18 +21,22 @@ function VirtualMachine () {
     // Bind event emitter and runtime to VM instance
     // @todo Post message (Web Worker) polyfill
     EventEmitter.call(instance);
-    instance.blocks = new Blocks();
-    instance.runtime = new Runtime(instance.blocks);
+    // @todo support multiple targets/sprites.
+    // This is just a demo/example.
+    var exampleSprite = new Sprite();
+    var exampleTargets = [exampleSprite.clones[0]];
+    instance.exampleSprite = exampleSprite;
+    instance.runtime = new Runtime(exampleTargets);
 
     /**
      * Event listeners for scratch-blocks.
      */
     instance.blockListener = (
-        instance.blocks.generateBlockListener(false, instance.runtime)
+        exampleSprite.blocks.generateBlockListener(false, instance.runtime)
     );
 
     instance.flyoutBlockListener = (
-        instance.blocks.generateBlockListener(true, instance.runtime)
+        exampleSprite.blocks.generateBlockListener(true, instance.runtime)
     );
 
     // Runtime emits are passed along as VM emits.
@@ -81,7 +85,7 @@ VirtualMachine.prototype.stopAll = function () {
  */
 VirtualMachine.prototype.getPlaygroundData = function () {
     this.emit('playgroundData', {
-        blocks: this.blocks,
+        blocks: this.exampleSprite.blocks,
         threads: this.runtime.threads
     });
 };
@@ -114,7 +118,7 @@ if (ENV_WORKER) {
         case 'getPlaygroundData':
             self.postMessage({
                 method: 'playgroundData',
-                blocks: self.vmInstance.blocks,
+                blocks: self.vmInstance.exampleSprite.blocks,
                 threads: self.vmInstance.runtime.threads
             });
             break;
diff --git a/src/sprites/clone.js b/src/sprites/clone.js
new file mode 100644
index 000000000..afbd56f1c
--- /dev/null
+++ b/src/sprites/clone.js
@@ -0,0 +1,16 @@
+var util = require('util');
+var Target = require('../engine/target');
+
+function Clone(spriteBlocks) {
+    Target.call(this, spriteBlocks);
+}
+util.inherits(Clone, Target);
+
+// Clone-level properties
+Clone.prototype.x = 0;
+
+Clone.prototype.y = 0;
+
+Clone.prototype.direction = 90;
+
+module.exports = Clone;
diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js
new file mode 100644
index 000000000..1608b0571
--- /dev/null
+++ b/src/sprites/sprite.js
@@ -0,0 +1,17 @@
+var Clone = require('./clone');
+var Blocks = require('../engine/blocks');
+
+function Sprite (blocks) {
+    // Sprites have: shared blocks, shared costumes, shared variables, etc.
+    if (!blocks) {
+        // Shared set of blocks for all clones.
+        blocks = new Blocks();
+    }
+    this.blocks = blocks;
+    this.clones = [];
+
+    // Initial single clone with the shared blocks.
+    this.clones.push(new Clone(this.blocks));
+}
+
+module.exports = Sprite;

From 19da0b003287d9d011b4238b3e73ff1fdcf6e64f Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 29 Jun 2016 20:56:55 -0400
Subject: [PATCH 02/40] Add renderer demo using scratch-render-webgl

---
 package.json                     |  3 +-
 playground/index.html            | 55 ++++++++++++++++++++++++++++++++
 playground/playground.css        |  2 +-
 playground/playground.js         | 13 ++++++++
 src/blocks/scratch3_motion.js    | 31 ++++++++++++++++++
 src/blocks/scratch3_operators.js |  7 +++-
 src/engine/execute.js            |  3 +-
 src/engine/runtime.js            |  4 +++
 src/index.js                     | 11 ++++++-
 src/sprites/clone.js             | 25 +++++++++++++++
 src/sprites/sprite.js            |  9 ++++--
 11 files changed, 155 insertions(+), 8 deletions(-)
 create mode 100644 src/blocks/scratch3_motion.js

diff --git a/package.json b/package.json
index 2d4d54773..2b6d7335a 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,8 @@
   "dependencies": {
     "htmlparser2": "3.9.0",
     "memoizee": "0.3.10",
-    "promise": "7.1.1"
+    "promise": "7.1.1",
+    "scratch-render-webgl": "git+https://github.com/LLK/scratch-render-webgl.git"
   },
   "devDependencies": {
     "eslint": "2.7.0",
diff --git a/playground/index.html b/playground/index.html
index 82535807b..e11d72c82 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -13,9 +13,14 @@
         <button id="greenflag">Green flag</button>
         <button id="stopall">Stop</button>
         <p>
+            <a id="renderexplorer-link" href="#">Renderer</a><br />
             <a id="threadexplorer-link" href="#">VM Threads</a><br />
             <a id="blockexplorer-link" href="#">VM Block Representation</a>
         </p>
+        <div id="tab-renderexplorer">
+            Render<br />
+            <canvas id="scratch-stage" style="width: 480px; height: 360px;"></canvas>
+        </div>
         <div id="tab-threadexplorer">
             Thread explorer
             <pre id="threadexplorer"></pre>
@@ -240,6 +245,54 @@
               </value>
             </block>
         </category>
+        <category name="Motion">
+            <block type="motion_movesteps">
+             <value name="STEPS">
+               <shadow type="math_number">
+                 <field name="NUM">10</field>
+               </shadow>
+             </value>
+           </block>
+           <block type="motion_turnright">
+             <value name="DEGREES">
+               <shadow type="math_number">
+                 <field name="NUM">15</field>
+               </shadow>
+             </value>
+           </block>
+           <block type="motion_turnleft">
+             <value name="DEGREES">
+               <shadow type="math_number">
+                 <field name="NUM">15</field>
+               </shadow>
+             </value>
+           </block>
+           <block type="motion_pointindirection">
+             <value name="DIRECTION">
+               <shadow type="math_number">
+                 <field name="NUM">90</field>
+               </shadow>
+             </value>
+           </block>
+           <block type="motion_pointtowards">
+             <value name="TOWARDS">
+               <shadow type="motion_pointtowards_menu">
+               </shadow>
+             </value>
+           </block>
+           <block type="motion_gotoxy">
+             <value name="X">
+               <shadow type="math_number">
+                 <field name="NUM">0</field>
+               </shadow>
+             </value>
+             <value name="Y">
+               <shadow type="math_number">
+                 <field name="NUM">0</field>
+               </shadow>
+             </value>
+           </block>
+        </category>
     </xml>
 
     <!-- Syntax highlighter -->
@@ -249,6 +302,8 @@
     <script src="../node_modules/scratch-blocks/blockly_compressed_vertical.js"></script>
     <script src="../node_modules/scratch-blocks/blocks_compressed.js"></script>
     <script src="../node_modules/scratch-blocks/blocks_compressed_vertical.js"></script>
+    <!-- Renderer -->
+    <script src="../node_modules/scratch-render-webgl/build/render-webgl.js"></script>
     <!-- VM Worker -->
     <script src="../vm.worker.js"></script>
     <!-- Playground -->
diff --git a/playground/playground.css b/playground/playground.css
index 451a7cad5..a15e1d81c 100644
--- a/playground/playground.css
+++ b/playground/playground.css
@@ -32,6 +32,6 @@ a {
     font-size: 10pt;
 }
 
-#tab-blockexplorer {
+#tab-blockexplorer, #tab-threadexplorer {
     display: none;
 }
diff --git a/playground/playground.js b/playground/playground.js
index be683ceb7..220549c9f 100644
--- a/playground/playground.js
+++ b/playground/playground.js
@@ -3,6 +3,10 @@ window.onload = function() {
     var vm = new window.VirtualMachine();
     window.vm = vm;
 
+    var canvas = document.getElementById('scratch-stage');
+    window.renderer = new window.RenderWebGLLocal(canvas);
+    window.renderer.connectWorker(window.vm.vmWorker);
+
     var toolbox = document.getElementById('toolbox');
     var workspace = window.Blockly.inject('blocks', {
         toolbox: toolbox,
@@ -85,16 +89,25 @@ window.onload = function() {
 
     var tabBlockExplorer = document.getElementById('tab-blockexplorer');
     var tabThreadExplorer = document.getElementById('tab-threadexplorer');
+    var tabRenderExplorer = document.getElementById('tab-renderexplorer');
 
     // Handlers to show different explorers.
     document.getElementById('threadexplorer-link').addEventListener('click',
         function () {
             tabBlockExplorer.style.display = 'none';
+            tabRenderExplorer.style.display = 'none';
             tabThreadExplorer.style.display = 'block';
         });
     document.getElementById('blockexplorer-link').addEventListener('click',
         function () {
             tabBlockExplorer.style.display = 'block';
+            tabRenderExplorer.style.display = 'none';
+            tabThreadExplorer.style.display = 'none';
+        });
+    document.getElementById('renderexplorer-link').addEventListener('click',
+        function () {
+            tabBlockExplorer.style.display = 'none';
+            tabRenderExplorer.style.display = 'block';
             tabThreadExplorer.style.display = 'none';
         });
 };
diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js
new file mode 100644
index 000000000..4fcc3b056
--- /dev/null
+++ b/src/blocks/scratch3_motion.js
@@ -0,0 +1,31 @@
+function Scratch3MotionBlocks(runtime) {
+    /**
+     * The runtime instantiating this block package.
+     * @type {Runtime}
+     */
+    this.runtime = runtime;
+}
+
+/**
+ * Retrieve the block primitives implemented by this package.
+ * @return {Object.<string, Function>} Mapping of opcode to Function.
+ */
+Scratch3MotionBlocks.prototype.getPrimitives = function() {
+    return {
+        'motion_gotoxy': this.goToXY,
+        'motion_turnright': this.turnRight
+    };
+};
+
+Scratch3MotionBlocks.prototype.goToXY = function (args, util) {
+    util.target.setXY(args.X, args.Y);
+};
+
+Scratch3MotionBlocks.prototype.turnRight = function (args, util) {
+    if (args.DEGREES !== args.DEGREES) {
+        throw "Bad degrees" + args.DEGREES;
+    }
+    util.target.setDirection(args.DEGREES + util.target.direction);
+};
+
+module.exports = Scratch3MotionBlocks;
diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js
index 75077061c..86cb50350 100644
--- a/src/blocks/scratch3_operators.js
+++ b/src/blocks/scratch3_operators.js
@@ -31,7 +31,12 @@ Scratch3OperatorsBlocks.prototype.getPrimitives = function() {
 };
 
 Scratch3OperatorsBlocks.prototype.number = function (args) {
-    return Number(args.NUM);
+    var num = Number(args.NUM);
+    if (num !== num) {
+        // NaN
+        return 0;
+    }
+    return num;
 };
 
 Scratch3OperatorsBlocks.prototype.text = function (args) {
diff --git a/src/engine/execute.js b/src/engine/execute.js
index 001178d56..aa413a76b 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -70,7 +70,8 @@ var execute = function (sequencer, thread) {
         stackFrame: currentStackFrame.executionContext,
         startSubstack: function (substackNum) {
             sequencer.stepToSubstack(thread, substackNum);
-        }
+        },
+        target: target
     });
 
     // Deal with any reported value.
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index a0db919ae..c51800cdb 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -6,6 +6,7 @@ var util = require('util');
 var defaultBlockPackages = {
     'scratch3_control': require('../blocks/scratch3_control'),
     'scratch3_event': require('../blocks/scratch3_event'),
+    'scratch3_motion': require('../blocks/scratch3_motion'),
     'scratch3_operators': require('../blocks/scratch3_operators')
 };
 
@@ -257,6 +258,9 @@ Runtime.prototype._setInterval = function(fcn, interval) {
 Runtime.prototype.start = function () {
     this._setInterval(function() {
         this._step();
+        if (self.renderer) {
+            self.renderer.draw();
+        }
     }.bind(this), Runtime.THREAD_STEP_INTERVAL);
 };
 
diff --git a/src/index.js b/src/index.js
index 08109c0a9..fff9c52db 100644
--- a/src/index.js
+++ b/src/index.js
@@ -24,6 +24,7 @@ function VirtualMachine () {
     // @todo support multiple targets/sprites.
     // This is just a demo/example.
     var exampleSprite = new Sprite();
+    exampleSprite.createClone();
     var exampleTargets = [exampleSprite.clones[0]];
     instance.exampleSprite = exampleSprite;
     instance.runtime = new Runtime(exampleTargets);
@@ -96,6 +97,10 @@ VirtualMachine.prototype.getPlaygroundData = function () {
  * from a worker environment.
  */
 if (ENV_WORKER) {
+    self.importScripts(
+        './node_modules/scratch-render-webgl/build/render-webgl-worker.js'
+    );
+    self.renderer = new self.RenderWebGLWorker();
     self.vmInstance = new VirtualMachine();
     self.onmessage = function (e) {
         var messageData = e.data;
@@ -123,7 +128,11 @@ if (ENV_WORKER) {
             });
             break;
         default:
-            throw 'Unknown method' + messageData.method;
+            if (e.data.id == 'RendererConnected') {
+                //initRenderWorker();
+            }
+            self.renderer.onmessage(e);
+            break;
         }
     };
     // Bind runtime's emitted events to postmessages.
diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index afbd56f1c..f4f99abbe 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -3,9 +3,19 @@ var Target = require('../engine/target');
 
 function Clone(spriteBlocks) {
     Target.call(this, spriteBlocks);
+    this.drawableID = null;
+    this.initDrawable();
 }
 util.inherits(Clone, Target);
 
+Clone.prototype.initDrawable = function () {
+    var createPromise = self.renderer.createDrawable();
+    var instance = this;
+    createPromise.then(function (id) {
+        instance.drawableID = id;
+    });
+};
+
 // Clone-level properties
 Clone.prototype.x = 0;
 
@@ -13,4 +23,19 @@ Clone.prototype.y = 0;
 
 Clone.prototype.direction = 90;
 
+Clone.prototype.setXY = function (x, y) {
+    this.x = x;
+    this.y = y;
+    self.renderer.updateDrawableProperties(this.drawableID, {
+        position: [this.x, this.y]
+    });
+};
+
+Clone.prototype.setDirection = function (direction) {
+    this.direction = direction;
+    self.renderer.updateDrawableProperties(this.drawableID, {
+        direction: this.direction
+    });
+};
+
 module.exports = Clone;
diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js
index 1608b0571..1ec25b3ba 100644
--- a/src/sprites/sprite.js
+++ b/src/sprites/sprite.js
@@ -9,9 +9,12 @@ function Sprite (blocks) {
     }
     this.blocks = blocks;
     this.clones = [];
-
-    // Initial single clone with the shared blocks.
-    this.clones.push(new Clone(this.blocks));
 }
 
+Sprite.prototype.createClone = function () {
+    var newClone = new Clone(this.blocks);
+    this.clones.push(newClone);
+    return newClone;
+};
+
 module.exports = Sprite;

From 7db38e8422b8512e80a3959e0d8243bc344c023c Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 29 Jun 2016 23:07:23 -0400
Subject: [PATCH 03/40] Implement a few math utilities

---
 src/util/math-util.js | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 src/util/math-util.js

diff --git a/src/util/math-util.js b/src/util/math-util.js
new file mode 100644
index 000000000..2dfd62a6e
--- /dev/null
+++ b/src/util/math-util.js
@@ -0,0 +1,20 @@
+function MathUtil () {}
+
+MathUtil.degToRad = function (deg) {
+    return (Math.PI * (90 - deg)) / 180;
+};
+
+MathUtil.radToDeg = function (rad) {
+    return rad * 180 / Math.PI;
+};
+
+MathUtil.clamp = function (n, min, max) {
+    return Math.min(Math.max(n, min), max);
+};
+
+MathUtil.wrapClamp = function (n, min, max) {
+    var range = (max - min) + 1;
+    return n - Math.floor((n - min) / range) * range;
+};
+
+module.exports = MathUtil;

From 727d2c0d23d00122939435f2ed16ef27ad276bd2 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 29 Jun 2016 23:07:34 -0400
Subject: [PATCH 04/40] Clamp clone direction like Scratch 2.0

---
 src/sprites/clone.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index f4f99abbe..b69c68d7a 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -1,4 +1,5 @@
 var util = require('util');
+var MathUtil = require('../util/math-util');
 var Target = require('../engine/target');
 
 function Clone(spriteBlocks) {
@@ -32,7 +33,7 @@ Clone.prototype.setXY = function (x, y) {
 };
 
 Clone.prototype.setDirection = function (direction) {
-    this.direction = direction;
+    this.direction = MathUtil.wrapClamp(direction, -179, 180);
     self.renderer.updateDrawableProperties(this.drawableID, {
         direction: this.direction
     });

From 42f0f66acdef27a58599d59f72c6b8ae784975ab Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 29 Jun 2016 23:08:10 -0400
Subject: [PATCH 05/40] Implement move steps, turn right, turn left, point in
 direction

---
 src/blocks/scratch3_motion.js | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js
index 4fcc3b056..f9171d9e7 100644
--- a/src/blocks/scratch3_motion.js
+++ b/src/blocks/scratch3_motion.js
@@ -1,3 +1,5 @@
+var MathUtil = require('../util/math-util');
+
 function Scratch3MotionBlocks(runtime) {
     /**
      * The runtime instantiating this block package.
@@ -12,20 +14,35 @@ function Scratch3MotionBlocks(runtime) {
  */
 Scratch3MotionBlocks.prototype.getPrimitives = function() {
     return {
+        'motion_movesteps': this.moveSteps,
         'motion_gotoxy': this.goToXY,
-        'motion_turnright': this.turnRight
+        'motion_turnright': this.turnRight,
+        'motion_turnleft': this.turnLeft,
+        'motion_pointindirection': this.pointInDirection
     };
 };
 
+Scratch3MotionBlocks.prototype.moveSteps = function (args, util) {
+    var radians = MathUtil.degToRad(util.target.direction);
+    var dx = args.STEPS * Math.cos(radians);
+    var dy = args.STEPS * Math.sin(radians);
+    util.target.setXY(util.target.x + dx, util.target.y + dy);
+};
+
 Scratch3MotionBlocks.prototype.goToXY = function (args, util) {
     util.target.setXY(args.X, args.Y);
 };
 
 Scratch3MotionBlocks.prototype.turnRight = function (args, util) {
-    if (args.DEGREES !== args.DEGREES) {
-        throw "Bad degrees" + args.DEGREES;
-    }
-    util.target.setDirection(args.DEGREES + util.target.direction);
+    util.target.setDirection(util.target.direction + args.DEGREES);
+};
+
+Scratch3MotionBlocks.prototype.turnLeft = function (args, util) {
+    util.target.setDirection(util.target.direction - args.DEGREES);
+};
+
+Scratch3MotionBlocks.prototype.pointInDirection = function (args, util) {
+    util.target.setDirection(args.DIRECTION);
 };
 
 module.exports = Scratch3MotionBlocks;

From 7db2981ddc66659657624be9461c2ce2c1ad74ec Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 29 Jun 2016 23:51:21 -0400
Subject: [PATCH 06/40] Add additional motion block implementations

---
 playground/index.html         | 164 ++++++++++++++++++++++++----------
 src/blocks/scratch3_motion.js |  32 ++++++-
 2 files changed, 147 insertions(+), 49 deletions(-)

diff --git a/playground/index.html b/playground/index.html
index e11d72c82..2d256e7a2 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -245,54 +245,122 @@
               </value>
             </block>
         </category>
-        <category name="Motion">
-            <block type="motion_movesteps">
-             <value name="STEPS">
-               <shadow type="math_number">
-                 <field name="NUM">10</field>
-               </shadow>
-             </value>
-           </block>
-           <block type="motion_turnright">
-             <value name="DEGREES">
-               <shadow type="math_number">
-                 <field name="NUM">15</field>
-               </shadow>
-             </value>
-           </block>
-           <block type="motion_turnleft">
-             <value name="DEGREES">
-               <shadow type="math_number">
-                 <field name="NUM">15</field>
-               </shadow>
-             </value>
-           </block>
-           <block type="motion_pointindirection">
-             <value name="DIRECTION">
-               <shadow type="math_number">
-                 <field name="NUM">90</field>
-               </shadow>
-             </value>
-           </block>
-           <block type="motion_pointtowards">
-             <value name="TOWARDS">
-               <shadow type="motion_pointtowards_menu">
-               </shadow>
-             </value>
-           </block>
-           <block type="motion_gotoxy">
-             <value name="X">
-               <shadow type="math_number">
-                 <field name="NUM">0</field>
-               </shadow>
-             </value>
-             <value name="Y">
-               <shadow type="math_number">
-                 <field name="NUM">0</field>
-               </shadow>
-             </value>
-           </block>
-        </category>
+        <category name="Looks">
+        <block type="looks_changeeffectby">
+          <value name="EFFECT">
+            <shadow type="looks_effectmenu"></shadow>
+          </value>
+          <value name="CHANGE">
+            <shadow type="math_number">
+              <field name="NUM">10</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="looks_seteffectto">
+          <value name="EFFECT">
+            <shadow type="looks_effectmenu"></shadow>
+          </value>
+          <value name="VALUE">
+            <shadow type="math_number">
+              <field name="NUM">10</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="looks_cleargraphiceffects"></block>
+        <block type="looks_changesizeby">
+          <value name="CHANGE">
+            <shadow type="math_number">
+              <field name="NUM">10</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="looks_setsizeto">
+          <value name="SIZE">
+            <shadow type="math_number">
+              <field name="NUM">100</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="looks_size"></block>
+      </category>
+      <category name="Motion">
+      <block type="motion_movesteps">
+        <value name="STEPS">
+          <shadow type="math_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_turnright">
+        <value name="DEGREES">
+          <shadow type="math_number">
+            <field name="NUM">15</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_turnleft">
+        <value name="DEGREES">
+          <shadow type="math_number">
+            <field name="NUM">15</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_pointindirection">
+        <value name="DIRECTION">
+          <shadow type="math_number">
+            <field name="NUM">90</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_pointtowards">
+        <value name="TOWARDS">
+          <shadow type="motion_pointtowards_menu">
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_gotoxy">
+        <value name="X">
+          <shadow type="math_number">
+            <field name="NUM">0</field>
+          </shadow>
+        </value>
+        <value name="Y">
+          <shadow type="math_number">
+            <field name="NUM">0</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_changexby">
+        <value name="DX">
+          <shadow type="math_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_setx">
+        <value name="X">
+          <shadow type="math_number">
+            <field name="NUM">0</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_changeyby">
+        <value name="DY">
+          <shadow type="math_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_sety">
+        <value name="Y">
+          <shadow type="math_number">
+            <field name="NUM">0</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_xposition"></block>
+      <block type="motion_yposition"></block>
+    </category>
     </xml>
 
     <!-- Syntax highlighter -->
diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js
index f9171d9e7..de9a2c480 100644
--- a/src/blocks/scratch3_motion.js
+++ b/src/blocks/scratch3_motion.js
@@ -18,7 +18,13 @@ Scratch3MotionBlocks.prototype.getPrimitives = function() {
         'motion_gotoxy': this.goToXY,
         'motion_turnright': this.turnRight,
         'motion_turnleft': this.turnLeft,
-        'motion_pointindirection': this.pointInDirection
+        'motion_pointindirection': this.pointInDirection,
+        'motion_changexby': this.changeX,
+        'motion_setx': this.setX,
+        'motion_changeyby': this.changeY,
+        'motion_sety': this.setY,
+        'motion_xposition': this.getX,
+        'motion_yposition': this.getY
     };
 };
 
@@ -45,4 +51,28 @@ Scratch3MotionBlocks.prototype.pointInDirection = function (args, util) {
     util.target.setDirection(args.DIRECTION);
 };
 
+Scratch3MotionBlocks.prototype.changeX = function (args, util) {
+    util.target.setXY(util.target.x + args.DX, util.target.y);
+};
+
+Scratch3MotionBlocks.prototype.setX = function (args, util) {
+    util.target.setXY(args.X, util.target.y);
+};
+
+Scratch3MotionBlocks.prototype.changeY = function (args, util) {
+    util.target.setXY(util.target.x, util.target.y + args.DY);
+};
+
+Scratch3MotionBlocks.prototype.setY = function (args, util) {
+    util.target.setXY(util.target.x, args.Y);
+};
+
+Scratch3MotionBlocks.prototype.getX = function (args, util) {
+    return util.target.x;
+};
+
+Scratch3MotionBlocks.prototype.getY = function (args, util) {
+    return util.target.y;
+};
+
 module.exports = Scratch3MotionBlocks;

From 1eaed6fff3ee68df3f65ba067b9a1c10ac2b9e59 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 30 Jun 2016 00:11:47 -0400
Subject: [PATCH 07/40] Implement graphic effects and size blocks

---
 playground/index.html        |  4 +--
 src/blocks/scratch3_looks.js | 54 ++++++++++++++++++++++++++++++++++++
 src/engine/runtime.js        |  1 +
 src/sprites/clone.js         | 33 ++++++++++++++++++++++
 4 files changed, 90 insertions(+), 2 deletions(-)
 create mode 100644 src/blocks/scratch3_looks.js

diff --git a/playground/index.html b/playground/index.html
index 2d256e7a2..3565073a0 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -252,7 +252,7 @@
           </value>
           <value name="CHANGE">
             <shadow type="math_number">
-              <field name="NUM">10</field>
+              <field name="NUM">25</field>
             </shadow>
           </value>
         </block>
@@ -262,7 +262,7 @@
           </value>
           <value name="VALUE">
             <shadow type="math_number">
-              <field name="NUM">10</field>
+              <field name="NUM">0</field>
             </shadow>
           </value>
         </block>
diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js
new file mode 100644
index 000000000..af8ed9d70
--- /dev/null
+++ b/src/blocks/scratch3_looks.js
@@ -0,0 +1,54 @@
+function Scratch3LooksBlocks(runtime) {
+    /**
+     * The runtime instantiating this block package.
+     * @type {Runtime}
+     */
+    this.runtime = runtime;
+}
+
+/**
+ * Retrieve the block primitives implemented by this package.
+ * @return {Object.<string, Function>} Mapping of opcode to Function.
+ */
+Scratch3LooksBlocks.prototype.getPrimitives = function() {
+    return {
+        'looks_effectmenu': this.effectMenu,
+        'looks_changeeffectby': this.changeEffect,
+        'looks_seteffectto': this.setEffect,
+        'looks_cleargraphiceffects': this.clearEffects,
+        'looks_changesizeby': this.changeSize,
+        'looks_setsizeto': this.setSize,
+        'looks_size': this.getSize
+    };
+};
+
+Scratch3LooksBlocks.prototype.effectMenu = function (args) {
+    return args.EFFECT.toLowerCase();
+};
+
+Scratch3LooksBlocks.prototype.changeEffect = function (args, util) {
+    var newValue = args.CHANGE + util.target.effects[args.EFFECT];
+    util.target.setEffect(args.EFFECT, newValue);
+};
+
+Scratch3LooksBlocks.prototype.setEffect = function (args, util) {
+    util.target.setEffect(args.EFFECT, args.VALUE);
+};
+
+Scratch3LooksBlocks.prototype.clearEffects = function (args, util) {
+    util.target.clearEffects();
+};
+
+Scratch3LooksBlocks.prototype.changeSize = function (args, util) {
+    util.target.setSize(util.target.size + args.CHANGE);
+};
+
+Scratch3LooksBlocks.prototype.setSize = function (args, util) {
+    util.target.setSize(args.SIZE);
+};
+
+Scratch3LooksBlocks.prototype.getSize = function (args, util) {
+    return util.target.size;
+};
+
+module.exports = Scratch3LooksBlocks;
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index c51800cdb..8159729f5 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -6,6 +6,7 @@ var util = require('util');
 var defaultBlockPackages = {
     'scratch3_control': require('../blocks/scratch3_control'),
     'scratch3_event': require('../blocks/scratch3_event'),
+    'scratch3_looks': require('../blocks/scratch3_looks'),
     'scratch3_motion': require('../blocks/scratch3_motion'),
     'scratch3_operators': require('../blocks/scratch3_operators')
 };
diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index b69c68d7a..cfee61556 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -24,6 +24,18 @@ Clone.prototype.y = 0;
 
 Clone.prototype.direction = 90;
 
+Clone.prototype.size = 100;
+
+Clone.prototype.effects = {
+    'color': 0,
+    'fisheye': 0,
+    'whirl': 0,
+    'pixelate': 0,
+    'mosaic': 0,
+    'brightness': 0,
+    'ghost': 0
+};
+
 Clone.prototype.setXY = function (x, y) {
     this.x = x;
     this.y = y;
@@ -39,4 +51,25 @@ Clone.prototype.setDirection = function (direction) {
     });
 };
 
+Clone.prototype.setSize = function (size) {
+    this.size = MathUtil.clamp(size, 5, 535);
+    self.renderer.updateDrawableProperties(this.drawableID, {
+        scale: this.size
+    });
+};
+
+Clone.prototype.setEffect = function (effectName, value) {
+    this.effects[effectName] = value;
+    var props = {};
+    props[effectName] = this.effects[effectName];
+    self.renderer.updateDrawableProperties(this.drawableID, props);
+};
+
+Clone.prototype.clearEffects = function () {
+    for (var effectName in this.effects) {
+        this.effects[effectName] = 0;
+    }
+    self.renderer.updateDrawableProperties(this.drawableID, this.effects);
+};
+
 module.exports = Clone;

From bb5acd1ef4f4989e4c0af69ed6b8936851ecf2db Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 30 Jun 2016 19:01:19 -0400
Subject: [PATCH 08/40] Fix merge issue in execute.js

---
 src/engine/execute.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/engine/execute.js b/src/engine/execute.js
index aa413a76b..9317add96 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -13,7 +13,7 @@ var execute = function (sequencer, thread) {
     var currentBlockId = thread.peekStack();
     var currentStackFrame = thread.peekStackFrame();
 
-    var opcode = runtime.blocks.getOpcode(currentBlockId);
+    var opcode = target.blocks.getOpcode(currentBlockId);
 
     if (!opcode) {
         console.warn('Could not get opcode for block: ' + currentBlockId);

From 5876681bc70b8c3456949e207f727b8feb29666d Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 30 Jun 2016 19:04:15 -0400
Subject: [PATCH 09/40] Version of random that truncates ints

---
 src/blocks/scratch3_operators.js | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js
index 86cb50350..c72adf29c 100644
--- a/src/blocks/scratch3_operators.js
+++ b/src/blocks/scratch3_operators.js
@@ -84,16 +84,16 @@ Scratch3OperatorsBlocks.prototype.not = function (args) {
 };
 
 Scratch3OperatorsBlocks.prototype.random = function (args) {
-    // As a demo, this implementation of random returns after 1 second of yield.
-    // @todo Match Scratch 2.0 implementation with int-truncation.
-    // See: http://bit.ly/1Qc0GzC
-    var examplePromise = new Promise(function(resolve) {
-        setTimeout(function() {
-            var res = (Math.random() * (args.TO - args.FROM)) + args.FROM;
-            resolve(res);
-        }, 1000);
-    });
-    return examplePromise;
+    var low = args.FROM <= args.TO ? args.FROM : args.TO;
+    var high = args.FROM <= args.TO ? args.TO : args.FROM;
+    if (low == high) return low;
+    // If both low and high are ints, truncate the result to an int.
+    var lowInt = low == parseInt(low);
+    var highInt = high == parseInt(high);
+    if (lowInt && highInt) {
+        return low + parseInt(Math.random() * ((high + 1) - low));
+    }
+    return (Math.random() * (high - low)) + low;
 };
 
 module.exports = Scratch3OperatorsBlocks;

From de6ba08866838602cbb5072e383a4c65e86cbb63 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 10:44:43 -0400
Subject: [PATCH 10/40] Add single-frame yield mode

---
 src/blocks/scratch3_control.js |  8 +++++++-
 src/engine/execute.js          | 12 ++++++++++--
 src/engine/sequencer.js        | 15 +++++++++++----
 src/engine/thread.js           | 15 +++++++++++----
 4 files changed, 39 insertions(+), 11 deletions(-)

diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js
index e13edefd9..161934ea4 100644
--- a/src/blocks/scratch3_control.js
+++ b/src/blocks/scratch3_control.js
@@ -37,7 +37,13 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
 };
 
 Scratch3ControlBlocks.prototype.forever = function(args, util) {
-    util.startSubstack();
+    if (!util.stackFrame.executed) {
+        util.stackFrame.executed = true;
+        util.startSubstack();
+    } else {
+        util.stackFrame.executed = false;
+        util.yieldFrame();
+    }
 };
 
 Scratch3ControlBlocks.prototype.wait = function(args) {
diff --git a/src/engine/execute.js b/src/engine/execute.js
index 9317add96..529990ca0 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -63,8 +63,14 @@ var execute = function (sequencer, thread) {
 
     var primitiveReportedValue = null;
     primitiveReportedValue = blockFunction(argValues, {
-        yield: thread.yield.bind(thread),
+        yield: function() {
+            thread.setStatus(Thread.STATUS_YIELD);
+        },
+        yieldFrame: function() {
+            thread.setStatus(Thread.STATUS_YIELD_FRAME);
+        },
         done: function() {
+            thread.setStatus(Thread.STATUS_RUNNING);
             sequencer.proceedThread(thread);
         },
         stackFrame: currentStackFrame.executionContext,
@@ -84,17 +90,19 @@ var execute = function (sequencer, thread) {
     if (isPromise) {
         if (thread.status === Thread.STATUS_RUNNING) {
             // Primitive returned a promise; automatically yield thread.
-            thread.status = Thread.STATUS_YIELD;
+            thread.setStatus(Thread.STATUS_YIELD);
         }
         // Promise handlers
         primitiveReportedValue.then(function(resolvedValue) {
             // Promise resolved: the primitive reported a value.
             thread.pushReportedValue(resolvedValue);
+            thread.setStatus(Thread.STATUS_RUNNING);
             sequencer.proceedThread(thread);
         }, function(rejectionReason) {
             // Promise rejected: the primitive had some error.
             // Log it and proceed.
             console.warn('Primitive rejected promise: ', rejectionReason);
+            thread.setStatus(Thread.STATUS_RUNNING);
             sequencer.proceedThread(thread);
         });
     } else if (thread.status === Thread.STATUS_RUNNING) {
diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index e70955e69..f780ba255 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -36,6 +36,13 @@ Sequencer.prototype.stepThreads = function (threads) {
     var inactiveThreads = [];
     // If all of the threads are yielding, we should yield.
     var numYieldingThreads = 0;
+    // Clear all yield statuses that were for the previous frame.
+    for (var t = 0; t < threads.length; t++) {
+        if (threads[t].status === Thread.STATUS_YIELD_FRAME) {
+            threads[t].setStatus(Thread.STATUS_RUNNING);
+        }
+    }
+
     // While there are still threads to run and we are within WORK_TIME,
     // continue executing threads.
     while (threads.length > 0 &&
@@ -51,8 +58,10 @@ Sequencer.prototype.stepThreads = function (threads) {
             if (activeThread.status === Thread.STATUS_RUNNING) {
                 // Normal-mode thread: step.
                 this.startThread(activeThread);
-            } else if (activeThread.status === Thread.STATUS_YIELD) {
+            } else if (activeThread.status === Thread.STATUS_YIELD ||
+                       activeThread.status === Thread.STATUS_YIELD_FRAME) {
                 // Yielding thread: do nothing for this step.
+                numYieldingThreads++;
                 continue;
             }
             if (activeThread.stack.length === 0 &&
@@ -148,8 +157,6 @@ Sequencer.prototype.proceedThread = function (thread) {
     var currentBlockId = thread.peekStack();
     // Mark the status as done and proceed to the next block.
     this.runtime.glowBlock(currentBlockId, false);
-    // If the block was yielding, move back to running state.
-    thread.status = Thread.STATUS_RUNNING;
     // Pop from the stack - finished this level of execution.
     thread.popStack();
     // Push next connected block, if there is one.
@@ -164,7 +171,7 @@ Sequencer.prototype.proceedThread = function (thread) {
     }
     // If we still can't find a next block to run, mark the thread as done.
     if (thread.peekStack() === null) {
-        thread.status = Thread.STATUS_DONE;
+        thread.setStatus(Thread.STATUS_DONE);
     }
 };
 
diff --git a/src/engine/thread.js b/src/engine/thread.js
index e5c4df2e4..d1bd73fc0 100644
--- a/src/engine/thread.js
+++ b/src/engine/thread.js
@@ -46,12 +46,18 @@ Thread.STATUS_RUNNING = 0;
  */
 Thread.STATUS_YIELD = 1;
 
+/**
+ * Thread status for a single-frame yield.
+ * @const
+ */
+Thread.STATUS_YIELD_FRAME = 2;
+
 /**
  * Thread status for a finished/done thread.
  * Thread is in this state when there are no more blocks to execute.
  * @const
  */
-Thread.STATUS_DONE = 2;
+Thread.STATUS_DONE = 3;
 
 /**
  * Push stack and update stack frames appropriately.
@@ -118,10 +124,11 @@ Thread.prototype.pushReportedValue = function (value) {
 };
 
 /**
- * Yields the thread.
+ * Set thread status.
+ * @param {number} status Enum representing thread status.
  */
-Thread.prototype.yield = function () {
-    this.status = Thread.STATUS_YIELD;
+Thread.prototype.setStatus = function (status) {
+    this.status = status;
 };
 
 module.exports = Thread;

From 39c71b559d5da903159d17b8c6f670d3bd252a8d Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 10:50:31 -0400
Subject: [PATCH 11/40] Update repeat implementation to execute once per frame

---
 src/blocks/scratch3_control.js | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js
index 161934ea4..d938f66ab 100644
--- a/src/blocks/scratch3_control.js
+++ b/src/blocks/scratch3_control.js
@@ -28,15 +28,27 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
     if (util.stackFrame.loopCounter === undefined) {
         util.stackFrame.loopCounter = parseInt(args.TIMES);
     }
-    // Decrease counter
-    util.stackFrame.loopCounter--;
-    // If we still have some left, start the substack
-    if (util.stackFrame.loopCounter >= 0) {
-        util.startSubstack();
+    // Only execute once per frame.
+    // When the substack finishes, `repeat` will be executed again and
+    // the second branch will be taken, yielding for the rest of the frame.
+    if (!util.stackFrame.executed) {
+        util.stackFrame.executed = true;
+        // Decrease counter
+        util.stackFrame.loopCounter--;
+        // If we still have some left, start the substack
+        if (util.stackFrame.loopCounter >= 0) {
+            util.startSubstack();
+        }
+    } else {
+        util.stackFrame.executed = false;
+        util.yieldFrame();
     }
 };
 
 Scratch3ControlBlocks.prototype.forever = function(args, util) {
+    // Only execute once per frame.
+    // When the substack finishes, `forever` will be executed again and
+    // the second branch will be taken, yielding for the rest of the frame.
     if (!util.stackFrame.executed) {
         util.stackFrame.executed = true;
         util.startSubstack();

From dda4fc8332279e6855c1ddcf3175f77bb57fb849 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 11:24:06 -0400
Subject: [PATCH 12/40] Yield frame on an empty substack

---
 src/engine/sequencer.js | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index f780ba255..838cb1cfa 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -86,9 +86,10 @@ Sequencer.prototype.stepThreads = function (threads) {
 Sequencer.prototype.startThread = function (thread) {
     var currentBlockId = thread.peekStack();
     if (!currentBlockId) {
-        // A "null block" - empty substack. Pop the stack.
+        // A "null block" - empty substack.
+        // Yield for the frame.
         thread.popStack();
-        thread.status = Thread.STATUS_DONE;
+        thread.setStatus(Thread.STATUS_YIELD_FRAME);
         return;
     }
     // Start showing run feedback in the editor.
@@ -165,12 +166,8 @@ Sequencer.prototype.proceedThread = function (thread) {
     if (nextBlockId) {
         thread.pushStack(nextBlockId);
     }
-    // Pop from the stack until we have a next block.
-    while (thread.peekStack() === null && thread.stack.length > 0) {
-        thread.popStack();
-    }
-    // If we still can't find a next block to run, mark the thread as done.
-    if (thread.peekStack() === null) {
+    // If we can't find a next block to run, mark the thread as done.
+    if (!thread.peekStack()) {
         thread.setStatus(Thread.STATUS_DONE);
     }
 };

From e4f6c9e90cafdb958416d9c42a6aba7be31fc113 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 11:25:26 -0400
Subject: [PATCH 13/40] "Repeat until" implementation

---
 src/blocks/scratch3_control.js | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js
index d938f66ab..9498cc019 100644
--- a/src/blocks/scratch3_control.js
+++ b/src/blocks/scratch3_control.js
@@ -15,6 +15,7 @@ function Scratch3ControlBlocks(runtime) {
 Scratch3ControlBlocks.prototype.getPrimitives = function() {
     return {
         'control_repeat': this.repeat,
+        'control_repeat_until': this.repeatUntil,
         'control_forever': this.forever,
         'control_wait': this.wait,
         'control_if': this.if,
@@ -45,15 +46,31 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
     }
 };
 
+Scratch3ControlBlocks.prototype.repeatUntil = function(args, util) {
+    // Only execute once per frame.
+    // When the substack finishes, `repeat` will be executed again and
+    // the second branch will be taken, yielding for the rest of the frame.
+    if (!util.stackFrame.executedInFrame) {
+        util.stackFrame.executedInFrame = true;
+        // If the condition is true, start the substack.
+        if (!args.CONDITION) {
+            util.startSubstack();
+        }
+    } else {
+        util.stackFrame.executedInFrame = false;
+        util.yieldFrame();
+    }
+};
+
 Scratch3ControlBlocks.prototype.forever = function(args, util) {
     // Only execute once per frame.
     // When the substack finishes, `forever` will be executed again and
     // the second branch will be taken, yielding for the rest of the frame.
-    if (!util.stackFrame.executed) {
-        util.stackFrame.executed = true;
+    if (!util.stackFrame.executedInFrame) {
+        util.stackFrame.executedInFrame = true;
         util.startSubstack();
     } else {
-        util.stackFrame.executed = false;
+        util.stackFrame.executedInFrame = false;
         util.yieldFrame();
     }
 };
@@ -69,8 +86,8 @@ Scratch3ControlBlocks.prototype.wait = function(args) {
 Scratch3ControlBlocks.prototype.if = function(args, util) {
     // Only execute one time. `if` will be returned to
     // when the substack finishes, but it shouldn't execute again.
-    if (util.stackFrame.executed === undefined) {
-        util.stackFrame.executed = true;
+    if (util.stackFrame.executedInFrame === undefined) {
+        util.stackFrame.executedInFrame = true;
         if (args.CONDITION) {
             util.startSubstack();
         }

From ad30fa80596cd3df3ffe1bb807c8e8ea446e1422 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 11:27:01 -0400
Subject: [PATCH 14/40] Temporarily remove per-block glow

---
 src/engine/sequencer.js | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index 838cb1cfa..342ad8857 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -92,12 +92,8 @@ Sequencer.prototype.startThread = function (thread) {
         thread.setStatus(Thread.STATUS_YIELD_FRAME);
         return;
     }
-    // Start showing run feedback in the editor.
-    this.runtime.glowBlock(currentBlockId, true);
-
     // Execute the current block
     execute(this, thread);
-
     // If the block executed without yielding and without doing control flow,
     // move to done.
     if (thread.status === Thread.STATUS_RUNNING &&
@@ -157,7 +153,6 @@ Sequencer.prototype.stepToReporter = function (thread, blockId, inputName) {
 Sequencer.prototype.proceedThread = function (thread) {
     var currentBlockId = thread.peekStack();
     // Mark the status as done and proceed to the next block.
-    this.runtime.glowBlock(currentBlockId, false);
     // Pop from the stack - finished this level of execution.
     thread.popStack();
     // Push next connected block, if there is one.

From 57217f00440153d636c3feb0b4fc2a4644676bdc Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 11:29:32 -0400
Subject: [PATCH 15/40] Rename `executed` in repeat, ifElse

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

diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js
index 9498cc019..d1f6a7444 100644
--- a/src/blocks/scratch3_control.js
+++ b/src/blocks/scratch3_control.js
@@ -32,8 +32,8 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
     // Only execute once per frame.
     // When the substack finishes, `repeat` will be executed again and
     // the second branch will be taken, yielding for the rest of the frame.
-    if (!util.stackFrame.executed) {
-        util.stackFrame.executed = true;
+    if (!util.stackFrame.executedInFrame) {
+        util.stackFrame.executedInFrame = true;
         // Decrease counter
         util.stackFrame.loopCounter--;
         // If we still have some left, start the substack
@@ -97,8 +97,8 @@ Scratch3ControlBlocks.prototype.if = function(args, util) {
 Scratch3ControlBlocks.prototype.ifElse = function(args, util) {
     // Only execute one time. `ifElse` will be returned to
     // when the substack finishes, but it shouldn't execute again.
-    if (util.stackFrame.executed === undefined) {
-        util.stackFrame.executed = true;
+    if (util.stackFrame.executedInFrame === undefined) {
+        util.stackFrame.executedInFrame = true;
         if (args.CONDITION) {
             util.startSubstack(1);
         } else {

From bb68fcab25bc2ae432ca696c86e6e91f5c794720 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 11:41:52 -0400
Subject: [PATCH 16/40] Additional fix for repeat's executedInFrame

---
 src/blocks/scratch3_control.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/blocks/scratch3_control.js b/src/blocks/scratch3_control.js
index d1f6a7444..d3cd2fdf6 100644
--- a/src/blocks/scratch3_control.js
+++ b/src/blocks/scratch3_control.js
@@ -41,7 +41,7 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
             util.startSubstack();
         }
     } else {
-        util.stackFrame.executed = false;
+        util.stackFrame.executedInFrame = false;
         util.yieldFrame();
     }
 };

From 660029010dbb6aed899963919c0ac7fc8b3763cc Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 11:52:43 -0400
Subject: [PATCH 17/40] Feed in requestAnimationFrame events to VM

Not sure exactly how to use these yet, but it seems helpful to have them in there.
---
 playground/playground.js |  7 +++++++
 src/engine/runtime.js    | 22 +++++-----------------
 src/index.js             | 10 ++++++++++
 src/worker.js            |  4 ++++
 4 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/playground/playground.js b/playground/playground.js
index 220549c9f..a819effc7 100644
--- a/playground/playground.js
+++ b/playground/playground.js
@@ -79,6 +79,13 @@ window.onload = function() {
     // Run threads
     vm.start();
 
+    // Inform VM of animation frames.
+    var animate = function() {
+        window.vm.animationFrame();
+        requestAnimationFrame(animate);
+    };
+    requestAnimationFrame(animate);
+
     // Handlers for green flag and stop all.
     document.getElementById('greenflag').addEventListener('click', function() {
         vm.greenFlag();
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index 8159729f5..afd930246 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -236,32 +236,20 @@ Runtime.prototype.targetForThread = function (thread) {
 };
 
 /**
- * setInterval implementation that works in a WebWorker or not.
- * @param {?Function} fcn Function to call.
- * @param {number} interval Interval at which to call it.
- * @return {number} Value returned by setInterval.
+ * Handle an animation frame from the main thread.
  */
-Runtime.prototype._setInterval = function(fcn, interval) {
-    var setInterval = null;
-    if (typeof window !== 'undefined' && window.setInterval) {
-        setInterval = window.setInterval;
-    } else if (typeof self !== 'undefined' && self.setInterval) {
-        setInterval = self.setInterval;
-    } else {
-        return;
+Runtime.prototype.animationFrame = function () {
+    if (self.renderer) {
+        self.renderer.draw();
     }
-    return setInterval(fcn, interval);
 };
 
 /**
  * Set up timers to repeatedly step in a browser
  */
 Runtime.prototype.start = function () {
-    this._setInterval(function() {
+    self.setInterval(function() {
         this._step();
-        if (self.renderer) {
-            self.renderer.draw();
-        }
     }.bind(this), Runtime.THREAD_STEP_INTERVAL);
 };
 
diff --git a/src/index.js b/src/index.js
index fff9c52db..15b218071 100644
--- a/src/index.js
+++ b/src/index.js
@@ -91,6 +91,13 @@ VirtualMachine.prototype.getPlaygroundData = function () {
     });
 };
 
+/**
+ * Handle an animation frame.
+ */
+VirtualMachine.prototype.animationFrame = function () {
+    this.runtime.animationFrame();
+};
+
 /*
  * Worker handlers: for all public methods available above,
  * we must also provide a message handler in case the VM is run
@@ -127,6 +134,9 @@ if (ENV_WORKER) {
                 threads: self.vmInstance.runtime.threads
             });
             break;
+        case 'animationFrame':
+            self.vmInstance.animationFrame();
+            break;
         default:
             if (e.data.id == 'RendererConnected') {
                 //initRenderWorker();
diff --git a/src/worker.js b/src/worker.js
index e96c57bf3..70e6da5cb 100644
--- a/src/worker.js
+++ b/src/worker.js
@@ -63,6 +63,10 @@ VirtualMachine.prototype.stopAll = function () {
     this.vmWorker.postMessage({method: 'stopAll'});
 };
 
+VirtualMachine.prototype.animationFrame = function () {
+    this.vmWorker.postMessage({method: 'animationFrame'});
+};
+
 /**
  * Export and bind to `window`
  */

From 6891a3a5dd139648d945357067b909bcf8a57a32 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 12:56:45 -0400
Subject: [PATCH 18/40] Add direction reporter

---
 src/blocks/scratch3_motion.js | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/blocks/scratch3_motion.js b/src/blocks/scratch3_motion.js
index de9a2c480..730eee03f 100644
--- a/src/blocks/scratch3_motion.js
+++ b/src/blocks/scratch3_motion.js
@@ -24,7 +24,8 @@ Scratch3MotionBlocks.prototype.getPrimitives = function() {
         'motion_changeyby': this.changeY,
         'motion_sety': this.setY,
         'motion_xposition': this.getX,
-        'motion_yposition': this.getY
+        'motion_yposition': this.getY,
+        'motion_direction': this.getDirection
     };
 };
 
@@ -75,4 +76,8 @@ Scratch3MotionBlocks.prototype.getY = function (args, util) {
     return util.target.y;
 };
 
+Scratch3MotionBlocks.prototype.getDirection = function (args, util) {
+    return util.target.direction;
+};
+
 module.exports = Scratch3MotionBlocks;

From ce941c6fd81c2cdd9b4c483c4663ee03bf9593fc Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 12:56:59 -0400
Subject: [PATCH 19/40] Add show/hide blocks using ghost effect as backend

---
 src/blocks/scratch3_looks.js | 10 ++++++++++
 src/sprites/clone.js         | 12 ++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js
index af8ed9d70..b2bad1ef7 100644
--- a/src/blocks/scratch3_looks.js
+++ b/src/blocks/scratch3_looks.js
@@ -12,6 +12,8 @@ function Scratch3LooksBlocks(runtime) {
  */
 Scratch3LooksBlocks.prototype.getPrimitives = function() {
     return {
+        'looks_show': this.show,
+        'looks_hide': this.hide,
         'looks_effectmenu': this.effectMenu,
         'looks_changeeffectby': this.changeEffect,
         'looks_seteffectto': this.setEffect,
@@ -22,6 +24,14 @@ Scratch3LooksBlocks.prototype.getPrimitives = function() {
     };
 };
 
+Scratch3LooksBlocks.prototype.show = function (args, util) {
+    util.target.setVisible(true);
+};
+
+Scratch3LooksBlocks.prototype.hide = function (args, util) {
+    util.target.setVisible(false);
+};
+
 Scratch3LooksBlocks.prototype.effectMenu = function (args) {
     return args.EFFECT.toLowerCase();
 };
diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index cfee61556..a1c8b5c8d 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -24,6 +24,8 @@ Clone.prototype.y = 0;
 
 Clone.prototype.direction = 90;
 
+Clone.prototype.visible = true;
+
 Clone.prototype.size = 100;
 
 Clone.prototype.effects = {
@@ -51,6 +53,16 @@ Clone.prototype.setDirection = function (direction) {
     });
 };
 
+Clone.prototype.setVisible = function (visible) {
+    this.visible = visible;
+    // @todo: Until visibility is implemented in the renderer, use a ghost.
+    if (this.visible) {
+        this.setEffect('ghost', 0);
+    } else {
+        this.setEffect('ghost', 100);
+    }
+};
+
 Clone.prototype.setSize = function (size) {
     this.size = MathUtil.clamp(size, 5, 535);
     self.renderer.updateDrawableProperties(this.drawableID, {

From a47a9a9b7ec8ba48b5a56392d26b18589dcd2c72 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 12:57:13 -0400
Subject: [PATCH 20/40] Add show, hide, direction to palette

---
 playground/index.html | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/playground/index.html b/playground/index.html
index 3565073a0..c08854f88 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -246,6 +246,8 @@
             </block>
         </category>
         <category name="Looks">
+        <block type="looks_show"></block>
+        <block type="looks_hide"></block>
         <block type="looks_changeeffectby">
           <value name="EFFECT">
             <shadow type="looks_effectmenu"></shadow>
@@ -360,6 +362,7 @@
       </block>
       <block type="motion_xposition"></block>
       <block type="motion_yposition"></block>
+      <block type="motion_direction"></block>
     </category>
     </xml>
 

From 890be6611e7bd947108a4b8b5c0651f417ad597c Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 13:09:22 -0400
Subject: [PATCH 21/40] Only request debug data from VM thread if a debug tab
 is open

---
 playground/playground.js | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/playground/playground.js b/playground/playground.js
index a819effc7..800894bb9 100644
--- a/playground/playground.js
+++ b/playground/playground.js
@@ -51,11 +51,14 @@ window.onload = function() {
         }
     };
 
+    // Only request data from the VM thread if the appropriate tab is open.
+    window.exploreTabOpen = false;
     var getPlaygroundData = function () {
         vm.getPlaygroundData();
-        window.requestAnimationFrame(getPlaygroundData);
+        if (window.exploreTabOpen) {
+            window.requestAnimationFrame(getPlaygroundData);
+        }
     };
-    getPlaygroundData();
 
     vm.on('playgroundData', function(data) {
         updateThreadExplorer(data.threads);
@@ -101,18 +104,23 @@ window.onload = function() {
     // Handlers to show different explorers.
     document.getElementById('threadexplorer-link').addEventListener('click',
         function () {
+            window.exploreTabOpen = true;
+            getPlaygroundData();
             tabBlockExplorer.style.display = 'none';
             tabRenderExplorer.style.display = 'none';
             tabThreadExplorer.style.display = 'block';
         });
     document.getElementById('blockexplorer-link').addEventListener('click',
         function () {
+            window.exploreTabOpen = true;
+            getPlaygroundData();
             tabBlockExplorer.style.display = 'block';
             tabRenderExplorer.style.display = 'none';
             tabThreadExplorer.style.display = 'none';
         });
     document.getElementById('renderexplorer-link').addEventListener('click',
         function () {
+            window.exploreTabOpen = false;
             tabBlockExplorer.style.display = 'none';
             tabRenderExplorer.style.display = 'block';
             tabThreadExplorer.style.display = 'none';

From 0ae0ea5f22deeadb84fdeb0a746e3879625e4baf Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Fri, 1 Jul 2016 16:30:33 -0400
Subject: [PATCH 22/40] Run threads for 60fps

---
 src/engine/runtime.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index afd930246..c51be795d 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -77,7 +77,7 @@ util.inherits(Runtime, EventEmitter);
 /**
  * How rapidly we try to step threads, in ms.
  */
-Runtime.THREAD_STEP_INTERVAL = 1000 / 30;
+Runtime.THREAD_STEP_INTERVAL = 1000 / 60;
 
 
 // -----------------------------------------------------------------------------

From 34c46adb9c6750522e8f5999fa05a8c4be8b4f75 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 13:17:14 -0400
Subject: [PATCH 23/40] Toolbox update

---
 playground/index.html | 548 ++++++++++++++++++++++++------------------
 1 file changed, 310 insertions(+), 238 deletions(-)

diff --git a/playground/index.html b/playground/index.html
index c08854f88..8ba16c2c1 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -33,259 +33,302 @@
     <div id="blocks"></div>
 
     <xml id="toolbox" style="display: none">
-        <category name="Events">
-          <block type="event_whenflagclicked"></block>
-          <block type="event_whenbroadcastreceived">
-            <value name="BROADCAST_OPTION">
-              <shadow type="event_broadcast_menu"></shadow>
-            </value>
-          </block>
-          <block type="event_broadcast">
-            <value name="BROADCAST_OPTION">
-              <shadow type="event_broadcast_menu"></shadow>
-            </value>
-          </block>
-          <block type="event_broadcastandwait">
-            <value name="BROADCAST_OPTION">
-              <shadow type="event_broadcast_menu"></shadow>
-            </value>
-          </block>
-        </category>
-        <category name="Control">
-          <block type="control_wait">
-            <value name="DURATION">
-              <shadow type="math_number">
-                <field name="NUM">1</field>
-              </shadow>
-            </value>
-          </block>
-          <block type="control_repeat">
-            <value name="TIMES">
-              <shadow type="math_number">
-                <field name="NUM">4</field>
-              </shadow>
-            </value>
-          </block>
-          <block type="control_forever"></block>
-          <block type="control_if"></block>
-          <block type="control_if_else"></block>
-          <block type="control_wait_until"></block>
-          <block type="control_repeat_until"></block>
-          <block type="control_stop">
-            <value name="STOP_OPTION">
-              <shadow type="control_stop_menu"></shadow>
-            </value>
-          </block>
-          <block type="control_start_as_clone"></block>
-          <block type="control_create_clone_of">
-            <value name="CLONE_OPTION">
-              <shadow type="control_create_clone_of_menu"></shadow>
-            </value>
-          </block>
-          <block type="control_delete_this_clone"></block>
-        </category>
-        <category name="Operators">
-            <block type="operator_add">
-              <value name="NUM1">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-              <value name="NUM2">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_subtract">
-              <value name="NUM1">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-              <value name="NUM2">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_multiply">
-              <value name="NUM1">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-              <value name="NUM2">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_divide">
-              <value name="NUM1">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-              <value name="NUM2">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_random">
-              <value name="FROM">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="TO">
-                <shadow type="math_number">
-                  <field name="NUM">10</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_lt">
-              <value name="OPERAND1">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-              <value name="OPERAND2">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_equals">
-              <value name="OPERAND1">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-              <value name="OPERAND2">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_gt">
-              <value name="OPERAND1">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-              <value name="OPERAND2">
-                <shadow type="text">
-                  <field name="TEXT"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_and"></block>
-            <block type="operator_or"></block>
-            <block type="operator_not"></block>
-            <block type="operator_join">
-              <value name="STRING1">
-                <shadow type="text">
-                  <field name="TEXT">hello</field>
-                </shadow>
-              </value>
-              <value name="STRING2">
-                <shadow type="text">
-                  <field name="TEXT">world</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_letter_of">
-              <value name="LETTER">
-                <shadow type="math_number">
-                  <field name="NUM">1</field>
-                </shadow>
-              </value>
-              <value name="STRING">
-                <shadow type="text">
-                  <field name="TEXT">world</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_length">
-              <value name="STRING">
-                <shadow type="text">
-                  <field name="TEXT">world</field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_mod">
-              <value name="NUM1">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-              <value name="NUM2">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_round">
-              <value name="NUM">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-            <block type="operator_mathop">
-              <value name="OPERATOR">
-                <shadow type="operator_mathop_menu"></shadow>
-              </value>
-              <value name="NUM">
-                <shadow type="math_number">
-                  <field name="NUM"></field>
-                </shadow>
-              </value>
-            </block>
-        </category>
-        <category name="Looks">
-        <block type="looks_show"></block>
-        <block type="looks_hide"></block>
-        <block type="looks_changeeffectby">
-          <value name="EFFECT">
-            <shadow type="looks_effectmenu"></shadow>
-          </value>
-          <value name="CHANGE">
+    <category name="Events">
+      <block type="event_whenflagclicked"></block>
+      <block type="event_whenkeypressed">
+        <value name="KEY_OPTION">
+          <shadow type="event_keyoptions"></shadow>
+        </value>
+      </block>
+      <block type="event_whenbroadcastreceived">
+        <value name="BROADCAST_OPTION">
+          <shadow type="event_broadcast_menu"></shadow>
+        </value>
+      </block>
+      <block type="event_broadcast">
+        <value name="BROADCAST_OPTION">
+          <shadow type="event_broadcast_menu"></shadow>
+        </value>
+      </block>
+      <block type="event_broadcastandwait">
+        <value name="BROADCAST_OPTION">
+          <shadow type="event_broadcast_menu"></shadow>
+        </value>
+      </block>
+    </category>
+    <category name="Control">
+      <block type="control_wait">
+        <value name="DURATION">
+          <shadow type="math_positive_number">
+            <field name="NUM">1</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="control_repeat">
+        <value name="TIMES">
+          <shadow type="math_whole_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="control_forever"></block>
+      <block type="control_if"></block>
+      <block type="control_if_else"></block>
+      <block type="control_wait_until"></block>
+      <block type="control_repeat_until"></block>
+      <block type="control_stop">
+        <value name="STOP_OPTION">
+          <shadow type="control_stop_menu"></shadow>
+        </value>
+      </block>
+      <block type="control_start_as_clone"></block>
+      <block type="control_create_clone_of">
+        <value name="CLONE_OPTION">
+          <shadow type="control_create_clone_of_menu"></shadow>
+        </value>
+      </block>
+      <block type="control_delete_this_clone"></block>
+    </category>
+    <category name="Operators">
+        <block type="operator_add">
+          <value name="NUM1">
             <shadow type="math_number">
-              <field name="NUM">25</field>
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+          <value name="NUM2">
+            <shadow type="math_number">
+              <field name="NUM"></field>
             </shadow>
           </value>
         </block>
-        <block type="looks_seteffectto">
-          <value name="EFFECT">
-            <shadow type="looks_effectmenu"></shadow>
-          </value>
-          <value name="VALUE">
+        <block type="operator_subtract">
+          <value name="NUM1">
             <shadow type="math_number">
-              <field name="NUM">0</field>
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+          <value name="NUM2">
+            <shadow type="math_number">
+              <field name="NUM"></field>
             </shadow>
           </value>
         </block>
-        <block type="looks_cleargraphiceffects"></block>
-        <block type="looks_changesizeby">
-          <value name="CHANGE">
+        <block type="operator_multiply">
+          <value name="NUM1">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+          <value name="NUM2">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_divide">
+          <value name="NUM1">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+          <value name="NUM2">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_random">
+          <value name="FROM">
+            <shadow type="math_number">
+              <field name="NUM">1</field>
+            </shadow>
+          </value>
+          <value name="TO">
             <shadow type="math_number">
               <field name="NUM">10</field>
             </shadow>
           </value>
         </block>
-        <block type="looks_setsizeto">
-          <value name="SIZE">
-            <shadow type="math_number">
-              <field name="NUM">100</field>
+        <block type="operator_lt">
+          <value name="OPERAND1">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
+          <value name="OPERAND2">
+            <shadow type="text">
+              <field name="TEXT"></field>
             </shadow>
           </value>
         </block>
-        <block type="looks_size"></block>
-      </category>
-      <category name="Motion">
+        <block type="operator_equals">
+          <value name="OPERAND1">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
+          <value name="OPERAND2">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_gt">
+          <value name="OPERAND1">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
+          <value name="OPERAND2">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_and"></block>
+        <block type="operator_or"></block>
+        <block type="operator_not"></block>
+        <block type="operator_join">
+          <value name="STRING1">
+            <shadow type="text">
+              <field name="TEXT">hello</field>
+            </shadow>
+          </value>
+          <value name="STRING2">
+            <shadow type="text">
+              <field name="TEXT">world</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_letter_of">
+          <value name="LETTER">
+            <shadow type="math_whole_number">
+              <field name="NUM">1</field>
+            </shadow>
+          </value>
+          <value name="STRING">
+            <shadow type="text">
+              <field name="TEXT">world</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_length">
+          <value name="STRING">
+            <shadow type="text">
+              <field name="TEXT">world</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_mod">
+          <value name="NUM1">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+          <value name="NUM2">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_round">
+          <value name="NUM">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+        </block>
+        <block type="operator_mathop">
+          <value name="OPERATOR">
+            <shadow type="operator_mathop_menu"></shadow>
+          </value>
+          <value name="NUM">
+            <shadow type="math_number">
+              <field name="NUM"></field>
+            </shadow>
+          </value>
+        </block>
+    </category>
+    <category name="Looks">
+      <block type="looks_sayforsecs">
+        <value name="MESSAGE">
+          <shadow type="text">
+            <field name="TEXT">Hello!</field>
+          </shadow>
+        </value>
+        <value name="SECS">
+          <shadow type="math_number">
+            <field name="NUM">2</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_say">
+        <value name="MESSAGE">
+          <shadow type="text">
+            <field name="TEXT">Hello!</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_thinkforsecs">
+        <value name="MESSAGE">
+          <shadow type="text">
+            <field name="TEXT">Hmm...</field>
+          </shadow>
+        </value>
+        <value name="SECS">
+          <shadow type="math_number">
+            <field name="NUM">2</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_think">
+        <value name="MESSAGE">
+          <shadow type="text">
+            <field name="TEXT">Hmm...</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_show"></block>
+      <block type="looks_hide"></block>
+      <block type="looks_changeeffectby">
+        <value name="EFFECT">
+          <shadow type="looks_effectmenu"></shadow>
+        </value>
+        <value name="CHANGE">
+          <shadow type="math_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_seteffectto">
+        <value name="EFFECT">
+          <shadow type="looks_effectmenu"></shadow>
+        </value>
+        <value name="VALUE">
+          <shadow type="math_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_cleargraphiceffects"></block>
+      <block type="looks_changesizeby">
+        <value name="CHANGE">
+          <shadow type="math_number">
+            <field name="NUM">10</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_setsizeto">
+        <value name="SIZE">
+          <shadow type="math_number">
+            <field name="NUM">100</field>
+          </shadow>
+        </value>
+      </block>
+      <block type="looks_size"></block>
+    </category>
+    <category name="Motion">
       <block type="motion_movesteps">
         <value name="STEPS">
           <shadow type="math_number">
@@ -332,6 +375,29 @@
           </shadow>
         </value>
       </block>
+      <block type="motion_goto">
+        <value name="TO">
+          <shadow type="motion_goto_menu">
+          </shadow>
+        </value>
+      </block>
+      <block type="motion_glidesecstoxy">
+        <value name="SECS">
+          <shadow type="math_number">
+            <field name="NUM">1</field>
+          </shadow>
+        </value>
+        <value name="X">
+          <shadow type="math_number">
+            <field name="NUM">0</field>
+          </shadow>
+        </value>
+        <value name="Y">
+          <shadow type="math_number">
+            <field name="NUM">0</field>
+          </shadow>
+        </value>
+      </block>
       <block type="motion_changexby">
         <value name="DX">
           <shadow type="math_number">
@@ -360,11 +426,17 @@
           </shadow>
         </value>
       </block>
+      <block type="motion_ifonedgebounce"></block>
+      <block type="motion_setrotationstyle">
+        <value name="STYLE">
+          <shadow type="motion_setrotationstyle_menu"></shadow>
+        </value>
+      </block>
       <block type="motion_xposition"></block>
       <block type="motion_yposition"></block>
       <block type="motion_direction"></block>
     </category>
-    </xml>
+  </xml>
 
     <!-- Syntax highlighter -->
     <script src="../node_modules/highlightjs/highlight.pack.min.js"></script>

From 460760bd06cd5893e1a5f710a555e23316bf9e3e Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 13:47:32 -0400
Subject: [PATCH 24/40] Stub "say" and "think" blocks with `console.log`

---
 src/blocks/scratch3_looks.js | 34 ++++++++++++++++++++++++++++++++++
 src/sprites/clone.js         | 14 ++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js
index b2bad1ef7..33e1ef3bf 100644
--- a/src/blocks/scratch3_looks.js
+++ b/src/blocks/scratch3_looks.js
@@ -12,6 +12,10 @@ function Scratch3LooksBlocks(runtime) {
  */
 Scratch3LooksBlocks.prototype.getPrimitives = function() {
     return {
+        'looks_say': this.say,
+        'looks_sayforsecs': this.sayforsecs,
+        'looks_think': this.think,
+        'looks_thinkforsecs': this.sayforsecs,
         'looks_show': this.show,
         'looks_hide': this.hide,
         'looks_effectmenu': this.effectMenu,
@@ -24,6 +28,36 @@ Scratch3LooksBlocks.prototype.getPrimitives = function() {
     };
 };
 
+Scratch3LooksBlocks.prototype.say = function (args, util) {
+    util.target.setSay('say', args.MESSAGE);
+};
+
+Scratch3LooksBlocks.prototype.sayforsecs = function (args, util) {
+    util.target.setSay('say', args.MESSAGE);
+    return new Promise(function(resolve) {
+        setTimeout(function() {
+            // Clear say bubble and proceed.
+            util.target.setSay();
+            resolve();
+        }, 1000 * args.SECS);
+    });
+};
+
+Scratch3LooksBlocks.prototype.think = function (args, util) {
+    util.target.setSay('think', args.MESSAGE);
+};
+
+Scratch3LooksBlocks.prototype.thinkforsecs = function (args, util) {
+    util.target.setSay('think', args.MESSAGE);
+    return new Promise(function(resolve) {
+        setTimeout(function() {
+            // Clear say bubble and proceed.
+            util.target.setSay();
+            resolve();
+        }, 1000 * args.SECS);
+    });
+};
+
 Scratch3LooksBlocks.prototype.show = function (args, util) {
     util.target.setVisible(true);
 };
diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index a1c8b5c8d..1ffca405b 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -53,6 +53,20 @@ Clone.prototype.setDirection = function (direction) {
     });
 };
 
+/**
+ * Set a say bubble on this clone.
+ * @param {?string} type Type of say bubble: "say", "think", or null.
+ * @param {?string} message Message to put in say bubble.
+ */
+Clone.prototype.setSay = function (type, message) {
+    // @todo: Render to stage.
+    if (!type || !message) {
+        console.log('Clearing say bubble');
+        return;
+    }
+    console.log('Setting say bubble:', type, message);
+};
+
 Clone.prototype.setVisible = function (visible) {
     this.visible = visible;
     // @todo: Until visibility is implemented in the renderer, use a ghost.

From 798368b6c5010426e37f29c4c4002df9c6acccce Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 13:57:58 -0400
Subject: [PATCH 25/40] Add documentation in src/sprites/clone.js

---
 src/sprites/clone.js | 57 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index 1ffca405b..ce64fda52 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -9,6 +9,9 @@ function Clone(spriteBlocks) {
 }
 util.inherits(Clone, Target);
 
+/**
+ * Create a clone's drawable with the renderer.
+ */
 Clone.prototype.initDrawable = function () {
     var createPromise = self.renderer.createDrawable();
     var instance = this;
@@ -17,17 +20,41 @@ Clone.prototype.initDrawable = function () {
     });
 };
 
-// Clone-level properties
+// Clone-level properties.
+/**
+ * Scratch X coordinate. Currently should range from -240 to 240.
+ * @type {!number}
+ */
 Clone.prototype.x = 0;
 
+/**
+ * Scratch Y coordinate. Currently should range from -180 to 180.
+ * @type {!number}
+ */
 Clone.prototype.y = 0;
 
+/**
+ * Scratch direction. Currently should range from -179 to 180.
+ * @type {!number}
+ */
 Clone.prototype.direction = 90;
 
+/**
+ * Whether the clone is currently visible.
+ * @type {!boolean}
+ */
 Clone.prototype.visible = true;
 
+/**
+ * Size of clone as a percent of costume size. Ranges from 5% to 535%.
+ * @type {!number}
+ */
 Clone.prototype.size = 100;
 
+/**
+ * Map of current graphic effect values.
+ * @type {!Object.<string, number>}
+ */
 Clone.prototype.effects = {
     'color': 0,
     'fisheye': 0,
@@ -37,7 +64,13 @@ Clone.prototype.effects = {
     'brightness': 0,
     'ghost': 0
 };
+// End clone-level properties.
 
+/**
+ * Set the X and Y coordinates of a clone.
+ * @param {!number} x New X coordinate of clone, in Scratch coordinates.
+ * @param {!number} y New Y coordinate of clone, in Scratch coordinates.
+ */
 Clone.prototype.setXY = function (x, y) {
     this.x = x;
     this.y = y;
@@ -46,7 +79,12 @@ Clone.prototype.setXY = function (x, y) {
     });
 };
 
+/**
+ * Set the direction of a clone.
+ * @param {!number} direction New direction of clone.
+ */
 Clone.prototype.setDirection = function (direction) {
+    // Keep direction between -179 and +180.
     this.direction = MathUtil.wrapClamp(direction, -179, 180);
     self.renderer.updateDrawableProperties(this.drawableID, {
         direction: this.direction
@@ -67,6 +105,10 @@ Clone.prototype.setSay = function (type, message) {
     console.log('Setting say bubble:', type, message);
 };
 
+/**
+ * Set visibility of the clone; i.e., whether it's shown or hidden.
+ * @param {!boolean} visible True if the sprite should be shown.
+ */
 Clone.prototype.setVisible = function (visible) {
     this.visible = visible;
     // @todo: Until visibility is implemented in the renderer, use a ghost.
@@ -77,13 +119,23 @@ Clone.prototype.setVisible = function (visible) {
     }
 };
 
+/**
+ * Set size of the clone, as a percentage of the costume size.
+ * @param {!number} size Size of clone, from 5 to 535.
+ */
 Clone.prototype.setSize = function (size) {
+    // Keep size between 5% and 535%.
     this.size = MathUtil.clamp(size, 5, 535);
     self.renderer.updateDrawableProperties(this.drawableID, {
         scale: this.size
     });
 };
 
+/**
+ * Set a particular graphic effect on this clone.
+ * @param {!string} effectName Name of effect (see `Clone.prototype.effects`).
+ * @param {!number} value Numerical magnitude of effect.
+ */
 Clone.prototype.setEffect = function (effectName, value) {
     this.effects[effectName] = value;
     var props = {};
@@ -91,6 +143,9 @@ Clone.prototype.setEffect = function (effectName, value) {
     self.renderer.updateDrawableProperties(this.drawableID, props);
 };
 
+/**
+ * Clear all graphic effects on this clone.
+ */
 Clone.prototype.clearEffects = function () {
     for (var effectName in this.effects) {
         this.effects[effectName] = 0;

From 2e01caa8a6e5be07ea2354c5ea201b360f98cc28 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 14:04:36 -0400
Subject: [PATCH 26/40] Add documentation for math-util functions.

---
 src/util/math-util.js | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/src/util/math-util.js b/src/util/math-util.js
index 2dfd62a6e..b53698ef1 100644
--- a/src/util/math-util.js
+++ b/src/util/math-util.js
@@ -1,17 +1,45 @@
 function MathUtil () {}
 
+/**
+ * Convert a value from degrees to radians.
+ * @param {!number} deg Value in degrees.
+ * @return {!number} Equivalent value in radians.
+ */
 MathUtil.degToRad = function (deg) {
     return (Math.PI * (90 - deg)) / 180;
 };
 
+/**
+ * Convert a value from radians to degrees.
+ * @param {!number} rad Value in radians.
+ * @return {!number} Equivalent value in degrees.
+ */
 MathUtil.radToDeg = function (rad) {
     return rad * 180 / Math.PI;
 };
 
+/**
+ * Clamp a number between two limits.
+ * If n < min, return min. If n > max, return max. Else, return n.
+ * @param {!number} n Number to clamp.
+ * @param {!number} min Minimum limit.
+ * @param {!number} max Maximum limit.
+ * @return {!number} Value of n clamped to min and max.
+ */
 MathUtil.clamp = function (n, min, max) {
     return Math.min(Math.max(n, min), max);
 };
 
+/**
+ * Keep a number between two limits, wrapping "extra" into the range.
+ * e.g., wrapClamp(7, 1, 5) == 2
+ * wrapClamp(0, 1, 5) == 5
+ * wrapClamp(-11, -10, 6) == 6, etc.
+ * @param {!number} n Number to wrap.
+ * @param {!number} min Minimum limit.
+ * @param {!number} max Maximum limit.
+ * @return {!number} Value of n wrapped between min and max.
+ */
 MathUtil.wrapClamp = function (n, min, max) {
     var range = (max - min) + 1;
     return n - Math.floor((n - min) / range) * range;

From 7c24bdc612ffca680b2595104d28a75665227a10 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 14:09:06 -0400
Subject: [PATCH 27/40] More documentation in sprite, clone

---
 src/sprites/clone.js  |  5 +++++
 src/sprites/sprite.js | 11 ++++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index ce64fda52..169d72cc7 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -2,6 +2,11 @@ var util = require('util');
 var MathUtil = require('../util/math-util');
 var Target = require('../engine/target');
 
+/**
+ * Clone (instance) of a sprite. 
+ * @param {!Blocks} spriteBlocks Reference to the sprite's blocks.
+ * @constructor
+ */
 function Clone(spriteBlocks) {
     Target.call(this, spriteBlocks);
     this.drawableID = null;
diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js
index 1ec25b3ba..b4abdbbb5 100644
--- a/src/sprites/sprite.js
+++ b/src/sprites/sprite.js
@@ -1,8 +1,13 @@
 var Clone = require('./clone');
 var Blocks = require('../engine/blocks');
 
+/**
+ * Sprite to be used on the Scratch stage.
+ * All clones of a sprite have shared blocks, shared costumes, shared variables.
+ * @param {?Blocks} blocks Shared blocks object for all clones of sprite.
+ * @constructor
+ */
 function Sprite (blocks) {
-    // Sprites have: shared blocks, shared costumes, shared variables, etc.
     if (!blocks) {
         // Shared set of blocks for all clones.
         blocks = new Blocks();
@@ -11,6 +16,10 @@ function Sprite (blocks) {
     this.clones = [];
 }
 
+/**
+ * Create a clone of this sprite.
+ * @returns {!Clone} Newly created clone.
+ */
 Sprite.prototype.createClone = function () {
     var newClone = new Clone(this.blocks);
     this.clones.push(newClone);

From 30dc285a37b78a2a1a27d92ce3ddbb79387d1b12 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 14:13:03 -0400
Subject: [PATCH 28/40] Add implementations for math_positive_number and
 math_whole_number

---
 src/blocks/scratch3_operators.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js
index c72adf29c..d3a7f1921 100644
--- a/src/blocks/scratch3_operators.js
+++ b/src/blocks/scratch3_operators.js
@@ -15,6 +15,8 @@ function Scratch3OperatorsBlocks(runtime) {
 Scratch3OperatorsBlocks.prototype.getPrimitives = function() {
     return {
         'math_number': this.number,
+        'math_positive_number': this.number,
+        'math_whole_number': this.number,
         'text': this.text,
         'operator_add': this.add,
         'operator_subtract': this.subtract,

From d4353458ff2f44d2423a2483466f147b7b35dc0a Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 6 Jul 2016 14:16:44 -0400
Subject: [PATCH 29/40] Don't quit loop when a thread is yielding

---
 src/engine/sequencer.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index 342ad8857..175fc68d1 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -62,7 +62,6 @@ Sequencer.prototype.stepThreads = function (threads) {
                        activeThread.status === Thread.STATUS_YIELD_FRAME) {
                 // Yielding thread: do nothing for this step.
                 numYieldingThreads++;
-                continue;
             }
             if (activeThread.stack.length === 0 &&
                 activeThread.status === Thread.STATUS_DONE) {

From 9c6dca8131ec5fe1d40a93ccd75e07277cd22813 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 7 Jul 2016 19:42:38 -0400
Subject: [PATCH 30/40] Add visual reporting of top-level reporter execution

---
 playground/playground.js |  3 +++
 src/engine/execute.js    |  8 ++++++++
 src/engine/runtime.js    | 15 +++++++++++++++
 src/index.js             |  6 ++++++
 4 files changed, 32 insertions(+)

diff --git a/playground/playground.js b/playground/playground.js
index 800894bb9..47631b201 100644
--- a/playground/playground.js
+++ b/playground/playground.js
@@ -78,6 +78,9 @@ window.onload = function() {
     vm.on('BLOCK_GLOW_OFF', function(data) {
         workspace.glowBlock(data.id, false);
     });
+    vm.on('VISUAL_REPORT', function(data) {
+        workspace.reportValue(data.id, data.value);
+    });
 
     // Run threads
     vm.start();
diff --git a/src/engine/execute.js b/src/engine/execute.js
index 529990ca0..db111637d 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -96,6 +96,10 @@ var execute = function (sequencer, thread) {
         primitiveReportedValue.then(function(resolvedValue) {
             // Promise resolved: the primitive reported a value.
             thread.pushReportedValue(resolvedValue);
+            // Report the value visually if necessary.
+            if (thread.peekStack() === thread.topBlock) {
+                runtime.visualReport(thread.peekStack(), resolvedValue);
+            }
             thread.setStatus(Thread.STATUS_RUNNING);
             sequencer.proceedThread(thread);
         }, function(rejectionReason) {
@@ -107,6 +111,10 @@ var execute = function (sequencer, thread) {
         });
     } else if (thread.status === Thread.STATUS_RUNNING) {
         thread.pushReportedValue(primitiveReportedValue);
+        // Report the value visually if necessary.
+        if (thread.peekStack() === thread.topBlock) {
+            runtime.visualReport(thread.peekStack(), primitiveReportedValue);
+        }
     }
 };
 
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index c51be795d..f92a91db7 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -69,6 +69,12 @@ Runtime.BLOCK_GLOW_ON = 'BLOCK_GLOW_ON';
  */
 Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF';
 
+/**
+ * Event name for visual value report.
+ * @const {string}
+ */
+Runtime.VISUAL_REPORT = 'VISUAL_REPORT';
+
 /**
  * Inherit from EventEmitter
  */
@@ -218,6 +224,15 @@ Runtime.prototype.glowBlock = function (blockId, isGlowing) {
     }
 };
 
+/**
+ * Emit value for reporter to show in the blocks.
+ * @param {string} blockId ID for the block.
+ * @param {string} value Value to show associated with the block.
+ */
+Runtime.prototype.visualReport = function (blockId, value) {
+    this.emit(Runtime.VISUAL_REPORT, blockId, String(value));
+};
+
 /**
  * Return the Target for a particular thread.
  * @param {!Thread} thread Thread to determine target for.
diff --git a/src/index.js b/src/index.js
index 15b218071..965c8cd9a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -53,6 +53,9 @@ function VirtualMachine () {
     instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (id) {
         instance.emit(Runtime.BLOCK_GLOW_OFF, {id: id});
     });
+    instance.runtime.on(Runtime.VISUAL_REPORT, function (id, value) {
+        instance.emit(Runtime.VISUAL_REPORT, {id: id, value: value});
+    });
 }
 
 /**
@@ -158,6 +161,9 @@ if (ENV_WORKER) {
     self.vmInstance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (id) {
         self.postMessage({method: Runtime.BLOCK_GLOW_OFF, id: id});
     });
+    self.vmInstance.runtime.on(Runtime.VISUAL_REPORT, function (id, value) {
+        self.postMessage({method: Runtime.VISUAL_REPORT, id: id, value: value});
+    });
 }
 
 /**

From 1f19d7a20932d64a08fc8859cc222d022d04b8af Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 7 Jul 2016 19:44:26 -0400
Subject: [PATCH 31/40] Only visually report when an actual value was returned

---
 src/engine/execute.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/engine/execute.js b/src/engine/execute.js
index db111637d..8d750eee3 100644
--- a/src/engine/execute.js
+++ b/src/engine/execute.js
@@ -97,7 +97,8 @@ var execute = function (sequencer, thread) {
             // Promise resolved: the primitive reported a value.
             thread.pushReportedValue(resolvedValue);
             // Report the value visually if necessary.
-            if (thread.peekStack() === thread.topBlock) {
+            if (typeof resolvedValue !== 'undefined' &&
+                thread.peekStack() === thread.topBlock) {
                 runtime.visualReport(thread.peekStack(), resolvedValue);
             }
             thread.setStatus(Thread.STATUS_RUNNING);
@@ -112,7 +113,8 @@ var execute = function (sequencer, thread) {
     } else if (thread.status === Thread.STATUS_RUNNING) {
         thread.pushReportedValue(primitiveReportedValue);
         // Report the value visually if necessary.
-        if (thread.peekStack() === thread.topBlock) {
+        if (typeof primitiveReportedValue !== 'undefined' &&
+            thread.peekStack() === thread.topBlock) {
             runtime.visualReport(thread.peekStack(), primitiveReportedValue);
         }
     }

From c650de852081d7ebbcb5597e0885cfb775fb345c Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Wed, 13 Jul 2016 16:52:46 -0400
Subject: [PATCH 32/40] Real version of `Clone.prototype.setVisible`

---
 src/sprites/clone.js | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index 169d72cc7..6a0f22fee 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -3,7 +3,7 @@ var MathUtil = require('../util/math-util');
 var Target = require('../engine/target');
 
 /**
- * Clone (instance) of a sprite. 
+ * Clone (instance) of a sprite.
  * @param {!Blocks} spriteBlocks Reference to the sprite's blocks.
  * @constructor
  */
@@ -116,12 +116,9 @@ Clone.prototype.setSay = function (type, message) {
  */
 Clone.prototype.setVisible = function (visible) {
     this.visible = visible;
-    // @todo: Until visibility is implemented in the renderer, use a ghost.
-    if (this.visible) {
-        this.setEffect('ghost', 0);
-    } else {
-        this.setEffect('ghost', 100);
-    }
+    self.renderer.updateDrawableProperties(this.drawableID, {
+        visible: this.visible
+    });
 };
 
 /**

From 0465ee2076cbbd494d83dc21f23c8e8b50b8811e Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 14 Jul 2016 13:22:11 -0400
Subject: [PATCH 33/40] Add --host flag and index redirect for playground

---
 Makefile   | 2 +-
 index.html | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)
 create mode 100644 index.html

diff --git a/Makefile b/Makefile
index 835c16f79..e2a04c2b6 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ watch:
 	$(WEBPACK) --watch
 
 serve:
-	$(WEBPACK_DEV_SERVER) --content-base ./
+	$(WEBPACK_DEV_SERVER) --host 0.0.0.0 --content-base ./
 
 # ------------------------------------------------------------------------------
 
diff --git a/index.html b/index.html
new file mode 100644
index 000000000..42edce430
--- /dev/null
+++ b/index.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <meta http-equiv="refresh" content="0; URL='/playground'" />
+	<title>Redirect to playground</title>
+</head>
+</html>

From 1098178fc74697de7c663b8e91ff0eb7fc4feac3 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 14 Jul 2016 13:24:39 -0400
Subject: [PATCH 34/40] Add Blockly messages to playground

---
 playground/index.html | 1 +
 1 file changed, 1 insertion(+)

diff --git a/playground/index.html b/playground/index.html
index 8ba16c2c1..28781ebc7 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -445,6 +445,7 @@
     <script src="../node_modules/scratch-blocks/blockly_compressed_vertical.js"></script>
     <script src="../node_modules/scratch-blocks/blocks_compressed.js"></script>
     <script src="../node_modules/scratch-blocks/blocks_compressed_vertical.js"></script>
+    <script src="../node_modules/scratch-blocks/msg/messages.js"></script>
     <!-- Renderer -->
     <script src="../node_modules/scratch-render-webgl/build/render-webgl.js"></script>
     <!-- VM Worker -->

From 30735bc06e8722178ddfe12acb29369d50b875ba Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Thu, 14 Jul 2016 13:25:56 -0400
Subject: [PATCH 35/40] Update toolbox XML July 14

---
 playground/index.html | 1094 ++++++++++++++++++++++++++---------------
 1 file changed, 691 insertions(+), 403 deletions(-)

diff --git a/playground/index.html b/playground/index.html
index 28781ebc7..e38c10793 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -33,409 +33,697 @@
     <div id="blocks"></div>
 
     <xml id="toolbox" style="display: none">
-    <category name="Events">
-      <block type="event_whenflagclicked"></block>
-      <block type="event_whenkeypressed">
-        <value name="KEY_OPTION">
-          <shadow type="event_keyoptions"></shadow>
-        </value>
-      </block>
-      <block type="event_whenbroadcastreceived">
-        <value name="BROADCAST_OPTION">
-          <shadow type="event_broadcast_menu"></shadow>
-        </value>
-      </block>
-      <block type="event_broadcast">
-        <value name="BROADCAST_OPTION">
-          <shadow type="event_broadcast_menu"></shadow>
-        </value>
-      </block>
-      <block type="event_broadcastandwait">
-        <value name="BROADCAST_OPTION">
-          <shadow type="event_broadcast_menu"></shadow>
-        </value>
-      </block>
-    </category>
-    <category name="Control">
-      <block type="control_wait">
-        <value name="DURATION">
-          <shadow type="math_positive_number">
-            <field name="NUM">1</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="control_repeat">
-        <value name="TIMES">
-          <shadow type="math_whole_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="control_forever"></block>
-      <block type="control_if"></block>
-      <block type="control_if_else"></block>
-      <block type="control_wait_until"></block>
-      <block type="control_repeat_until"></block>
-      <block type="control_stop">
-        <value name="STOP_OPTION">
-          <shadow type="control_stop_menu"></shadow>
-        </value>
-      </block>
-      <block type="control_start_as_clone"></block>
-      <block type="control_create_clone_of">
-        <value name="CLONE_OPTION">
-          <shadow type="control_create_clone_of_menu"></shadow>
-        </value>
-      </block>
-      <block type="control_delete_this_clone"></block>
-    </category>
-    <category name="Operators">
-        <block type="operator_add">
-          <value name="NUM1">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-          <value name="NUM2">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_subtract">
-          <value name="NUM1">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-          <value name="NUM2">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_multiply">
-          <value name="NUM1">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-          <value name="NUM2">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_divide">
-          <value name="NUM1">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-          <value name="NUM2">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_random">
-          <value name="FROM">
-            <shadow type="math_number">
-              <field name="NUM">1</field>
-            </shadow>
-          </value>
-          <value name="TO">
-            <shadow type="math_number">
-              <field name="NUM">10</field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_lt">
-          <value name="OPERAND1">
-            <shadow type="text">
-              <field name="TEXT"></field>
-            </shadow>
-          </value>
-          <value name="OPERAND2">
-            <shadow type="text">
-              <field name="TEXT"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_equals">
-          <value name="OPERAND1">
-            <shadow type="text">
-              <field name="TEXT"></field>
-            </shadow>
-          </value>
-          <value name="OPERAND2">
-            <shadow type="text">
-              <field name="TEXT"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_gt">
-          <value name="OPERAND1">
-            <shadow type="text">
-              <field name="TEXT"></field>
-            </shadow>
-          </value>
-          <value name="OPERAND2">
-            <shadow type="text">
-              <field name="TEXT"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_and"></block>
-        <block type="operator_or"></block>
-        <block type="operator_not"></block>
-        <block type="operator_join">
-          <value name="STRING1">
-            <shadow type="text">
-              <field name="TEXT">hello</field>
-            </shadow>
-          </value>
-          <value name="STRING2">
-            <shadow type="text">
-              <field name="TEXT">world</field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_letter_of">
-          <value name="LETTER">
-            <shadow type="math_whole_number">
-              <field name="NUM">1</field>
-            </shadow>
-          </value>
-          <value name="STRING">
-            <shadow type="text">
-              <field name="TEXT">world</field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_length">
-          <value name="STRING">
-            <shadow type="text">
-              <field name="TEXT">world</field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_mod">
-          <value name="NUM1">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-          <value name="NUM2">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_round">
-          <value name="NUM">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-        <block type="operator_mathop">
-          <value name="OPERATOR">
-            <shadow type="operator_mathop_menu"></shadow>
-          </value>
-          <value name="NUM">
-            <shadow type="math_number">
-              <field name="NUM"></field>
-            </shadow>
-          </value>
-        </block>
-    </category>
-    <category name="Looks">
-      <block type="looks_sayforsecs">
-        <value name="MESSAGE">
-          <shadow type="text">
-            <field name="TEXT">Hello!</field>
-          </shadow>
-        </value>
-        <value name="SECS">
-          <shadow type="math_number">
-            <field name="NUM">2</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_say">
-        <value name="MESSAGE">
-          <shadow type="text">
-            <field name="TEXT">Hello!</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_thinkforsecs">
-        <value name="MESSAGE">
-          <shadow type="text">
-            <field name="TEXT">Hmm...</field>
-          </shadow>
-        </value>
-        <value name="SECS">
-          <shadow type="math_number">
-            <field name="NUM">2</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_think">
-        <value name="MESSAGE">
-          <shadow type="text">
-            <field name="TEXT">Hmm...</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_show"></block>
-      <block type="looks_hide"></block>
-      <block type="looks_changeeffectby">
-        <value name="EFFECT">
-          <shadow type="looks_effectmenu"></shadow>
-        </value>
-        <value name="CHANGE">
-          <shadow type="math_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_seteffectto">
-        <value name="EFFECT">
-          <shadow type="looks_effectmenu"></shadow>
-        </value>
-        <value name="VALUE">
-          <shadow type="math_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_cleargraphiceffects"></block>
-      <block type="looks_changesizeby">
-        <value name="CHANGE">
-          <shadow type="math_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_setsizeto">
-        <value name="SIZE">
-          <shadow type="math_number">
-            <field name="NUM">100</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="looks_size"></block>
-    </category>
-    <category name="Motion">
-      <block type="motion_movesteps">
-        <value name="STEPS">
-          <shadow type="math_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_turnright">
-        <value name="DEGREES">
-          <shadow type="math_number">
-            <field name="NUM">15</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_turnleft">
-        <value name="DEGREES">
-          <shadow type="math_number">
-            <field name="NUM">15</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_pointindirection">
-        <value name="DIRECTION">
-          <shadow type="math_number">
-            <field name="NUM">90</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_pointtowards">
-        <value name="TOWARDS">
-          <shadow type="motion_pointtowards_menu">
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_gotoxy">
-        <value name="X">
-          <shadow type="math_number">
-            <field name="NUM">0</field>
-          </shadow>
-        </value>
-        <value name="Y">
-          <shadow type="math_number">
-            <field name="NUM">0</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_goto">
-        <value name="TO">
-          <shadow type="motion_goto_menu">
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_glidesecstoxy">
-        <value name="SECS">
-          <shadow type="math_number">
-            <field name="NUM">1</field>
-          </shadow>
-        </value>
-        <value name="X">
-          <shadow type="math_number">
-            <field name="NUM">0</field>
-          </shadow>
-        </value>
-        <value name="Y">
-          <shadow type="math_number">
-            <field name="NUM">0</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_changexby">
-        <value name="DX">
-          <shadow type="math_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_setx">
-        <value name="X">
-          <shadow type="math_number">
-            <field name="NUM">0</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_changeyby">
-        <value name="DY">
-          <shadow type="math_number">
-            <field name="NUM">10</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_sety">
-        <value name="Y">
-          <shadow type="math_number">
-            <field name="NUM">0</field>
-          </shadow>
-        </value>
-      </block>
-      <block type="motion_ifonedgebounce"></block>
-      <block type="motion_setrotationstyle">
-        <value name="STYLE">
-          <shadow type="motion_setrotationstyle_menu"></shadow>
-        </value>
-      </block>
-      <block type="motion_xposition"></block>
-      <block type="motion_yposition"></block>
-      <block type="motion_direction"></block>
-    </category>
+        <category name="Motion" colour="#4C97FF">
+            <block type="motion_movesteps">
+              <value name="STEPS">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_turnright">
+              <value name="DEGREES">
+                <shadow type="math_number">
+                  <field name="NUM">15</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_turnleft">
+              <value name="DEGREES">
+                <shadow type="math_number">
+                  <field name="NUM">15</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_pointindirection">
+              <value name="DIRECTION">
+                <shadow type="math_angle">
+                  <field name="NUM">90</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_pointtowards">
+              <value name="TOWARDS">
+                <shadow type="motion_pointtowards_menu">
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_gotoxy">
+              <value name="X">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+              <value name="Y">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_goto">
+              <value name="TO">
+                <shadow type="motion_goto_menu">
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_glidesecstoxy">
+              <value name="SECS">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+              <value name="X">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+              <value name="Y">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_changexby">
+              <value name="DX">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_setx">
+              <value name="X">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_changeyby">
+              <value name="DY">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_sety">
+              <value name="Y">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="motion_ifonedgebounce"></block>
+            <block type="motion_setrotationstyle">
+              <value name="STYLE">
+                <shadow type="motion_setrotationstyle_menu"></shadow>
+              </value>
+            </block>
+            <block type="motion_xposition"></block>
+            <block type="motion_yposition"></block>
+            <block type="motion_direction"></block>
+          </category>
+          <category name="Looks" colour="#9966FF">
+            <block type="looks_sayforsecs">
+              <value name="MESSAGE">
+                <shadow type="text">
+                  <field name="TEXT">Hello!</field>
+                </shadow>
+              </value>
+              <value name="SECS">
+                <shadow type="math_number">
+                  <field name="NUM">2</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_say">
+              <value name="MESSAGE">
+                <shadow type="text">
+                  <field name="TEXT">Hello!</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_thinkforsecs">
+              <value name="MESSAGE">
+                <shadow type="text">
+                  <field name="TEXT">Hmm...</field>
+                </shadow>
+              </value>
+              <value name="SECS">
+                <shadow type="math_number">
+                  <field name="NUM">2</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_think">
+              <value name="MESSAGE">
+                <shadow type="text">
+                  <field name="TEXT">Hmm...</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_show"></block>
+            <block type="looks_hide"></block>
+            <block type="looks_switchcostumeto">
+              <value name="COSTUME">
+                <shadow type="looks_costume"></shadow>
+              </value>
+    		    </block>
+            <block type="looks_nextcostume"></block>
+            <block type="looks_nextbackdrop"></block>
+            <block type="looks_switchbackdropto">
+              <value name="COSTUME">
+                <shadow type="looks_backdrops"></shadow>
+              </value>
+    		    </block>
+            <block type="looks_switchbackdroptoandwait">
+              <value name="COSTUME">
+                <shadow type="looks_backdrops"></shadow>
+              </value>
+    		    </block>
+            <block type="looks_changeeffectby">
+              <value name="EFFECT">
+                <shadow type="looks_effectmenu"></shadow>
+              </value>
+              <value name="CHANGE">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_seteffectto">
+              <value name="EFFECT">
+                <shadow type="looks_effectmenu"></shadow>
+              </value>
+              <value name="VALUE">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_cleargraphiceffects"></block>
+            <block type="looks_changesizeby">
+              <value name="CHANGE">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_setsizeto">
+              <value name="SIZE">
+                <shadow type="math_number">
+                  <field name="NUM">100</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_gotofront"></block>
+            <block type="looks_gobacklayers">
+              <value name="NUM">
+                <shadow type="math_integer">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="looks_costumeorder"></block>
+            <block type="looks_backdroporder"></block>
+            <block type="looks_backdropname"></block>
+            <block type="looks_size"></block>
+          </category>
+          <category name="Sound" colour="#D65CD6">
+            <block type="sound_play">
+              <value name="SOUND_MENU">
+                <shadow type="sound_sounds_option"></shadow>
+              </value>
+            </block>
+            <block type="sound_playuntildone">
+              <value name="SOUND_MENU">
+                <shadow type="sound_sounds_option"></shadow>
+              </value>
+            </block>
+            <block type="sound_stopallsounds"></block>
+            <block type="sound_playdrumforbeats">
+              <value name="DRUMTYPE">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+              <value name="BEATS">
+                <shadow type="math_number">
+                  <field name="NUM">0.25</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_restforbeats">
+              <value name="BEATS">
+                <shadow type="math_number">
+                  <field name="NUM">0.25</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_playnoteforbeats">
+              <value name="NOTE">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+              <value name="BEATS">
+                <shadow type="math_number">
+                  <field name="NUM">0.5</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_setinstrumentto">
+              <value name="INSTRUMENT">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_changevolumeby">
+              <value name="VOLUME">
+                <shadow type="math_number">
+                  <field name="NUM">-10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_setvolumeto">
+              <value name="VOLUME">
+                <shadow type="math_number">
+                  <field name="NUM">100</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_volume"></block>
+            <block type="sound_changetempoby">
+              <value name="TEMPO">
+                <shadow type="math_number">
+                  <field name="NUM">20</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_settempotobpm">
+              <value name="TEMPO">
+                <shadow type="math_number">
+                  <field name="NUM">60</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="sound_tempo"></block>
+          </category>
+          <category name="Pen" colour="#00B295">
+            <block type="pen_clear"></block>
+            <block type="pen_stamp"></block>
+            <block type="pen_pendown"></block>
+            <block type="pen_penup"></block>
+            <block type="pen_setpencolortocolor">
+              <value name="COLOR">
+                <shadow type="colour_picker">
+                </shadow>
+              </value>
+    		    </block>
+            <block type="pen_changepencolorby">
+              <value name="COLOR">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+    		    </block>
+            <block type="pen_setpencolortonum">
+              <value name="COLOR">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+    		    </block>
+            <block type="pen_changepenshadeby">
+              <value name="SHADE">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+    		    </block>
+            <block type="pen_setpenshadeto">
+              <value name="SHADE">
+                <shadow type="math_number">
+                  <field name="NUM">50</field>
+                </shadow>
+              </value>
+    		    </block>
+            <block type="pen_changepensizeby">
+              <value name="SIZE">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+    		    </block>
+            <block type="pen_setpensizeto">
+              <value name="SIZE">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+    		    </block>
+          </category>
+          <category name="Data" colour="#FF8C1A">
+            <block type="data_variable">
+              <value name="VARIABLE">
+                <shadow type="data_variablemenu"></shadow>
+              </value>
+            </block>
+            <block type="data_setvariableto">
+              <value name="VARIABLE">
+               <shadow type="data_variablemenu"></shadow>
+              </value>
+              <value name="VALUE">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="data_changevariableby">
+              <value name="VARIABLE">
+               <shadow type="data_variablemenu"></shadow>
+              </value>
+              <value name="VALUE">
+                <shadow type="math_number">
+                  <field name="NUM">0</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="data_showvariable">
+              <value name="VARIABLE">
+               <shadow type="data_variablemenu"></shadow>
+              </value>
+            </block>
+            <block type="data_hidevariable">
+              <value name="VARIABLE">
+               <shadow type="data_variablemenu"></shadow>
+              </value>
+            </block>
+          </category>
+          <category name="Events" colour="#FFD500">
+            <block type="event_whenflagclicked"></block>
+            <block type="event_whenkeypressed">
+              <value name="KEY_OPTION">
+                <shadow type="event_keyoptions"></shadow>
+              </value>
+            </block>
+            <block type="event_whenthisspriteclicked"></block>
+            <block type="event_whenbackdropswitchesto">
+              <value name="BACKDROP">
+                <shadow type="event_backdrops"></shadow>
+              </value>
+            </block>
+            <block type="event_whengreaterthan">
+              <value name="WHENGREATERTHANMENU">
+                <shadow type="event_whengreaterthanmenu"></shadow>
+              </value>
+              <value name="VALUE">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="event_whenbroadcastreceived">
+              <value name="BROADCAST_OPTION">
+                <shadow type="event_broadcast_menu"></shadow>
+              </value>
+            </block>
+            <block type="event_broadcast">
+              <value name="BROADCAST_OPTION">
+                <shadow type="event_broadcast_menu"></shadow>
+              </value>
+            </block>
+            <block type="event_broadcastandwait">
+              <value name="BROADCAST_OPTION">
+                <shadow type="event_broadcast_menu"></shadow>
+              </value>
+            </block>
+          </category>
+          <category name="Control" colour="#FFAB19">
+            <block type="control_wait">
+              <value name="DURATION">
+                <shadow type="math_positive_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="control_repeat">
+              <value name="TIMES">
+                <shadow type="math_whole_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="control_forever"></block>
+            <block type="control_if"></block>
+            <block type="control_if_else"></block>
+            <block type="control_wait_until"></block>
+            <block type="control_repeat_until"></block>
+            <block type="control_stop">
+              <value name="STOP_OPTION">
+                <shadow type="control_stop_menu"></shadow>
+              </value>
+            </block>
+            <block type="control_start_as_clone"></block>
+            <block type="control_create_clone_of">
+              <value name="CLONE_OPTION">
+                <shadow type="control_create_clone_of_menu"></shadow>
+              </value>
+            </block>
+            <block type="control_delete_this_clone"></block>
+          </category>
+          <category name="Sensing" colour="#4CBFE6">
+            <block type="sensing_touchingobject">
+              <value name="TOUCHINGOBJECTMENU">
+                <shadow type="sensing_touchingobjectmenu"></shadow>
+              </value>
+            </block>
+            <block type="sensing_touchingcolor">
+              <value name="COLOR">
+                <shadow type="colour_picker"></shadow>
+              </value>
+            </block>
+            <block type="sensing_coloristouchingcolor">
+              <value name="COLOR">
+                <shadow type="colour_picker"></shadow>
+              </value>
+              <value name="COLOR2">
+                <shadow type="colour_picker"></shadow>
+              </value>
+            </block>
+            <block type="sensing_distanceto">
+              <value name="DISTANCETOMENU">
+                <shadow type="sensing_distancetomenu"></shadow>
+              </value>
+            </block>
+            <block type="sensing_askandwait">
+              <value name="QUESTION">
+                <shadow type="text">
+                  <field name="TEXT">What's your name?</field>
+                </shadow>
+              </value>
+            </block>
+      		<block type="sensing_answer"></block>
+    	    <block type="sensing_keypressed">
+              <value name="KEY_OPTIONS">
+                <shadow type="sensing_keyoptions"></shadow>
+              </value>
+          </block>
+      		<block type="sensing_mousedown"></block>
+      		<block type="sensing_mousex"></block>
+      		<block type="sensing_mousey"></block>
+      		<block type="sensing_loudness"></block>
+    	    <block type="sensing_videoon">
+              <value name="VIDEOONMENU1">
+                <shadow type="sensing_videoonmenuone"></shadow>
+              </value>
+              <value name="VIDEOONMENU2">
+                <shadow type="sensing_videoonmenutwo"></shadow>
+              </value>
+          </block>
+    	    <block type="sensing_videotoggle">
+              <value name="VIDEOTOGGLEMENU">
+                <shadow type="sensing_videotogglemenu"></shadow>
+              </value>
+          </block>
+          <block type="sensing_setvideotransparency">
+            <value name="TRANSPARENCY">
+              <shadow type="math_number">
+                <field name="NUM">50</field>
+              </shadow>
+            </value>
+      		</block>
+      		<block type="sensing_timer"></block>
+      		<block type="sensing_resettimer"></block>
+      	    <block type="sensing_current">
+                <value name="CURRENTMENU">
+                  <shadow type="sensing_currentmenu"></shadow>
+                </value>
+              </block>
+      		<block type="sensing_dayssince2000"></block>
+      		<block type="sensing_username"></block>
+    	  </category>
+        <category name="Operators" colour="#40BF4A">
+            <block type="operator_add">
+              <value name="NUM1">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+              <value name="NUM2">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_subtract">
+              <value name="NUM1">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+              <value name="NUM2">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_multiply">
+              <value name="NUM1">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+              <value name="NUM2">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_divide">
+              <value name="NUM1">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+              <value name="NUM2">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_random">
+              <value name="FROM">
+                <shadow type="math_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+              <value name="TO">
+                <shadow type="math_number">
+                  <field name="NUM">10</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_lt">
+              <value name="OPERAND1">
+                <shadow type="text">
+                  <field name="TEXT"></field>
+                </shadow>
+              </value>
+              <value name="OPERAND2">
+                <shadow type="text">
+                  <field name="TEXT"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_equals">
+              <value name="OPERAND1">
+                <shadow type="text">
+                  <field name="TEXT"></field>
+                </shadow>
+              </value>
+              <value name="OPERAND2">
+                <shadow type="text">
+                  <field name="TEXT"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_gt">
+              <value name="OPERAND1">
+                <shadow type="text">
+                  <field name="TEXT"></field>
+                </shadow>
+              </value>
+              <value name="OPERAND2">
+                <shadow type="text">
+                  <field name="TEXT"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_and"></block>
+            <block type="operator_or"></block>
+            <block type="operator_not"></block>
+            <block type="operator_join">
+              <value name="STRING1">
+                <shadow type="text">
+                  <field name="TEXT">hello</field>
+                </shadow>
+              </value>
+              <value name="STRING2">
+                <shadow type="text">
+                  <field name="TEXT">world</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_letter_of">
+              <value name="LETTER">
+                <shadow type="math_whole_number">
+                  <field name="NUM">1</field>
+                </shadow>
+              </value>
+              <value name="STRING">
+                <shadow type="text">
+                  <field name="TEXT">world</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_length">
+              <value name="STRING">
+                <shadow type="text">
+                  <field name="TEXT">world</field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_mod">
+              <value name="NUM1">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+              <value name="NUM2">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_round">
+              <value name="NUM">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+            <block type="operator_mathop">
+              <value name="OPERATOR">
+                <shadow type="operator_mathop_menu"></shadow>
+              </value>
+              <value name="NUM">
+                <shadow type="math_number">
+                  <field name="NUM"></field>
+                </shadow>
+              </value>
+            </block>
+          </category>
+          <category name="More Blocks" colour="#FF6680"></category>
   </xml>
 
     <!-- Syntax highlighter -->

From c47a061edb3477ec9d7104ec56ab910174536231 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Mon, 8 Aug 2016 15:43:52 -0400
Subject: [PATCH 36/40] Updates for newly released scratch-render repo

---
 Makefile              | 2 +-
 package.json          | 4 ++--
 playground/index.html | 2 +-
 src/index.js          | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile
index e2a04c2b6..c3614f5ac 100644
--- a/Makefile
+++ b/Makefile
@@ -31,4 +31,4 @@ coverage:
 
 # ------------------------------------------------------------------------------
 
-.PHONY: build lint test coverage benchmark
+.PHONY: build lint test coverage benchmark serve
diff --git a/package.json b/package.json
index 2b6d7335a..9f28cbd83 100644
--- a/package.json
+++ b/package.json
@@ -16,14 +16,14 @@
   "dependencies": {
     "htmlparser2": "3.9.0",
     "memoizee": "0.3.10",
-    "promise": "7.1.1",
-    "scratch-render-webgl": "git+https://github.com/LLK/scratch-render-webgl.git"
+    "promise": "7.1.1"
   },
   "devDependencies": {
     "eslint": "2.7.0",
     "highlightjs": "8.7.0",
     "json-loader": "0.5.4",
     "scratch-blocks": "git+https://git@github.com/LLK/scratch-blocks.git",
+    "scratch-render": "git+https://git@github.com/LLK/scratch-render.git",
     "tap": "5.7.1",
     "webpack": "1.13.0",
     "webpack-dev-server": "1.14.1"
diff --git a/playground/index.html b/playground/index.html
index e38c10793..bcfb4426f 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -735,7 +735,7 @@
     <script src="../node_modules/scratch-blocks/blocks_compressed_vertical.js"></script>
     <script src="../node_modules/scratch-blocks/msg/messages.js"></script>
     <!-- Renderer -->
-    <script src="../node_modules/scratch-render-webgl/build/render-webgl.js"></script>
+    <script src="../node_modules/scratch-render/render.js"></script>
     <!-- VM Worker -->
     <script src="../vm.worker.js"></script>
     <!-- Playground -->
diff --git a/src/index.js b/src/index.js
index 965c8cd9a..db99f1d15 100644
--- a/src/index.js
+++ b/src/index.js
@@ -108,7 +108,7 @@ VirtualMachine.prototype.animationFrame = function () {
  */
 if (ENV_WORKER) {
     self.importScripts(
-        './node_modules/scratch-render-webgl/build/render-webgl-worker.js'
+        './node_modules/scratch-render/render-worker.js'
     );
     self.renderer = new self.RenderWebGLWorker();
     self.vmInstance = new VirtualMachine();

From 0b826c0dc97fb060c08847c208256ad566ee70b6 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Mon, 8 Aug 2016 15:44:55 -0400
Subject: [PATCH 37/40] Remove unused Promise in scratch3_operators

---
 src/blocks/scratch3_operators.js | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js
index d3a7f1921..6ddae6e9a 100644
--- a/src/blocks/scratch3_operators.js
+++ b/src/blocks/scratch3_operators.js
@@ -1,5 +1,3 @@
-var Promise = require('promise');
-
 function Scratch3OperatorsBlocks(runtime) {
     /**
      * The runtime instantiating this block package.

From a6ad34f0029f6c8abde9c482e3f13d7b68790bfc Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Mon, 8 Aug 2016 16:44:48 -0400
Subject: [PATCH 38/40] Add primitive for `math_angle`, fixing point in
 direction

---
 src/blocks/scratch3_operators.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/blocks/scratch3_operators.js b/src/blocks/scratch3_operators.js
index 6ddae6e9a..fa43950ae 100644
--- a/src/blocks/scratch3_operators.js
+++ b/src/blocks/scratch3_operators.js
@@ -15,6 +15,7 @@ Scratch3OperatorsBlocks.prototype.getPrimitives = function() {
         'math_number': this.number,
         'math_positive_number': this.number,
         'math_whole_number': this.number,
+        'math_angle': this.number,
         'text': this.text,
         'operator_add': this.add,
         'operator_subtract': this.subtract,

From 67c3ceff86623cc56f918f93dc356e4865546970 Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Mon, 8 Aug 2016 16:47:52 -0400
Subject: [PATCH 39/40] Update clone's scale to match renderer update

---
 src/sprites/clone.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index 6a0f22fee..aa168291c 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -129,7 +129,7 @@ Clone.prototype.setSize = function (size) {
     // Keep size between 5% and 535%.
     this.size = MathUtil.clamp(size, 5, 535);
     self.renderer.updateDrawableProperties(this.drawableID, {
-        scale: this.size
+        scale: [this.size, this.size]
     });
 };
 

From 2c031d87f6a24d54c9b107538f5fdbdcad895d0b Mon Sep 17 00:00:00 2001
From: Tim Mickel <tim.mickel@gmail.com>
Date: Mon, 8 Aug 2016 18:29:44 -0400
Subject: [PATCH 40/40] Make renderer/self nullable in the Clone

Fixes a test issue where renderer is not necessarily defined.
---
 src/sprites/clone.js | 73 +++++++++++++++++++++++++++++++-------------
 1 file changed, 51 insertions(+), 22 deletions(-)

diff --git a/src/sprites/clone.js b/src/sprites/clone.js
index aa168291c..8370ac73e 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/clone.js
@@ -9,20 +9,37 @@ var Target = require('../engine/target');
  */
 function Clone(spriteBlocks) {
     Target.call(this, spriteBlocks);
+    /**
+     * Reference to the global renderer for this VM, if one exists.
+     * @type {?RenderWebGLWorker}
+     */
+    this.renderer = null;
+    // If this is not true, there is no renderer (e.g., running in a test env).
+    if (typeof self !== 'undefined' && self.renderer) {
+        // Pull from `self.renderer`.
+        this.renderer = self.renderer;
+    }
+    /**
+     * ID of the drawable for this clone returned by the renderer, if rendered.
+     * @type {?Number}
+     */
     this.drawableID = null;
+
     this.initDrawable();
 }
 util.inherits(Clone, Target);
 
 /**
- * Create a clone's drawable with the renderer.
+ * Create a clone's drawable with the this.renderer.
  */
 Clone.prototype.initDrawable = function () {
-    var createPromise = self.renderer.createDrawable();
-    var instance = this;
-    createPromise.then(function (id) {
-        instance.drawableID = id;
-    });
+    if (this.renderer) {
+        var createPromise = this.renderer.createDrawable();
+        var instance = this;
+        createPromise.then(function (id) {
+            instance.drawableID = id;
+        });
+    }
 };
 
 // Clone-level properties.
@@ -79,9 +96,11 @@ Clone.prototype.effects = {
 Clone.prototype.setXY = function (x, y) {
     this.x = x;
     this.y = y;
-    self.renderer.updateDrawableProperties(this.drawableID, {
-        position: [this.x, this.y]
-    });
+    if (this.renderer) {
+        this.renderer.updateDrawableProperties(this.drawableID, {
+            position: [this.x, this.y]
+        });
+    }
 };
 
 /**
@@ -91,9 +110,11 @@ Clone.prototype.setXY = function (x, y) {
 Clone.prototype.setDirection = function (direction) {
     // Keep direction between -179 and +180.
     this.direction = MathUtil.wrapClamp(direction, -179, 180);
-    self.renderer.updateDrawableProperties(this.drawableID, {
-        direction: this.direction
-    });
+    if (this.renderer) {
+        this.renderer.updateDrawableProperties(this.drawableID, {
+            direction: this.direction
+        });
+    }
 };
 
 /**
@@ -116,9 +137,11 @@ Clone.prototype.setSay = function (type, message) {
  */
 Clone.prototype.setVisible = function (visible) {
     this.visible = visible;
-    self.renderer.updateDrawableProperties(this.drawableID, {
-        visible: this.visible
-    });
+    if (this.renderer) {
+        this.renderer.updateDrawableProperties(this.drawableID, {
+            visible: this.visible
+        });
+    }
 };
 
 /**
@@ -128,9 +151,11 @@ Clone.prototype.setVisible = function (visible) {
 Clone.prototype.setSize = function (size) {
     // Keep size between 5% and 535%.
     this.size = MathUtil.clamp(size, 5, 535);
-    self.renderer.updateDrawableProperties(this.drawableID, {
-        scale: [this.size, this.size]
-    });
+    if (this.renderer) {
+        this.renderer.updateDrawableProperties(this.drawableID, {
+            scale: [this.size, this.size]
+        });
+    }
 };
 
 /**
@@ -140,9 +165,11 @@ Clone.prototype.setSize = function (size) {
  */
 Clone.prototype.setEffect = function (effectName, value) {
     this.effects[effectName] = value;
-    var props = {};
-    props[effectName] = this.effects[effectName];
-    self.renderer.updateDrawableProperties(this.drawableID, props);
+    if (this.renderer) {
+        var props = {};
+        props[effectName] = this.effects[effectName];
+        this.renderer.updateDrawableProperties(this.drawableID, props);
+    }
 };
 
 /**
@@ -152,7 +179,9 @@ Clone.prototype.clearEffects = function () {
     for (var effectName in this.effects) {
         this.effects[effectName] = 0;
     }
-    self.renderer.updateDrawableProperties(this.drawableID, this.effects);
+    if (this.renderer) {
+        this.renderer.updateDrawableProperties(this.drawableID, this.effects);
+    }
 };
 
 module.exports = Clone;