mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-07-25 13:39:03 -04:00
Merge pull request #687 from cwillisf/builtin-extensions
Builtin extensions
This commit is contained in:
commit
7051ccfd69
9 changed files with 373 additions and 69 deletions
|
@ -31,6 +31,7 @@
|
|||
"babel-loader": "^7.0.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"copy-webpack-plugin": "4.0.1",
|
||||
"escape-html": "1.0.3",
|
||||
"eslint": "^4.5.0",
|
||||
"eslint-config-scratch": "^4.0.0",
|
||||
"expose-loader": "0.7.3",
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const ArgumentType = require('../extension-support/argument-type');
|
||||
const BlockType = require('../extension-support/block-type');
|
||||
const Cast = require('../util/cast');
|
||||
const Clone = require('../util/clone');
|
||||
const Color = require('../util/color');
|
||||
|
@ -235,24 +237,116 @@ class Scratch3PenBlocks {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieve the block primitives implemented by this package.
|
||||
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||
* @returns {object} metadata for this extension and its blocks.
|
||||
*/
|
||||
getPrimitives () {
|
||||
getInfo () {
|
||||
return {
|
||||
pen_clear: this.clear,
|
||||
pen_stamp: this.stamp,
|
||||
pen_pendown: this.penDown,
|
||||
pen_penup: this.penUp,
|
||||
pen_setpencolortocolor: this.setPenColorToColor,
|
||||
pen_changepencolorby: this.changePenHueBy,
|
||||
pen_setpencolortonum: this.setPenHueToNumber,
|
||||
pen_changepenshadeby: this.changePenShadeBy,
|
||||
pen_setpenshadeto: this.setPenShadeToNumber,
|
||||
pen_changepensizeby: this.changePenSizeBy,
|
||||
pen_setpensizeto: this.setPenSizeTo,
|
||||
pen_changepentransparencyby: this.changePenTransparencyBy,
|
||||
pen_setpentransparencyto: this.setPenTransparencyTo
|
||||
id: 'pen',
|
||||
name: 'Pen',
|
||||
blocks: [
|
||||
{
|
||||
opcode: 'clear',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
NUM1: {
|
||||
type: ArgumentType.NUMBER
|
||||
},
|
||||
NUM2: {
|
||||
type: ArgumentType.NUMBER
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'stamp',
|
||||
blockType: BlockType.COMMAND
|
||||
},
|
||||
{
|
||||
opcode: 'penDown',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'pen down'
|
||||
},
|
||||
{
|
||||
opcode: 'penUp',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'pen up'
|
||||
},
|
||||
{
|
||||
opcode: 'setPenColorToColor',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'set pen color to [COLOR]',
|
||||
arguments: {
|
||||
COLOR: {
|
||||
type: ArgumentType.COLOR
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'changePenHueBy',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'change pen color by [COLOR]',
|
||||
arguments: {
|
||||
COLOR: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'setPenHueToNumber',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'set pen color to [COLOR]',
|
||||
arguments: {
|
||||
COLOR: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'changePenShadeBy',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'change pen shade by [SHADE]',
|
||||
arguments: {
|
||||
SHADE: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'setPenShadeToNumber',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'set pen shade to [SHADE]',
|
||||
arguments: {
|
||||
SHADE: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 50
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'changePenSizeBy',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'change pen size by [SIZE]',
|
||||
arguments: {
|
||||
SIZE: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'setPenSizeTo',
|
||||
blockType: BlockType.COMMAND,
|
||||
text: 'set pen size to [SIZE]',
|
||||
arguments: {
|
||||
SIZE: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const ArgumentType = require('../extension-support/argument-type');
|
||||
const BlockType = require('../extension-support/block-type');
|
||||
const color = require('../util/color');
|
||||
const log = require('../util/log');
|
||||
|
||||
|
@ -371,9 +373,9 @@ const TiltDirection = {
|
|||
class Scratch3WeDo2Blocks {
|
||||
|
||||
/**
|
||||
* @return {string} - the name of this extension.
|
||||
* @return {string} - the ID of this extension.
|
||||
*/
|
||||
static get EXTENSION_NAME () {
|
||||
static get EXTENSION_ID () {
|
||||
return 'wedo2';
|
||||
}
|
||||
|
||||
|
@ -395,7 +397,184 @@ class Scratch3WeDo2Blocks {
|
|||
*/
|
||||
this.runtime = runtime;
|
||||
|
||||
this.runtime.HACK_WeDo2Blocks = this;
|
||||
this.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object} metadata for this extension and its blocks.
|
||||
*/
|
||||
getInfo () {
|
||||
return {
|
||||
id: Scratch3WeDo2Blocks.EXTENSION_ID,
|
||||
name: 'WeDo 2.0',
|
||||
blocks: [
|
||||
{
|
||||
opcode: 'motorOnFor',
|
||||
text: 'turn [MOTOR_ID] on for [DURATION] seconds',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
MOTOR_ID: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'motorID',
|
||||
defaultValue: MotorID.DEFAULT
|
||||
},
|
||||
DURATION: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'motorOn',
|
||||
text: 'turn [MOTOR_ID] on',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
MOTOR_ID: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'motorID',
|
||||
defaultValue: MotorID.DEFAULT
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'motorOff',
|
||||
text: 'turn [MOTOR_ID] off',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
MOTOR_ID: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'motorID',
|
||||
defaultValue: MotorID.DEFAULT
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'startMotorPower',
|
||||
text: 'set [MOTOR_ID] power to [POWER]',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
MOTOR_ID: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'motorID',
|
||||
defaultValue: MotorID.DEFAULT
|
||||
},
|
||||
POWER: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'setMotorDirection',
|
||||
text: 'set [MOTOR_ID] direction to [DIRECTION]',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
MOTOR_ID: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'motorID',
|
||||
defaultValue: MotorID.DEFAULT
|
||||
},
|
||||
DIRECTION: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'motorDirection',
|
||||
defaultValue: MotorDirection.FORWARD
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'setLightHue',
|
||||
text: 'set light color to [HUE]',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
HUE: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 50
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'playNoteFor',
|
||||
text: 'play note [NOTE] for [DURATION] seconds',
|
||||
blockType: BlockType.COMMAND,
|
||||
arguments: {
|
||||
NOTE: {
|
||||
type: ArgumentType.NUMBER, // TODO: ArgumentType.MIDI_NOTE?
|
||||
defaultValue: 60
|
||||
},
|
||||
DURATION: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'whenDistance',
|
||||
text: 'when distance [OP] [REFERENCE]',
|
||||
blockType: BlockType.HAT,
|
||||
arguments: {
|
||||
OP: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'lessMore',
|
||||
defaultValue: '<'
|
||||
},
|
||||
REFERENCE: {
|
||||
type: ArgumentType.NUMBER,
|
||||
defaultValue: 50
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'whenTilted',
|
||||
text: 'when tilted [DIRECTION]',
|
||||
func: 'isTilted',
|
||||
blockType: BlockType.HAT,
|
||||
arguments: {
|
||||
DIRECTION: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'tiltDirectionAny',
|
||||
defaultValue: TiltDirection.ANY
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'getDistance',
|
||||
text: 'distance',
|
||||
blockType: BlockType.REPORTER
|
||||
},
|
||||
{
|
||||
opcode: 'isTilted',
|
||||
text: 'tilted [DIRECTION]?',
|
||||
blockType: BlockType.REPORTER,
|
||||
arguments: {
|
||||
DIRECTION: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'tiltDirectionAny',
|
||||
defaultValue: TiltDirection.ANY
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
opcode: 'getTiltAngle',
|
||||
text: 'tilt angle [DIRECTION]',
|
||||
blockType: BlockType.REPORTER,
|
||||
arguments: {
|
||||
DIRECTION: {
|
||||
type: ArgumentType.STRING,
|
||||
menu: 'tiltDirection',
|
||||
defaultValue: TiltDirection.UP
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
menus: {
|
||||
motorID: [MotorID.DEFAULT, MotorID.A, MotorID.B, MotorID.ALL],
|
||||
motorDirection: [MotorDirection.FORWARD, MotorDirection.BACKWARD, MotorDirection.REVERSE],
|
||||
tiltDirection: [TiltDirection.UP, TiltDirection.DOWN, TiltDirection.LEFT, TiltDirection.RIGHT],
|
||||
tiltDirectionAny:
|
||||
[TiltDirection.UP, TiltDirection.DOWN, TiltDirection.LEFT, TiltDirection.RIGHT, TiltDirection.ANY],
|
||||
lessMore: ['<', '>']
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -407,7 +586,7 @@ class Scratch3WeDo2Blocks {
|
|||
}
|
||||
const deviceManager = this.runtime.ioDevices.deviceManager;
|
||||
const finder = this._finder =
|
||||
deviceManager.searchAndConnect(Scratch3WeDo2Blocks.EXTENSION_NAME, WeDo2.DEVICE_TYPE);
|
||||
deviceManager.searchAndConnect(Scratch3WeDo2Blocks.EXTENSION_ID, WeDo2.DEVICE_TYPE);
|
||||
this._finder.promise.then(
|
||||
socket => {
|
||||
if (this._finder === finder) {
|
||||
|
@ -427,27 +606,6 @@ class Scratch3WeDo2Blocks {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the block primitives implemented by this package.
|
||||
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||
*/
|
||||
getPrimitives () {
|
||||
return {
|
||||
wedo2_motorOnFor: this.motorOnFor,
|
||||
wedo2_motorOn: this.motorOn,
|
||||
wedo2_motorOff: this.motorOff,
|
||||
wedo2_startMotorPower: this.startMotorPower,
|
||||
wedo2_setMotorDirection: this.setMotorDirection,
|
||||
wedo2_setLightHue: this.setLightHue,
|
||||
wedo2_playNoteFor: this.playNoteFor,
|
||||
wedo2_whenDistance: this.whenDistance,
|
||||
wedo2_whenTilted: this.whenTilted,
|
||||
wedo2_getDistance: this.getDistance,
|
||||
wedo2_isTilted: this.isTilted,
|
||||
wedo2_getTiltAngle: this.getTiltAngle
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn specified motor(s) on for a specified duration.
|
||||
* @param {object} args - the block's arguments.
|
||||
|
|
|
@ -91,6 +91,16 @@ class SharedDispatch {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a particular service lives on another worker.
|
||||
* @param {string} service - the service to check.
|
||||
* @returns {boolean} - true if the service is remote (calls must cross a Worker boundary), false otherwise.
|
||||
* @private
|
||||
*/
|
||||
_isRemoteService (service) {
|
||||
return this._getServiceProvider(service).isRemote;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link call}, but force the call to be posted through a particular communication channel.
|
||||
* @param {object} provider - send the call through this object's `postMessage` function.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const EventEmitter = require('events');
|
||||
const {OrderedMap} = require('immutable');
|
||||
const escapeHtml = require('escape-html');
|
||||
|
||||
const ArgumentType = require('../extension-support/argument-type');
|
||||
const Blocks = require('./blocks');
|
||||
|
@ -19,12 +20,10 @@ const defaultBlockPackages = {
|
|||
scratch3_looks: require('../blocks/scratch3_looks'),
|
||||
scratch3_motion: require('../blocks/scratch3_motion'),
|
||||
scratch3_operators: require('../blocks/scratch3_operators'),
|
||||
scratch3_pen: require('../blocks/scratch3_pen'),
|
||||
scratch3_sound: require('../blocks/scratch3_sound'),
|
||||
scratch3_sensing: require('../blocks/scratch3_sensing'),
|
||||
scratch3_data: require('../blocks/scratch3_data'),
|
||||
scratch3_procedures: require('../blocks/scratch3_procedures'),
|
||||
scratch3_wedo2: require('../blocks/scratch3_wedo2')
|
||||
scratch3_procedures: require('../blocks/scratch3_procedures')
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -469,7 +468,9 @@ class Runtime extends EventEmitter {
|
|||
|
||||
const argInfo = blockInfo.arguments[placeholder] || {};
|
||||
const argTypeInfo = ArgumentTypeMap[argInfo.type] || {};
|
||||
const defaultValue = (typeof argInfo.defaultValue === 'undefined' ? '' : argInfo.defaultValue.toString());
|
||||
const defaultValue = (typeof argInfo.defaultValue === 'undefined' ?
|
||||
'' :
|
||||
escapeHtml(argInfo.defaultValue.toString()));
|
||||
|
||||
// <value> is the ScratchBlocks name for a block input.
|
||||
// The <shadow> is a placeholder for a reporter and is visible when there's no reporter in this input.
|
||||
|
|
|
@ -3,6 +3,16 @@ const log = require('../util/log');
|
|||
|
||||
const BlockType = require('./block-type');
|
||||
|
||||
// These extensions are currently built into the VM repository but should not be loaded at startup.
|
||||
// TODO: move these out into a separate repository?
|
||||
// TODO: change extension spec so that library info, including extension ID, can be collected through static methods
|
||||
const Scratch3PenBlocks = require('../blocks/scratch3_pen');
|
||||
const Scratch3WeDo2Blocks = require('../blocks/scratch3_wedo2');
|
||||
const builtinExtensions = {
|
||||
pen: Scratch3PenBlocks,
|
||||
wedo2: Scratch3WeDo2Blocks
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {object} ArgumentInfo - Information about an extension block argument
|
||||
* @property {ArgumentType} type - the type of value this argument can take
|
||||
|
@ -39,7 +49,7 @@ const BlockType = require('./block-type');
|
|||
*/
|
||||
|
||||
class ExtensionManager {
|
||||
constructor () {
|
||||
constructor (runtime) {
|
||||
/**
|
||||
* The ID number to provide to the next extension worker.
|
||||
* @type {int}
|
||||
|
@ -60,17 +70,30 @@ class ExtensionManager {
|
|||
*/
|
||||
this.pendingWorkers = [];
|
||||
|
||||
/**
|
||||
* Keep a reference to the runtime so we can construct internal extension objects.
|
||||
* TODO: remove this in favor of extensions accessing the runtime as a service.
|
||||
* @type {Runtime}
|
||||
*/
|
||||
this.runtime = runtime;
|
||||
|
||||
dispatch.setService('extensions', this).catch(e => {
|
||||
log.error(`ExtensionManager was unable to register extension service: ${JSON.stringify(e)}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an extension by URL
|
||||
* @param {string} extensionURL - the URL for the extension to load
|
||||
* Load an extension by URL or internal extension ID
|
||||
* @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension
|
||||
* @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure
|
||||
*/
|
||||
loadExtensionURL (extensionURL) {
|
||||
if (builtinExtensions.hasOwnProperty(extensionURL)) {
|
||||
const extension = builtinExtensions[extensionURL];
|
||||
const extensionInstance = new extension(this.runtime);
|
||||
return this._registerInternalExtension(extensionInstance);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// If we `require` this at the global level it breaks non-webpack targets, including tests
|
||||
const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker');
|
||||
|
@ -175,7 +198,17 @@ class ExtensionManager {
|
|||
blockInfo.opcode = this._sanitizeID(blockInfo.opcode);
|
||||
blockInfo.func = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode;
|
||||
blockInfo.text = blockInfo.text || blockInfo.opcode;
|
||||
blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func);
|
||||
|
||||
/**
|
||||
* This is only here because the VM performs poorly when blocks return promises.
|
||||
* @TODO make it possible for the VM to resolve a promise and continue during the same frame.
|
||||
*/
|
||||
if (dispatch._isRemoteService(serviceName)) {
|
||||
blockInfo.func = dispatch.call.bind(dispatch, serviceName, blockInfo.func);
|
||||
} else {
|
||||
const serviceObject = dispatch.services[serviceName];
|
||||
blockInfo.func = serviceObject[blockInfo.func].bind(serviceObject);
|
||||
}
|
||||
return blockInfo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -500,27 +500,27 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'clearPenTrails': {
|
||||
opcode: 'pen_clear',
|
||||
opcode: 'pen.clear',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
'stampCostume': {
|
||||
opcode: 'pen_stamp',
|
||||
opcode: 'pen.stamp',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
'putPenDown': {
|
||||
opcode: 'pen_pendown',
|
||||
opcode: 'pen.penDown',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
'putPenUp': {
|
||||
opcode: 'pen_penup',
|
||||
opcode: 'pen.penUp',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
'penColor:': {
|
||||
opcode: 'pen_setpencolortocolor',
|
||||
opcode: 'pen.setPenColorToColor',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -530,7 +530,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'changePenHueBy:': {
|
||||
opcode: 'pen_changepencolorby',
|
||||
opcode: 'pen.changePenHueBy',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -540,7 +540,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'setPenHueTo:': {
|
||||
opcode: 'pen_setpencolortonum',
|
||||
opcode: 'pen.setPenHueToNumber',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -550,7 +550,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'changePenShadeBy:': {
|
||||
opcode: 'pen_changepenshadeby',
|
||||
opcode: 'pen.changePenShadeBy',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -560,7 +560,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'setPenShadeTo:': {
|
||||
opcode: 'pen_setpenshadeto',
|
||||
opcode: 'pen.setPenShadeToNumber',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -570,7 +570,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'changePenSizeBy:': {
|
||||
opcode: 'pen_changepensizeby',
|
||||
opcode: 'pen.changePenSizeBy',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -580,7 +580,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'penSize:': {
|
||||
opcode: 'pen_setpensizeto',
|
||||
opcode: 'pen.setPenSizeTo',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
|
|
@ -68,7 +68,7 @@ class VirtualMachine extends EventEmitter {
|
|||
this.emit(Runtime.EXTENSION_ADDED, blocksInfo);
|
||||
});
|
||||
|
||||
this.extensionManager = new ExtensionManager();
|
||||
this.extensionManager = new ExtensionManager(this.runtime);
|
||||
|
||||
this.blockListener = this.blockListener.bind(this);
|
||||
this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
const Worker = require('tiny-worker');
|
||||
const path = require('path');
|
||||
const test = require('tap').test;
|
||||
|
||||
const Scratch3PenBlocks = require('../../src/blocks/scratch3_pen');
|
||||
const VirtualMachine = require('../../src/index');
|
||||
const dispatch = require('../../src/dispatch/central-dispatch');
|
||||
|
||||
const makeTestStorage = require('../fixtures/make-test-storage');
|
||||
const extract = require('../fixtures/extract');
|
||||
|
@ -10,6 +12,9 @@ const extract = require('../fixtures/extract');
|
|||
const uri = path.resolve(__dirname, '../fixtures/pen.sb2');
|
||||
const project = extract(uri);
|
||||
|
||||
// By default Central Dispatch works with the Worker class built into the browser. Tell it to use TinyWorker instead.
|
||||
dispatch.workerClass = Worker;
|
||||
|
||||
test('pen', t => {
|
||||
const vm = new VirtualMachine();
|
||||
vm.attachStorage(makeTestStorage());
|
||||
|
@ -42,14 +47,16 @@ test('pen', t => {
|
|||
vm.clear();
|
||||
vm.setCompatibilityMode(false);
|
||||
vm.setTurboMode(false);
|
||||
vm.loadProject(project).then(() => {
|
||||
vm.greenFlag();
|
||||
vm.loadProject(project)
|
||||
.then(() => vm.extensionManager.loadExtensionURL('pen')) /** @TODO: loadProject should load extensions */
|
||||
.then(() => {
|
||||
vm.greenFlag();
|
||||
|
||||
// After two seconds, get playground data and stop
|
||||
setTimeout(() => {
|
||||
vm.getPlaygroundData();
|
||||
vm.stopAll();
|
||||
}, 2000);
|
||||
});
|
||||
// After two seconds, get playground data and stop
|
||||
setTimeout(() => {
|
||||
vm.getPlaygroundData();
|
||||
vm.stopAll();
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue