From cac52020a4c5fdd8af0a54758130764760e38edf Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 17:18:20 -0500 Subject: [PATCH 01/42] Add Export To Rendered Target --- src/sprites/rendered-target.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 50ad7e8f5..57eab25b0 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -64,6 +64,21 @@ RenderedTarget.prototype.initDrawable = function () { } }; +RenderedTarget.prototype.export = function () { + var result = new Object(); + var notSaved = ["renderer","effects","sprite","drawableID","runtime"]; + for (x in this) { + if (typeof(this[x]) === "function") { + continue; + } + if (notSaved.indexOf(x) == -1) { + result[x] = this[x]; + } + } + result.sprite = this.sprite.export(); + return result; +}; + /** * Whether this represents an "original" non-clone rendered-target for a sprite, * i.e., created by the editor and not clone blocks. From 0f4e80cbfcbaaec25016246a216d0d1615b7ae7f Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 17:23:28 -0500 Subject: [PATCH 02/42] Add Export To Sprite --- src/sprites/sprite.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index 0f189206a..479647c3f 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -54,4 +54,18 @@ Sprite.prototype.createClone = function () { return newClone; }; +Sprite.prototype.export = function () { + var result = new Object(); + var notSaved = ["clones","runtime"]; + for (x in this) { + if (typeof(this[x]) === "function") { + continue; + } + if (notSaved.indexOf(x) == -1) { + result[x] = this[x]; + } + } + return result; +}; + module.exports = Sprite; From 16449ae73dcb62ea0041dfd3ba1564f1476d4d1a Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 17:50:31 -0500 Subject: [PATCH 03/42] Add Loading And Exporting --- src/index.js | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 4ed724f39..f7473a723 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,8 @@ var EventEmitter = require('events'); var util = require('util'); - +var Blocks = require('../engine/blocks'); +var RenderedTarget = require('../sprites/rendered-target'); +var Sprite = require('../sprites/sprite'); var Runtime = require('./engine/runtime'); var sb2import = require('./import/sb2import'); @@ -154,6 +156,41 @@ VirtualMachine.prototype.loadProject = function (json) { this.runtime.setEditingTarget(this.editingTarget); }; +VirtualMachine.prototype.exportToJson = function () { + this.clear(); + var obj = new Object(); + var i = 0; + obj.sprites = []; + for (; i < this.runtime.targets; i++) { + obj.sprites.push(this.runtime.targets[i].export()); + } + obj.meta.name = "WIP"; + obj.meta.useragent = navigator.userAgent; + return JSON.stringify(obj); +} + +VirtualMachine.prototype.importFromJson = function (json) { + var obj = JSON.parse(json); + var i = 0; + for (; i < obj.sprites.length; i++) { + var blocks = new Blocks(); + var sprite = new Sprite(blocks, this.runtime); + for (y in obj.sprites[i].sprite) { + sprite[y] = obj.sprites[i].sprite[y]; + } + var target = sprite.createClone(); + this.runtime.targets.push(target); + for (x in obj.sprites[i]) { + target[x] = obj.sprites[y]; + } + } + this.editingTarget = this.runtime.targets[1]; + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); +} + /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. * @param {?string} json JSON string representing the sprite. From 51d829786c62b4ab9cfce8fe178ae213b511e551 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 18:07:50 -0500 Subject: [PATCH 04/42] Update index.js --- src/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.js b/src/index.js index f7473a723..29e752289 100644 --- a/src/index.js +++ b/src/index.js @@ -181,6 +181,9 @@ VirtualMachine.prototype.importFromJson = function (json) { var target = sprite.createClone(); this.runtime.targets.push(target); for (x in obj.sprites[i]) { + if (x == "sprite") { + continue; + } target[x] = obj.sprites[y]; } } From 1777ab6a210c273c575949be951a87397ba5404d Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 18:09:28 -0500 Subject: [PATCH 05/42] Update index.js --- src/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 29e752289..0dcdeadfc 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,7 @@ var EventEmitter = require('events'); var util = require('util'); -var Blocks = require('../engine/blocks'); -var RenderedTarget = require('../sprites/rendered-target'); -var Sprite = require('../sprites/sprite'); +var Blocks = require('./engine/blocks'); +var Sprite = require('./sprites/sprite'); var Runtime = require('./engine/runtime'); var sb2import = require('./import/sb2import'); From f4959305abfc007d2a97b6515d1cad5277f05e92 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 18:14:12 -0500 Subject: [PATCH 06/42] Update index.js --- src/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index 0dcdeadfc..8d7a9f74c 100644 --- a/src/index.js +++ b/src/index.js @@ -163,10 +163,10 @@ VirtualMachine.prototype.exportToJson = function () { for (; i < this.runtime.targets; i++) { obj.sprites.push(this.runtime.targets[i].export()); } - obj.meta.name = "WIP"; + obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; return JSON.stringify(obj); -} +}; VirtualMachine.prototype.importFromJson = function (json) { var obj = JSON.parse(json); @@ -174,16 +174,18 @@ VirtualMachine.prototype.importFromJson = function (json) { for (; i < obj.sprites.length; i++) { var blocks = new Blocks(); var sprite = new Sprite(blocks, this.runtime); + var y = null; for (y in obj.sprites[i].sprite) { sprite[y] = obj.sprites[i].sprite[y]; } var target = sprite.createClone(); this.runtime.targets.push(target); + var x = null; for (x in obj.sprites[i]) { - if (x == "sprite") { + if (x === 'sprite') { continue; } - target[x] = obj.sprites[y]; + target[x] = obj.sprites[x]; } } this.editingTarget = this.runtime.targets[1]; @@ -191,7 +193,7 @@ VirtualMachine.prototype.importFromJson = function (json) { this.emitTargetsUpdate(); this.emitWorkspaceUpdate(); this.runtime.setEditingTarget(this.editingTarget); -} +}; /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. From 1980fbc98d22f8a85ca19015ab8daa751531c549 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 18:53:38 -0500 Subject: [PATCH 07/42] Update rendered-target.js --- src/sprites/rendered-target.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 57eab25b0..cd2fe42d7 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -66,12 +66,13 @@ RenderedTarget.prototype.initDrawable = function () { RenderedTarget.prototype.export = function () { var result = new Object(); - var notSaved = ["renderer","effects","sprite","drawableID","runtime"]; + var notSaved = ['renderer', 'effects', 'sprite', 'drawableID', 'runtime']; + var x = null; for (x in this) { - if (typeof(this[x]) === "function") { + if (typeof this[x] === 'function') { continue; } - if (notSaved.indexOf(x) == -1) { + if (notSaved.indexOf(x) === -1) { result[x] = this[x]; } } From 74b8378a7c5c0ea130e2b7b80605438caaf7ca89 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 18:55:38 -0500 Subject: [PATCH 08/42] Update sprite.js --- src/sprites/sprite.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js index 479647c3f..bdfbd20a2 100644 --- a/src/sprites/sprite.js +++ b/src/sprites/sprite.js @@ -56,12 +56,13 @@ Sprite.prototype.createClone = function () { Sprite.prototype.export = function () { var result = new Object(); - var notSaved = ["clones","runtime"]; + var notSaved = ['clones', 'runtime']; + var x = null; for (x in this) { - if (typeof(this[x]) === "function") { + if (typeof this[x] === 'function') { continue; } - if (notSaved.indexOf(x) == -1) { + if (notSaved.indexOf(x) === -1) { result[x] = this[x]; } } From 72b44b3ec713a1be62525b91dd65c29719e6e53a Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 29 Nov 2016 18:57:35 -0500 Subject: [PATCH 09/42] Update rendered-target.js --- src/sprites/rendered-target.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index cd2fe42d7..c237f8451 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -66,7 +66,7 @@ RenderedTarget.prototype.initDrawable = function () { RenderedTarget.prototype.export = function () { var result = new Object(); - var notSaved = ['renderer', 'effects', 'sprite', 'drawableID', 'runtime']; + var notSaved = ['renderer', 'effects', 'sprite', 'drawableID', 'runtime', 'id']; var x = null; for (x in this) { if (typeof this[x] === 'function') { From dd8675991502b50c0e0f7392555fcc87c3d70da2 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 15:34:01 -0500 Subject: [PATCH 10/42] Update rendered-target.js --- src/sprites/rendered-target.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index c237f8451..b78e09bee 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -64,9 +64,9 @@ RenderedTarget.prototype.initDrawable = function () { } }; -RenderedTarget.prototype.export = function () { +RenderedTarget.prototype.toJSON = function () { var result = new Object(); - var notSaved = ['renderer', 'effects', 'sprite', 'drawableID', 'runtime', 'id']; + var notSaved = ['renderer', 'effects', 'sprite', 'drawableID', 'runtime', 'id', 'blocks']; var x = null; for (x in this) { if (typeof this[x] === 'function') { From 0707c4bcc8668553d06df9fa05894cd09b58a7b9 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 15:35:35 -0500 Subject: [PATCH 11/42] Update index.js --- src/index.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/index.js b/src/index.js index 8d7a9f74c..0fb9efbf3 100644 --- a/src/index.js +++ b/src/index.js @@ -156,19 +156,16 @@ VirtualMachine.prototype.loadProject = function (json) { }; VirtualMachine.prototype.exportToJson = function () { - this.clear(); var obj = new Object(); var i = 0; - obj.sprites = []; - for (; i < this.runtime.targets; i++) { - obj.sprites.push(this.runtime.targets[i].export()); - } + obj.sprites = this.runtime.targets; obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; return JSON.stringify(obj); }; VirtualMachine.prototype.importFromJson = function (json) { + this.clear(); var obj = JSON.parse(json); var i = 0; for (; i < obj.sprites.length; i++) { From a520990a80c5d1fc9acd3e7c591041344b44c0a1 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 15:37:35 -0500 Subject: [PATCH 12/42] Update index.js --- src/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.js b/src/index.js index 0fb9efbf3..a9e1fe329 100644 --- a/src/index.js +++ b/src/index.js @@ -157,7 +157,6 @@ VirtualMachine.prototype.loadProject = function (json) { VirtualMachine.prototype.exportToJson = function () { var obj = new Object(); - var i = 0; obj.sprites = this.runtime.targets; obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; From 424b41ac315b0fc62fc32b42a67e34712aa80015 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 15:41:55 -0500 Subject: [PATCH 13/42] Pretty Print --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index a9e1fe329..204970429 100644 --- a/src/index.js +++ b/src/index.js @@ -160,7 +160,7 @@ VirtualMachine.prototype.exportToJson = function () { obj.sprites = this.runtime.targets; obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; - return JSON.stringify(obj); + return JSON.stringify(obj, null, 2); }; VirtualMachine.prototype.importFromJson = function (json) { From 34d2676db36bf682e73c96186e30641a2625d9af Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 15:57:56 -0500 Subject: [PATCH 14/42] Update index.js --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index 204970429..149f06798 100644 --- a/src/index.js +++ b/src/index.js @@ -158,6 +158,7 @@ VirtualMachine.prototype.loadProject = function (json) { VirtualMachine.prototype.exportToJson = function () { var obj = new Object(); obj.sprites = this.runtime.targets; + obj.meta = new Object(); obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; return JSON.stringify(obj, null, 2); From f2a08b466d56cb4da4ad3467a8417d86d9ce9032 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 16:03:52 -0500 Subject: [PATCH 15/42] Update Names --- src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 149f06798..4e6878e07 100644 --- a/src/index.js +++ b/src/index.js @@ -155,7 +155,7 @@ VirtualMachine.prototype.loadProject = function (json) { this.runtime.setEditingTarget(this.editingTarget); }; -VirtualMachine.prototype.exportToJson = function () { +VirtualMachine.prototype.toJSON = function () { var obj = new Object(); obj.sprites = this.runtime.targets; obj.meta = new Object(); @@ -164,7 +164,7 @@ VirtualMachine.prototype.exportToJson = function () { return JSON.stringify(obj, null, 2); }; -VirtualMachine.prototype.importFromJson = function (json) { +VirtualMachine.prototype.fromJSON = function (json) { this.clear(); var obj = JSON.parse(json); var i = 0; From 05a827c0eaf37a1f7be60c2bff3dbf86b129006d Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Fri, 2 Dec 2016 16:30:34 -0500 Subject: [PATCH 16/42] Update index.js --- src/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/index.js b/src/index.js index 4e6878e07..d6fb1107c 100644 --- a/src/index.js +++ b/src/index.js @@ -170,9 +170,16 @@ VirtualMachine.prototype.fromJSON = function (json) { var i = 0; for (; i < obj.sprites.length; i++) { var blocks = new Blocks(); + var z = null; + for (z in obj.sprites[i].sprite.blocks) { + blocks[z] = obj.sprites[i].sprite.blocks[z]; + } var sprite = new Sprite(blocks, this.runtime); var y = null; for (y in obj.sprites[i].sprite) { + if (y === 'blocks') { + continue; + } sprite[y] = obj.sprites[i].sprite[y]; } var target = sprite.createClone(); From 39e71aa4f41a60e4245fe14cb545334cc8c6046a Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 3 Dec 2016 13:25:15 -0500 Subject: [PATCH 17/42] Add To Playground --- playground/index.html | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/playground/index.html b/playground/index.html index 4c0a454bc..b981ef2f4 100644 --- a/playground/index.html +++ b/playground/index.html @@ -52,9 +52,9 @@

- +   - +

@@ -70,18 +70,17 @@ From 7fe597c9f98058579b8a16f8769e2a4dafc88927 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 3 Dec 2016 16:29:56 -0500 Subject: [PATCH 18/42] Now Actually loads Rendered Target Properties --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index d6fb1107c..d25ce3c78 100644 --- a/src/index.js +++ b/src/index.js @@ -189,7 +189,7 @@ VirtualMachine.prototype.fromJSON = function (json) { if (x === 'sprite') { continue; } - target[x] = obj.sprites[x]; + target[x] = obj.sprites[i][x]; } } this.editingTarget = this.runtime.targets[1]; From c4750d64b6e15a32fccc004cc54995837f2f3be2 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 3 Dec 2016 16:50:06 -0500 Subject: [PATCH 19/42] Fix Drawable Properties Not Getting Updated --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index d25ce3c78..1d64030d1 100644 --- a/src/index.js +++ b/src/index.js @@ -191,6 +191,7 @@ VirtualMachine.prototype.fromJSON = function (json) { } target[x] = obj.sprites[i][x]; } + target.updateAllDrawablePropertie(); } this.editingTarget = this.runtime.targets[1]; // Update the VM user's knowledge of targets and blocks on the workspace. From a58bcb93e64cf0b941574b95aeea67d06e81ae6d Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 4 Dec 2016 08:39:22 -0500 Subject: [PATCH 20/42] Update index.js --- src/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 1d64030d1..73f2e7886 100644 --- a/src/index.js +++ b/src/index.js @@ -155,13 +155,17 @@ VirtualMachine.prototype.loadProject = function (json) { this.runtime.setEditingTarget(this.editingTarget); }; +VirtualMachine.prototype.toPrettyJSON = function () { + return JSON.stringify(this.toJSON(), null, 4); +} + VirtualMachine.prototype.toJSON = function () { var obj = new Object(); obj.sprites = this.runtime.targets; obj.meta = new Object(); obj.meta.name = 'WIP'; obj.meta.useragent = navigator.userAgent; - return JSON.stringify(obj, null, 2); + return obj; }; VirtualMachine.prototype.fromJSON = function (json) { From 532df2079b72ca4ed01157169ae2404fb1796c94 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sun, 4 Dec 2016 08:39:46 -0500 Subject: [PATCH 21/42] Update index.html --- playground/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/index.html b/playground/index.html index b981ef2f4..13c345b25 100644 --- a/playground/index.html +++ b/playground/index.html @@ -72,7 +72,7 @@ diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 53a7cfc4d..4278656f2 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -15,8 +15,8 @@ const specMap = require('./sb2_specmap'); const Variable = require('../engine/variable'); const List = require('../engine/list'); -const loadCostume = require('./load-costume.js'); -const loadSound = require('./load-sound.js'); +const loadCostume = require('../import/load-costume.js'); +const loadSound = require('../import/load-sound.js'); /** * Convert a Scratch 2.0 procedure string (e.g., "my_procedure %s %b %n") diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 7543b4cd1..4eb06dd5e 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -4,27 +4,26 @@ * JSON and then generates all needed scratch-vm runtime structures. */ -var package = require('../../package.json'); -var Blocks = require('../engine/blocks'); -var RenderedTarget = require('../sprites/rendered-target'); -var Sprite = require('../sprites/sprite'); -var Variable = require('../engine/variable'); -var List = require('../engine/list'); +const vmPackage = require('../../package.json'); +const Blocks = require('../engine/blocks'); +const Sprite = require('../sprites/sprite'); +const Variable = require('../engine/variable'); +const List = require('../engine/list'); /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. * @return {string} Serialized runtime instance. */ -var serialize = function (runtime) { +const serialize = function (runtime) { // Fetch targets - var obj = Object.create(null); + const obj = Object.create(null); obj.targets = runtime.targets; // Assemble metadata - var meta = Object.create(null); + const meta = Object.create(null); meta.semver = '3.0.0'; - meta.vm = package.version; + meta.vm = vmPackage.version; // Attach full user agent string to metadata if available meta.agent = null; @@ -37,36 +36,36 @@ var serialize = function (runtime) { /** * Parse a single "Scratch object" and create all its in-memory VM objects. - * @param {!Object} object From-JSON "Scratch object:" sprite, stage, watcher. + * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher. * @param {!Runtime} runtime Runtime object to load all structures into. * @return {?Target} Target created (stage or sprite). */ -var parseScratchObject = function (object, runtime) { +const parseScratchObject = function (object, runtime) { if (!object.hasOwnProperty('name')) { // Watcher/monitor - skip this object until those are implemented in VM. // @todo return; } // Blocks container for this object. - var blocks = new Blocks(); + const blocks = new Blocks(); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. - var sprite = new Sprite(blocks, runtime); + const sprite = new Sprite(blocks, runtime); // Sprite/stage name from JSON. if (object.hasOwnProperty('name')) { sprite.name = object.name; } if (object.hasOwnProperty('blocks')) { - for (blockId in object.blocks) { + for (let blockId in object.blocks) { blocks.createBlock(object.blocks[blockId]); } - console.log(blocks); + // console.log(blocks); } // Costumes from JSON. if (object.hasOwnProperty('costumes') || object.hasOwnProperty('costume')) { - for (var i = 0; i < object.costumeCount; i++) { - var costume = object.costumes[i]; + for (let i = 0; i < object.costumeCount; i++) { + const costume = object.costumes[i]; // @todo: Make sure all the relevant metadata is being pulled out. sprite.costumes.push({ skin: costume.skin, @@ -79,8 +78,8 @@ var parseScratchObject = function (object, runtime) { } // Sounds from JSON if (object.hasOwnProperty('sounds')) { - for (var s = 0; s < object.sounds.length; s++) { - var sound = object.sounds[s]; + for (let s = 0; s < object.sounds.length; s++) { + const sound = object.sounds[s]; sprite.sounds.push({ format: sound.format, fileUrl: sound.fileUrl, @@ -93,13 +92,13 @@ var parseScratchObject = function (object, runtime) { } } // Create the first clone, and load its run-state from JSON. - var target = sprite.createClone(); + const target = sprite.createClone(); // Add it to the runtime's list of targets. runtime.targets.push(target); // Load target properties from JSON. if (object.hasOwnProperty('variables')) { - for (var j = 0; j < object.variables.length; j++) { - var variable = object.variables[j]; + for (let j = 0; j < object.variables.length; j++) { + const variable = object.variables[j]; target.variables[variable.name] = new Variable( variable.name, variable.value, @@ -108,8 +107,8 @@ var parseScratchObject = function (object, runtime) { } } if (object.hasOwnProperty('lists')) { - for (var k = 0; k < object.lists.length; k++) { - var list = object.lists[k]; + for (let k = 0; k < object.lists.length; k++) { + const list = object.lists[k]; // @todo: monitor properties. target.lists[list.listName] = new List( list.listName, @@ -143,8 +142,8 @@ var parseScratchObject = function (object, runtime) { } target.updateAllDrawableProperties(); - console.log("returning target:"); - console.log(target); + // console.log('returning target:'); + // console.log(target); return target; }; @@ -154,8 +153,8 @@ var parseScratchObject = function (object, runtime) { * @param {string} json Stringified JSON representation of a VM runtime. * @param {Runtime} runtime Runtime instance */ -var deserialize = function (json, runtime) { - for (var i = 0; i < json.targets.length; i++) { +const deserialize = function (json, runtime) { + for (let i = 0; i < json.targets.length; i++) { parseScratchObject(json.targets[i], runtime); } }; diff --git a/src/virtual-machine.js b/src/virtual-machine.js index a69539a06..745d9089a 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -145,22 +145,7 @@ class VirtualMachine extends EventEmitter { */ loadProject (json) { // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0. - return this.fromJSON(json, this.runtime).then(targets => { - this.clear(); - for (let n = 0; n < targets.length; n++) { - if (targets[n] !== null) { - this.runtime.targets.push(targets[n]); - targets[n].updateAllDrawableProperties(); - } - } - // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; - - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); - }); + return this.fromJSON(json); } /** @@ -181,7 +166,7 @@ class VirtualMachine extends EventEmitter { } /** - * return a project in a Scratch 3.0 JSON representation. + * @returns {string} Project in a Scratch 3.0 JSON representation. */ saveProjectSb3 () { // @todo: Handle other formats, e.g., Scratch 1.4, Scratch 2.0. @@ -199,6 +184,7 @@ class VirtualMachine extends EventEmitter { /** * Load a project from a Scratch JSON representation. * @param {string} json JSON string representing a project. + * @returns {Promise} Promise that resolves after the project has loaded */ fromJSON (json) { // Clear the current runtime @@ -214,38 +200,46 @@ class VirtualMachine extends EventEmitter { // @todo This is an extremely naïve / dangerous way of determining version. // See `scratch-parser` for a more sophisticated validation // methodology that should be adapted for use here - if ((typeof json.meta !== 'undefined') && (typeof json.meta.semver !== 'undefined') ) { - sb3.deserialize(json, this.runtime); + let deserializer; + if ((typeof json.meta !== 'undefined') && (typeof json.meta.semver !== 'undefined')) { + deserializer = sb3; } else { - sb2.deserialize(json, this.runtime); + deserializer = sb2; } - // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; + return deserializer.deserialize(json, this.runtime).then(targets => { + this.clear(); + for (let n = 0; n < targets.length; n++) { + if (targets[n] !== null) { + this.runtime.targets.push(targets[n]); + targets[n].updateAllDrawableProperties(); + } + } + // Select the first target for editing, e.g., the first sprite. + this.editingTarget = this.runtime.targets[1]; - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); } /** * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format. * @param {string} json JSON string representing the sprite. + * @returns {Promise} Promise that resolves after the sprite is added */ addSprite2 (json) { // Select new sprite. - this.editingTarget = sb2.deserialize(json, this.runtime, true); - /* @todo Promisify this - .then(targets => { - this.runtime.targets.push(targets[0]); - this.editingTarget = targets[0]; - }) - */ - // Update the VM user's knowledge of targets and blocks on the workspace. - this.emitTargetsUpdate(); - this.emitWorkspaceUpdate(); - this.runtime.setEditingTarget(this.editingTarget); + return sb2.deserialize(json, this.runtime, true).then(targets => { + this.runtime.targets.push(targets[0]); + this.editingTarget = targets[0]; + // Update the VM user's knowledge of targets and blocks on the workspace. + this.emitTargetsUpdate(); + this.emitWorkspaceUpdate(); + this.runtime.setEditingTarget(this.editingTarget); + }); } /** diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 547b645ea..2602783fd 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -5,7 +5,7 @@ const extract = require('../fixtures/extract'); const renderedTarget = require('../../src/sprites/rendered-target'); const runtime = require('../../src/engine/runtime'); -const sb2 = require('../../src/import/sb2import'); +const sb2 = require('../../src/serialization/sb2'); test('spec', t => { t.type(sb2, 'function'); diff --git a/test/unit/serialization_sb2.js b/test/unit/serialization_sb2.js index 530311d33..7a0bec03f 100644 --- a/test/unit/serialization_sb2.js +++ b/test/unit/serialization_sb2.js @@ -1,51 +1,53 @@ -var path = require('path'); -var test = require('tap').test; -var extract = require('../fixtures/extract'); +const path = require('path'); +const test = require('tap').test; +const extract = require('../fixtures/extract'); -var renderedTarget = require('../../src/sprites/rendered-target'); -var runtime = require('../../src/engine/runtime'); -var sb2 = require('../../src/serialization/sb2'); +const RenderedTarget = require('../../src/sprites/rendered-target'); +const Runtime = require('../../src/engine/runtime'); +const sb2 = require('../../src/serialization/sb2'); -test('spec', function (t) { +test('spec', t => { t.type(sb2, 'object'); t.type(sb2.deserialize, 'function'); t.end(); }); -test('default', function (t) { +test('default', t => { // Get SB2 JSON (string) - var uri = path.resolve(__dirname, '../fixtures/default.sb2'); - var file = extract(uri); + const uri = path.resolve(__dirname, '../fixtures/default.sb2'); + const file = extract(uri); // Create runtime instance & load SB2 into it - var rt = new runtime(); - sb2.deserialize(file, rt); + const rt = new Runtime(); + sb2.deserialize(file, rt).then(targets => { + console.dir(targets); - // Test - t.type(file, 'string'); - t.type(rt, 'object'); - t.type(rt.targets, 'object'); + // Test + t.type(file, 'string'); + t.type(rt, 'object'); + t.type(targets, 'object'); - t.ok(rt.targets[0] instanceof renderedTarget); - t.type(rt.targets[0].id, 'string'); - t.type(rt.targets[0].blocks, 'object'); - t.type(rt.targets[0].variables, 'object'); - t.type(rt.targets[0].lists, 'object'); + t.ok(targets[0] instanceof RenderedTarget); + t.type(targets[0].id, 'string'); + t.type(targets[0].blocks, 'object'); + t.type(targets[0].variables, 'object'); + t.type(targets[0].lists, 'object'); - t.equal(rt.targets[0].isOriginal, true); - t.equal(rt.targets[0].currentCostume, 0); - t.equal(rt.targets[0].isOriginal, true); - t.equal(rt.targets[0].isStage, true); + t.equal(targets[0].isOriginal, true); + t.equal(targets[0].currentCostume, 0); + t.equal(targets[0].isOriginal, true); + t.equal(targets[0].isStage, true); - t.ok(rt.targets[1] instanceof renderedTarget); - t.type(rt.targets[1].id, 'string'); - t.type(rt.targets[1].blocks, 'object'); - t.type(rt.targets[1].variables, 'object'); - t.type(rt.targets[1].lists, 'object'); + t.ok(targets[1] instanceof RenderedTarget); + t.type(targets[1].id, 'string'); + t.type(targets[1].blocks, 'object'); + t.type(targets[1].variables, 'object'); + t.type(targets[1].lists, 'object'); - t.equal(rt.targets[1].isOriginal, true); - t.equal(rt.targets[1].currentCostume, 0); - t.equal(rt.targets[1].isOriginal, true); - t.equal(rt.targets[1].isStage, false); - t.end(); + t.equal(targets[1].isOriginal, true); + t.equal(targets[1].currentCostume, 0); + t.equal(targets[1].isOriginal, true); + t.equal(targets[1].isStage, false); + t.end(); + }); }); diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js index 0ec56284d..761b0b54c 100644 --- a/test/unit/serialization_sb3.js +++ b/test/unit/serialization_sb3.js @@ -1,19 +1,19 @@ -var test = require('tap').test; -var VirtualMachine = require('../../src/index'); -var sb3 = require('../../src/serialization/sb3'); +const test = require('tap').test; +const VirtualMachine = require('../../src/index'); +const sb3 = require('../../src/serialization/sb3'); -test('serialize', function (t) { - var vm = new VirtualMachine(); +test('serialize', t => { + const vm = new VirtualMachine(); vm.fromJSON(JSON.stringify(require('../fixtures/demo.json'))); - var result = sb3.serialize(vm.runtime); + const result = sb3.serialize(vm.runtime); console.dir(JSON.stringify(result)); - // @todo Analyize + // @todo Analyze t.end(); }); -test('deserialize', function (t) { - var vm = new VirtualMachine(); - var result = sb3.deserialize('', vm.runtime); +test('deserialize', t => { + const vm = new VirtualMachine(); + const result = sb3.deserialize('', vm.runtime); // @todo Analyize t.end(); }); From b2ea201707e6fc1c4b4780b1959570159cca5979 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Wed, 26 Apr 2017 17:45:36 -0400 Subject: [PATCH 33/42] Don't include asset data in exported JSON --- src/sprites/rendered-target.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 9880d4750..d48bbd24f 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -763,11 +763,23 @@ class RenderedTarget extends Target { this.dragging = false; } + assetListToJSON (assetList) { + const exclude = ['data', 'url']; + return assetList.map(asset => + Object.keys(asset).reduce((rAsset, prop) => { + if (exclude.indexOf(prop) === -1) rAsset[prop] = asset[prop]; + return rAsset; + }, {}) + ); + } + /** * Serialize sprite info, used when emitting events about the sprite * @returns {object} Sprite data as a simple object */ toJSON () { + const costumes = this.assetListToJSON(this.getCostumes()); + const sounds = this.assetListToJSON(this.getSounds()); return { id: this.id, name: this.getName(), @@ -777,15 +789,15 @@ class RenderedTarget extends Target { size: this.size, direction: this.direction, draggable: this.draggable, - costume: this.getCurrentCostume(), - costumeCount: this.getCostumes().length, + costume: costumes[this.currentCostume], + costumeCount: costumes.length, visible: this.visible, rotationStyle: this.rotationStyle, blocks: this.blocks._blocks, variables: this.variables, lists: this.lists, - costumes: this.getCostumes(), - sounds: this.getSounds() + costumes: costumes, + sounds: sounds }; } From c9ce6776aa9f084dee1fa5bb71da7f6c73f83b47 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 27 Apr 2017 17:08:06 -0400 Subject: [PATCH 34/42] Get SB3 imports working For now, store the md5 + extension on costumes in the VM. This way we can load them the same way as we load SB2 assets. --- src/import/load-costume.js | 1 + src/serialization/sb3.js | 72 +++++++++++++++++----------------- src/sprites/rendered-target.js | 1 + 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 5d9fdc1d5..83ef78006 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -13,6 +13,7 @@ const log = require('../util/log'); * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error. */ const loadCostume = function (md5ext, costume, runtime) { + costume.md5ext = md5ext; if (!runtime.storage) { log.error('No storage module present; cannot load costume asset: ', md5ext); return Promise.resolve(costume); diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 4eb06dd5e..1f46ad94d 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -10,6 +10,9 @@ const Sprite = require('../sprites/sprite'); const Variable = require('../engine/variable'); const List = require('../engine/list'); +const loadCostume = require('../import/load-costume.js'); +const loadSound = require('../import/load-sound.js'); + /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. @@ -63,38 +66,33 @@ const parseScratchObject = function (object, runtime) { // console.log(blocks); } // Costumes from JSON. - if (object.hasOwnProperty('costumes') || object.hasOwnProperty('costume')) { - for (let i = 0; i < object.costumeCount; i++) { - const costume = object.costumes[i]; - // @todo: Make sure all the relevant metadata is being pulled out. - sprite.costumes.push({ - skin: costume.skin, - name: costume.name, - bitmapResolution: costume.bitmapResolution, - rotationCenterX: costume.rotationCenterX, - rotationCenterY: costume.rotationCenterY - }); - } - } + const costumePromises = (object.costumes || []).map(costumeSource => { + // @todo: Make sure all the relevant metadata is being pulled out. + const costume = { + skinId: null, + name: costumeSource.name, + bitmapResolution: costumeSource.bitmapResolution, + rotationCenterX: costumeSource.rotationCenterX, + rotationCenterY: costumeSource.rotationCenterY + }; + return loadCostume(costumeSource.md5ext, costume, runtime); + }); // Sounds from JSON - if (object.hasOwnProperty('sounds')) { - for (let s = 0; s < object.sounds.length; s++) { - const sound = object.sounds[s]; - sprite.sounds.push({ - format: sound.format, - fileUrl: sound.fileUrl, - rate: sound.rate, - sampleCount: sound.sampleCount, - soundID: sound.soundID, - name: sound.name, - md5: sound.md5 - }); - } - } + const soundPromises = (object.sounds || []).map(soundSource => { + const sound = { + format: soundSource.format, + fileUrl: soundSource.fileUrl, + rate: soundSource.rate, + sampleCount: soundSource.sampleCount, + soundID: soundSource.soundID, + name: soundSource.name, + md5: soundSource.md5, + data: null + }; + return loadSound(sound, runtime); + }); // Create the first clone, and load its run-state from JSON. const target = sprite.createClone(); - // Add it to the runtime's list of targets. - runtime.targets.push(target); // Load target properties from JSON. if (object.hasOwnProperty('variables')) { for (let j = 0; j < object.variables.length; j++) { @@ -140,11 +138,13 @@ const parseScratchObject = function (object, runtime) { if (object.hasOwnProperty('isStage')) { target.isStage = object.isStage; } - target.updateAllDrawableProperties(); - - // console.log('returning target:'); - // console.log(target); - return target; + Promise.all(costumePromises).then(costumes => { + sprite.costumes = costumes; + }); + Promise.all(soundPromises).then(sounds => { + sprite.sounds = sounds; + }); + return Promise.all(costumePromises.concat(soundPromises)).then(() => target); }; /** @@ -154,9 +154,7 @@ const parseScratchObject = function (object, runtime) { * @param {Runtime} runtime Runtime instance */ const deserialize = function (json, runtime) { - for (let i = 0; i < json.targets.length; i++) { - parseScratchObject(json.targets[i], runtime); - } + return Promise.all(json.targets.map(target => parseScratchObject(target, runtime))); }; module.exports = { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index d48bbd24f..bc863dfe9 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -789,6 +789,7 @@ class RenderedTarget extends Target { size: this.size, direction: this.direction, draggable: this.draggable, + currentCostume: this.currentCostume, costume: costumes[this.currentCostume], costumeCount: costumes.length, visible: this.visible, From f785117e1df9ae288fba891f1a1dc1ba84bd4a34 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 27 Apr 2017 17:49:57 -0400 Subject: [PATCH 35/42] Lint and fix failing tests --- src/serialization/sb2.js | 2 +- src/serialization/sb3.js | 7 ++++--- src/sprites/rendered-target.js | 15 +++++++++++---- src/virtual-machine.js | 7 ++++++- test/fixtures/demo.json | 1 + test/integration/import_sb2.js | 6 ++++-- test/unit/serialization_sb2.js | 6 +++--- test/unit/serialization_sb3.js | 13 ++++++++----- 8 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 test/fixtures/demo.json diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 4278656f2..a54b582f2 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -276,7 +276,7 @@ const parseScratchObject = function (object, runtime, topLevel) { /** * Top-level handler. Parse provided JSON, * and process the top-level object (the stage object). - * @param {!string} json SB2-format JSON to load. + * @param {!object} json SB2-format JSON to load. * @param {!Runtime} runtime Runtime object to load all structures into. * @param {boolean=} optForceSprite If set, treat as sprite (Sprite2). * @return {?Promise} Promise that resolves to the loaded targets when ready. diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 1f46ad94d..b072be199 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -16,7 +16,7 @@ const loadSound = require('../import/load-sound.js'); /** * Serializes the specified VM runtime. * @param {!Runtime} runtime VM runtime instance to be serialized. - * @return {string} Serialized runtime instance. + * @return {object} Serialized runtime instance. */ const serialize = function (runtime) { // Fetch targets @@ -150,11 +150,12 @@ const parseScratchObject = function (object, runtime) { /** * Deserializes the specified representation of a VM runtime and loads it into * the provided runtime instance. - * @param {string} json Stringified JSON representation of a VM runtime. + * @param {object} json JSON representation of a VM runtime. * @param {Runtime} runtime Runtime instance + * @returns {Promise} Promise that resolves to the list of targets after the project is deserialized */ const deserialize = function (json, runtime) { - return Promise.all(json.targets.map(target => parseScratchObject(target, runtime))); + return Promise.all((json.targets || []).map(target => parseScratchObject(target, runtime))); }; module.exports = { diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index bc863dfe9..1151d3fd1 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -763,14 +763,21 @@ class RenderedTarget extends Target { this.dragging = false; } + + /** + * Helper method to clean out data from each asset when serializing to JSON + * @param {Array} assetList list of costumes or sounds + * @returns {Array} list with raw data removed from each asset + */ assetListToJSON (assetList) { const exclude = ['data', 'url']; - return assetList.map(asset => - Object.keys(asset).reduce((rAsset, prop) => { + return assetList.map(asset => { + if (typeof asset !== 'object') return asset; + return Object.keys(asset).reduce((rAsset, prop) => { if (exclude.indexOf(prop) === -1) rAsset[prop] = asset[prop]; return rAsset; - }, {}) - ); + }, {}); + }); } /** diff --git a/src/virtual-machine.js b/src/virtual-machine.js index 745d9089a..87ed8e4da 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -231,7 +231,12 @@ class VirtualMachine extends EventEmitter { * @returns {Promise} Promise that resolves after the sprite is added */ addSprite2 (json) { - // Select new sprite. + // Validate & parse + if (typeof json !== 'string') return; + json = JSON.parse(json); + if (typeof json !== 'object') return; + + // Select new sprite. return sb2.deserialize(json, this.runtime, true).then(targets => { this.runtime.targets.push(targets[0]); this.editingTarget = targets[0]; diff --git a/test/fixtures/demo.json b/test/fixtures/demo.json new file mode 100644 index 000000000..ed8e2cb88 --- /dev/null +++ b/test/fixtures/demo.json @@ -0,0 +1 @@ +{"targets":[{"id":"s_QE@H1K/8hy}:|+gG%x","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":2,"costume":{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"},"visible":true,"rotationStyle":"all around","blocks":{"7ypP@K@sZk@[e3]h],~|":{"id":"7ypP@K@sZk@[e3]h],~|","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"yG|5uo#Ak+sr:az:5:eY","shadow":false,"x":25.5,"y":13.200000000000001,"topLevel":true,"parent":null},"yG|5uo#Ak+sr:az:5:eY":{"id":"yG|5uo#Ak+sr:az:5:eY","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"2iU#B(lcgK/gc63NBzN|","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"7ypP@K@sZk@[e3]h],~|"},"2iU#B(lcgK/gc63NBzN|":{"id":"2iU#B(lcgK/gc63NBzN|","opcode":"control_create_clone_of","inputs":{"CLONE_OPTION":{"name":"CLONE_OPTION","block":"%?Y.y*q)s4{*.qKf8lB:","shadow":"%?Y.y*q)s4{*.qKf8lB:"}},"fields":{},"next":"t}S1*5H.`;8kh{g=}vJy","shadow":false,"parent":"yG|5uo#Ak+sr:az:5:eY"},"%?Y.y*q)s4{*.qKf8lB:":{"id":"%?Y.y*q)s4{*.qKf8lB:","opcode":"control_create_clone_of_menu","inputs":{},"fields":{"CLONE_OPTION":{"name":"CLONE_OPTION","value":"DINO GIF"}},"next":null,"topLevel":false,"parent":"2iU#B(lcgK/gc63NBzN|","shadow":true},"t}S1*5H.`;8kh{g=}vJy":{"id":"t}S1*5H.`;8kh{g=}vJy","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"Uj`3=G9oI~d[C}#2:su[","shadow":"Uj`3=G9oI~d[C}#2:su["}},"fields":{},"next":null,"shadow":false,"parent":"2iU#B(lcgK/gc63NBzN|"},"Uj`3=G9oI~d[C}#2:su[":{"id":"Uj`3=G9oI~d[C}#2:su[","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"t}S1*5H.`;8kh{g=}vJy","shadow":true},"S/Jj?9hjDRtY=k:.:c7p":{"id":"S/Jj?9hjDRtY=k:.:c7p","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"`d7M;g9pr/DWO`;S#^rr","shadow":false,"x":370.5,"y":11,"topLevel":true,"parent":null},"`d7M;g9pr/DWO`;S#^rr":{"id":"`d7M;g9pr/DWO`;S#^rr","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"osMx/MH`NA?V4AkwQc6d","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"S/Jj?9hjDRtY=k:.:c7p"},"osMx/MH`NA?V4AkwQc6d":{"id":"osMx/MH`NA?V4AkwQc6d","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Gp@?LHeu1YD1`2)0-Y,L","shadow":"Gp@?LHeu1YD1`2)0-Y,L"},"CHANGE":{"name":"CHANGE","block":":eos6@gH{97`g]POQN@V","shadow":":eos6@gH{97`g]POQN@V"}},"fields":{},"next":"T4gQhzYc.rPpX-^7;^-)","shadow":false,"parent":"`d7M;g9pr/DWO`;S#^rr"},"Gp@?LHeu1YD1`2)0-Y,L":{"id":"Gp@?LHeu1YD1`2)0-Y,L","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},":eos6@gH{97`g]POQN@V":{"id":":eos6@gH{97`g]POQN@V","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},"T4gQhzYc.rPpX-^7;^-)":{"id":"T4gQhzYc.rPpX-^7;^-)","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"~cS,w!*#5R*K,3yJ9CaJ","shadow":"~cS,w!*#5R*K,3yJ9CaJ"}},"fields":{},"next":null,"shadow":false,"parent":"osMx/MH`NA?V4AkwQc6d"},"~cS,w!*#5R*K,3yJ9CaJ":{"id":"~cS,w!*#5R*K,3yJ9CaJ","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.01}},"next":null,"topLevel":false,"parent":"T4gQhzYc.rPpX-^7;^-)","shadow":true},"+M8;.D~|?HC;a~/XN3t;":{"id":"+M8;.D~|?HC;a~/XN3t;","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"2bPLnQYJj]pm*?:I?khj","shadow":false,"x":670.5,"y":11,"topLevel":true,"parent":null},"2bPLnQYJj]pm*?:I?khj":{"id":"2bPLnQYJj]pm*?:I?khj","opcode":"sound_setvolumeto","inputs":{"VOLUME":{"name":"VOLUME","block":"3Ta/k?.*T/,0I(e7_aC[","shadow":"3Ta/k?.*T/,0I(e7_aC["}},"fields":{},"next":"m;jn7{tsh:Jv[E6CQh[/","shadow":false,"parent":"+M8;.D~|?HC;a~/XN3t;"},"3Ta/k?.*T/,0I(e7_aC[":{"id":"3Ta/k?.*T/,0I(e7_aC[","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":25}},"next":null,"topLevel":false,"parent":"2bPLnQYJj]pm*?:I?khj","shadow":true},"m;jn7{tsh:Jv[E6CQh[/":{"id":"m;jn7{tsh:Jv[E6CQh[/","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":")Z07}:0g~RGd,s~U)~kB","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"2bPLnQYJj]pm*?:I?khj"},")Z07}:0g~RGd,s~U)~kB":{"id":")Z07}:0g~RGd,s~U)~kB","opcode":"sound_playuntildone","inputs":{"SOUND_MENU":{"name":"SOUND_MENU","block":"}p)z*3Uq7ehzKKf:~o3v","shadow":"}p)z*3Uq7ehzKKf:~o3v"}},"fields":{},"next":null,"shadow":false,"parent":"m;jn7{tsh:Jv[E6CQh[/"},"}p)z*3Uq7ehzKKf:~o3v":{"id":"}p)z*3Uq7ehzKKf:~o3v","opcode":"sound_sounds_menu","inputs":{},"fields":{"SOUND_MENU":{"name":"SOUND_MENU","value":"mp3_missy.mp3"}},"next":null,"topLevel":false,"parent":")Z07}:0g~RGd,s~U)~kB","shadow":true}},"variables":{},"lists":{},"costumes":[{"name":"rays","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"md5ext":"87e963282db9e020e8c4d075891ea12b.svg"},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":7,"md5ext":"34d0806e6741143d4fe96670a5eaa285.png"},{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"}],"sounds":[{"name":"mp3_missy.mp3","format":"adpcm","rate":22050,"sampleCount":5122944,"soundID":-1,"md5":"2c37fef717622a964ce78e84bfbece24.wav"}]},{"id":"@ISBh_`?t1bOJ:.LL~3C","name":"DINO GIF","isStage":false,"x":151,"y":-71,"size":166.85922210054798,"direction":180,"draggable":false,"currentCostume":10,"costume":{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},"visible":false,"rotationStyle":"don't rotate","blocks":{"#;CdU0p1-8k9[w^#XV(u":{"id":"#;CdU0p1-8k9[w^#XV(u","opcode":"control_start_as_clone","inputs":{},"fields":{},"next":"6l~Oo(K*P-)@):?2p|G(","shadow":false,"x":234,"y":134,"topLevel":true,"parent":null},"6l~Oo(K*P-)@):?2p|G(":{"id":"6l~Oo(K*P-)@):?2p|G(","opcode":"motion_gotoxy","inputs":{"X":{"name":"X","block":"L:Qj-TF3)P[7/3(nV^*]","shadow":"}qT`4H)hBt8Jbk..R`S7"},"Y":{"name":"Y","block":"}eRncfN!fS@loj8_1ZXl","shadow":"3tp!#}gLA]cO_B/.AX%4"}},"fields":{},"next":"ld468~yG{.eVZc4S@w`W","shadow":false,"parent":"#;CdU0p1-8k9[w^#XV(u"},"L:Qj-TF3)P[7/3(nV^*]":{"id":"L:Qj-TF3)P[7/3(nV^*]","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"ld~5XDkWFDa?Amd=k6bW","shadow":"ld~5XDkWFDa?Amd=k6bW"},"TO":{"name":"TO","block":"K+x@j~L]9!-N,n@8Z#l`","shadow":"K+x@j~L]9!-N,n@8Z#l`"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"ld~5XDkWFDa?Amd=k6bW":{"id":"ld~5XDkWFDa?Amd=k6bW","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"K+x@j~L]9!-N,n@8Z#l`":{"id":"K+x@j~L]9!-N,n@8Z#l`","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"}qT`4H)hBt8Jbk..R`S7":{"id":"}qT`4H)hBt8Jbk..R`S7","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"}eRncfN!fS@loj8_1ZXl":{"id":"}eRncfN!fS@loj8_1ZXl","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"]^nFI;t}04+C(lEI;|@1","shadow":"]^nFI;t}04+C(lEI;|@1"},"TO":{"name":"TO","block":"Tj|#PEq7#JA%|^H!ihS%","shadow":"Tj|#PEq7#JA%|^H!ihS%"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"]^nFI;t}04+C(lEI;|@1":{"id":"]^nFI;t}04+C(lEI;|@1","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"Tj|#PEq7#JA%|^H!ihS%":{"id":"Tj|#PEq7#JA%|^H!ihS%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"3tp!#}gLA]cO_B/.AX%4":{"id":"3tp!#}gLA]cO_B/.AX%4","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"ld468~yG{.eVZc4S@w`W":{"id":"ld468~yG{.eVZc4S@w`W","opcode":"looks_setsizeto","inputs":{"SIZE":{"name":"SIZE","block":"mkS,VQOXuWF.(ee3V=cZ","shadow":"_QQ^FvSp8Fa9@rrmH4[|"}},"fields":{},"next":"O+}^kn4mH_uJ*^,r8Sx.","shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"mkS,VQOXuWF.(ee3V=cZ":{"id":"mkS,VQOXuWF.(ee3V=cZ","opcode":"operator_mathop","inputs":{"OPERATOR":{"name":"OPERATOR","block":"K)-C:gdAKj5A3y8Hc#(e","shadow":"K)-C:gdAKj5A3y8Hc#(e"},"NUM":{"name":"NUM","block":"fe?R].BsZj~{suM/rHyn","shadow":"0p08Y@;LZSR0}`z_4s4o"}},"fields":{},"next":null,"shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"K)-C:gdAKj5A3y8Hc#(e":{"id":"K)-C:gdAKj5A3y8Hc#(e","opcode":"operator_mathop_menu","inputs":{},"fields":{"OPERATOR":{"name":"OPERATOR","value":"sqrt"}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"fe?R].BsZj~{suM/rHyn":{"id":"fe?R].BsZj~{suM/rHyn","opcode":"operator_add","inputs":{"NUM1":{"name":"NUM1","block":"P;n47a4j9,n;jU^FJ*JH","shadow":"ZbQYAd}A`|4]M,Y|e0Cv"},"NUM2":{"name":"NUM2","block":"iMtB?%lO_r4|aQJ}h#,3","shadow":"8q4Um6]-t@`p0cY{5u/2"}},"fields":{},"next":null,"shadow":false,"parent":"mkS,VQOXuWF.(ee3V=cZ"},"P;n47a4j9,n;jU^FJ*JH":{"id":"P;n47a4j9,n;jU^FJ*JH","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"X4`FPG|)JR^_CJ.S;u8{","shadow":")o=P{gJ0m;nU20x4M/M5"},"NUM2":{"name":"NUM2","block":"eKGS?+y?pX.pZZ3qx,{I","shadow":"rM%zS%LxHG^H`a1jBjQ("}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"X4`FPG|)JR^_CJ.S;u8{":{"id":"X4`FPG|)JR^_CJ.S;u8{","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},")o=P{gJ0m;nU20x4M/M5":{"id":")o=P{gJ0m;nU20x4M/M5","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"eKGS?+y?pX.pZZ3qx,{I":{"id":"eKGS?+y?pX.pZZ3qx,{I","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},"rM%zS%LxHG^H`a1jBjQ(":{"id":"rM%zS%LxHG^H`a1jBjQ(","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"ZbQYAd}A`|4]M,Y|e0Cv":{"id":"ZbQYAd}A`|4]M,Y|e0Cv","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"iMtB?%lO_r4|aQJ}h#,3":{"id":"iMtB?%lO_r4|aQJ}h#,3","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"vK;Sl+lUa5#n`0;8_57i","shadow":"8K_TE);KCR*F+J=HPCW="},"NUM2":{"name":"NUM2","block":"d:,mr_K3A0LCTjIwsQ+W","shadow":"A{@q-Y:VVxEldYH+G28~"}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"vK;Sl+lUa5#n`0;8_57i":{"id":"vK;Sl+lUa5#n`0;8_57i","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"8K_TE);KCR*F+J=HPCW=":{"id":"8K_TE);KCR*F+J=HPCW=","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"d:,mr_K3A0LCTjIwsQ+W":{"id":"d:,mr_K3A0LCTjIwsQ+W","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"A{@q-Y:VVxEldYH+G28~":{"id":"A{@q-Y:VVxEldYH+G28~","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"8q4Um6]-t@`p0cY{5u/2":{"id":"8q4Um6]-t@`p0cY{5u/2","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"0p08Y@;LZSR0}`z_4s4o":{"id":"0p08Y@;LZSR0}`z_4s4o","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"_QQ^FvSp8Fa9@rrmH4[|":{"id":"_QQ^FvSp8Fa9@rrmH4[|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"ld468~yG{.eVZc4S@w`W","shadow":true},"O+}^kn4mH_uJ*^,r8Sx.":{"id":"O+}^kn4mH_uJ*^,r8Sx.","opcode":"looks_switchcostumeto","inputs":{"COSTUME":{"name":"COSTUME","block":"gsQ=q0*LFg#?._Pf)8/5","shadow":"ZLsh:H.`4k+ilKIgy9Om"}},"fields":{},"next":"=^MT[Pp3!76o,)?P^{zJ","shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"gsQ=q0*LFg#?._Pf)8/5":{"id":"gsQ=q0*LFg#?._Pf)8/5","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q_di1L-7d!WT0gdBJ-*D","shadow":"q_di1L-7d!WT0gdBJ-*D"},"TO":{"name":"TO","block":"d?;5Y(V=I1j*o+U`/+B?","shadow":"d?;5Y(V=I1j*o+U`/+B?"}},"fields":{},"next":null,"shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"q_di1L-7d!WT0gdBJ-*D":{"id":"q_di1L-7d!WT0gdBJ-*D","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"d?;5Y(V=I1j*o+U`/+B?":{"id":"d?;5Y(V=I1j*o+U`/+B?","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"ZLsh:H.`4k+ilKIgy9Om":{"id":"ZLsh:H.`4k+ilKIgy9Om","opcode":"looks_costume","inputs":{},"fields":{"COSTUME":{"name":"COSTUME","value":""}},"next":null,"topLevel":false,"parent":"O+}^kn4mH_uJ*^,r8Sx.","shadow":true},"=^MT[Pp3!76o,)?P^{zJ":{"id":"=^MT[Pp3!76o,)?P^{zJ","opcode":"looks_seteffectto","inputs":{"EFFECT":{"name":"EFFECT","block":"Mo)x#%D(0x7?xaT@M9b7","shadow":"Mo)x#%D(0x7?xaT@M9b7"},"VALUE":{"name":"VALUE","block":"eALyDFSL6~qe8SstZU@h","shadow":"5t4PvBKJOJB|HZqoR+ml"}},"fields":{},"next":"@avT0YYyn{=N[-iFWOc?","shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"Mo)x#%D(0x7?xaT@M9b7":{"id":"Mo)x#%D(0x7?xaT@M9b7","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"eALyDFSL6~qe8SstZU@h":{"id":"eALyDFSL6~qe8SstZU@h","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q.L:wC~)cn@*Cp!uCy}|","shadow":"q.L:wC~)cn@*Cp!uCy}|"},"TO":{"name":"TO","block":"+xES7g}Qr2gVkL.xbY%}","shadow":"+xES7g}Qr2gVkL.xbY%}"}},"fields":{},"next":null,"shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"q.L:wC~)cn@*Cp!uCy}|":{"id":"q.L:wC~)cn@*Cp!uCy}|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"+xES7g}Qr2gVkL.xbY%}":{"id":"+xES7g}Qr2gVkL.xbY%}","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":255}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"5t4PvBKJOJB|HZqoR+ml":{"id":"5t4PvBKJOJB|HZqoR+ml","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"@avT0YYyn{=N[-iFWOc?":{"id":"@avT0YYyn{=N[-iFWOc?","opcode":"looks_show","inputs":{},"fields":{},"next":"`vr!jyW0.E6dta8DwNv4","shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"`vr!jyW0.E6dta8DwNv4":{"id":"`vr!jyW0.E6dta8DwNv4","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":"Ir.;~`tk3R#u~#5N.F8P","shadow":"Ir.;~`tk3R#u~#5N.F8P"},"SUBSTACK":{"name":"SUBSTACK","block":"Xd(MU5_A{vy=f7o3aOv;","shadow":null}},"fields":{},"next":"?*18UH)X%/FL?=-HoaFW","shadow":false,"parent":"@avT0YYyn{=N[-iFWOc?"},"Ir.;~`tk3R#u~#5N.F8P":{"id":"Ir.;~`tk3R#u~#5N.F8P","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":2}},"next":null,"topLevel":false,"parent":"`vr!jyW0.E6dta8DwNv4","shadow":true},"Xd(MU5_A{vy=f7o3aOv;":{"id":"Xd(MU5_A{vy=f7o3aOv;","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":":hx/sF-?usjK]g/V+)M_","shadow":":hx/sF-?usjK]g/V+)M_"},"SUBSTACK":{"name":"SUBSTACK","block":"Fcmgbh}L@,@C[lM?S65e","shadow":null}},"fields":{},"next":"hBZmg@}{YmiB4yi(@v7W","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},":hx/sF-?usjK]g/V+)M_":{"id":":hx/sF-?usjK]g/V+)M_","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":50}},"next":null,"topLevel":false,"parent":"Xd(MU5_A{vy=f7o3aOv;","shadow":true},"Fcmgbh}L@,@C[lM?S65e":{"id":"Fcmgbh}L@,@C[lM?S65e","opcode":"looks_nextcostume","inputs":{},"fields":{},"next":"rqjWA2+Txj=LLtMRwHV[","shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"rqjWA2+Txj=LLtMRwHV[":{"id":"rqjWA2+Txj=LLtMRwHV[","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Da3Re+N?LCCVwLZ0Fb/3","shadow":"Da3Re+N?LCCVwLZ0Fb/3"},"CHANGE":{"name":"CHANGE","block":"Mx1%w~K(6ww{H^u648yC","shadow":"Mx1%w~K(6ww{H^u648yC"}},"fields":{},"next":"F_lXv!E*#-GeI!e?WMRF","shadow":false,"parent":"Fcmgbh}L@,@C[lM?S65e"},"Da3Re+N?LCCVwLZ0Fb/3":{"id":"Da3Re+N?LCCVwLZ0Fb/3","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"Mx1%w~K(6ww{H^u648yC":{"id":"Mx1%w~K(6ww{H^u648yC","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"F_lXv!E*#-GeI!e?WMRF":{"id":"F_lXv!E*#-GeI!e?WMRF","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"w=N8X@IkId2Sh@=T96B@","shadow":"w=N8X@IkId2Sh@=T96B@"}},"fields":{},"next":null,"shadow":false,"parent":"rqjWA2+Txj=LLtMRwHV["},"w=N8X@IkId2Sh@=T96B@":{"id":"w=N8X@IkId2Sh@=T96B@","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.03}},"next":null,"topLevel":false,"parent":"F_lXv!E*#-GeI!e?WMRF","shadow":true},"hBZmg@}{YmiB4yi(@v7W":{"id":"hBZmg@}{YmiB4yi(@v7W","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"]+.*TT{]bS/TLaH,fC`-","shadow":"]+.*TT{]bS/TLaH,fC`-"}},"fields":{},"next":null,"shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"]+.*TT{]bS/TLaH,fC`-":{"id":"]+.*TT{]bS/TLaH,fC`-","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"hBZmg@}{YmiB4yi(@v7W","shadow":true},"?*18UH)X%/FL?=-HoaFW":{"id":"?*18UH)X%/FL?=-HoaFW","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"C6RAoGQiY%,s~4-sxGIn","shadow":"C6RAoGQiY%,s~4-sxGIn"}},"fields":{},"next":"2cQ#K+HkhAdqo)E)ApFo","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},"C6RAoGQiY%,s~4-sxGIn":{"id":"C6RAoGQiY%,s~4-sxGIn","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":3}},"next":null,"topLevel":false,"parent":"?*18UH)X%/FL?=-HoaFW","shadow":true},"2cQ#K+HkhAdqo)E)ApFo":{"id":"2cQ#K+HkhAdqo)E)ApFo","opcode":"control_delete_this_clone","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"?*18UH)X%/FL?=-HoaFW"}},"variables":{},"lists":{},"costumes":[{"name":"dino_0000_Layer-16","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":1,"md5ext":"4e330592c1f1b80a4b182cc00cf8d6bf.png"},{"name":"dino_0001_Layer-15","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":2,"md5ext":"7a68a03190a519ecc4c3e1e6a6f32f41.png"},{"name":"dino_0002_Layer-14","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":3,"md5ext":"17427d759cac720f1575a36d624d10cc.png"},{"name":"dino_0003_Layer-13","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":5,"md5ext":"6563f3acffd6827bbdc86358599c6815.png"},{"name":"dino_0004_Layer-12","bitmapResolution":2,"rotationCenterX":94,"rotationCenterY":134,"skinId":4,"md5ext":"a2aca11ef43fde45cc3431402b25ba9e.png"},{"name":"dino_0005_Layer-11","bitmapResolution":2,"rotationCenterX":88,"rotationCenterY":134,"skinId":6,"md5ext":"79d431943530899519080b2b8ffe8f9f.png"},{"name":"dino_0006_Layer-10","bitmapResolution":2,"rotationCenterX":84,"rotationCenterY":130,"skinId":9,"md5ext":"67104cc6c05f895a5c108c2a920ff8d1.png"},{"name":"dino_0007_Layer-9","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":10,"md5ext":"39736b1f4fb42cf59463a1bfdcba2b5d.png"},{"name":"dino_0008_Layer-8","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":11,"md5ext":"16308e806c3a594832337aeac7cf52e0.png"},{"name":"dino_0009_Layer-7","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":12,"md5ext":"ecd4cfd287a4f150d45411df5722362b.png"},{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},{"name":"dino_0011_Layer-5","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":18,"md5ext":"b00cf2e17ff81b5d42bb0d269362e845.png"},{"name":"dino_0012_Layer-4","bitmapResolution":2,"rotationCenterX":96,"rotationCenterY":134,"skinId":14,"md5ext":"3a25736b45392dcf740f91331aa13961.png"},{"name":"dino_0013_Layer-3","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":134,"skinId":17,"md5ext":"33276549c55db74a56090148fe1e8efa.png"},{"name":"dino_0014_Layer-2","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":130,"skinId":16,"md5ext":"5b71a9c255135caa2e7c34ce072480aa.png"},{"name":"dino_0015_Layer-1","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":15,"md5ext":"4ee69e1b27147fd818144d611c138263.png"}],"sounds":[{"name":"alien creak1","format":"","rate":11025,"sampleCount":8045,"soundID":-1,"md5":"0377a7476136e5e8c780c64a4828922d.wav"}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} \ No newline at end of file diff --git a/test/integration/import_sb2.js b/test/integration/import_sb2.js index 2602783fd..3b375a44c 100644 --- a/test/integration/import_sb2.js +++ b/test/integration/import_sb2.js @@ -8,7 +8,7 @@ const runtime = require('../../src/engine/runtime'); const sb2 = require('../../src/serialization/sb2'); test('spec', t => { - t.type(sb2, 'function'); + t.type(sb2.deserialize, 'function'); t.end(); }); @@ -16,13 +16,15 @@ test('default', t => { // Get SB2 JSON (string) const uri = path.resolve(__dirname, '../fixtures/default.sb2'); const file = extract(uri); + const json = JSON.parse(file); // Create runtime instance & load SB2 into it const rt = new runtime(); attachTestStorage(rt); - sb2(file, rt).then(targets => { + sb2.deserialize(json, rt).then(targets => { // Test t.type(file, 'string'); + t.type(json, 'object'); t.type(rt, 'object'); t.type(targets, 'object'); diff --git a/test/unit/serialization_sb2.js b/test/unit/serialization_sb2.js index 7a0bec03f..cd3294f0a 100644 --- a/test/unit/serialization_sb2.js +++ b/test/unit/serialization_sb2.js @@ -16,14 +16,14 @@ test('default', t => { // Get SB2 JSON (string) const uri = path.resolve(__dirname, '../fixtures/default.sb2'); const file = extract(uri); + const json = JSON.parse(file); // Create runtime instance & load SB2 into it const rt = new Runtime(); - sb2.deserialize(file, rt).then(targets => { - console.dir(targets); - + sb2.deserialize(json, rt).then(targets => { // Test t.type(file, 'string'); + t.type(json, 'object'); t.type(rt, 'object'); t.type(targets, 'object'); diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js index 761b0b54c..a43ce204f 100644 --- a/test/unit/serialization_sb3.js +++ b/test/unit/serialization_sb3.js @@ -1,19 +1,22 @@ const test = require('tap').test; const VirtualMachine = require('../../src/index'); const sb3 = require('../../src/serialization/sb3'); +const demoSb3 = require('../fixtures/demo.json'); test('serialize', t => { const vm = new VirtualMachine(); - vm.fromJSON(JSON.stringify(require('../fixtures/demo.json'))); + vm.fromJSON(JSON.stringify(demoSb3)); const result = sb3.serialize(vm.runtime); - console.dir(JSON.stringify(result)); // @todo Analyze + t.type(JSON.stringify(result), 'string'); t.end(); }); test('deserialize', t => { const vm = new VirtualMachine(); - const result = sb3.deserialize('', vm.runtime); - // @todo Analyize - t.end(); + sb3.deserialize('', vm.runtime).then(targets => { + // @todo Analyize + t.type(targets, 'object'); + t.end(); + }); }); From c5cf6d9b40d1da4fd506ecd7c5247a1f20daa5c2 Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 12:29:10 -0400 Subject: [PATCH 36/42] Use asset id reference on sounds --- src/import/load-sound.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/import/load-sound.js b/src/import/load-sound.js index f54f5fe32..a308e1516 100644 --- a/src/import/load-sound.js +++ b/src/import/load-sound.js @@ -19,10 +19,17 @@ const loadSound = function (sound, runtime) { } const idParts = sound.md5.split('.'); const md5 = idParts[0]; - return runtime.storage.load(runtime.storage.AssetType.Sound, md5).then(soundAsset => { - sound.data = soundAsset.data; - return runtime.audioEngine.decodeSound(sound).then(() => sound); - }); + return runtime.storage.load(runtime.storage.AssetType.Sound, md5) + .then(soundAsset => { + sound.assetId = soundAsset.assetId; + sound.assetType = runtime.storage.AssetType.Sound; + return runtime.audioEngine.decodeSound(Object.assign( + {}, + sound, + {data: soundAsset.data} + )); + }) + .then(() => sound); }; module.exports = loadSound; From 2181e51c38108479b2b471f2b44b371556f7a7fb Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 12:29:28 -0400 Subject: [PATCH 37/42] Don't store costume data uris on costumes --- src/import/load-costume.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/import/load-costume.js b/src/import/load-costume.js index 83ef78006..a9323398d 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -13,7 +13,6 @@ const log = require('../util/log'); * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error. */ const loadCostume = function (md5ext, costume, runtime) { - costume.md5ext = md5ext; if (!runtime.storage) { log.error('No storage module present; cannot load costume asset: ', md5ext); return Promise.resolve(costume); @@ -31,7 +30,8 @@ const loadCostume = function (md5ext, costume, runtime) { ]; let promise = runtime.storage.load(assetType, md5).then(costumeAsset => { - costume.url = costumeAsset.encodeDataURI(); + costume.assetId = costumeAsset.assetId; + costume.assetType = assetType; return costumeAsset; }); From efe31f4825e54530cec82509bf369565ef2073db Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 12:41:54 -0400 Subject: [PATCH 38/42] Fix costume deserialization --- src/serialization/sb3.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index b072be199..4477a7b51 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -75,7 +75,8 @@ const parseScratchObject = function (object, runtime) { rotationCenterX: costumeSource.rotationCenterX, rotationCenterY: costumeSource.rotationCenterY }; - return loadCostume(costumeSource.md5ext, costume, runtime); + const costumeMd5 = `${costumeSource.assetId}.${costumeSource.assetType.runtimeFormat}`; + return loadCostume(costumeMd5, costume, runtime); }); // Sounds from JSON const soundPromises = (object.sounds || []).map(soundSource => { From baee9a2793f7e80c6abfb31ede84afe9f431095d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 4 May 2017 13:03:42 -0400 Subject: [PATCH 39/42] Don't clean costume and sound lists They are already clean, since we don't store the data on them any more. --- src/sprites/rendered-target.js | 21 ++------------------- test/fixtures/demo.json | 2 +- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index 1151d3fd1..694d78612 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -764,29 +764,12 @@ class RenderedTarget extends Target { } - /** - * Helper method to clean out data from each asset when serializing to JSON - * @param {Array} assetList list of costumes or sounds - * @returns {Array} list with raw data removed from each asset - */ - assetListToJSON (assetList) { - const exclude = ['data', 'url']; - return assetList.map(asset => { - if (typeof asset !== 'object') return asset; - return Object.keys(asset).reduce((rAsset, prop) => { - if (exclude.indexOf(prop) === -1) rAsset[prop] = asset[prop]; - return rAsset; - }, {}); - }); - } - /** * Serialize sprite info, used when emitting events about the sprite * @returns {object} Sprite data as a simple object */ toJSON () { - const costumes = this.assetListToJSON(this.getCostumes()); - const sounds = this.assetListToJSON(this.getSounds()); + const costumes = this.getCostumes(); return { id: this.id, name: this.getName(), @@ -805,7 +788,7 @@ class RenderedTarget extends Target { variables: this.variables, lists: this.lists, costumes: costumes, - sounds: sounds + sounds: this.getSounds() }; } diff --git a/test/fixtures/demo.json b/test/fixtures/demo.json index ed8e2cb88..d54626da6 100644 --- a/test/fixtures/demo.json +++ b/test/fixtures/demo.json @@ -1 +1 @@ -{"targets":[{"id":"s_QE@H1K/8hy}:|+gG%x","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":2,"costume":{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"},"visible":true,"rotationStyle":"all around","blocks":{"7ypP@K@sZk@[e3]h],~|":{"id":"7ypP@K@sZk@[e3]h],~|","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"yG|5uo#Ak+sr:az:5:eY","shadow":false,"x":25.5,"y":13.200000000000001,"topLevel":true,"parent":null},"yG|5uo#Ak+sr:az:5:eY":{"id":"yG|5uo#Ak+sr:az:5:eY","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"2iU#B(lcgK/gc63NBzN|","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"7ypP@K@sZk@[e3]h],~|"},"2iU#B(lcgK/gc63NBzN|":{"id":"2iU#B(lcgK/gc63NBzN|","opcode":"control_create_clone_of","inputs":{"CLONE_OPTION":{"name":"CLONE_OPTION","block":"%?Y.y*q)s4{*.qKf8lB:","shadow":"%?Y.y*q)s4{*.qKf8lB:"}},"fields":{},"next":"t}S1*5H.`;8kh{g=}vJy","shadow":false,"parent":"yG|5uo#Ak+sr:az:5:eY"},"%?Y.y*q)s4{*.qKf8lB:":{"id":"%?Y.y*q)s4{*.qKf8lB:","opcode":"control_create_clone_of_menu","inputs":{},"fields":{"CLONE_OPTION":{"name":"CLONE_OPTION","value":"DINO GIF"}},"next":null,"topLevel":false,"parent":"2iU#B(lcgK/gc63NBzN|","shadow":true},"t}S1*5H.`;8kh{g=}vJy":{"id":"t}S1*5H.`;8kh{g=}vJy","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"Uj`3=G9oI~d[C}#2:su[","shadow":"Uj`3=G9oI~d[C}#2:su["}},"fields":{},"next":null,"shadow":false,"parent":"2iU#B(lcgK/gc63NBzN|"},"Uj`3=G9oI~d[C}#2:su[":{"id":"Uj`3=G9oI~d[C}#2:su[","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"t}S1*5H.`;8kh{g=}vJy","shadow":true},"S/Jj?9hjDRtY=k:.:c7p":{"id":"S/Jj?9hjDRtY=k:.:c7p","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"`d7M;g9pr/DWO`;S#^rr","shadow":false,"x":370.5,"y":11,"topLevel":true,"parent":null},"`d7M;g9pr/DWO`;S#^rr":{"id":"`d7M;g9pr/DWO`;S#^rr","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"osMx/MH`NA?V4AkwQc6d","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"S/Jj?9hjDRtY=k:.:c7p"},"osMx/MH`NA?V4AkwQc6d":{"id":"osMx/MH`NA?V4AkwQc6d","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Gp@?LHeu1YD1`2)0-Y,L","shadow":"Gp@?LHeu1YD1`2)0-Y,L"},"CHANGE":{"name":"CHANGE","block":":eos6@gH{97`g]POQN@V","shadow":":eos6@gH{97`g]POQN@V"}},"fields":{},"next":"T4gQhzYc.rPpX-^7;^-)","shadow":false,"parent":"`d7M;g9pr/DWO`;S#^rr"},"Gp@?LHeu1YD1`2)0-Y,L":{"id":"Gp@?LHeu1YD1`2)0-Y,L","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},":eos6@gH{97`g]POQN@V":{"id":":eos6@gH{97`g]POQN@V","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"osMx/MH`NA?V4AkwQc6d","shadow":true},"T4gQhzYc.rPpX-^7;^-)":{"id":"T4gQhzYc.rPpX-^7;^-)","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"~cS,w!*#5R*K,3yJ9CaJ","shadow":"~cS,w!*#5R*K,3yJ9CaJ"}},"fields":{},"next":null,"shadow":false,"parent":"osMx/MH`NA?V4AkwQc6d"},"~cS,w!*#5R*K,3yJ9CaJ":{"id":"~cS,w!*#5R*K,3yJ9CaJ","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.01}},"next":null,"topLevel":false,"parent":"T4gQhzYc.rPpX-^7;^-)","shadow":true},"+M8;.D~|?HC;a~/XN3t;":{"id":"+M8;.D~|?HC;a~/XN3t;","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"2bPLnQYJj]pm*?:I?khj","shadow":false,"x":670.5,"y":11,"topLevel":true,"parent":null},"2bPLnQYJj]pm*?:I?khj":{"id":"2bPLnQYJj]pm*?:I?khj","opcode":"sound_setvolumeto","inputs":{"VOLUME":{"name":"VOLUME","block":"3Ta/k?.*T/,0I(e7_aC[","shadow":"3Ta/k?.*T/,0I(e7_aC["}},"fields":{},"next":"m;jn7{tsh:Jv[E6CQh[/","shadow":false,"parent":"+M8;.D~|?HC;a~/XN3t;"},"3Ta/k?.*T/,0I(e7_aC[":{"id":"3Ta/k?.*T/,0I(e7_aC[","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":25}},"next":null,"topLevel":false,"parent":"2bPLnQYJj]pm*?:I?khj","shadow":true},"m;jn7{tsh:Jv[E6CQh[/":{"id":"m;jn7{tsh:Jv[E6CQh[/","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":")Z07}:0g~RGd,s~U)~kB","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"2bPLnQYJj]pm*?:I?khj"},")Z07}:0g~RGd,s~U)~kB":{"id":")Z07}:0g~RGd,s~U)~kB","opcode":"sound_playuntildone","inputs":{"SOUND_MENU":{"name":"SOUND_MENU","block":"}p)z*3Uq7ehzKKf:~o3v","shadow":"}p)z*3Uq7ehzKKf:~o3v"}},"fields":{},"next":null,"shadow":false,"parent":"m;jn7{tsh:Jv[E6CQh[/"},"}p)z*3Uq7ehzKKf:~o3v":{"id":"}p)z*3Uq7ehzKKf:~o3v","opcode":"sound_sounds_menu","inputs":{},"fields":{"SOUND_MENU":{"name":"SOUND_MENU","value":"mp3_missy.mp3"}},"next":null,"topLevel":false,"parent":")Z07}:0g~RGd,s~U)~kB","shadow":true}},"variables":{},"lists":{},"costumes":[{"name":"rays","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"md5ext":"87e963282db9e020e8c4d075891ea12b.svg"},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":7,"md5ext":"34d0806e6741143d4fe96670a5eaa285.png"},{"name":"twirl2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":8,"md5ext":"dbf20d7d4dbcc82d427828bbffde5d8d.png"}],"sounds":[{"name":"mp3_missy.mp3","format":"adpcm","rate":22050,"sampleCount":5122944,"soundID":-1,"md5":"2c37fef717622a964ce78e84bfbece24.wav"}]},{"id":"@ISBh_`?t1bOJ:.LL~3C","name":"DINO GIF","isStage":false,"x":151,"y":-71,"size":166.85922210054798,"direction":180,"draggable":false,"currentCostume":10,"costume":{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},"visible":false,"rotationStyle":"don't rotate","blocks":{"#;CdU0p1-8k9[w^#XV(u":{"id":"#;CdU0p1-8k9[w^#XV(u","opcode":"control_start_as_clone","inputs":{},"fields":{},"next":"6l~Oo(K*P-)@):?2p|G(","shadow":false,"x":234,"y":134,"topLevel":true,"parent":null},"6l~Oo(K*P-)@):?2p|G(":{"id":"6l~Oo(K*P-)@):?2p|G(","opcode":"motion_gotoxy","inputs":{"X":{"name":"X","block":"L:Qj-TF3)P[7/3(nV^*]","shadow":"}qT`4H)hBt8Jbk..R`S7"},"Y":{"name":"Y","block":"}eRncfN!fS@loj8_1ZXl","shadow":"3tp!#}gLA]cO_B/.AX%4"}},"fields":{},"next":"ld468~yG{.eVZc4S@w`W","shadow":false,"parent":"#;CdU0p1-8k9[w^#XV(u"},"L:Qj-TF3)P[7/3(nV^*]":{"id":"L:Qj-TF3)P[7/3(nV^*]","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"ld~5XDkWFDa?Amd=k6bW","shadow":"ld~5XDkWFDa?Amd=k6bW"},"TO":{"name":"TO","block":"K+x@j~L]9!-N,n@8Z#l`","shadow":"K+x@j~L]9!-N,n@8Z#l`"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"ld~5XDkWFDa?Amd=k6bW":{"id":"ld~5XDkWFDa?Amd=k6bW","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"K+x@j~L]9!-N,n@8Z#l`":{"id":"K+x@j~L]9!-N,n@8Z#l`","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":200}},"next":null,"topLevel":false,"parent":"L:Qj-TF3)P[7/3(nV^*]","shadow":true},"}qT`4H)hBt8Jbk..R`S7":{"id":"}qT`4H)hBt8Jbk..R`S7","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"}eRncfN!fS@loj8_1ZXl":{"id":"}eRncfN!fS@loj8_1ZXl","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"]^nFI;t}04+C(lEI;|@1","shadow":"]^nFI;t}04+C(lEI;|@1"},"TO":{"name":"TO","block":"Tj|#PEq7#JA%|^H!ihS%","shadow":"Tj|#PEq7#JA%|^H!ihS%"}},"fields":{},"next":null,"shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"]^nFI;t}04+C(lEI;|@1":{"id":"]^nFI;t}04+C(lEI;|@1","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":-114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"Tj|#PEq7#JA%|^H!ihS%":{"id":"Tj|#PEq7#JA%|^H!ihS%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":114}},"next":null,"topLevel":false,"parent":"}eRncfN!fS@loj8_1ZXl","shadow":true},"3tp!#}gLA]cO_B/.AX%4":{"id":"3tp!#}gLA]cO_B/.AX%4","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"6l~Oo(K*P-)@):?2p|G(","shadow":true},"ld468~yG{.eVZc4S@w`W":{"id":"ld468~yG{.eVZc4S@w`W","opcode":"looks_setsizeto","inputs":{"SIZE":{"name":"SIZE","block":"mkS,VQOXuWF.(ee3V=cZ","shadow":"_QQ^FvSp8Fa9@rrmH4[|"}},"fields":{},"next":"O+}^kn4mH_uJ*^,r8Sx.","shadow":false,"parent":"6l~Oo(K*P-)@):?2p|G("},"mkS,VQOXuWF.(ee3V=cZ":{"id":"mkS,VQOXuWF.(ee3V=cZ","opcode":"operator_mathop","inputs":{"OPERATOR":{"name":"OPERATOR","block":"K)-C:gdAKj5A3y8Hc#(e","shadow":"K)-C:gdAKj5A3y8Hc#(e"},"NUM":{"name":"NUM","block":"fe?R].BsZj~{suM/rHyn","shadow":"0p08Y@;LZSR0}`z_4s4o"}},"fields":{},"next":null,"shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"K)-C:gdAKj5A3y8Hc#(e":{"id":"K)-C:gdAKj5A3y8Hc#(e","opcode":"operator_mathop_menu","inputs":{},"fields":{"OPERATOR":{"name":"OPERATOR","value":"sqrt"}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"fe?R].BsZj~{suM/rHyn":{"id":"fe?R].BsZj~{suM/rHyn","opcode":"operator_add","inputs":{"NUM1":{"name":"NUM1","block":"P;n47a4j9,n;jU^FJ*JH","shadow":"ZbQYAd}A`|4]M,Y|e0Cv"},"NUM2":{"name":"NUM2","block":"iMtB?%lO_r4|aQJ}h#,3","shadow":"8q4Um6]-t@`p0cY{5u/2"}},"fields":{},"next":null,"shadow":false,"parent":"mkS,VQOXuWF.(ee3V=cZ"},"P;n47a4j9,n;jU^FJ*JH":{"id":"P;n47a4j9,n;jU^FJ*JH","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"X4`FPG|)JR^_CJ.S;u8{","shadow":")o=P{gJ0m;nU20x4M/M5"},"NUM2":{"name":"NUM2","block":"eKGS?+y?pX.pZZ3qx,{I","shadow":"rM%zS%LxHG^H`a1jBjQ("}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"X4`FPG|)JR^_CJ.S;u8{":{"id":"X4`FPG|)JR^_CJ.S;u8{","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},")o=P{gJ0m;nU20x4M/M5":{"id":")o=P{gJ0m;nU20x4M/M5","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"eKGS?+y?pX.pZZ3qx,{I":{"id":"eKGS?+y?pX.pZZ3qx,{I","opcode":"motion_xposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"P;n47a4j9,n;jU^FJ*JH"},"rM%zS%LxHG^H`a1jBjQ(":{"id":"rM%zS%LxHG^H`a1jBjQ(","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"P;n47a4j9,n;jU^FJ*JH","shadow":true},"ZbQYAd}A`|4]M,Y|e0Cv":{"id":"ZbQYAd}A`|4]M,Y|e0Cv","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"iMtB?%lO_r4|aQJ}h#,3":{"id":"iMtB?%lO_r4|aQJ}h#,3","opcode":"operator_multiply","inputs":{"NUM1":{"name":"NUM1","block":"vK;Sl+lUa5#n`0;8_57i","shadow":"8K_TE);KCR*F+J=HPCW="},"NUM2":{"name":"NUM2","block":"d:,mr_K3A0LCTjIwsQ+W","shadow":"A{@q-Y:VVxEldYH+G28~"}},"fields":{},"next":null,"shadow":false,"parent":"fe?R].BsZj~{suM/rHyn"},"vK;Sl+lUa5#n`0;8_57i":{"id":"vK;Sl+lUa5#n`0;8_57i","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"8K_TE);KCR*F+J=HPCW=":{"id":"8K_TE);KCR*F+J=HPCW=","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"d:,mr_K3A0LCTjIwsQ+W":{"id":"d:,mr_K3A0LCTjIwsQ+W","opcode":"motion_yposition","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"iMtB?%lO_r4|aQJ}h#,3"},"A{@q-Y:VVxEldYH+G28~":{"id":"A{@q-Y:VVxEldYH+G28~","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"iMtB?%lO_r4|aQJ}h#,3","shadow":true},"8q4Um6]-t@`p0cY{5u/2":{"id":"8q4Um6]-t@`p0cY{5u/2","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"fe?R].BsZj~{suM/rHyn","shadow":true},"0p08Y@;LZSR0}`z_4s4o":{"id":"0p08Y@;LZSR0}`z_4s4o","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"mkS,VQOXuWF.(ee3V=cZ","shadow":true},"_QQ^FvSp8Fa9@rrmH4[|":{"id":"_QQ^FvSp8Fa9@rrmH4[|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"ld468~yG{.eVZc4S@w`W","shadow":true},"O+}^kn4mH_uJ*^,r8Sx.":{"id":"O+}^kn4mH_uJ*^,r8Sx.","opcode":"looks_switchcostumeto","inputs":{"COSTUME":{"name":"COSTUME","block":"gsQ=q0*LFg#?._Pf)8/5","shadow":"ZLsh:H.`4k+ilKIgy9Om"}},"fields":{},"next":"=^MT[Pp3!76o,)?P^{zJ","shadow":false,"parent":"ld468~yG{.eVZc4S@w`W"},"gsQ=q0*LFg#?._Pf)8/5":{"id":"gsQ=q0*LFg#?._Pf)8/5","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q_di1L-7d!WT0gdBJ-*D","shadow":"q_di1L-7d!WT0gdBJ-*D"},"TO":{"name":"TO","block":"d?;5Y(V=I1j*o+U`/+B?","shadow":"d?;5Y(V=I1j*o+U`/+B?"}},"fields":{},"next":null,"shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"q_di1L-7d!WT0gdBJ-*D":{"id":"q_di1L-7d!WT0gdBJ-*D","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"d?;5Y(V=I1j*o+U`/+B?":{"id":"d?;5Y(V=I1j*o+U`/+B?","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"gsQ=q0*LFg#?._Pf)8/5","shadow":true},"ZLsh:H.`4k+ilKIgy9Om":{"id":"ZLsh:H.`4k+ilKIgy9Om","opcode":"looks_costume","inputs":{},"fields":{"COSTUME":{"name":"COSTUME","value":""}},"next":null,"topLevel":false,"parent":"O+}^kn4mH_uJ*^,r8Sx.","shadow":true},"=^MT[Pp3!76o,)?P^{zJ":{"id":"=^MT[Pp3!76o,)?P^{zJ","opcode":"looks_seteffectto","inputs":{"EFFECT":{"name":"EFFECT","block":"Mo)x#%D(0x7?xaT@M9b7","shadow":"Mo)x#%D(0x7?xaT@M9b7"},"VALUE":{"name":"VALUE","block":"eALyDFSL6~qe8SstZU@h","shadow":"5t4PvBKJOJB|HZqoR+ml"}},"fields":{},"next":"@avT0YYyn{=N[-iFWOc?","shadow":false,"parent":"O+}^kn4mH_uJ*^,r8Sx."},"Mo)x#%D(0x7?xaT@M9b7":{"id":"Mo)x#%D(0x7?xaT@M9b7","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"eALyDFSL6~qe8SstZU@h":{"id":"eALyDFSL6~qe8SstZU@h","opcode":"operator_random","inputs":{"FROM":{"name":"FROM","block":"q.L:wC~)cn@*Cp!uCy}|","shadow":"q.L:wC~)cn@*Cp!uCy}|"},"TO":{"name":"TO","block":"+xES7g}Qr2gVkL.xbY%}","shadow":"+xES7g}Qr2gVkL.xbY%}"}},"fields":{},"next":null,"shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"q.L:wC~)cn@*Cp!uCy}|":{"id":"q.L:wC~)cn@*Cp!uCy}|","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"+xES7g}Qr2gVkL.xbY%}":{"id":"+xES7g}Qr2gVkL.xbY%}","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":255}},"next":null,"topLevel":false,"parent":"eALyDFSL6~qe8SstZU@h","shadow":true},"5t4PvBKJOJB|HZqoR+ml":{"id":"5t4PvBKJOJB|HZqoR+ml","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":10}},"next":null,"topLevel":false,"parent":"=^MT[Pp3!76o,)?P^{zJ","shadow":true},"@avT0YYyn{=N[-iFWOc?":{"id":"@avT0YYyn{=N[-iFWOc?","opcode":"looks_show","inputs":{},"fields":{},"next":"`vr!jyW0.E6dta8DwNv4","shadow":false,"parent":"=^MT[Pp3!76o,)?P^{zJ"},"`vr!jyW0.E6dta8DwNv4":{"id":"`vr!jyW0.E6dta8DwNv4","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":"Ir.;~`tk3R#u~#5N.F8P","shadow":"Ir.;~`tk3R#u~#5N.F8P"},"SUBSTACK":{"name":"SUBSTACK","block":"Xd(MU5_A{vy=f7o3aOv;","shadow":null}},"fields":{},"next":"?*18UH)X%/FL?=-HoaFW","shadow":false,"parent":"@avT0YYyn{=N[-iFWOc?"},"Ir.;~`tk3R#u~#5N.F8P":{"id":"Ir.;~`tk3R#u~#5N.F8P","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":2}},"next":null,"topLevel":false,"parent":"`vr!jyW0.E6dta8DwNv4","shadow":true},"Xd(MU5_A{vy=f7o3aOv;":{"id":"Xd(MU5_A{vy=f7o3aOv;","opcode":"control_repeat","inputs":{"TIMES":{"name":"TIMES","block":":hx/sF-?usjK]g/V+)M_","shadow":":hx/sF-?usjK]g/V+)M_"},"SUBSTACK":{"name":"SUBSTACK","block":"Fcmgbh}L@,@C[lM?S65e","shadow":null}},"fields":{},"next":"hBZmg@}{YmiB4yi(@v7W","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},":hx/sF-?usjK]g/V+)M_":{"id":":hx/sF-?usjK]g/V+)M_","opcode":"math_whole_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":50}},"next":null,"topLevel":false,"parent":"Xd(MU5_A{vy=f7o3aOv;","shadow":true},"Fcmgbh}L@,@C[lM?S65e":{"id":"Fcmgbh}L@,@C[lM?S65e","opcode":"looks_nextcostume","inputs":{},"fields":{},"next":"rqjWA2+Txj=LLtMRwHV[","shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"rqjWA2+Txj=LLtMRwHV[":{"id":"rqjWA2+Txj=LLtMRwHV[","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"Da3Re+N?LCCVwLZ0Fb/3","shadow":"Da3Re+N?LCCVwLZ0Fb/3"},"CHANGE":{"name":"CHANGE","block":"Mx1%w~K(6ww{H^u648yC","shadow":"Mx1%w~K(6ww{H^u648yC"}},"fields":{},"next":"F_lXv!E*#-GeI!e?WMRF","shadow":false,"parent":"Fcmgbh}L@,@C[lM?S65e"},"Da3Re+N?LCCVwLZ0Fb/3":{"id":"Da3Re+N?LCCVwLZ0Fb/3","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"Mx1%w~K(6ww{H^u648yC":{"id":"Mx1%w~K(6ww{H^u648yC","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"rqjWA2+Txj=LLtMRwHV[","shadow":true},"F_lXv!E*#-GeI!e?WMRF":{"id":"F_lXv!E*#-GeI!e?WMRF","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"w=N8X@IkId2Sh@=T96B@","shadow":"w=N8X@IkId2Sh@=T96B@"}},"fields":{},"next":null,"shadow":false,"parent":"rqjWA2+Txj=LLtMRwHV["},"w=N8X@IkId2Sh@=T96B@":{"id":"w=N8X@IkId2Sh@=T96B@","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.03}},"next":null,"topLevel":false,"parent":"F_lXv!E*#-GeI!e?WMRF","shadow":true},"hBZmg@}{YmiB4yi(@v7W":{"id":"hBZmg@}{YmiB4yi(@v7W","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"]+.*TT{]bS/TLaH,fC`-","shadow":"]+.*TT{]bS/TLaH,fC`-"}},"fields":{},"next":null,"shadow":false,"parent":"Xd(MU5_A{vy=f7o3aOv;"},"]+.*TT{]bS/TLaH,fC`-":{"id":"]+.*TT{]bS/TLaH,fC`-","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":0.5}},"next":null,"topLevel":false,"parent":"hBZmg@}{YmiB4yi(@v7W","shadow":true},"?*18UH)X%/FL?=-HoaFW":{"id":"?*18UH)X%/FL?=-HoaFW","opcode":"control_wait","inputs":{"DURATION":{"name":"DURATION","block":"C6RAoGQiY%,s~4-sxGIn","shadow":"C6RAoGQiY%,s~4-sxGIn"}},"fields":{},"next":"2cQ#K+HkhAdqo)E)ApFo","shadow":false,"parent":"`vr!jyW0.E6dta8DwNv4"},"C6RAoGQiY%,s~4-sxGIn":{"id":"C6RAoGQiY%,s~4-sxGIn","opcode":"math_positive_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":3}},"next":null,"topLevel":false,"parent":"?*18UH)X%/FL?=-HoaFW","shadow":true},"2cQ#K+HkhAdqo)E)ApFo":{"id":"2cQ#K+HkhAdqo)E)ApFo","opcode":"control_delete_this_clone","inputs":{},"fields":{},"next":null,"shadow":false,"parent":"?*18UH)X%/FL?=-HoaFW"}},"variables":{},"lists":{},"costumes":[{"name":"dino_0000_Layer-16","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":1,"md5ext":"4e330592c1f1b80a4b182cc00cf8d6bf.png"},{"name":"dino_0001_Layer-15","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":2,"md5ext":"7a68a03190a519ecc4c3e1e6a6f32f41.png"},{"name":"dino_0002_Layer-14","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":3,"md5ext":"17427d759cac720f1575a36d624d10cc.png"},{"name":"dino_0003_Layer-13","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":5,"md5ext":"6563f3acffd6827bbdc86358599c6815.png"},{"name":"dino_0004_Layer-12","bitmapResolution":2,"rotationCenterX":94,"rotationCenterY":134,"skinId":4,"md5ext":"a2aca11ef43fde45cc3431402b25ba9e.png"},{"name":"dino_0005_Layer-11","bitmapResolution":2,"rotationCenterX":88,"rotationCenterY":134,"skinId":6,"md5ext":"79d431943530899519080b2b8ffe8f9f.png"},{"name":"dino_0006_Layer-10","bitmapResolution":2,"rotationCenterX":84,"rotationCenterY":130,"skinId":9,"md5ext":"67104cc6c05f895a5c108c2a920ff8d1.png"},{"name":"dino_0007_Layer-9","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":10,"md5ext":"39736b1f4fb42cf59463a1bfdcba2b5d.png"},{"name":"dino_0008_Layer-8","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":11,"md5ext":"16308e806c3a594832337aeac7cf52e0.png"},{"name":"dino_0009_Layer-7","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":122,"skinId":12,"md5ext":"ecd4cfd287a4f150d45411df5722362b.png"},{"name":"dino_0010_Layer-6","bitmapResolution":2,"rotationCenterX":80,"rotationCenterY":126,"skinId":13,"md5ext":"214be4e1341a9a49cfcc11c45c14e0e3.png"},{"name":"dino_0011_Layer-5","bitmapResolution":2,"rotationCenterX":92,"rotationCenterY":130,"skinId":18,"md5ext":"b00cf2e17ff81b5d42bb0d269362e845.png"},{"name":"dino_0012_Layer-4","bitmapResolution":2,"rotationCenterX":96,"rotationCenterY":134,"skinId":14,"md5ext":"3a25736b45392dcf740f91331aa13961.png"},{"name":"dino_0013_Layer-3","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":134,"skinId":17,"md5ext":"33276549c55db74a56090148fe1e8efa.png"},{"name":"dino_0014_Layer-2","bitmapResolution":2,"rotationCenterX":86,"rotationCenterY":130,"skinId":16,"md5ext":"5b71a9c255135caa2e7c34ce072480aa.png"},{"name":"dino_0015_Layer-1","bitmapResolution":2,"rotationCenterX":82,"rotationCenterY":134,"skinId":15,"md5ext":"4ee69e1b27147fd818144d611c138263.png"}],"sounds":[{"name":"alien creak1","format":"","rate":11025,"sampleCount":8045,"soundID":-1,"md5":"0377a7476136e5e8c780c64a4828922d.wav"}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} \ No newline at end of file +{"targets":[{"id":"poXv!.~0@*~j*F(Om/X5","name":"Stage","isStage":true,"x":0,"y":0,"size":100,"direction":90,"draggable":false,"currentCostume":10,"costume":{"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":7,"assetId":"7da4181ee167de7b3f5d1a91880277ff","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},"costumeCount":11,"visible":true,"rotationStyle":"all around","blocks":{"O7D.%E~TH^ULpAuHM8)@":{"id":"O7D.%E~TH^ULpAuHM8)@","opcode":"event_whenflagclicked","inputs":{},"fields":{},"next":"78+=E[P7b8!mBXMwQF`@","shadow":false,"x":138,"y":356.40000000000003,"topLevel":true,"parent":null},"78+=E[P7b8!mBXMwQF`@":{"id":"78+=E[P7b8!mBXMwQF`@","opcode":"control_forever","inputs":{"SUBSTACK":{"name":"SUBSTACK","block":"nlnk{2XqtVI*Qg,admbW","shadow":null}},"fields":{},"next":null,"shadow":false,"parent":"O7D.%E~TH^ULpAuHM8)@"},"nlnk{2XqtVI*Qg,admbW":{"id":"nlnk{2XqtVI*Qg,admbW","opcode":"looks_changeeffectby","inputs":{"EFFECT":{"name":"EFFECT","block":"*vh;qV87Q}5IP@sW=)wD","shadow":"*vh;qV87Q}5IP@sW=)wD"},"CHANGE":{"name":"CHANGE","block":"woYo[[v=PD(`R;qW{PZ%","shadow":"woYo[[v=PD(`R;qW{PZ%"}},"fields":{},"next":null,"shadow":false,"parent":"78+=E[P7b8!mBXMwQF`@"},"*vh;qV87Q}5IP@sW=)wD":{"id":"*vh;qV87Q}5IP@sW=)wD","opcode":"looks_effectmenu","inputs":{},"fields":{"EFFECT":{"name":"EFFECT","value":"color"}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true},"woYo[[v=PD(`R;qW{PZ%":{"id":"woYo[[v=PD(`R;qW{PZ%","opcode":"math_number","inputs":{},"fields":{"NUM":{"name":"NUM","value":1}},"next":null,"topLevel":false,"parent":"nlnk{2XqtVI*Qg,admbW","shadow":true}},"variables":{"vy":{"name":"vy","value":-3.5165000000000006,"isCloud":false},"g":{"name":"g","value":"-.5","isCloud":false},"vx":{"name":"vx","value":1.4580000000000002,"isCloud":false},"i":{"name":"i","value":3,"isCloud":false},"d1":{"name":"d1","value":0,"isCloud":false},"x":{"name":"x","value":233.9173278872492,"isCloud":false},"acceleration":{"name":"acceleration","value":"-3","isCloud":false}},"lists":{},"costumes":[{"name":"blue sky2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":0,"assetId":"7623e679b2baa2e7d48808614820844f","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"woods","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":3,"assetId":"1e0f7a4c932423b13250b5cb44928109","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"party","bitmapResolution":1,"rotationCenterX":251,"rotationCenterY":189,"skinId":2,"assetId":"108160d0e44d1c340182e31c9dc0758a","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"boardwalk","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":6,"assetId":"de0e54cd11551566f044e7e6bc588b2c","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"blue sky3","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":179,"skinId":1,"assetId":"2024d59c1980e667e8f656134796e2c1","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},{"name":"underwater1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":5,"assetId":"f339c6f31b11ea71d0fb8d607cec392e","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"underwater2","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":11,"assetId":"1517c21786d2d0edc2f3037408d850bd","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"stars","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":4,"assetId":"e87fed9c2a968dbeae8c94617e600e8c","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"parking-ramp","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":9,"assetId":"a7832479977c166ca0057f2a99a73305","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"backdrop1","bitmapResolution":2,"rotationCenterX":480,"rotationCenterY":360,"skinId":10,"assetId":"f67dc7de38bac6fbb0ab68e46352521d","assetType":{"contentType":"image/png","name":"ImageBitmap","runtimeFormat":"png"}},{"name":"backdrop2","bitmapResolution":1,"rotationCenterX":240,"rotationCenterY":180,"skinId":7,"assetId":"7da4181ee167de7b3f5d1a91880277ff","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}}],"sounds":[{"name":"pop","format":"","rate":11025,"sampleCount":258,"soundID":-1,"md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","assetType":{"contentType":"audio/x-wav","name":"Sound","runtimeFormat":"wav"}}]},{"id":"9!d8G^[6i]k*:l[W%4;l","name":"Earth","isStage":false,"x":-10,"y":10,"size":100,"direction":90,"draggable":false,"currentCostume":0,"costume":{"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"skinId":8,"assetId":"814197522984a302972998b1a7f92d91","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}},"costumeCount":1,"visible":true,"rotationStyle":"all around","blocks":{},"variables":{},"lists":{},"costumes":[{"name":"earth","bitmapResolution":1,"rotationCenterX":72,"rotationCenterY":72,"skinId":8,"assetId":"814197522984a302972998b1a7f92d91","assetType":{"contentType":"image/svg+xml","name":"ImageVector","runtimeFormat":"svg"}}],"sounds":[{"name":"pop","format":"","rate":11025,"sampleCount":258,"soundID":-1,"md5":"83a9787d4cb6f3b7632b4ddfebf74367.wav","data":null,"assetId":"83a9787d4cb6f3b7632b4ddfebf74367","assetType":{"contentType":"audio/x-wav","name":"Sound","runtimeFormat":"wav"}}]}],"meta":{"semver":"3.0.0","vm":"0.1.0","agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"}} From 635b40ec2b89e454532924564fe5a0d96d3d814e Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 02:22:12 -0400 Subject: [PATCH 40/42] Log parsing errors --- src/virtual-machine.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index f5906958e..fce3244ad 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -191,9 +191,15 @@ class VirtualMachine extends EventEmitter { this.clear(); // Validate & parse - if (typeof json !== 'string') return; + if (typeof json !== 'string') { + log.error('Failed to parse project. Non-string supplied to fromJSON.'); + return; + } json = JSON.parse(json); - if (typeof json !== 'object') return; + if (typeof json !== 'object') { + log.error('Failed to parse project. JSON supplied to fromJSON is not an object.'); + return; + } // Establish version, deserialize, and load into runtime // @todo Support Scratch 1.4 @@ -232,9 +238,15 @@ class VirtualMachine extends EventEmitter { */ addSprite2 (json) { // Validate & parse - if (typeof json !== 'string') return; + if (typeof json !== 'string') { + log.error('Failed to parse sprite. Non-string supplied to addSprite2.'); + return; + } json = JSON.parse(json); - if (typeof json !== 'object') return; + if (typeof json !== 'object') { + log.error('Failed to parse sprite. JSON supplied to addSprite2 is not an object.'); + return; + } // Select new sprite. return sb2.deserialize(json, this.runtime, true).then(targets => { From 33fc7978684076f85fa6575770eddae0c035366d Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 02:24:10 -0400 Subject: [PATCH 41/42] Select the stage if the project has no sprites --- src/virtual-machine.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/virtual-machine.js b/src/virtual-machine.js index fce3244ad..f85caf4b7 100644 --- a/src/virtual-machine.js +++ b/src/virtual-machine.js @@ -222,7 +222,11 @@ class VirtualMachine extends EventEmitter { } } // Select the first target for editing, e.g., the first sprite. - this.editingTarget = this.runtime.targets[1]; + if (this.runtime.targets.length > 1) { + this.editingTarget = this.runtime.targets[1]; + } else { + this.editingTarget = this.runtime.targets[0]; + } // Update the VM user's knowledge of targets and blocks on the workspace. this.emitTargetsUpdate(); From 620a4bd57b5780e8f1b83bbbf660102bb76f93ec Mon Sep 17 00:00:00 2001 From: Ray Schamp Date: Thu, 11 May 2017 12:47:06 -0400 Subject: [PATCH 42/42] Require scratch-storage minimum version of 0.1.0 This gives us the caching behavior required for serialization --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e359b393..ed0e2d185 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "scratch-audio": "latest", "scratch-blocks": "latest", "scratch-render": "latest", - "scratch-storage": "latest", + "scratch-storage": "^0.1.0", "script-loader": "0.7.0", "stats.js": "^0.17.0", "tap": "^10.2.0",