Merge pull request from kchadha/comment-save-load

Comment Save & Load
This commit is contained in:
kchadha 2018-06-14 18:42:19 -04:00 committed by GitHub
commit 29b79878bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 180 additions and 2 deletions
src
serialization
sprites
test

View file

@ -8,6 +8,7 @@ const vmPackage = require('../../package.json');
const Blocks = require('../engine/blocks');
const Sprite = require('../sprites/sprite');
const Variable = require('../engine/variable');
const Comment = require('../engine/comment');
const StageLayering = require('../engine/stage-layering');
const log = require('../util/log');
const uid = require('../util/uid');
@ -207,6 +208,9 @@ const serializeBlock = function (block) {
if (block.mutation) {
obj.mutation = block.mutation;
}
if (block.comment) {
obj.comment = block.comment;
}
return obj;
};
@ -384,6 +388,27 @@ const serializeVariables = function (variables) {
return obj;
};
const serializeComments = function (comments) {
const obj = Object.create(null);
for (const commentId in comments) {
if (!comments.hasOwnProperty(commentId)) continue;
const comment = comments[commentId];
const serializedComment = Object.create(null);
serializedComment.id = comment.id;
serializedComment.blockId = comment.blockId;
serializedComment.x = comment.x;
serializedComment.y = comment.y;
serializedComment.width = comment.width;
serializedComment.height = comment.height;
serializedComment.minimized = comment.minimized;
serializedComment.text = comment.text;
obj[commentId] = serializedComment;
}
return obj;
};
/**
* Serialize the given target. Only serialize properties that are necessary
* for saving and loading this target.
@ -399,6 +424,7 @@ const serializeTarget = function (target) {
obj.lists = vars.lists;
obj.broadcasts = vars.broadcasts;
obj.blocks = serializeBlocks(target.blocks);
obj.comments = serializeComments(target.comments);
obj.currentCostume = target.currentCostume;
obj.costumes = target.costumes.map(serializeCostume);
obj.sounds = target.sounds.map(serializeSound);
@ -834,6 +860,24 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
target.variables[newBroadcast.id] = newBroadcast;
}
}
if (object.hasOwnProperty('comments')) {
for (const commentId in object.comments) {
const comment = object.comments[commentId];
const newComment = new Comment(
comment.id,
comment.text,
comment.x,
comment.y,
comment.width,
comment.height,
comment.minimized
);
if (comment.blockId) {
newComment.blockId = comment.blockId;
}
target.comments[newComment.id] = newComment;
}
}
if (object.hasOwnProperty('x')) {
target.x = object.x;
}

View file

@ -1103,6 +1103,7 @@ class RenderedTarget extends Target {
costumeCount: costumes.length,
visible: this.visible,
rotationStyle: this.rotationStyle,
comments: this.comments,
blocks: this.blocks._blocks,
variables: this.variables,
costumes: costumes,

BIN
test/fixtures/comments.sb3 vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,91 @@
const path = require('path');
const test = require('tap').test;
const makeTestStorage = require('../fixtures/make-test-storage');
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
const VirtualMachine = require('../../src/index');
const projectUri = path.resolve(__dirname, '../fixtures/comments.sb3');
const project = readFileToBuffer(projectUri);
test('load an sb3 project with comments', t => {
const vm = new VirtualMachine();
vm.attachStorage(makeTestStorage());
// Evaluate playground data and exit
vm.on('playgroundData', e => {
const threads = JSON.parse(e.threads);
t.equal(threads.length, 0);
const stage = vm.runtime.targets[0];
const target = vm.runtime.targets[1];
const stageComments = Object.values(stage.comments);
// Stage has 1 comment, and it is minimized.
t.equal(stageComments.length, 1);
t.equal(stageComments[0].minimized, true);
t.equal(stageComments[0].text, 'A minimized stage comment.');
// The stage comment is a workspace comment
t.equal(stageComments[0].blockId, null);
// Sprite 1 has 6 Comments, 1 workspace comment, and 5 block comments
const targetComments = Object.values(target.comments);
t.equal(targetComments.length, 6);
const spriteWorkspaceComments = targetComments.filter(comment => comment.blockId === null);
t.equal(spriteWorkspaceComments.length, 1);
t.equal(spriteWorkspaceComments[0].minimized, false);
t.equal(spriteWorkspaceComments[0].text, 'This is a workspace comment.');
// Test the sprite block comments
const blockComments = targetComments.filter(comment => !!comment.blockId);
t.equal(blockComments.length, 5);
t.equal(blockComments[0].minimized, true);
t.equal(blockComments[0].text, '1. Green Flag Comment.');
const greenFlagBlock = target.blocks.getBlock(blockComments[0].blockId);
t.equal(greenFlagBlock.comment, blockComments[0].id);
t.equal(greenFlagBlock.opcode, 'event_whenflagclicked');
t.equal(blockComments[1].minimized, true);
t.equal(blockComments[1].text, '2. Turn 15 Degrees Comment.');
const turnRightBlock = target.blocks.getBlock(blockComments[1].blockId);
t.equal(turnRightBlock.comment, blockComments[1].id);
t.equal(turnRightBlock.opcode, 'motion_turnright');
t.equal(blockComments[2].minimized, false);
t.equal(blockComments[2].text, '3. Comment for a loop.');
const repeatBlock = target.blocks.getBlock(blockComments[2].blockId);
t.equal(repeatBlock.comment, blockComments[2].id);
t.equal(repeatBlock.opcode, 'control_repeat');
t.equal(blockComments[3].minimized, false);
t.equal(blockComments[3].text, '4. Comment for a block nested in a loop.');
const changeColorBlock = target.blocks.getBlock(blockComments[3].blockId);
t.equal(changeColorBlock.comment, blockComments[3].id);
t.equal(changeColorBlock.opcode, 'looks_changeeffectby');
t.equal(blockComments[4].minimized, false);
t.equal(blockComments[4].text, '5. Comment for a block outside of a loop.');
const stopAllBlock = target.blocks.getBlock(blockComments[4].blockId);
t.equal(stopAllBlock.comment, blockComments[4].id);
t.equal(stopAllBlock.opcode, 'control_stop');
t.end();
process.nextTick(process.exit);
});
// Start VM, load project, and run
t.doesNotThrow(() => {
vm.start();
vm.clear();
vm.setCompatibilityMode(false);
vm.setTurboMode(false);
vm.loadProject(project).then(() => {
vm.greenFlag();
setTimeout(() => {
vm.getPlaygroundData();
vm.stopAll();
}, 100);
});
});
});

View file

@ -3,11 +3,12 @@ const path = require('path');
const VirtualMachine = require('../../src/index');
const sb3 = require('../../src/serialization/sb3');
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
const projectPath = path.resolve(__dirname, '../fixtures/clone-cleanup.sb2');
const exampleProjectPath = path.resolve(__dirname, '../fixtures/clone-cleanup.sb2');
const commentsSB2ProjectPath = path.resolve(__dirname, '../fixtures/comments.sb2');
test('serialize', t => {
const vm = new VirtualMachine();
vm.loadProject(readFileToBuffer(projectPath))
vm.loadProject(readFileToBuffer(exampleProjectPath))
.then(() => {
const result = sb3.serialize(vm.runtime);
// @todo Analyze
@ -24,3 +25,44 @@ test('deserialize', t => {
t.end();
});
});
test('serialize sb2 project with comments as sb3', t => {
const vm = new VirtualMachine();
vm.loadProject(readFileToBuffer(commentsSB2ProjectPath))
.then(() => {
const result = sb3.serialize(vm.runtime);
t.type(JSON.stringify(result), 'string');
t.type(result.targets, 'object');
t.equal(Array.isArray(result.targets), true);
t.equal(result.targets.length, 2);
const stage = result.targets[0];
t.equal(stage.isStage, true);
// The stage has 0 blocks, and 1 workspace comment
t.type(stage.blocks, 'object');
t.equal(Object.keys(stage.blocks).length, 0);
t.type(stage.comments, 'object');
t.equal(Object.keys(stage.comments).length, 1);
const stageBlockComments = Object.values(stage.comments).filter(comment => !!comment.blockId);
const stageWorkspaceComments = Object.values(stage.comments).filter(comment => comment.blockId === null);
t.equal(stageBlockComments.length, 0);
t.equal(stageWorkspaceComments.length, 1);
const sprite = result.targets[1];
t.equal(sprite.isStage, false);
t.type(sprite.blocks, 'object');
// Sprite 1 has 6 blocks, 5 block comments, and 1 workspace comment
t.equal(Object.keys(sprite.blocks).length, 6);
t.type(sprite.comments, 'object');
t.equal(Object.keys(sprite.comments).length, 6);
const spriteBlockComments = Object.values(sprite.comments).filter(comment => !!comment.blockId);
const spriteWorkspaceComments = Object.values(sprite.comments).filter(comment => comment.blockId === null);
t.equal(spriteBlockComments.length, 5);
t.equal(spriteWorkspaceComments.length, 1);
t.end();
});
});