mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-08-28 22:30:40 -04:00
Move build outputs into a dist/
subdirectory (#375)
* Move Node output: /dist.js => /dist/node/scratch-vm.js * Move web output: /vm{.js,.min.js} => /dist/web/scratch-vm{.js,.min.js} * Update build output references in package.json and the playground's index.html * Move the VirtualMachine class out of index.js into its own file, referenced by index.js. The VirtualMachine class is otherwise unchanged. * Add .gitattributes rules for new file types which were added to this repository without specifying their text/binary attributes * Turn on source maps in webpack and add corresponding .gitignore rule
This commit is contained in:
parent
b2777aced1
commit
9b04392211
8 changed files with 371 additions and 358 deletions
|
@ -1,7 +1,4 @@
|
|||
build/*
|
||||
dist.js
|
||||
coverage/*
|
||||
dist/*
|
||||
node_modules/*
|
||||
playground/*
|
||||
vm.js
|
||||
vm.min.js
|
||||
coverage/*
|
||||
|
|
5
.gitattributes
vendored
5
.gitattributes
vendored
|
@ -5,8 +5,10 @@
|
|||
# People who (for example) rsync between Windows and Linux need this.
|
||||
|
||||
# File types which we know are binary
|
||||
*.sb2 binary
|
||||
|
||||
# Prefer LF for most file types
|
||||
*.css text eol=lf
|
||||
*.frag text eol=lf
|
||||
*.htm text eol=lf
|
||||
*.html text eol=lf
|
||||
|
@ -17,13 +19,16 @@
|
|||
*.md text eol=lf
|
||||
*.vert text eol=lf
|
||||
*.xml text eol=lf
|
||||
*.yml text eol=lf
|
||||
|
||||
# Prefer LF for these files
|
||||
.editorconfig text eol=lf
|
||||
.eslintignore text eol=lf
|
||||
.eslintrc text eol=lf
|
||||
.gitattributes text eol=lf
|
||||
.gitignore text eol=lf
|
||||
.gitmodules text eol=lf
|
||||
.npmignore text eol=lf
|
||||
LICENSE text eol=lf
|
||||
Makefile text eol=lf
|
||||
README text eol=lf
|
||||
|
|
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -8,11 +8,16 @@ npm-*
|
|||
# Testing
|
||||
/.nyc_output
|
||||
/coverage
|
||||
/dist.js
|
||||
/vm.js
|
||||
/vm.min.js
|
||||
|
||||
# IDEA
|
||||
/.idea
|
||||
|
||||
# Build
|
||||
/dist
|
||||
/playground/assets
|
||||
/playground/media
|
||||
/playground/scratch-vm.js
|
||||
/playground/scratch-vm.js.map
|
||||
/playground/vendor.js
|
||||
/playground/vm.js
|
||||
/playground/vendor.js.map
|
||||
/playground/zenburn.css
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/LLK/scratch-vm.git"
|
||||
},
|
||||
"main": "./dist.js",
|
||||
"main": "./dist/node/scratch-vm.js",
|
||||
"scripts": {
|
||||
"build": "./node_modules/.bin/webpack --progress --colors --bail",
|
||||
"coverage": "./node_modules/.bin/tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
<!-- FPS counter, Syntax highlighter, Blocks, Renderer -->
|
||||
<script src="./vendor.js"></script>
|
||||
<!-- VM Worker -->
|
||||
<script src="./vm.js"></script>
|
||||
<script src="./scratch-vm.js"></script>
|
||||
<!-- Playground -->
|
||||
<script src="./playground.js"></script>
|
||||
<script>
|
||||
|
|
338
src/index.js
338
src/index.js
|
@ -1,339 +1,3 @@
|
|||
var EventEmitter = require('events');
|
||||
var util = require('util');
|
||||
|
||||
var Runtime = require('./engine/runtime');
|
||||
var sb2import = require('./import/sb2import');
|
||||
|
||||
/**
|
||||
* Handles connections between blocks, stage, and extensions.
|
||||
* @constructor
|
||||
*/
|
||||
var VirtualMachine = function () {
|
||||
var instance = this;
|
||||
// Bind event emitter and runtime to VM instance
|
||||
EventEmitter.call(instance);
|
||||
/**
|
||||
* VM runtime, to store blocks, I/O devices, sprites/targets, etc.
|
||||
* @type {!Runtime}
|
||||
*/
|
||||
instance.runtime = new Runtime();
|
||||
/**
|
||||
* The "currently editing"/selected target ID for the VM.
|
||||
* Block events from any Blockly workspace are routed to this target.
|
||||
* @type {!string}
|
||||
*/
|
||||
instance.editingTarget = null;
|
||||
// Runtime emits are passed along as VM emits.
|
||||
instance.runtime.on(Runtime.SCRIPT_GLOW_ON, function (glowData) {
|
||||
instance.emit(Runtime.SCRIPT_GLOW_ON, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, function (glowData) {
|
||||
instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.BLOCK_GLOW_ON, function (glowData) {
|
||||
instance.emit(Runtime.BLOCK_GLOW_ON, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (glowData) {
|
||||
instance.emit(Runtime.BLOCK_GLOW_OFF, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.PROJECT_RUN_START, function () {
|
||||
instance.emit(Runtime.PROJECT_RUN_START);
|
||||
});
|
||||
instance.runtime.on(Runtime.PROJECT_RUN_STOP, function () {
|
||||
instance.emit(Runtime.PROJECT_RUN_STOP);
|
||||
});
|
||||
instance.runtime.on(Runtime.VISUAL_REPORT, function (visualReport) {
|
||||
instance.emit(Runtime.VISUAL_REPORT, visualReport);
|
||||
});
|
||||
instance.runtime.on(Runtime.SPRITE_INFO_REPORT, function (spriteInfo) {
|
||||
instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo);
|
||||
});
|
||||
|
||||
this.blockListener = this.blockListener.bind(this);
|
||||
this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from EventEmitter
|
||||
*/
|
||||
util.inherits(VirtualMachine, EventEmitter);
|
||||
|
||||
/**
|
||||
* Start running the VM - do this before anything else.
|
||||
*/
|
||||
VirtualMachine.prototype.start = function () {
|
||||
this.runtime.start();
|
||||
};
|
||||
|
||||
/**
|
||||
* "Green flag" handler - start all threads starting with a green flag.
|
||||
*/
|
||||
VirtualMachine.prototype.greenFlag = function () {
|
||||
this.runtime.greenFlag();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether the VM is in "turbo mode."
|
||||
* When true, loops don't yield to redraw.
|
||||
* @param {Boolean} turboModeOn Whether turbo mode should be set.
|
||||
*/
|
||||
VirtualMachine.prototype.setTurboMode = function (turboModeOn) {
|
||||
this.runtime.turboMode = !!turboModeOn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether the VM is in 2.0 "compatibility mode."
|
||||
* When true, ticks go at 2.0 speed (30 TPS).
|
||||
* @param {Boolean} compatibilityModeOn Whether compatibility mode is set.
|
||||
*/
|
||||
VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) {
|
||||
this.runtime.setCompatibilityMode(!!compatibilityModeOn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop all threads and running activities.
|
||||
*/
|
||||
VirtualMachine.prototype.stopAll = function () {
|
||||
this.runtime.stopAll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear out current running project data.
|
||||
*/
|
||||
VirtualMachine.prototype.clear = function () {
|
||||
this.runtime.dispose();
|
||||
this.editingTarget = null;
|
||||
this.emitTargetsUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get data for playground. Data comes back in an emitted event.
|
||||
*/
|
||||
VirtualMachine.prototype.getPlaygroundData = function () {
|
||||
var instance = this;
|
||||
// Only send back thread data for the current editingTarget.
|
||||
var threadData = this.runtime.threads.filter(function (thread) {
|
||||
return thread.target === instance.editingTarget;
|
||||
});
|
||||
// Remove the target key, since it's a circular reference.
|
||||
var filteredThreadData = JSON.stringify(threadData, function (key, value) {
|
||||
if (key === 'target') return;
|
||||
return value;
|
||||
}, 2);
|
||||
this.emit('playgroundData', {
|
||||
blocks: this.editingTarget.blocks,
|
||||
threads: filteredThreadData
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Post I/O data to the virtual devices.
|
||||
* @param {?string} device Name of virtual I/O device.
|
||||
* @param {Object} data Any data object to post to the I/O device.
|
||||
*/
|
||||
VirtualMachine.prototype.postIOData = function (device, data) {
|
||||
if (this.runtime.ioDevices[device]) {
|
||||
this.runtime.ioDevices[device].postData(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a project from a Scratch 2.0 JSON representation.
|
||||
* @param {?string} json JSON string representing the project.
|
||||
*/
|
||||
VirtualMachine.prototype.loadProject = function (json) {
|
||||
this.clear();
|
||||
// @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0.
|
||||
sb2import(json, this.runtime);
|
||||
// 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format.
|
||||
* @param {?string} json JSON string representing the sprite.
|
||||
*/
|
||||
VirtualMachine.prototype.addSprite2 = function (json) {
|
||||
// Select new sprite.
|
||||
this.editingTarget = sb2import(json, this.runtime, true);
|
||||
// Update the VM user's knowledge of targets and blocks on the workspace.
|
||||
this.emitTargetsUpdate();
|
||||
this.emitWorkspaceUpdate();
|
||||
this.runtime.setEditingTarget(this.editingTarget);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a costume to the current editing target.
|
||||
* @param {!Object} costumeObject Object representing the costume.
|
||||
*/
|
||||
VirtualMachine.prototype.addCostume = function (costumeObject) {
|
||||
this.editingTarget.sprite.costumes.push(costumeObject);
|
||||
// Switch to the costume.
|
||||
this.editingTarget.setCostume(
|
||||
this.editingTarget.sprite.costumes.length - 1
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a backdrop to the stage.
|
||||
* @param {!Object} backdropObject Object representing the backdrop.
|
||||
*/
|
||||
VirtualMachine.prototype.addBackdrop = function (backdropObject) {
|
||||
var stage = this.runtime.getTargetForStage();
|
||||
stage.sprite.costumes.push(backdropObject);
|
||||
// Switch to the backdrop.
|
||||
stage.setCostume(stage.sprite.costumes.length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a sprite.
|
||||
* @param {string} targetId ID of a target whose sprite to rename.
|
||||
* @param {string} newName New name of the sprite.
|
||||
*/
|
||||
VirtualMachine.prototype.renameSprite = function (targetId, newName) {
|
||||
var target = this.runtime.getTargetById(targetId);
|
||||
if (target) {
|
||||
if (!target.isSprite()) {
|
||||
throw new Error('Cannot rename non-sprite targets.');
|
||||
}
|
||||
var sprite = target.sprite;
|
||||
if (!sprite) {
|
||||
throw new Error('No sprite associated with this target.');
|
||||
}
|
||||
sprite.name = newName;
|
||||
this.emitTargetsUpdate();
|
||||
} else {
|
||||
throw new Error('No target with the provided id.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a sprite and all its clones.
|
||||
* @param {string} targetId ID of a target whose sprite to delete.
|
||||
*/
|
||||
VirtualMachine.prototype.deleteSprite = function (targetId) {
|
||||
var target = this.runtime.getTargetById(targetId);
|
||||
if (target) {
|
||||
if (!target.isSprite()) {
|
||||
throw new Error('Cannot delete non-sprite targets.');
|
||||
}
|
||||
var sprite = target.sprite;
|
||||
if (!sprite) {
|
||||
throw new Error('No sprite associated with this target.');
|
||||
}
|
||||
var currentEditingTarget = this.editingTarget;
|
||||
for (var i = 0; i < sprite.clones.length; i++) {
|
||||
var clone = sprite.clones[i];
|
||||
this.runtime.stopForTarget(sprite.clones[i]);
|
||||
this.runtime.disposeTarget(sprite.clones[i]);
|
||||
// Ensure editing target is switched if we are deleting it.
|
||||
if (clone === currentEditingTarget) {
|
||||
this.setEditingTarget(this.runtime.targets[0].id);
|
||||
}
|
||||
}
|
||||
// Sprite object should be deleted by GC.
|
||||
this.emitTargetsUpdate();
|
||||
} else {
|
||||
throw new Error('No target with the provided id.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the renderer for the VM/runtime
|
||||
* @param {!RenderWebGL} renderer The renderer to attach
|
||||
*/
|
||||
VirtualMachine.prototype.attachRenderer = function (renderer) {
|
||||
this.runtime.attachRenderer(renderer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the audio engine for the VM/runtime
|
||||
* @param {!AudioEngine} audioEngine The audio engine to attach
|
||||
*/
|
||||
VirtualMachine.prototype.attachAudioEngine = function (audioEngine) {
|
||||
this.runtime.attachAudioEngine(audioEngine);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a Blockly event for the current editing target.
|
||||
* @param {!Blockly.Event} e Any Blockly event.
|
||||
*/
|
||||
VirtualMachine.prototype.blockListener = function (e) {
|
||||
if (this.editingTarget) {
|
||||
this.editingTarget.blocks.blocklyListen(e, this.runtime);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a Blockly event for the flyout.
|
||||
* @param {!Blockly.Event} e Any Blockly event.
|
||||
*/
|
||||
VirtualMachine.prototype.flyoutBlockListener = function (e) {
|
||||
this.runtime.flyoutBlocks.blocklyListen(e, this.runtime);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an editing target. An editor UI can use this function to switch
|
||||
* between editing different targets, sprites, etc.
|
||||
* After switching the editing target, the VM may emit updates
|
||||
* to the list of targets and any attached workspace blocks
|
||||
* (see `emitTargetsUpdate` and `emitWorkspaceUpdate`).
|
||||
* @param {string} targetId Id of target to set as editing.
|
||||
*/
|
||||
VirtualMachine.prototype.setEditingTarget = function (targetId) {
|
||||
// Has the target id changed? If not, exit.
|
||||
if (targetId === this.editingTarget.id) {
|
||||
return;
|
||||
}
|
||||
var target = this.runtime.getTargetById(targetId);
|
||||
if (target) {
|
||||
this.editingTarget = target;
|
||||
// Emit appropriate UI updates.
|
||||
this.emitTargetsUpdate();
|
||||
this.emitWorkspaceUpdate();
|
||||
this.runtime.setEditingTarget(target);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit metadata about available targets.
|
||||
* An editor UI could use this to display a list of targets and show
|
||||
* the currently editing one.
|
||||
*/
|
||||
VirtualMachine.prototype.emitTargetsUpdate = function () {
|
||||
this.emit('targetsUpdate', {
|
||||
// [[target id, human readable target name], ...].
|
||||
targetList: this.runtime.targets.filter(function (target) {
|
||||
// Don't report clones.
|
||||
return !target.hasOwnProperty('isOriginal') || target.isOriginal;
|
||||
}).map(function (target) {
|
||||
return target.toJSON();
|
||||
}),
|
||||
// Currently editing target id.
|
||||
editingTarget: this.editingTarget ? this.editingTarget.id : null
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit an Blockly/scratch-blocks compatible XML representation
|
||||
* of the current editing target's blocks.
|
||||
*/
|
||||
VirtualMachine.prototype.emitWorkspaceUpdate = function () {
|
||||
this.emit('workspaceUpdate', {
|
||||
xml: this.editingTarget.blocks.toXML()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Post/edit sprite info for the current editing target.
|
||||
* @param {object} data An object with sprite info data to set.
|
||||
*/
|
||||
VirtualMachine.prototype.postSpriteInfo = function (data) {
|
||||
this.editingTarget.postSpriteInfo(data);
|
||||
};
|
||||
var VirtualMachine = require('./virtual-machine');
|
||||
|
||||
module.exports = VirtualMachine;
|
||||
|
|
339
src/virtual-machine.js
Normal file
339
src/virtual-machine.js
Normal file
|
@ -0,0 +1,339 @@
|
|||
var EventEmitter = require('events');
|
||||
var util = require('util');
|
||||
|
||||
var Runtime = require('./engine/runtime');
|
||||
var sb2import = require('./import/sb2import');
|
||||
|
||||
/**
|
||||
* Handles connections between blocks, stage, and extensions.
|
||||
* @constructor
|
||||
*/
|
||||
var VirtualMachine = function () {
|
||||
var instance = this;
|
||||
// Bind event emitter and runtime to VM instance
|
||||
EventEmitter.call(instance);
|
||||
/**
|
||||
* VM runtime, to store blocks, I/O devices, sprites/targets, etc.
|
||||
* @type {!Runtime}
|
||||
*/
|
||||
instance.runtime = new Runtime();
|
||||
/**
|
||||
* The "currently editing"/selected target ID for the VM.
|
||||
* Block events from any Blockly workspace are routed to this target.
|
||||
* @type {!string}
|
||||
*/
|
||||
instance.editingTarget = null;
|
||||
// Runtime emits are passed along as VM emits.
|
||||
instance.runtime.on(Runtime.SCRIPT_GLOW_ON, function (glowData) {
|
||||
instance.emit(Runtime.SCRIPT_GLOW_ON, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.SCRIPT_GLOW_OFF, function (glowData) {
|
||||
instance.emit(Runtime.SCRIPT_GLOW_OFF, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.BLOCK_GLOW_ON, function (glowData) {
|
||||
instance.emit(Runtime.BLOCK_GLOW_ON, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (glowData) {
|
||||
instance.emit(Runtime.BLOCK_GLOW_OFF, glowData);
|
||||
});
|
||||
instance.runtime.on(Runtime.PROJECT_RUN_START, function () {
|
||||
instance.emit(Runtime.PROJECT_RUN_START);
|
||||
});
|
||||
instance.runtime.on(Runtime.PROJECT_RUN_STOP, function () {
|
||||
instance.emit(Runtime.PROJECT_RUN_STOP);
|
||||
});
|
||||
instance.runtime.on(Runtime.VISUAL_REPORT, function (visualReport) {
|
||||
instance.emit(Runtime.VISUAL_REPORT, visualReport);
|
||||
});
|
||||
instance.runtime.on(Runtime.SPRITE_INFO_REPORT, function (spriteInfo) {
|
||||
instance.emit(Runtime.SPRITE_INFO_REPORT, spriteInfo);
|
||||
});
|
||||
|
||||
this.blockListener = this.blockListener.bind(this);
|
||||
this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from EventEmitter
|
||||
*/
|
||||
util.inherits(VirtualMachine, EventEmitter);
|
||||
|
||||
/**
|
||||
* Start running the VM - do this before anything else.
|
||||
*/
|
||||
VirtualMachine.prototype.start = function () {
|
||||
this.runtime.start();
|
||||
};
|
||||
|
||||
/**
|
||||
* "Green flag" handler - start all threads starting with a green flag.
|
||||
*/
|
||||
VirtualMachine.prototype.greenFlag = function () {
|
||||
this.runtime.greenFlag();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether the VM is in "turbo mode."
|
||||
* When true, loops don't yield to redraw.
|
||||
* @param {Boolean} turboModeOn Whether turbo mode should be set.
|
||||
*/
|
||||
VirtualMachine.prototype.setTurboMode = function (turboModeOn) {
|
||||
this.runtime.turboMode = !!turboModeOn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether the VM is in 2.0 "compatibility mode."
|
||||
* When true, ticks go at 2.0 speed (30 TPS).
|
||||
* @param {Boolean} compatibilityModeOn Whether compatibility mode is set.
|
||||
*/
|
||||
VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) {
|
||||
this.runtime.setCompatibilityMode(!!compatibilityModeOn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop all threads and running activities.
|
||||
*/
|
||||
VirtualMachine.prototype.stopAll = function () {
|
||||
this.runtime.stopAll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear out current running project data.
|
||||
*/
|
||||
VirtualMachine.prototype.clear = function () {
|
||||
this.runtime.dispose();
|
||||
this.editingTarget = null;
|
||||
this.emitTargetsUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get data for playground. Data comes back in an emitted event.
|
||||
*/
|
||||
VirtualMachine.prototype.getPlaygroundData = function () {
|
||||
var instance = this;
|
||||
// Only send back thread data for the current editingTarget.
|
||||
var threadData = this.runtime.threads.filter(function (thread) {
|
||||
return thread.target === instance.editingTarget;
|
||||
});
|
||||
// Remove the target key, since it's a circular reference.
|
||||
var filteredThreadData = JSON.stringify(threadData, function (key, value) {
|
||||
if (key === 'target') return;
|
||||
return value;
|
||||
}, 2);
|
||||
this.emit('playgroundData', {
|
||||
blocks: this.editingTarget.blocks,
|
||||
threads: filteredThreadData
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Post I/O data to the virtual devices.
|
||||
* @param {?string} device Name of virtual I/O device.
|
||||
* @param {Object} data Any data object to post to the I/O device.
|
||||
*/
|
||||
VirtualMachine.prototype.postIOData = function (device, data) {
|
||||
if (this.runtime.ioDevices[device]) {
|
||||
this.runtime.ioDevices[device].postData(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a project from a Scratch 2.0 JSON representation.
|
||||
* @param {?string} json JSON string representing the project.
|
||||
*/
|
||||
VirtualMachine.prototype.loadProject = function (json) {
|
||||
this.clear();
|
||||
// @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0.
|
||||
sb2import(json, this.runtime);
|
||||
// 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format.
|
||||
* @param {?string} json JSON string representing the sprite.
|
||||
*/
|
||||
VirtualMachine.prototype.addSprite2 = function (json) {
|
||||
// Select new sprite.
|
||||
this.editingTarget = sb2import(json, this.runtime, true);
|
||||
// Update the VM user's knowledge of targets and blocks on the workspace.
|
||||
this.emitTargetsUpdate();
|
||||
this.emitWorkspaceUpdate();
|
||||
this.runtime.setEditingTarget(this.editingTarget);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a costume to the current editing target.
|
||||
* @param {!Object} costumeObject Object representing the costume.
|
||||
*/
|
||||
VirtualMachine.prototype.addCostume = function (costumeObject) {
|
||||
this.editingTarget.sprite.costumes.push(costumeObject);
|
||||
// Switch to the costume.
|
||||
this.editingTarget.setCostume(
|
||||
this.editingTarget.sprite.costumes.length - 1
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a backdrop to the stage.
|
||||
* @param {!Object} backdropObject Object representing the backdrop.
|
||||
*/
|
||||
VirtualMachine.prototype.addBackdrop = function (backdropObject) {
|
||||
var stage = this.runtime.getTargetForStage();
|
||||
stage.sprite.costumes.push(backdropObject);
|
||||
// Switch to the backdrop.
|
||||
stage.setCostume(stage.sprite.costumes.length - 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a sprite.
|
||||
* @param {string} targetId ID of a target whose sprite to rename.
|
||||
* @param {string} newName New name of the sprite.
|
||||
*/
|
||||
VirtualMachine.prototype.renameSprite = function (targetId, newName) {
|
||||
var target = this.runtime.getTargetById(targetId);
|
||||
if (target) {
|
||||
if (!target.isSprite()) {
|
||||
throw new Error('Cannot rename non-sprite targets.');
|
||||
}
|
||||
var sprite = target.sprite;
|
||||
if (!sprite) {
|
||||
throw new Error('No sprite associated with this target.');
|
||||
}
|
||||
sprite.name = newName;
|
||||
this.emitTargetsUpdate();
|
||||
} else {
|
||||
throw new Error('No target with the provided id.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a sprite and all its clones.
|
||||
* @param {string} targetId ID of a target whose sprite to delete.
|
||||
*/
|
||||
VirtualMachine.prototype.deleteSprite = function (targetId) {
|
||||
var target = this.runtime.getTargetById(targetId);
|
||||
if (target) {
|
||||
if (!target.isSprite()) {
|
||||
throw new Error('Cannot delete non-sprite targets.');
|
||||
}
|
||||
var sprite = target.sprite;
|
||||
if (!sprite) {
|
||||
throw new Error('No sprite associated with this target.');
|
||||
}
|
||||
var currentEditingTarget = this.editingTarget;
|
||||
for (var i = 0; i < sprite.clones.length; i++) {
|
||||
var clone = sprite.clones[i];
|
||||
this.runtime.stopForTarget(sprite.clones[i]);
|
||||
this.runtime.disposeTarget(sprite.clones[i]);
|
||||
// Ensure editing target is switched if we are deleting it.
|
||||
if (clone === currentEditingTarget) {
|
||||
this.setEditingTarget(this.runtime.targets[0].id);
|
||||
}
|
||||
}
|
||||
// Sprite object should be deleted by GC.
|
||||
this.emitTargetsUpdate();
|
||||
} else {
|
||||
throw new Error('No target with the provided id.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the renderer for the VM/runtime
|
||||
* @param {!RenderWebGL} renderer The renderer to attach
|
||||
*/
|
||||
VirtualMachine.prototype.attachRenderer = function (renderer) {
|
||||
this.runtime.attachRenderer(renderer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the audio engine for the VM/runtime
|
||||
* @param {!AudioEngine} audioEngine The audio engine to attach
|
||||
*/
|
||||
VirtualMachine.prototype.attachAudioEngine = function (audioEngine) {
|
||||
this.runtime.attachAudioEngine(audioEngine);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a Blockly event for the current editing target.
|
||||
* @param {!Blockly.Event} e Any Blockly event.
|
||||
*/
|
||||
VirtualMachine.prototype.blockListener = function (e) {
|
||||
if (this.editingTarget) {
|
||||
this.editingTarget.blocks.blocklyListen(e, this.runtime);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a Blockly event for the flyout.
|
||||
* @param {!Blockly.Event} e Any Blockly event.
|
||||
*/
|
||||
VirtualMachine.prototype.flyoutBlockListener = function (e) {
|
||||
this.runtime.flyoutBlocks.blocklyListen(e, this.runtime);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an editing target. An editor UI can use this function to switch
|
||||
* between editing different targets, sprites, etc.
|
||||
* After switching the editing target, the VM may emit updates
|
||||
* to the list of targets and any attached workspace blocks
|
||||
* (see `emitTargetsUpdate` and `emitWorkspaceUpdate`).
|
||||
* @param {string} targetId Id of target to set as editing.
|
||||
*/
|
||||
VirtualMachine.prototype.setEditingTarget = function (targetId) {
|
||||
// Has the target id changed? If not, exit.
|
||||
if (targetId === this.editingTarget.id) {
|
||||
return;
|
||||
}
|
||||
var target = this.runtime.getTargetById(targetId);
|
||||
if (target) {
|
||||
this.editingTarget = target;
|
||||
// Emit appropriate UI updates.
|
||||
this.emitTargetsUpdate();
|
||||
this.emitWorkspaceUpdate();
|
||||
this.runtime.setEditingTarget(target);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit metadata about available targets.
|
||||
* An editor UI could use this to display a list of targets and show
|
||||
* the currently editing one.
|
||||
*/
|
||||
VirtualMachine.prototype.emitTargetsUpdate = function () {
|
||||
this.emit('targetsUpdate', {
|
||||
// [[target id, human readable target name], ...].
|
||||
targetList: this.runtime.targets.filter(function (target) {
|
||||
// Don't report clones.
|
||||
return !target.hasOwnProperty('isOriginal') || target.isOriginal;
|
||||
}).map(function (target) {
|
||||
return target.toJSON();
|
||||
}),
|
||||
// Currently editing target id.
|
||||
editingTarget: this.editingTarget ? this.editingTarget.id : null
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit an Blockly/scratch-blocks compatible XML representation
|
||||
* of the current editing target's blocks.
|
||||
*/
|
||||
VirtualMachine.prototype.emitWorkspaceUpdate = function () {
|
||||
this.emit('workspaceUpdate', {
|
||||
xml: this.editingTarget.blocks.toXML()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Post/edit sprite info for the current editing target.
|
||||
* @param {object} data An object with sprite info data to set.
|
||||
*/
|
||||
VirtualMachine.prototype.postSpriteInfo = function (data) {
|
||||
this.editingTarget.postSpriteInfo(data);
|
||||
};
|
||||
|
||||
module.exports = VirtualMachine;
|
|
@ -9,6 +9,7 @@ var base = {
|
|||
host: '0.0.0.0',
|
||||
port: process.env.PORT || 8073
|
||||
},
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
|
@ -29,14 +30,15 @@ var base = {
|
|||
};
|
||||
|
||||
module.exports = [
|
||||
// Web-compatible, playground
|
||||
// Web-compatible
|
||||
defaultsDeep({}, base, {
|
||||
target: 'web',
|
||||
entry: {
|
||||
'vm': './src/index.js',
|
||||
'vm.min': './src/index.js'
|
||||
'scratch-vm': './src/index.js',
|
||||
'scratch-vm.min': './src/index.js'
|
||||
},
|
||||
output: {
|
||||
path: __dirname,
|
||||
path: path.resolve(__dirname, 'dist/web'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
|
@ -48,24 +50,25 @@ module.exports = [
|
|||
])
|
||||
}
|
||||
}),
|
||||
// Webpack-compatible
|
||||
// Node-compatible
|
||||
defaultsDeep({}, base, {
|
||||
target: 'node',
|
||||
entry: {
|
||||
dist: './src/index.js'
|
||||
'scratch-vm': './src/index.js'
|
||||
},
|
||||
|
||||
output: {
|
||||
library: 'VirtualMachine',
|
||||
libraryTarget: 'commonjs2',
|
||||
path: __dirname,
|
||||
path: path.resolve(__dirname, 'dist/node'),
|
||||
filename: '[name].js'
|
||||
}
|
||||
}),
|
||||
// Playground
|
||||
defaultsDeep({}, base, {
|
||||
target: 'web',
|
||||
entry: {
|
||||
vm: './src/index.js',
|
||||
vendor: [
|
||||
'scratch-vm': './src/index.js',
|
||||
'vendor': [
|
||||
// FPS counter
|
||||
'stats.js/build/stats.min.js',
|
||||
// Syntax highlighter
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue