mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-06-10 04:21:09 -04:00
Merge branch 'develop' into greenkeeper/tap-11.0.1
This commit is contained in:
commit
a9dd658445
18 changed files with 492 additions and 119 deletions
|
@ -59,6 +59,7 @@
|
|||
"socket.io-client": "2.0.4",
|
||||
"stats.js": "^0.17.0",
|
||||
"tap": "^11.0.1",
|
||||
"text-encoding": "0.6.4",
|
||||
"tiny-worker": "^2.1.1",
|
||||
"webpack": "^3.10.0",
|
||||
"webpack-dev-server": "^2.4.1",
|
||||
|
|
|
@ -233,21 +233,19 @@ class Scratch3LooksBlocks {
|
|||
looks_cleargraphiceffects: this.clearEffects,
|
||||
looks_changesizeby: this.changeSize,
|
||||
looks_setsizeto: this.setSize,
|
||||
looks_gotofront: this.goToFront,
|
||||
looks_gobacklayers: this.goBackLayers,
|
||||
looks_gotofrontback: this.goToFrontBack,
|
||||
looks_goforwardbackwardlayers: this.goForwardBackwardLayers,
|
||||
looks_size: this.getSize,
|
||||
looks_costumeorder: this.getCostumeIndex,
|
||||
looks_backdroporder: this.getBackdropIndex,
|
||||
looks_backdropname: this.getBackdropName
|
||||
looks_costumenumbername: this.getCostumeNumberName,
|
||||
looks_backdropnumbername: this.getBackdropNumberName
|
||||
};
|
||||
}
|
||||
|
||||
getMonitored () {
|
||||
return {
|
||||
looks_size: {isSpriteSpecific: true},
|
||||
looks_costumeorder: {isSpriteSpecific: true},
|
||||
looks_backdroporder: {},
|
||||
looks_backdropname: {}
|
||||
looks_costumenumbername: {isSpriteSpecific: true},
|
||||
looks_backdropnumbername: {}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -409,32 +407,45 @@ class Scratch3LooksBlocks {
|
|||
util.target.setSize(size);
|
||||
}
|
||||
|
||||
goToFront (args, util) {
|
||||
goToFrontBack (args, util) {
|
||||
if (!util.target.isStage) {
|
||||
util.target.goToFront();
|
||||
if (args.FRONT_BACK === 'front') {
|
||||
util.target.goToFront();
|
||||
} else {
|
||||
util.target.goToBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goBackLayers (args, util) {
|
||||
util.target.goBackLayers(args.NUM);
|
||||
goForwardBackwardLayers (args, util) {
|
||||
if (!util.target.isStage) {
|
||||
if (args.FORWARD_BACKWARD === 'forward') {
|
||||
util.target.goForwardLayers(Cast.toNumber(args.NUM));
|
||||
} else {
|
||||
util.target.goBackwardLayers(Cast.toNumber(args.NUM));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getSize (args, util) {
|
||||
return Math.round(util.target.size);
|
||||
}
|
||||
|
||||
getBackdropIndex () {
|
||||
const stage = this.runtime.getTargetForStage();
|
||||
return stage.currentCostume + 1;
|
||||
}
|
||||
|
||||
getBackdropName () {
|
||||
getBackdropNumberName (args) {
|
||||
const stage = this.runtime.getTargetForStage();
|
||||
if (args.NUMBER_NAME === 'number') {
|
||||
return stage.currentCostume + 1;
|
||||
}
|
||||
// Else return name
|
||||
return stage.sprite.costumes[stage.currentCostume].name;
|
||||
}
|
||||
|
||||
getCostumeIndex (args, util) {
|
||||
return util.target.currentCostume + 1;
|
||||
getCostumeNumberName (args, util) {
|
||||
if (args.NUMBER_NAME === 'number') {
|
||||
return util.target.currentCostume + 1;
|
||||
}
|
||||
// Else return name
|
||||
return util.target.sprite.costumes[util.target.currentCostume].name;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ class Scratch3MotionBlocks {
|
|||
let targetX = 0;
|
||||
let targetY = 0;
|
||||
if (targetName === '_mouse_') {
|
||||
targetX = util.ioQuery('mouse', 'getX');
|
||||
targetY = util.ioQuery('mouse', 'getY');
|
||||
targetX = util.ioQuery('mouse', 'getScratchX');
|
||||
targetY = util.ioQuery('mouse', 'getScratchY');
|
||||
} else if (targetName === '_random_') {
|
||||
const stageWidth = this.runtime.constructor.STAGE_WIDTH;
|
||||
const stageHeight = this.runtime.constructor.STAGE_HEIGHT;
|
||||
|
@ -106,8 +106,8 @@ class Scratch3MotionBlocks {
|
|||
let targetX = 0;
|
||||
let targetY = 0;
|
||||
if (args.TOWARDS === '_mouse_') {
|
||||
targetX = util.ioQuery('mouse', 'getX');
|
||||
targetY = util.ioQuery('mouse', 'getY');
|
||||
targetX = util.ioQuery('mouse', 'getScratchX');
|
||||
targetY = util.ioQuery('mouse', 'getScratchY');
|
||||
} else {
|
||||
const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS);
|
||||
if (!pointTarget) return;
|
||||
|
@ -155,7 +155,7 @@ class Scratch3MotionBlocks {
|
|||
util.yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glideTo (args, util) {
|
||||
const targetXY = this.getTargetXY(args.TO, util);
|
||||
if (targetXY) {
|
||||
|
|
|
@ -40,6 +40,7 @@ class Scratch3SensingBlocks {
|
|||
sensing_of: this.getAttributeOf,
|
||||
sensing_mousex: this.getMouseX,
|
||||
sensing_mousey: this.getMouseY,
|
||||
sensing_setdragmode: this.setDragMode,
|
||||
sensing_mousedown: this.getMouseDown,
|
||||
sensing_keypressed: this.getKeyPressed,
|
||||
sensing_current: this.current,
|
||||
|
@ -55,7 +56,6 @@ class Scratch3SensingBlocks {
|
|||
sensing_answer: {},
|
||||
sensing_loudness: {},
|
||||
sensing_timer: {},
|
||||
sensing_of: {},
|
||||
sensing_current: {}
|
||||
};
|
||||
}
|
||||
|
@ -119,8 +119,8 @@ class Scratch3SensingBlocks {
|
|||
touchingObject (args, util) {
|
||||
const requestedObject = args.TOUCHINGOBJECTMENU;
|
||||
if (requestedObject === '_mouse_') {
|
||||
const mouseX = util.ioQuery('mouse', 'getX');
|
||||
const mouseY = util.ioQuery('mouse', 'getY');
|
||||
const mouseX = util.ioQuery('mouse', 'getClientX');
|
||||
const mouseY = util.ioQuery('mouse', 'getClientY');
|
||||
return util.target.isTouchingPoint(mouseX, mouseY);
|
||||
} else if (requestedObject === '_edge_') {
|
||||
return util.target.isTouchingEdge();
|
||||
|
@ -146,8 +146,8 @@ class Scratch3SensingBlocks {
|
|||
let targetX = 0;
|
||||
let targetY = 0;
|
||||
if (args.DISTANCETOMENU === '_mouse_') {
|
||||
targetX = util.ioQuery('mouse', 'getX');
|
||||
targetY = util.ioQuery('mouse', 'getY');
|
||||
targetX = util.ioQuery('mouse', 'getScratchX');
|
||||
targetY = util.ioQuery('mouse', 'getScratchY');
|
||||
} else {
|
||||
const distTarget = this.runtime.getSpriteTargetByName(
|
||||
args.DISTANCETOMENU
|
||||
|
@ -162,6 +162,10 @@ class Scratch3SensingBlocks {
|
|||
return Math.sqrt((dx * dx) + (dy * dy));
|
||||
}
|
||||
|
||||
setDragMode (args, util) {
|
||||
util.target.setDraggable(args.DRAG_MODE === 'draggable');
|
||||
}
|
||||
|
||||
getTimer (args, util) {
|
||||
return util.ioQuery('clock', 'projectTimer');
|
||||
}
|
||||
|
@ -171,11 +175,11 @@ class Scratch3SensingBlocks {
|
|||
}
|
||||
|
||||
getMouseX (args, util) {
|
||||
return util.ioQuery('mouse', 'getX');
|
||||
return util.ioQuery('mouse', 'getScratchX');
|
||||
}
|
||||
|
||||
getMouseY (args, util) {
|
||||
return util.ioQuery('mouse', 'getY');
|
||||
return util.ioQuery('mouse', 'getScratchY');
|
||||
}
|
||||
|
||||
getMouseDown (args, util) {
|
||||
|
|
|
@ -3,6 +3,7 @@ const mutationAdapter = require('./mutation-adapter');
|
|||
const xmlEscape = require('../util/xml-escape');
|
||||
const MonitorRecord = require('./monitor-record');
|
||||
const Clone = require('../util/clone');
|
||||
const {Map} = require('immutable');
|
||||
|
||||
/**
|
||||
* @fileoverview
|
||||
|
@ -382,22 +383,38 @@ class Blocks {
|
|||
block.fields[args.name].id = args.value;
|
||||
}
|
||||
} else {
|
||||
// Changing the value in a dropdown
|
||||
block.fields[args.name].value = args.value;
|
||||
|
||||
if (!optRuntime){
|
||||
break;
|
||||
}
|
||||
|
||||
const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block;
|
||||
if (flyoutBlock.isMonitored) {
|
||||
optRuntime.requestUpdateMonitor(Map({
|
||||
id: flyoutBlock.id,
|
||||
params: this._getBlockParams(flyoutBlock)
|
||||
}));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'mutation':
|
||||
block.mutation = mutationAdapter(args.value);
|
||||
break;
|
||||
case 'checkbox':
|
||||
case 'checkbox': {
|
||||
block.isMonitored = args.value;
|
||||
if (optRuntime) {
|
||||
const isSpriteSpecific = optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
|
||||
optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific;
|
||||
block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null;
|
||||
if (!optRuntime) {
|
||||
break;
|
||||
}
|
||||
if (optRuntime && wasMonitored && !block.isMonitored) {
|
||||
|
||||
const isSpriteSpecific = optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
|
||||
optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific;
|
||||
block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null;
|
||||
|
||||
if (wasMonitored && !block.isMonitored) {
|
||||
optRuntime.requestRemoveMonitor(block.id);
|
||||
} else if (optRuntime && !wasMonitored && block.isMonitored) {
|
||||
} else if (!wasMonitored && block.isMonitored) {
|
||||
optRuntime.requestAddMonitor(MonitorRecord({
|
||||
// @todo(vm#564) this will collide if multiple sprites use same block
|
||||
id: block.id,
|
||||
|
@ -411,6 +428,7 @@ class Blocks {
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.resetCache();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ const BlockUtility = require('./block-utility');
|
|||
const log = require('../util/log');
|
||||
const Thread = require('./thread');
|
||||
const {Map} = require('immutable');
|
||||
const cast = require('../util/cast');
|
||||
|
||||
/**
|
||||
* Single BlockUtility instance reused by execute for every pritimive ran.
|
||||
|
@ -211,7 +212,30 @@ const execute = function (sequencer, thread) {
|
|||
currentStackFrame.waitingReporter = null;
|
||||
thread.popStack();
|
||||
}
|
||||
argValues[inputName] = currentStackFrame.reported[inputName];
|
||||
const inputValue = currentStackFrame.reported[inputName];
|
||||
if (inputName === 'BROADCAST_INPUT') {
|
||||
const broadcastInput = inputs[inputName];
|
||||
// Check if something is plugged into the broadcast block, or
|
||||
// if the shadow dropdown menu is being used.
|
||||
if (broadcastInput.block === broadcastInput.shadow) {
|
||||
// Shadow dropdown menu is being used.
|
||||
// Get the appropriate information out of it.
|
||||
const shadow = blockContainer.getBlock(broadcastInput.shadow);
|
||||
const broadcastField = shadow.fields.BROADCAST_OPTION;
|
||||
argValues.BROADCAST_OPTION = {
|
||||
id: broadcastField.id,
|
||||
name: broadcastField.value
|
||||
};
|
||||
} else {
|
||||
// Something is plugged into the broadcast input.
|
||||
// Cast it to a string. We don't need an id here.
|
||||
argValues.BROADCAST_OPTION = {
|
||||
name: cast.toString(inputValue)
|
||||
};
|
||||
}
|
||||
} else {
|
||||
argValues[inputName] = inputValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add any mutation to args (e.g., for procedures).
|
||||
|
|
|
@ -98,12 +98,19 @@ class Target extends EventEmitter {
|
|||
* if it exists.
|
||||
* @param {string} id Id of the variable.
|
||||
* @param {string} name Name of the variable.
|
||||
* @return {!Variable} Variable object.
|
||||
* @return {?Variable} Variable object.
|
||||
*/
|
||||
lookupBroadcastMsg (id, name) {
|
||||
const broadcastMsg = this.lookupVariableById(id);
|
||||
let broadcastMsg;
|
||||
if (id) {
|
||||
broadcastMsg = this.lookupVariableById(id);
|
||||
} else if (name) {
|
||||
broadcastMsg = this.lookupBroadcastByInputValue(name);
|
||||
} else {
|
||||
log.error('Cannot find broadcast message if neither id nor name are provided.');
|
||||
}
|
||||
if (broadcastMsg) {
|
||||
if (broadcastMsg.name !== name) {
|
||||
if (name && (broadcastMsg.name.toLowerCase() !== name.toLowerCase())) {
|
||||
log.error(`Found broadcast message with id: ${id}, but` +
|
||||
`its name, ${broadcastMsg.name} did not match expected name ${name}.`);
|
||||
}
|
||||
|
@ -115,6 +122,23 @@ class Target extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a broadcast message with the given name and return the variable
|
||||
* if it exists. Does not create a new broadcast message variable if
|
||||
* it doesn't exist.
|
||||
* @param {string} name Name of the variable.
|
||||
* @return {?Variable} Variable object.
|
||||
*/
|
||||
lookupBroadcastByInputValue (name) {
|
||||
const vars = this.variables;
|
||||
for (const propName in vars) {
|
||||
if ((vars[propName].type === Variable.BROADCAST_MESSAGE_TYPE) &&
|
||||
(vars[propName].name.toLowerCase() === name.toLowerCase())) {
|
||||
return vars[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a variable object.
|
||||
* Search begins for local variables; then look for globals.
|
||||
|
@ -141,7 +165,7 @@ class Target extends EventEmitter {
|
|||
* Search begins for local lists; then look for globals.
|
||||
* @param {!string} id Id of the list.
|
||||
* @param {!string} name Name of the list.
|
||||
* @return {!List} List object.
|
||||
* @return {!Varible} Variable object representing the found/created list.
|
||||
*/
|
||||
lookupOrCreateList (id, name) {
|
||||
const list = this.lookupVariableById(id);
|
||||
|
|
|
@ -181,14 +181,18 @@ class Scratch3PenBlocks {
|
|||
* @param {RenderedTarget} target - the target which has moved.
|
||||
* @param {number} oldX - the previous X position.
|
||||
* @param {number} oldY - the previous Y position.
|
||||
* @param {boolean} isForce - whether the movement was forced.
|
||||
* @private
|
||||
*/
|
||||
_onTargetMoved (target, oldX, oldY) {
|
||||
const penSkinId = this._getPenLayerID();
|
||||
if (penSkinId >= 0) {
|
||||
const penState = this._getPenState(target);
|
||||
this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y);
|
||||
this.runtime.requestRedraw();
|
||||
_onTargetMoved (target, oldX, oldY, isForce) {
|
||||
// Only move the pen if the movement isn't forced (ie. dragged).
|
||||
if (!isForce) {
|
||||
const penSkinId = this._getPenLayerID();
|
||||
if (penSkinId >= 0) {
|
||||
const penState = this._getPenState(target);
|
||||
this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y);
|
||||
this.runtime.requestRedraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,7 +450,7 @@ class Scratch3PenBlocks {
|
|||
blockType: BlockType.COMMAND,
|
||||
text: formatMessage({
|
||||
id: 'pen.setHue',
|
||||
default: 'set pen hue to [HUE]',
|
||||
default: 'set pen color to [HUE]',
|
||||
description: 'legacy pen blocks - set pen color to number'
|
||||
}),
|
||||
arguments: {
|
||||
|
@ -462,7 +466,7 @@ class Scratch3PenBlocks {
|
|||
blockType: BlockType.COMMAND,
|
||||
text: formatMessage({
|
||||
id: 'pen.changeHue',
|
||||
default: 'change pen hue by [HUE]',
|
||||
default: 'change pen color by [HUE]',
|
||||
description: 'legacy pen blocks - change pen color'
|
||||
}),
|
||||
arguments: {
|
||||
|
@ -673,6 +677,8 @@ class Scratch3PenBlocks {
|
|||
const hueValue = Cast.toNumber(args.HUE);
|
||||
const colorValue = hueValue / 2;
|
||||
this._setOrChangeColorParam(ColorParam.COLOR, colorValue, penState, false);
|
||||
|
||||
this._legacyUpdatePenColor(penState);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -686,6 +692,8 @@ class Scratch3PenBlocks {
|
|||
const hueChange = Cast.toNumber(args.HUE);
|
||||
const colorChange = hueChange / 2;
|
||||
this._setOrChangeColorParam(ColorParam.COLOR, colorChange, penState, true);
|
||||
|
||||
this._legacyUpdatePenColor(penState);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -705,25 +713,10 @@ class Scratch3PenBlocks {
|
|||
newShade = newShade % 200;
|
||||
if (newShade < 0) newShade += 200;
|
||||
|
||||
// Create the new color in RGB using the scratch 2 "shade" model
|
||||
let rgb = Color.hsvToRgb({h: penState.color * 360 / 100, s: 1, v: 1});
|
||||
const shade = (newShade > 100) ? 200 - newShade : newShade;
|
||||
if (shade < 50) {
|
||||
rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60);
|
||||
} else {
|
||||
rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60);
|
||||
}
|
||||
|
||||
// Update the pen state according to new color
|
||||
const hsv = Color.rgbToHsv(rgb);
|
||||
penState.color = 100 * hsv.h / 360;
|
||||
penState.saturation = 100 * hsv.s;
|
||||
penState.brightness = 100 * hsv.v;
|
||||
|
||||
// And store the shade that was used to compute this new color for later use.
|
||||
penState._shade = newShade;
|
||||
|
||||
this._updatePenColor(penState);
|
||||
this._legacyUpdatePenColor(penState);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -739,6 +732,29 @@ class Scratch3PenBlocks {
|
|||
this.setPenShadeToNumber({SHADE: penState._shade + shadeChange}, util);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the pen state's color from its hue & shade values, Scratch 2.0 style.
|
||||
* @param {object} penState - update the HSV & RGB values in this pen state from its hue & shade values.
|
||||
* @private
|
||||
*/
|
||||
_legacyUpdatePenColor (penState) {
|
||||
// Create the new color in RGB using the scratch 2 "shade" model
|
||||
let rgb = Color.hsvToRgb({h: penState.color * 360 / 100, s: 1, v: 1});
|
||||
const shade = (penState._shade > 100) ? 200 - penState._shade : penState._shade;
|
||||
if (shade < 50) {
|
||||
rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60);
|
||||
} else {
|
||||
rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60);
|
||||
}
|
||||
|
||||
// Update the pen state according to new color
|
||||
const hsv = Color.rgbToHsv(rgb);
|
||||
penState.color = 100 * hsv.h / 360;
|
||||
penState.saturation = 100 * hsv.s;
|
||||
penState.brightness = 100 * hsv.v;
|
||||
|
||||
this._updatePenColor(penState);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Scratch3PenBlocks;
|
||||
|
|
|
@ -40,10 +40,20 @@ class Mouse {
|
|||
*/
|
||||
postData (data) {
|
||||
if (data.x) {
|
||||
this._x = data.x - (data.canvasWidth / 2);
|
||||
this._clientX = data.x;
|
||||
this._scratchX = MathUtil.clamp(
|
||||
480 * ((data.x / data.canvasWidth) - 0.5),
|
||||
-240,
|
||||
240
|
||||
);
|
||||
}
|
||||
if (data.y) {
|
||||
this._y = data.y - (data.canvasHeight / 2);
|
||||
this._clientY = data.y;
|
||||
this._scratchY = MathUtil.clamp(
|
||||
-360 * ((data.y / data.canvasHeight) - 0.5),
|
||||
-180,
|
||||
180
|
||||
);
|
||||
}
|
||||
if (typeof data.isDown !== 'undefined') {
|
||||
this._isDown = data.isDown;
|
||||
|
@ -54,19 +64,35 @@ class Mouse {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the X position of the mouse.
|
||||
* @return {number} Clamped X position of the mouse cursor.
|
||||
* Get the X position of the mouse in client coordinates.
|
||||
* @return {number} Non-clamped X position of the mouse cursor.
|
||||
*/
|
||||
getX () {
|
||||
return MathUtil.clamp(this._x, -240, 240);
|
||||
getClientX () {
|
||||
return this._clientX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y position of the mouse.
|
||||
* Get the Y position of the mouse in client coordinates.
|
||||
* @return {number} Non-clamped Y position of the mouse cursor.
|
||||
*/
|
||||
getClientY () {
|
||||
return this._clientY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X position of the mouse in scratch coordinates.
|
||||
* @return {number} Clamped X position of the mouse cursor.
|
||||
*/
|
||||
getScratchX () {
|
||||
return this._scratchX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y position of the mouse in scratch coordinates.
|
||||
* @return {number} Clamped Y position of the mouse cursor.
|
||||
*/
|
||||
getY () {
|
||||
return MathUtil.clamp(-this._y, -180, 180);
|
||||
getScratchY () {
|
||||
return this._scratchY;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -170,14 +170,23 @@ const generateVariableIdGetter = (function () {
|
|||
|
||||
const globalBroadcastMsgStateGenerator = (function () {
|
||||
let broadcastMsgNameMap = {};
|
||||
const allBroadcastFields = [];
|
||||
const emptyStringName = uid();
|
||||
return function (topLevel) {
|
||||
if (topLevel) broadcastMsgNameMap = {};
|
||||
return {
|
||||
broadcastMsgMapUpdater: function (name) {
|
||||
broadcastMsgMapUpdater: function (name, field) {
|
||||
name = name.toLowerCase();
|
||||
if (name === '') {
|
||||
name = emptyStringName;
|
||||
}
|
||||
broadcastMsgNameMap[name] = `broadcastMsgId-${name}`;
|
||||
allBroadcastFields.push(field);
|
||||
return broadcastMsgNameMap[name];
|
||||
},
|
||||
globalBroadcastMsgs: broadcastMsgNameMap
|
||||
globalBroadcastMsgs: broadcastMsgNameMap,
|
||||
allBroadcastFields: allBroadcastFields,
|
||||
emptyMsgName: emptyStringName
|
||||
};
|
||||
};
|
||||
}());
|
||||
|
@ -340,6 +349,31 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
|||
// all other targets have finished processing.
|
||||
if (target.isStage) {
|
||||
const allBroadcastMsgs = globalBroadcastMsgObj.globalBroadcastMsgs;
|
||||
const allBroadcastMsgFields = globalBroadcastMsgObj.allBroadcastFields;
|
||||
const oldEmptyMsgName = globalBroadcastMsgObj.emptyMsgName;
|
||||
if (allBroadcastMsgs[oldEmptyMsgName]) {
|
||||
// Find a fresh 'messageN'
|
||||
let currIndex = 1;
|
||||
while (allBroadcastMsgs[`message${currIndex}`]) {
|
||||
currIndex += 1;
|
||||
}
|
||||
const newEmptyMsgName = `message${currIndex}`;
|
||||
// Add the new empty message name to the broadcast message
|
||||
// name map, and assign it the old id.
|
||||
// Then, delete the old entry in map.
|
||||
allBroadcastMsgs[newEmptyMsgName] = allBroadcastMsgs[oldEmptyMsgName];
|
||||
delete allBroadcastMsgs[oldEmptyMsgName];
|
||||
// Now update all the broadcast message fields with
|
||||
// the new empty message name.
|
||||
for (let i = 0; i < allBroadcastMsgFields.length; i++) {
|
||||
if (allBroadcastMsgFields[i].value === '') {
|
||||
allBroadcastMsgFields[i].value = newEmptyMsgName;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Traverse the broadcast message name map and create
|
||||
// broadcast messages as variables on the stage (which is this
|
||||
// target).
|
||||
for (const msgName in allBroadcastMsgs) {
|
||||
const msgId = allBroadcastMsgs[msgName];
|
||||
const newMsg = new Variable(
|
||||
|
@ -494,6 +528,11 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension
|
|||
if (shadowObscured) {
|
||||
fieldValue = '#990000';
|
||||
}
|
||||
} else if (expectedArg.inputOp === 'event_broadcast_menu') {
|
||||
fieldName = 'BROADCAST_OPTION';
|
||||
if (shadowObscured) {
|
||||
fieldValue = '';
|
||||
}
|
||||
} else if (shadowObscured) {
|
||||
// Filled drop-down menu.
|
||||
fieldValue = '';
|
||||
|
@ -503,6 +542,23 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension
|
|||
name: fieldName,
|
||||
value: fieldValue
|
||||
};
|
||||
// event_broadcast_menus have some extra properties to add to the
|
||||
// field and a different value than the rest
|
||||
if (expectedArg.inputOp === 'event_broadcast_menu') {
|
||||
if (!shadowObscured) {
|
||||
// Need to update the broadcast message name map with
|
||||
// the value of this field.
|
||||
// Also need to provide the fields[fieldName] object,
|
||||
// so that we can later update its value property, e.g.
|
||||
// if sb2 message name is empty string, we will later
|
||||
// replace this field's value with messageN
|
||||
// once we can traverse through all the existing message names
|
||||
// and come up with a fresh messageN.
|
||||
const broadcastId = addBroadcastMsg(fieldValue, fields[fieldName]);
|
||||
fields[fieldName].id = broadcastId;
|
||||
}
|
||||
fields[fieldName].variableType = expectedArg.variableType;
|
||||
}
|
||||
activeBlock.children.push({
|
||||
id: inputUid,
|
||||
opcode: expectedArg.inputOp,
|
||||
|
@ -529,8 +585,14 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension
|
|||
// Add `id` property to variable fields
|
||||
activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg);
|
||||
} else if (expectedArg.fieldName === 'BROADCAST_OPTION') {
|
||||
// add the name in this field to the broadcast msg name map
|
||||
const broadcastId = addBroadcastMsg(providedArg);
|
||||
// Add the name in this field to the broadcast msg name map.
|
||||
// Also need to provide the fields[fieldName] object,
|
||||
// so that we can later update its value property, e.g.
|
||||
// if sb2 message name is empty string, we will later
|
||||
// replace this field's value with messageN
|
||||
// once we can traverse through all the existing message names
|
||||
// and come up with a fresh messageN.
|
||||
const broadcastId = addBroadcastMsg(providedArg, activeBlock.fields[expectedArg.fieldName]);
|
||||
activeBlock.fields[expectedArg.fieldName].id = broadcastId;
|
||||
}
|
||||
const varType = expectedArg.variableType;
|
||||
|
@ -539,6 +601,41 @@ const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extension
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updates for blocks that have new menus (e.g. in Looks)
|
||||
switch (oldOpcode) {
|
||||
case 'comeToFront':
|
||||
activeBlock.fields.FRONT_BACK = {
|
||||
name: 'FRONT_BACK',
|
||||
value: 'front'
|
||||
};
|
||||
break;
|
||||
case 'goBackByLayers:':
|
||||
activeBlock.fields.FORWARD_BACKWARD = {
|
||||
name: 'FORWARD_BACKWARD',
|
||||
value: 'backward'
|
||||
};
|
||||
break;
|
||||
case 'backgroundIndex':
|
||||
activeBlock.fields.NUMBER_NAME = {
|
||||
name: 'NUMBER_NAME',
|
||||
value: 'number'
|
||||
};
|
||||
break;
|
||||
case 'sceneName':
|
||||
activeBlock.fields.NUMBER_NAME = {
|
||||
name: 'NUMBER_NAME',
|
||||
value: 'name'
|
||||
};
|
||||
break;
|
||||
case 'costumeIndex':
|
||||
activeBlock.fields.NUMBER_NAME = {
|
||||
name: 'NUMBER_NAME',
|
||||
value: 'number'
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
// Special cases to generate mutations.
|
||||
if (oldOpcode === 'stopScripts') {
|
||||
// Mutation for stop block: if the argument is 'other scripts',
|
||||
|
|
|
@ -345,12 +345,12 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'comeToFront': {
|
||||
opcode: 'looks_gotofront',
|
||||
opcode: 'looks_gotofrontback',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
'goBackByLayers:': {
|
||||
opcode: 'looks_gobacklayers',
|
||||
opcode: 'looks_goforwardbackwardlayers',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
|
@ -360,12 +360,12 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'costumeIndex': {
|
||||
opcode: 'looks_costumeorder',
|
||||
opcode: 'looks_costumenumbername',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
'sceneName': {
|
||||
opcode: 'looks_backdropname',
|
||||
opcode: 'looks_backdropnumbername',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
|
@ -390,7 +390,7 @@ const specMap = {
|
|||
]
|
||||
},
|
||||
'backgroundIndex': {
|
||||
opcode: 'looks_backdroporder',
|
||||
opcode: 'looks_backdropnumbername',
|
||||
argMap: [
|
||||
]
|
||||
},
|
||||
|
@ -665,8 +665,9 @@ const specMap = {
|
|||
opcode: 'event_broadcast',
|
||||
argMap: [
|
||||
{
|
||||
type: 'field',
|
||||
fieldName: 'BROADCAST_OPTION',
|
||||
type: 'input',
|
||||
inputOp: 'event_broadcast_menu',
|
||||
inputName: 'BROADCAST_INPUT',
|
||||
variableType: Variable.BROADCAST_MESSAGE_TYPE
|
||||
}
|
||||
]
|
||||
|
@ -675,8 +676,9 @@ const specMap = {
|
|||
opcode: 'event_broadcastandwait',
|
||||
argMap: [
|
||||
{
|
||||
type: 'field',
|
||||
fieldName: 'BROADCAST_OPTION',
|
||||
type: 'input',
|
||||
inputOp: 'event_broadcast_menu',
|
||||
inputName: 'BROADCAST_INPUT',
|
||||
variableType: Variable.BROADCAST_MESSAGE_TYPE
|
||||
}
|
||||
]
|
||||
|
@ -859,9 +861,8 @@ const specMap = {
|
|||
opcode: 'sensing_keypressed',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'sensing_keyoptions',
|
||||
inputName: 'KEY_OPTION'
|
||||
type: 'field',
|
||||
fieldName: 'KEY_OPTION'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -934,9 +935,8 @@ const specMap = {
|
|||
opcode: 'sensing_of',
|
||||
argMap: [
|
||||
{
|
||||
type: 'input',
|
||||
inputOp: 'sensing_of_property_menu',
|
||||
inputName: 'PROPERTY'
|
||||
type: 'field',
|
||||
fieldName: 'PROPERTY'
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
|
|
|
@ -203,7 +203,7 @@ class RenderedTarget extends Target {
|
|||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY);
|
||||
this.emit(RenderedTarget.EVENT_TARGET_MOVED, this, oldX, oldY, force);
|
||||
this.runtime.requestTargetsUpdate(this);
|
||||
}
|
||||
|
||||
|
@ -616,8 +616,7 @@ class RenderedTarget extends Target {
|
|||
// Limits test to this Drawable, so this will return true
|
||||
// even if the clone is obscured by another Drawable.
|
||||
const pickResult = this.runtime.renderer.pick(
|
||||
x + (this.runtime.constructor.STAGE_WIDTH / 2),
|
||||
-y + (this.runtime.constructor.STAGE_HEIGHT / 2),
|
||||
x, y,
|
||||
null, null,
|
||||
[this.drawableID]
|
||||
);
|
||||
|
@ -699,10 +698,29 @@ class RenderedTarget extends Target {
|
|||
}
|
||||
|
||||
/**
|
||||
* Move back a number of layers.
|
||||
* @param {number} nLayers How many layers to go back.
|
||||
* Move to the back layer.
|
||||
*/
|
||||
goBackLayers (nLayers) {
|
||||
goToBack () {
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, -Infinity, false, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move forward a number of layers.
|
||||
* @param {number} nLayers How many layers to go forward.
|
||||
*/
|
||||
goForwardLayers (nLayers) {
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, nLayers, true, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move backward a number of layers.
|
||||
* @param {number} nLayers How many layers to go backward.
|
||||
*/
|
||||
goBackwardLayers (nLayers) {
|
||||
if (this.renderer) {
|
||||
this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1);
|
||||
}
|
||||
|
@ -812,7 +830,6 @@ class RenderedTarget extends Target {
|
|||
newTarget.effects = JSON.parse(JSON.stringify(this.effects));
|
||||
newTarget.variables = JSON.parse(JSON.stringify(this.variables));
|
||||
newTarget.lists = JSON.parse(JSON.stringify(this.lists));
|
||||
newTarget.initDrawable();
|
||||
newTarget.updateAllDrawableProperties();
|
||||
newTarget.goBehindOther(this);
|
||||
return newTarget;
|
||||
|
@ -845,11 +862,10 @@ class RenderedTarget extends Target {
|
|||
*/
|
||||
postSpriteInfo (data) {
|
||||
const force = data.hasOwnProperty('force') ? data.force : null;
|
||||
if (data.hasOwnProperty('x')) {
|
||||
this.setXY(data.x, this.y, force);
|
||||
}
|
||||
if (data.hasOwnProperty('y')) {
|
||||
this.setXY(this.x, data.y, force);
|
||||
const isXChanged = data.hasOwnProperty('x');
|
||||
const isYChanged = data.hasOwnProperty('y');
|
||||
if (isXChanged || isYChanged) {
|
||||
this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force);
|
||||
}
|
||||
if (data.hasOwnProperty('direction')) {
|
||||
this.setDirection(data.direction);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const TextEncoder = require('text-encoding').TextEncoder;
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const centralDispatch = require('./dispatch/central-dispatch');
|
||||
|
@ -8,6 +9,7 @@ const sb2 = require('./serialization/sb2');
|
|||
const sb3 = require('./serialization/sb3');
|
||||
const StringUtil = require('./util/string-util');
|
||||
const formatMessage = require('format-message');
|
||||
const Variable = require('./engine/variable');
|
||||
|
||||
const {loadCostume} = require('./import/load-costume.js');
|
||||
const {loadSound} = require('./import/load-sound.js');
|
||||
|
@ -267,6 +269,8 @@ class VirtualMachine extends EventEmitter {
|
|||
targets.forEach(target => {
|
||||
this.runtime.targets.push(target);
|
||||
(/** @type RenderedTarget */ target).updateAllDrawableProperties();
|
||||
// Ensure unique sprite name
|
||||
if (target.isSprite()) this.renameSprite(target.id, target.getName());
|
||||
});
|
||||
// Select the first target for editing, e.g., the first sprite.
|
||||
if (wholeProject && (targets.length > 1)) {
|
||||
|
@ -447,7 +451,7 @@ class VirtualMachine extends EventEmitter {
|
|||
addBackdrop (md5ext, backdropObject) {
|
||||
return loadCostume(md5ext, backdropObject, this.runtime).then(() => {
|
||||
const stage = this.runtime.getTargetForStage();
|
||||
stage.sprite.costumes.push(backdropObject);
|
||||
stage.addCostume(backdropObject);
|
||||
stage.setCostume(stage.sprite.costumes.length - 1);
|
||||
});
|
||||
}
|
||||
|
@ -628,7 +632,7 @@ class VirtualMachine extends EventEmitter {
|
|||
*/
|
||||
setEditingTarget (targetId) {
|
||||
// Has the target id changed? If not, exit.
|
||||
if (targetId === this.editingTarget.id) {
|
||||
if (this.editingTarget && targetId === this.editingTarget.id) {
|
||||
return;
|
||||
}
|
||||
const target = this.runtime.getTargetById(targetId);
|
||||
|
@ -677,6 +681,35 @@ class VirtualMachine extends EventEmitter {
|
|||
* of the current editing target's blocks.
|
||||
*/
|
||||
emitWorkspaceUpdate () {
|
||||
// Create a list of broadcast message Ids according to the stage variables
|
||||
const stageVariables = this.runtime.getTargetForStage().variables;
|
||||
let messageIds = [];
|
||||
for (const varId in stageVariables) {
|
||||
if (stageVariables[varId].type === Variable.BROADCAST_MESSAGE_TYPE) {
|
||||
messageIds.push(varId);
|
||||
}
|
||||
}
|
||||
// Go through all blocks on all targets, removing referenced
|
||||
// broadcast ids from the list.
|
||||
for (let i = 0; i < this.runtime.targets.length; i++) {
|
||||
const currTarget = this.runtime.targets[i];
|
||||
const currBlocks = currTarget.blocks._blocks;
|
||||
for (const blockId in currBlocks) {
|
||||
if (currBlocks[blockId].fields.BROADCAST_OPTION) {
|
||||
const id = currBlocks[blockId].fields.BROADCAST_OPTION.id;
|
||||
const index = messageIds.indexOf(id);
|
||||
if (index !== -1) {
|
||||
messageIds = messageIds.slice(0, index)
|
||||
.concat(messageIds.slice(index + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Anything left in messageIds is not referenced by a block, so delete it.
|
||||
for (let i = 0; i < messageIds.length; i++) {
|
||||
const id = messageIds[i];
|
||||
delete this.runtime.getTargetForStage().variables[id];
|
||||
}
|
||||
const variableMap = Object.assign({},
|
||||
this.runtime.getTargetForStage().variables,
|
||||
this.editingTarget.variables
|
||||
|
|
52
test/unit/blocks_looks.js
Normal file
52
test/unit/blocks_looks.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
const test = require('tap').test;
|
||||
const Looks = require('../../src/blocks/scratch3_looks');
|
||||
const util = {
|
||||
target: {
|
||||
currentCostume: 0, // Internally, current costume is 0 indexed
|
||||
sprite: {
|
||||
costumes: [
|
||||
{name: 'first name'},
|
||||
{name: 'second name'},
|
||||
{name: 'third name'}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const fakeRuntime = {
|
||||
getTargetForStage: () => util.target, // Just return the dummy target above.
|
||||
on: () => {} // Stub out listener methods used in constructor.
|
||||
};
|
||||
const blocks = new Looks(fakeRuntime);
|
||||
|
||||
test('getCostumeNumberName returns 1-indexed costume number', t => {
|
||||
util.target.currentCostume = 0; // This is 0-indexed.
|
||||
const args = {NUMBER_NAME: 'number'};
|
||||
const number = blocks.getCostumeNumberName(args, util);
|
||||
t.strictEqual(number, 1);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('getCostumeNumberName can return costume name', t => {
|
||||
util.target.currentCostume = 0; // This is 0-indexed.
|
||||
const args = {NUMBER_NAME: 'name'};
|
||||
const number = blocks.getCostumeNumberName(args, util);
|
||||
t.strictEqual(number, 'first name');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('getBackdropNumberName returns 1-indexed costume number', t => {
|
||||
util.target.currentCostume = 2; // This is 0-indexed.
|
||||
const args = {NUMBER_NAME: 'number'};
|
||||
const number = blocks.getBackdropNumberName(args, util);
|
||||
t.strictEqual(number, 3);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('getBackdropNumberName can return costume name', t => {
|
||||
util.target.currentCostume = 2; // This is 0-indexed.
|
||||
const args = {NUMBER_NAME: 'name'};
|
||||
const number = blocks.getBackdropNumberName(args, util);
|
||||
t.strictEqual(number, 'third name');
|
||||
t.end();
|
||||
});
|
|
@ -1,6 +1,8 @@
|
|||
const test = require('tap').test;
|
||||
const Sensing = require('../../src/blocks/scratch3_sensing');
|
||||
const Runtime = require('../../src/engine/runtime');
|
||||
const Sprite = require('../../src/sprites/sprite');
|
||||
const RenderedTarget = require('../../src/sprites/rendered-target');
|
||||
|
||||
test('getPrimitives', t => {
|
||||
const rt = new Runtime();
|
||||
|
@ -65,3 +67,19 @@ test('ask and answer with a visible target', t => {
|
|||
|
||||
s.askAndWait({QUESTION: expectedQuestion}, util);
|
||||
});
|
||||
|
||||
test('set drag mode', t => {
|
||||
const runtime = new Runtime();
|
||||
runtime.requestTargetsUpdate = () => {}; // noop for testing
|
||||
const sensing = new Sensing(runtime);
|
||||
const s = new Sprite();
|
||||
const rt = new RenderedTarget(s, runtime);
|
||||
|
||||
sensing.setDragMode({DRAG_MODE: 'not draggable'}, {target: rt});
|
||||
t.strictEqual(rt.draggable, false);
|
||||
|
||||
sensing.setDragMode({DRAG_MODE: 'draggable'}, {target: rt});
|
||||
t.strictEqual(rt.draggable, true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
|
|
@ -8,8 +8,10 @@ test('spec', t => {
|
|||
|
||||
t.type(m, 'object');
|
||||
t.type(m.postData, 'function');
|
||||
t.type(m.getX, 'function');
|
||||
t.type(m.getY, 'function');
|
||||
t.type(m.getClientX, 'function');
|
||||
t.type(m.getClientY, 'function');
|
||||
t.type(m.getScratchX, 'function');
|
||||
t.type(m.getScratchY, 'function');
|
||||
t.type(m.getIsDown, 'function');
|
||||
t.end();
|
||||
});
|
||||
|
@ -19,14 +21,16 @@ test('mouseUp', t => {
|
|||
const m = new Mouse(rt);
|
||||
|
||||
m.postData({
|
||||
x: 1,
|
||||
x: -20,
|
||||
y: 10,
|
||||
isDown: false,
|
||||
canvasWidth: 480,
|
||||
canvasHeight: 360
|
||||
});
|
||||
t.strictEquals(m.getX(), -239);
|
||||
t.strictEquals(m.getY(), 170);
|
||||
t.strictEquals(m.getClientX(), -20);
|
||||
t.strictEquals(m.getClientY(), 10);
|
||||
t.strictEquals(m.getScratchX(), -240);
|
||||
t.strictEquals(m.getScratchY(), 170);
|
||||
t.strictEquals(m.getIsDown(), false);
|
||||
t.end();
|
||||
});
|
||||
|
@ -37,13 +41,32 @@ test('mouseDown', t => {
|
|||
|
||||
m.postData({
|
||||
x: 10,
|
||||
y: 100,
|
||||
y: 400,
|
||||
isDown: true,
|
||||
canvasWidth: 480,
|
||||
canvasHeight: 360
|
||||
});
|
||||
t.strictEquals(m.getX(), -230);
|
||||
t.strictEquals(m.getY(), 80);
|
||||
t.strictEquals(m.getClientX(), 10);
|
||||
t.strictEquals(m.getClientY(), 400);
|
||||
t.strictEquals(m.getScratchX(), -230);
|
||||
t.strictEquals(m.getScratchY(), -180);
|
||||
t.strictEquals(m.getIsDown(), true);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('at zoomed scale', t => {
|
||||
const rt = new Runtime();
|
||||
const m = new Mouse(rt);
|
||||
|
||||
m.postData({
|
||||
x: 240,
|
||||
y: 540,
|
||||
canvasWidth: 960,
|
||||
canvasHeight: 720
|
||||
});
|
||||
t.strictEquals(m.getClientX(), 240);
|
||||
t.strictEquals(m.getClientY(), 540);
|
||||
t.strictEquals(m.getScratchX(), -120);
|
||||
t.strictEquals(m.getScratchY(), -90);
|
||||
t.end();
|
||||
});
|
||||
|
|
|
@ -293,8 +293,12 @@ test('layers', t => {
|
|||
a.renderer = renderer;
|
||||
a.goToFront();
|
||||
t.equals(a.renderer.order, 5);
|
||||
a.goBackLayers(2);
|
||||
a.goBackwardLayers(2);
|
||||
t.equals(a.renderer.order, 3);
|
||||
a.goToBack();
|
||||
t.equals(a.renderer.order, 1);
|
||||
a.goForwardLayers(1);
|
||||
t.equals(a.renderer.order, 2);
|
||||
o.drawableID = 999;
|
||||
a.goBehindOther(o);
|
||||
t.equals(a.renderer.order, 1);
|
||||
|
|
|
@ -287,12 +287,18 @@ test('emitWorkspaceUpdate', t => {
|
|||
global: {
|
||||
toXML: () => 'global'
|
||||
}
|
||||
},
|
||||
blocks: {
|
||||
toXML: () => 'blocks'
|
||||
}
|
||||
}, {
|
||||
variables: {
|
||||
unused: {
|
||||
toXML: () => 'unused'
|
||||
}
|
||||
},
|
||||
blocks: {
|
||||
toXML: () => 'blocks'
|
||||
}
|
||||
}, {
|
||||
variables: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue