Merge pull request #4151 from scratchfoundation/renovate/major-eslint-config-scratch

chore(deps): update eslint-config-scratch (major)
This commit is contained in:
Christopher Willis-Ford 2023-12-15 17:46:53 -08:00 committed by GitHub
commit ef86783a79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 3610 additions and 2520 deletions

5691
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -74,8 +74,8 @@
"callsite": "1.0.0", "callsite": "1.0.0",
"copy-webpack-plugin": "4.5.4", "copy-webpack-plugin": "4.5.4",
"docdash": "1.2.0", "docdash": "1.2.0",
"eslint": "5.3.0", "eslint": "8.55.0",
"eslint-config-scratch": "5.1.0", "eslint-config-scratch": "9.0.3",
"expose-loader": "0.7.5", "expose-loader": "0.7.5",
"file-loader": "2.0.0", "file-loader": "2.0.0",
"format-message-cli": "6.2.0", "format-message-cli": "6.2.0",

View file

@ -540,7 +540,7 @@ class Scratch3LooksBlocks {
changeEffect (args, util) { changeEffect (args, util) {
const effect = Cast.toString(args.EFFECT).toLowerCase(); const effect = Cast.toString(args.EFFECT).toLowerCase();
const change = Cast.toNumber(args.CHANGE); const change = Cast.toNumber(args.CHANGE);
if (!util.target.effects.hasOwnProperty(effect)) return; if (!Object.prototype.hasOwnProperty.call(util.target.effects, effect)) return;
let newValue = change + util.target.effects[effect]; let newValue = change + util.target.effects[effect];
newValue = this.clampEffect(effect, newValue); newValue = this.clampEffect(effect, newValue);
util.target.setEffect(effect, newValue); util.target.setEffect(effect, newValue);

View file

@ -43,7 +43,7 @@ class Scratch3ProcedureBlocks {
// at earlier stack frames for the values of a given parameter (#1729) // at earlier stack frames for the values of a given parameter (#1729)
util.initParams(); util.initParams();
for (let i = 0; i < paramIds.length; i++) { for (let i = 0; i < paramIds.length; i++) {
if (args.hasOwnProperty(paramIds[i])) { if (Object.prototype.hasOwnProperty.call(args, paramIds[i])) {
util.pushParam(paramNames[i], args[paramIds[i]]); util.pushParam(paramNames[i], args[paramIds[i]]);
} else { } else {
util.pushParam(paramNames[i], paramDefaults[i]); util.pushParam(paramNames[i], paramDefaults[i]);

View file

@ -267,7 +267,7 @@ class Scratch3SoundBlocks {
const value = Cast.toNumber(args.VALUE); const value = Cast.toNumber(args.VALUE);
const soundState = this._getSoundState(util.target); const soundState = this._getSoundState(util.target);
if (!soundState.effects.hasOwnProperty(effect)) return; if (!Object.prototype.hasOwnProperty.call(soundState.effects, effect)) return;
if (change) { if (change) {
soundState.effects[effect] += value; soundState.effects[effect] += value;
@ -297,7 +297,7 @@ class Scratch3SoundBlocks {
_clearEffectsForTarget (target) { _clearEffectsForTarget (target) {
const soundState = this._getSoundState(target); const soundState = this._getSoundState(target);
for (const effect in soundState.effects) { for (const effect in soundState.effects) {
if (!soundState.effects.hasOwnProperty(effect)) continue; if (!Object.prototype.hasOwnProperty.call(soundState.effects, effect)) continue;
soundState.effects[effect] = 0; soundState.effects[effect] = 0;
} }
this._syncEffectsForTarget(target); this._syncEffectsForTarget(target);

View file

@ -50,6 +50,8 @@ class CentralDispatch extends SharedDispatch {
throw new Error(`Cannot use 'callSync' on remote provider for service ${service}.`); throw new Error(`Cannot use 'callSync' on remote provider for service ${service}.`);
} }
// TODO: verify correct `this` after switching from apply to spread
// eslint-disable-next-line prefer-spread
return provider[method].apply(provider, args); return provider[method].apply(provider, args);
} }
throw new Error(`Provider not found for service: ${service}`); throw new Error(`Provider not found for service: ${service}`);
@ -62,7 +64,7 @@ class CentralDispatch extends SharedDispatch {
* @param {object} provider - a local object which provides this service. * @param {object} provider - a local object which provides this service.
*/ */
setServiceSync (service, provider) { setServiceSync (service, provider) {
if (this.services.hasOwnProperty(service)) { if (Object.prototype.hasOwnProperty.call(this.services, service)) {
log.warn(`Central dispatch replacing existing service provider for ${service}`); log.warn(`Central dispatch replacing existing service provider for ${service}`);
} }
this.services[service] = provider; this.services[service] = provider;

View file

@ -82,6 +82,8 @@ class SharedDispatch {
return this._remoteTransferCall(provider, service, method, transfer, ...args); return this._remoteTransferCall(provider, service, method, transfer, ...args);
} }
// TODO: verify correct `this` after switching from apply to spread
// eslint-disable-next-line prefer-spread
const result = provider[method].apply(provider, args); const result = provider[method].apply(provider, args);
return Promise.resolve(result); return Promise.resolve(result);
} }

View file

@ -58,7 +58,7 @@ class WorkerDispatch extends SharedDispatch {
* @returns {Promise} - a promise which will resolve once the service is registered. * @returns {Promise} - a promise which will resolve once the service is registered.
*/ */
setService (service, provider) { setService (service, provider) {
if (this.services.hasOwnProperty(service)) { if (Object.prototype.hasOwnProperty.call(this.services, service)) {
log.warn(`Worker dispatch replacing existing service provider for ${service}`); log.warn(`Worker dispatch replacing existing service provider for ${service}`);
} }
this.services[service] = provider; this.services[service] = provider;

View file

@ -153,7 +153,7 @@ const domToBlocks = function (blocksDOM) {
// Flatten blocks object into a list. // Flatten blocks object into a list.
const blocksList = []; const blocksList = [];
for (const b in blocks) { for (const b in blocks) {
if (!blocks.hasOwnProperty(b)) continue; if (!Object.prototype.hasOwnProperty.call(blocks, b)) continue;
blocksList.push(blocks[b]); blocksList.push(blocks[b]);
} }
return blocksList; return blocksList;

View file

@ -232,6 +232,8 @@ class BlockUtility {
this.sequencer.runtime.ioDevices[device] && this.sequencer.runtime.ioDevices[device] &&
this.sequencer.runtime.ioDevices[device][func]) { this.sequencer.runtime.ioDevices[device][func]) {
const devObject = this.sequencer.runtime.ioDevices[device]; const devObject = this.sequencer.runtime.ioDevices[device];
// TODO: verify correct `this` after switching from apply to spread
// eslint-disable-next-line prefer-spread
return devObject[func].apply(devObject, args); return devObject[func].apply(devObject, args);
} }
} }

View file

@ -44,7 +44,7 @@ class RuntimeScriptCache {
if (Object.keys(fields).length === 0) { if (Object.keys(fields).length === 0) {
const inputs = container.getInputs(block); const inputs = container.getInputs(block);
for (const input in inputs) { for (const input in inputs) {
if (!inputs.hasOwnProperty(input)) continue; if (!Object.prototype.hasOwnProperty.call(inputs, input)) continue;
const id = inputs[input].block; const id = inputs[input].block;
const inputBlock = container.getBlock(id); const inputBlock = container.getBlock(id);
const inputFields = container.getFields(inputBlock); const inputFields = container.getFields(inputBlock);

View file

@ -231,7 +231,7 @@ class Blocks {
} }
for (const id in this._blocks) { for (const id in this._blocks) {
if (!this._blocks.hasOwnProperty(id)) continue; if (!Object.prototype.hasOwnProperty.call(this._blocks, id)) continue;
const block = this._blocks[id]; const block = this._blocks[id];
if (block.opcode === 'procedures_definition') { if (block.opcode === 'procedures_definition') {
const internal = this._getCustomBlockInternal(block); const internal = this._getCustomBlockInternal(block);
@ -267,7 +267,7 @@ class Blocks {
} }
for (const id in this._blocks) { for (const id in this._blocks) {
if (!this._blocks.hasOwnProperty(id)) continue; if (!Object.prototype.hasOwnProperty.call(this._blocks, id)) continue;
const block = this._blocks[id]; const block = this._blocks[id];
if (block.opcode === 'procedures_prototype' && if (block.opcode === 'procedures_prototype' &&
block.mutation.proccode === name) { block.mutation.proccode === name) {
@ -357,7 +357,7 @@ class Blocks {
case 'delete': case 'delete':
// Don't accept delete events for missing blocks, // Don't accept delete events for missing blocks,
// or shadow blocks being obscured. // or shadow blocks being obscured.
if (!this._blocks.hasOwnProperty(e.blockId) || if (!Object.prototype.hasOwnProperty.call(this._blocks, e.blockId) ||
this._blocks[e.blockId].shadow) { this._blocks[e.blockId].shadow) {
return; return;
} }
@ -397,7 +397,7 @@ class Blocks {
} }
break; break;
case 'var_rename': case 'var_rename':
if (editingTarget && editingTarget.variables.hasOwnProperty(e.varId)) { if (editingTarget && Object.prototype.hasOwnProperty.call(editingTarget.variables, e.varId)) {
// This is a local variable, rename on the current target // This is a local variable, rename on the current target
editingTarget.renameVariable(e.varId, e.newName); editingTarget.renameVariable(e.varId, e.newName);
// Update all the blocks on the current target that use // Update all the blocks on the current target that use
@ -416,7 +416,7 @@ class Blocks {
this.emitProjectChanged(); this.emitProjectChanged();
break; break;
case 'var_delete': { case 'var_delete': {
const target = (editingTarget && editingTarget.variables.hasOwnProperty(e.varId)) ? const target = (editingTarget && Object.prototype.hasOwnProperty.call(editingTarget.variables, e.varId)) ?
editingTarget : stage; editingTarget : stage;
target.deleteVariable(e.varId); target.deleteVariable(e.varId);
this.emitProjectChanged(); this.emitProjectChanged();
@ -445,20 +445,21 @@ class Blocks {
case 'comment_change': case 'comment_change':
if (this.runtime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
if (!currTarget.comments.hasOwnProperty(e.commentId)) { if (!Object.prototype.hasOwnProperty.call(currTarget.comments, e.commentId)) {
log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`); log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);
return; return;
} }
const comment = currTarget.comments[e.commentId]; const comment = currTarget.comments[e.commentId];
const change = e.newContents_; const change = e.newContents_;
if (change.hasOwnProperty('minimized')) { if (Object.prototype.hasOwnProperty.call(change, 'minimized')) {
comment.minimized = change.minimized; comment.minimized = change.minimized;
} }
if (change.hasOwnProperty('width') && change.hasOwnProperty('height')){ if (Object.prototype.hasOwnProperty.call(change, 'width') &&
Object.prototype.hasOwnProperty.call(change, 'height')) {
comment.width = change.width; comment.width = change.width;
comment.height = change.height; comment.height = change.height;
} }
if (change.hasOwnProperty('text')) { if (Object.prototype.hasOwnProperty.call(change, 'text')) {
comment.text = change.text; comment.text = change.text;
} }
this.emitProjectChanged(); this.emitProjectChanged();
@ -467,7 +468,7 @@ class Blocks {
case 'comment_move': case 'comment_move':
if (this.runtime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
if (currTarget && !currTarget.comments.hasOwnProperty(e.commentId)) { if (currTarget && !Object.prototype.hasOwnProperty.call(currTarget.comments, e.commentId)) {
log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`); log.warn(`Cannot change comment with id ${e.commentId} because it does not exist.`);
return; return;
} }
@ -482,7 +483,7 @@ class Blocks {
case 'comment_delete': case 'comment_delete':
if (this.runtime.getEditingTarget()) { if (this.runtime.getEditingTarget()) {
const currTarget = this.runtime.getEditingTarget(); const currTarget = this.runtime.getEditingTarget();
if (!currTarget.comments.hasOwnProperty(e.commentId)) { if (!Object.prototype.hasOwnProperty.call(currTarget.comments, e.commentId)) {
// If we're in this state, we have probably received // If we're in this state, we have probably received
// a delete event from a workspace that we switched from // a delete event from a workspace that we switched from
// (e.g. a delete event for a comment on sprite a's workspace // (e.g. a delete event for a comment on sprite a's workspace
@ -536,7 +537,7 @@ class Blocks {
createBlock (block) { createBlock (block) {
// Does the block already exist? // Does the block already exist?
// Could happen, e.g., for an unobscured shadow. // Could happen, e.g., for an unobscured shadow.
if (this._blocks.hasOwnProperty(block.id)) { if (Object.prototype.hasOwnProperty.call(this._blocks, block.id)) {
return; return;
} }
// Create new block. // Create new block.
@ -650,7 +651,7 @@ class Blocks {
} }
const isSpriteSpecific = isSpriteLocalVariable || const isSpriteSpecific = isSpriteLocalVariable ||
(this.runtime.monitorBlockInfo.hasOwnProperty(block.opcode) && (Object.prototype.hasOwnProperty.call(this.runtime.monitorBlockInfo, block.opcode) &&
this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific); this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific);
if (isSpriteSpecific) { if (isSpriteSpecific) {
// If creating a new sprite specific monitor, the only possible target is // If creating a new sprite specific monitor, the only possible target is
@ -692,7 +693,7 @@ class Blocks {
* @param {!object} e Blockly move event to be processed * @param {!object} e Blockly move event to be processed
*/ */
moveBlock (e) { moveBlock (e) {
if (!this._blocks.hasOwnProperty(e.id)) { if (!Object.prototype.hasOwnProperty.call(this._blocks, e.id)) {
return; return;
} }
@ -740,7 +741,7 @@ class Blocks {
// Moved to the new parent's input. // Moved to the new parent's input.
// Don't obscure the shadow block. // Don't obscure the shadow block.
let oldShadow = null; let oldShadow = null;
if (this._blocks[e.newParent].inputs.hasOwnProperty(e.newInput)) { if (Object.prototype.hasOwnProperty.call(this._blocks[e.newParent].inputs, e.newInput)) {
oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow; oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow;
} }
@ -990,7 +991,7 @@ class Blocks {
*/ */
_getCostumeField (blockId) { _getCostumeField (blockId) {
const block = this.getBlock(blockId); const block = this.getBlock(blockId);
if (block && block.fields.hasOwnProperty('COSTUME')) { if (block && Object.prototype.hasOwnProperty.call(block.fields, 'COSTUME')) {
return block.fields.COSTUME; return block.fields.COSTUME;
} }
return null; return null;
@ -1005,7 +1006,7 @@ class Blocks {
*/ */
_getSoundField (blockId) { _getSoundField (blockId) {
const block = this.getBlock(blockId); const block = this.getBlock(blockId);
if (block && block.fields.hasOwnProperty('SOUND_MENU')) { if (block && Object.prototype.hasOwnProperty.call(block.fields, 'SOUND_MENU')) {
return block.fields.SOUND_MENU; return block.fields.SOUND_MENU;
} }
return null; return null;
@ -1020,7 +1021,7 @@ class Blocks {
*/ */
_getBackdropField (blockId) { _getBackdropField (blockId) {
const block = this.getBlock(blockId); const block = this.getBlock(blockId);
if (block && block.fields.hasOwnProperty('BACKDROP')) { if (block && Object.prototype.hasOwnProperty.call(block.fields, 'BACKDROP')) {
return block.fields.BACKDROP; return block.fields.BACKDROP;
} }
return null; return null;
@ -1042,7 +1043,7 @@ class Blocks {
'DISTANCETOMENU', 'TOUCHINGOBJECTMENU', 'CLONE_OPTION']; 'DISTANCETOMENU', 'TOUCHINGOBJECTMENU', 'CLONE_OPTION'];
for (let i = 0; i < spriteMenuNames.length; i++) { for (let i = 0; i < spriteMenuNames.length; i++) {
const menuName = spriteMenuNames[i]; const menuName = spriteMenuNames[i];
if (block.fields.hasOwnProperty(menuName)) { if (Object.prototype.hasOwnProperty.call(block.fields, menuName)) {
return block.fields[menuName]; return block.fields[menuName];
} }
} }
@ -1085,7 +1086,7 @@ class Blocks {
const commentId = block.comment; const commentId = block.comment;
if (commentId) { if (commentId) {
if (comments) { if (comments) {
if (comments.hasOwnProperty(commentId)) { if (Object.prototype.hasOwnProperty.call(comments, commentId)) {
xmlString += comments[commentId].toXML(); xmlString += comments[commentId].toXML();
} else { } else {
log.warn(`Could not find comment with id: ${commentId} in provided comment descriptions.`); log.warn(`Could not find comment with id: ${commentId} in provided comment descriptions.`);
@ -1100,7 +1101,7 @@ class Blocks {
} }
// Add any inputs on this block. // Add any inputs on this block.
for (const input in block.inputs) { for (const input in block.inputs) {
if (!block.inputs.hasOwnProperty(input)) continue; if (!Object.prototype.hasOwnProperty.call(block.inputs, input)) continue;
const blockInput = block.inputs[input]; const blockInput = block.inputs[input];
// Only encode a value tag if the value input is occupied. // Only encode a value tag if the value input is occupied.
if (blockInput.block || blockInput.shadow) { if (blockInput.block || blockInput.shadow) {
@ -1117,7 +1118,7 @@ class Blocks {
} }
// Add any fields on this block. // Add any fields on this block.
for (const field in block.fields) { for (const field in block.fields) {
if (!block.fields.hasOwnProperty(field)) continue; if (!Object.prototype.hasOwnProperty.call(block.fields, field)) continue;
const blockField = block.fields[field]; const blockField = block.fields[field];
xmlString += `<field name="${blockField.name}"`; xmlString += `<field name="${blockField.name}"`;
const fieldId = blockField.id; const fieldId = blockField.id;

View file

@ -764,14 +764,14 @@ class Runtime extends EventEmitter {
*/ */
_registerBlockPackages () { _registerBlockPackages () {
for (const packageName in defaultBlockPackages) { for (const packageName in defaultBlockPackages) {
if (defaultBlockPackages.hasOwnProperty(packageName)) { if (Object.prototype.hasOwnProperty.call(defaultBlockPackages, packageName)) {
// @todo pass a different runtime depending on package privilege? // @todo pass a different runtime depending on package privilege?
const packageObject = new (defaultBlockPackages[packageName])(this); const packageObject = new (defaultBlockPackages[packageName])(this);
// Collect primitives from package. // Collect primitives from package.
if (packageObject.getPrimitives) { if (packageObject.getPrimitives) {
const packagePrimitives = packageObject.getPrimitives(); const packagePrimitives = packageObject.getPrimitives();
for (const op in packagePrimitives) { for (const op in packagePrimitives) {
if (packagePrimitives.hasOwnProperty(op)) { if (Object.prototype.hasOwnProperty.call(packagePrimitives, op)) {
this._primitives[op] = this._primitives[op] =
packagePrimitives[op].bind(packageObject); packagePrimitives[op].bind(packageObject);
} }
@ -781,7 +781,7 @@ class Runtime extends EventEmitter {
if (packageObject.getHats) { if (packageObject.getHats) {
const packageHats = packageObject.getHats(); const packageHats = packageObject.getHats();
for (const hatName in packageHats) { for (const hatName in packageHats) {
if (packageHats.hasOwnProperty(hatName)) { if (Object.prototype.hasOwnProperty.call(packageHats, hatName)) {
this._hats[hatName] = packageHats[hatName]; this._hats[hatName] = packageHats[hatName];
} }
} }
@ -851,7 +851,7 @@ class Runtime extends EventEmitter {
this._fillExtensionCategory(categoryInfo, extensionInfo); this._fillExtensionCategory(categoryInfo, extensionInfo);
for (const fieldTypeName in categoryInfo.customFieldTypes) { for (const fieldTypeName in categoryInfo.customFieldTypes) {
if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { if (Object.prototype.hasOwnProperty.call(extensionInfo.customFieldTypes, fieldTypeName)) {
const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName]; const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName];
// Emit events for custom field types from extension // Emit events for custom field types from extension
@ -894,7 +894,7 @@ class Runtime extends EventEmitter {
categoryInfo.menuInfo = {}; categoryInfo.menuInfo = {};
for (const menuName in extensionInfo.menus) { for (const menuName in extensionInfo.menus) {
if (extensionInfo.menus.hasOwnProperty(menuName)) { if (Object.prototype.hasOwnProperty.call(extensionInfo.menus, menuName)) {
const menuInfo = extensionInfo.menus[menuName]; const menuInfo = extensionInfo.menus[menuName];
const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo); const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo);
categoryInfo.menus.push(convertedMenu); categoryInfo.menus.push(convertedMenu);
@ -902,7 +902,7 @@ class Runtime extends EventEmitter {
} }
} }
for (const fieldTypeName in extensionInfo.customFieldTypes) { for (const fieldTypeName in extensionInfo.customFieldTypes) {
if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) { if (Object.prototype.hasOwnProperty.call(extensionInfo.customFieldTypes, fieldTypeName)) {
const fieldType = extensionInfo.customFieldTypes[fieldTypeName]; const fieldType = extensionInfo.customFieldTypes[fieldTypeName];
const fieldTypeInfo = this._buildCustomFieldInfo( const fieldTypeInfo = this._buildCustomFieldInfo(
fieldTypeName, fieldTypeName,
@ -1138,7 +1138,7 @@ class Runtime extends EventEmitter {
break; break;
case BlockType.HAT: case BlockType.HAT:
case BlockType.EVENT: case BlockType.EVENT:
if (!blockInfo.hasOwnProperty('isEdgeActivated')) { if (!Object.prototype.hasOwnProperty.call(blockInfo, 'isEdgeActivated')) {
// if absent, this property defaults to true // if absent, this property defaults to true
blockInfo.isEdgeActivated = true; blockInfo.isEdgeActivated = true;
} }
@ -1584,7 +1584,7 @@ class Runtime extends EventEmitter {
* @return {boolean} True if the op is known to be a hat. * @return {boolean} True if the op is known to be a hat.
*/ */
getIsHat (opcode) { getIsHat (opcode) {
return this._hats.hasOwnProperty(opcode); return Object.prototype.hasOwnProperty.call(this._hats, opcode);
} }
/** /**
@ -1593,7 +1593,7 @@ class Runtime extends EventEmitter {
* @return {boolean} True if the op is known to be a edge-activated hat. * @return {boolean} True if the op is known to be a edge-activated hat.
*/ */
getIsEdgeActivatedHat (opcode) { getIsEdgeActivatedHat (opcode) {
return this._hats.hasOwnProperty(opcode) && return Object.prototype.hasOwnProperty.call(this._hats, opcode) &&
this._hats[opcode].edgeActivated; this._hats[opcode].edgeActivated;
} }
@ -1817,7 +1817,7 @@ class Runtime extends EventEmitter {
*/ */
startHats (requestedHatOpcode, startHats (requestedHatOpcode,
optMatchFields, optTarget) { optMatchFields, optTarget) {
if (!this._hats.hasOwnProperty(requestedHatOpcode)) { if (!Object.prototype.hasOwnProperty.call(this._hats, requestedHatOpcode)) {
// No known hat with this opcode. // No known hat with this opcode.
return; return;
} }
@ -1827,7 +1827,7 @@ class Runtime extends EventEmitter {
const hatMeta = instance._hats[requestedHatOpcode]; const hatMeta = instance._hats[requestedHatOpcode];
for (const opts in optMatchFields) { for (const opts in optMatchFields) {
if (!optMatchFields.hasOwnProperty(opts)) continue; if (!Object.prototype.hasOwnProperty.call(optMatchFields, opts)) continue;
optMatchFields[opts] = optMatchFields[opts].toUpperCase(); optMatchFields[opts] = optMatchFields[opts].toUpperCase();
} }
@ -2062,7 +2062,7 @@ class Runtime extends EventEmitter {
const newTargets = []; const newTargets = [];
for (let i = 0; i < this.targets.length; i++) { for (let i = 0; i < this.targets.length; i++) {
this.targets[i].onStopAll(); this.targets[i].onStopAll();
if (this.targets[i].hasOwnProperty('isOriginal') && if (Object.prototype.hasOwnProperty.call(this.targets[i], 'isOriginal') &&
!this.targets[i].isOriginal) { !this.targets[i].isOriginal) {
this.targets[i].dispose(); this.targets[i].dispose();
} else { } else {
@ -2097,7 +2097,7 @@ class Runtime extends EventEmitter {
// Find all edge-activated hats, and add them to threads to be evaluated. // Find all edge-activated hats, and add them to threads to be evaluated.
for (const hatType in this._hats) { for (const hatType in this._hats) {
if (!this._hats.hasOwnProperty(hatType)) continue; if (!Object.prototype.hasOwnProperty.call(this._hats, hatType)) continue;
const hat = this._hats[hatType]; const hat = this._hats[hatType];
if (hat.edgeActivated) { if (hat.edgeActivated) {
this.startHats(hatType); this.startHats(hatType);
@ -2211,9 +2211,9 @@ class Runtime extends EventEmitter {
*/ */
_updateGlows (optExtraThreads) { _updateGlows (optExtraThreads) {
const searchThreads = []; const searchThreads = [];
searchThreads.push.apply(searchThreads, this.threads); searchThreads.push(...this.threads);
if (optExtraThreads) { if (optExtraThreads) {
searchThreads.push.apply(searchThreads, optExtraThreads); searchThreads.push(...optExtraThreads);
} }
// Set of scripts that request a glow this frame. // Set of scripts that request a glow this frame.
const requestedGlowsThisFrame = []; const requestedGlowsThisFrame = [];

View file

@ -101,7 +101,7 @@ class Target extends EventEmitter {
} }
hasEdgeActivatedValue (blockId) { hasEdgeActivatedValue (blockId) {
return this._edgeActivatedHatValues.hasOwnProperty(blockId); return Object.prototype.hasOwnProperty.call(this._edgeActivatedHatValues, blockId);
} }
/** /**
@ -186,13 +186,13 @@ class Target extends EventEmitter {
*/ */
lookupVariableById (id) { lookupVariableById (id) {
// If we have a local copy, return it. // If we have a local copy, return it.
if (this.variables.hasOwnProperty(id)) { if (Object.prototype.hasOwnProperty.call(this.variables, id)) {
return this.variables[id]; return this.variables[id];
} }
// If the stage has a global copy, return it. // If the stage has a global copy, return it.
if (this.runtime && !this.isStage) { if (this.runtime && !this.isStage) {
const stage = this.runtime.getTargetForStage(); const stage = this.runtime.getTargetForStage();
if (stage && stage.variables.hasOwnProperty(id)) { if (stage && Object.prototype.hasOwnProperty.call(stage.variables, id)) {
return stage.variables[id]; return stage.variables[id];
} }
} }
@ -264,7 +264,7 @@ class Target extends EventEmitter {
* Additional checks are made that the variable can be created as a cloud variable. * Additional checks are made that the variable can be created as a cloud variable.
*/ */
createVariable (id, name, type, isCloud) { createVariable (id, name, type, isCloud) {
if (!this.variables.hasOwnProperty(id)) { if (!Object.prototype.hasOwnProperty.call(this.variables, id)) {
const newVariable = new Variable(id, name, type, false); const newVariable = new Variable(id, name, type, false);
if (isCloud && this.isStage && this.runtime.canAddCloudVariable()) { if (isCloud && this.isStage && this.runtime.canAddCloudVariable()) {
newVariable.isCloud = true; newVariable.isCloud = true;
@ -288,7 +288,7 @@ class Target extends EventEmitter {
* @param {boolean} minimized Whether the comment is minimized. * @param {boolean} minimized Whether the comment is minimized.
*/ */
createComment (id, blockId, text, x, y, width, height, minimized) { createComment (id, blockId, text, x, y, width, height, minimized) {
if (!this.comments.hasOwnProperty(id)) { if (!Object.prototype.hasOwnProperty.call(this.comments, id)) {
const newComment = new Comment(id, text, x, y, const newComment = new Comment(id, text, x, y,
width, height, minimized); width, height, minimized);
if (blockId) { if (blockId) {
@ -311,7 +311,7 @@ class Target extends EventEmitter {
* @param {string} newName New name for the variable. * @param {string} newName New name for the variable.
*/ */
renameVariable (id, newName) { renameVariable (id, newName) {
if (this.variables.hasOwnProperty(id)) { if (Object.prototype.hasOwnProperty.call(this.variables, id)) {
const variable = this.variables[id]; const variable = this.variables[id];
if (variable.id === id) { if (variable.id === id) {
const oldName = variable.name; const oldName = variable.name;
@ -362,7 +362,7 @@ class Target extends EventEmitter {
* @param {string} id Id of variable to delete. * @param {string} id Id of variable to delete.
*/ */
deleteVariable (id) { deleteVariable (id) {
if (this.variables.hasOwnProperty(id)) { if (Object.prototype.hasOwnProperty.call(this.variables, id)) {
// Get info about the variable before deleting it // Get info about the variable before deleting it
const deletedVariableName = this.variables[id].name; const deletedVariableName = this.variables[id].name;
const deletedVariableWasCloud = this.variables[id].isCloud; const deletedVariableWasCloud = this.variables[id].isCloud;
@ -408,7 +408,7 @@ class Target extends EventEmitter {
* the original variable was not found. * the original variable was not found.
*/ */
duplicateVariable (id, optKeepOriginalId) { duplicateVariable (id, optKeepOriginalId) {
if (this.variables.hasOwnProperty(id)) { if (Object.prototype.hasOwnProperty.call(this.variables, id)) {
const originalVariable = this.variables[id]; const originalVariable = this.variables[id];
const newVariable = new Variable( const newVariable = new Variable(
optKeepOriginalId ? id : null, // conditionally keep original id or generate a new one optKeepOriginalId ? id : null, // conditionally keep original id or generate a new one
@ -695,7 +695,7 @@ class Target extends EventEmitter {
const unreferencedLocalVarIds = []; const unreferencedLocalVarIds = [];
if (Object.keys(this.variables).length > 0) { if (Object.keys(this.variables).length > 0) {
for (const localVarId in this.variables) { for (const localVarId in this.variables) {
if (!this.variables.hasOwnProperty(localVarId)) continue; if (!Object.prototype.hasOwnProperty.call(this.variables, localVarId)) continue;
if (!allReferences[localVarId]) unreferencedLocalVarIds.push(localVarId); if (!allReferences[localVarId]) unreferencedLocalVarIds.push(localVarId);
} }
} }
@ -720,7 +720,7 @@ class Target extends EventEmitter {
if (this.lookupVariableById(varId)) { if (this.lookupVariableById(varId)) {
// Found a variable with the id in either the target or the stage, // Found a variable with the id in either the target or the stage,
// figure out which one. // figure out which one.
if (this.variables.hasOwnProperty(varId)) { if (Object.prototype.hasOwnProperty.call(this.variables, varId)) {
// If the target has the variable, then check whether the stage // If the target has the variable, then check whether the stage
// has one with the same name and type. If it does, then rename // has one with the same name and type. If it does, then rename
// this target specific variable so that there is a distinction. // this target specific variable so that there is a distinction.

View file

@ -353,7 +353,7 @@ class Thread {
if (frame.params === null) { if (frame.params === null) {
continue; continue;
} }
if (frame.params.hasOwnProperty(paramName)) { if (Object.prototype.hasOwnProperty.call(frame.params, paramName)) {
return frame.params[paramName]; return frame.params[paramName];
} }
return null; return null;

View file

@ -116,7 +116,7 @@ class ExtensionManager {
* @param {string} extensionId - the ID of an internal extension * @param {string} extensionId - the ID of an internal extension
*/ */
loadExtensionIdSync (extensionId) { loadExtensionIdSync (extensionId) {
if (!builtinExtensions.hasOwnProperty(extensionId)) { if (!Object.prototype.hasOwnProperty.call(builtinExtensions, extensionId)) {
log.warn(`Could not find extension ${extensionId} in the built in extensions.`); log.warn(`Could not find extension ${extensionId} in the built in extensions.`);
return; return;
} }
@ -140,7 +140,7 @@ class ExtensionManager {
* @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure
*/ */
loadExtensionURL (extensionURL) { loadExtensionURL (extensionURL) {
if (builtinExtensions.hasOwnProperty(extensionURL)) { if (Object.prototype.hasOwnProperty.call(builtinExtensions, extensionURL)) {
/** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */ /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */
if (this.isExtensionLoaded(extensionURL)) { if (this.isExtensionLoaded(extensionURL)) {
const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`; const message = `Rejecting attempt to load a second extension with ID ${extensionURL}`;

View file

@ -368,7 +368,7 @@ class Scratch3MakeyMakeyBlocks {
*/ */
addSequence (sequenceString, sequenceArray) { addSequence (sequenceString, sequenceArray) {
// If we already have this sequence string, return. // If we already have this sequence string, return.
if (this.sequences.hasOwnProperty(sequenceString)) { if (Object.prototype.hasOwnProperty.call(this.sequences, sequenceString)) {
return; return;
} }
this.sequences[sequenceString] = { this.sequences[sequenceString] = {

View file

@ -569,7 +569,7 @@ class Scratch3PenBlocks {
penState.color = (hsv.h / 360) * 100; penState.color = (hsv.h / 360) * 100;
penState.saturation = hsv.s * 100; penState.saturation = hsv.s * 100;
penState.brightness = hsv.v * 100; penState.brightness = hsv.v * 100;
if (rgb.hasOwnProperty('a')) { if (Object.prototype.hasOwnProperty.call(rgb, 'a')) {
penState.transparency = 100 * (1 - (rgb.a / 255.0)); penState.transparency = 100 * (1 - (rgb.a / 255.0));
} else { } else {
penState.transparency = 0; penState.transparency = 0;

View file

@ -222,11 +222,11 @@ class Scratch3TranslateBlocks {
getLanguageCodeFromArg (arg) { getLanguageCodeFromArg (arg) {
const languageArg = Cast.toString(arg).toLowerCase(); const languageArg = Cast.toString(arg).toLowerCase();
// Check if the arg matches a language code in the menu. // Check if the arg matches a language code in the menu.
if (languageNames.menuMap.hasOwnProperty(languageArg)) { if (Object.prototype.hasOwnProperty.call(languageNames.menuMap, languageArg)) {
return languageArg; return languageArg;
} }
// Check for a dropped-in language name, and convert to a language code. // Check for a dropped-in language name, and convert to a language code.
if (languageNames.nameMap.hasOwnProperty(languageArg)) { if (Object.prototype.hasOwnProperty.call(languageNames.nameMap, languageArg)) {
return languageNames.nameMap[languageArg]; return languageNames.nameMap[languageArg];
} }

View file

@ -167,7 +167,6 @@ class Scratch3VideoSensingBlocks {
if (stage) { if (stage) {
stage.videoTransparency = transparency; stage.videoTransparency = transparency;
} }
return transparency;
} }
/** /**
@ -191,7 +190,6 @@ class Scratch3VideoSensingBlocks {
if (stage) { if (stage) {
stage.videoState = state; stage.videoState = state;
} }
return state;
} }
/** /**

View file

@ -100,9 +100,13 @@ const canvasPool = (function () {
*/ */
const fetchBitmapCanvas_ = function (costume, runtime, rotationCenter) { const fetchBitmapCanvas_ = function (costume, runtime, rotationCenter) {
if (!costume || !costume.asset) { // TODO: We can probably remove this check... if (!costume || !costume.asset) { // TODO: We can probably remove this check...
// TODO: reject with an Error (breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('Costume load failed. Assets were missing.'); return Promise.reject('Costume load failed. Assets were missing.');
} }
if (!runtime.v2BitmapAdapter) { if (!runtime.v2BitmapAdapter) {
// TODO: reject with an Error (breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('No V2 Bitmap adapter present.'); return Promise.reject('No V2 Bitmap adapter present.');
} }
@ -125,6 +129,8 @@ const fetchBitmapCanvas_ = function (costume, runtime, rotationCenter) {
image.onerror = null; image.onerror = null;
}; };
image.onerror = function () { image.onerror = function () {
// TODO: reject with an Error (breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
reject('Costume load failed. Asset could not be read.'); reject('Costume load failed. Asset could not be read.');
image.onload = null; image.onload = null;
image.onerror = null; image.onerror = null;
@ -194,6 +200,8 @@ const loadBitmap_ = function (costume, runtime, _rotationCenter) {
// somewhere and act on that error (like logging). // somewhere and act on that error (like logging).
// //
// Return a rejection to stop executing updateCostumeAsset. // Return a rejection to stop executing updateCostumeAsset.
// TODO: reject with an Error (breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('No V2 Bitmap adapter present.'); return Promise.reject('No V2 Bitmap adapter present.');
} }

View file

@ -42,7 +42,7 @@ class Mouse {
const drawableID = this.runtime.renderer.pick(x, y); const drawableID = this.runtime.renderer.pick(x, y);
for (let i = 0; i < this.runtime.targets.length; i++) { for (let i = 0; i < this.runtime.targets.length; i++) {
const target = this.runtime.targets[i]; const target = this.runtime.targets[i];
if (target.hasOwnProperty('drawableID') && if (Object.prototype.hasOwnProperty.call(target, 'drawableID') &&
target.drawableID === drawableID) { target.drawableID === drawableID) {
return target; return target;
} }

View file

@ -288,7 +288,7 @@ const parseMonitorObject = (object, runtime, targets, extensions) => {
let target = null; let target = null;
// List blocks don't come in with their target name set. // List blocks don't come in with their target name set.
// Find the target by searching for a target with matching variable name/type. // Find the target by searching for a target with matching variable name/type.
if (!object.hasOwnProperty('target')) { if (!Object.prototype.hasOwnProperty.call(object, 'target')) {
for (let i = 0; i < targets.length; i++) { for (let i = 0; i < targets.length; i++) {
const currTarget = targets[i]; const currTarget = targets[i];
const listVariables = Object.keys(currTarget.variables).filter(key => { const listVariables = Object.keys(currTarget.variables).filter(key => {
@ -326,7 +326,7 @@ const parseMonitorObject = (object, runtime, targets, extensions) => {
block.id = getVariableId(object.param, Variable.SCALAR_TYPE); block.id = getVariableId(object.param, Variable.SCALAR_TYPE);
} else if (object.cmd === 'contentsOfList:') { } else if (object.cmd === 'contentsOfList:') {
block.id = getVariableId(object.param, Variable.LIST_TYPE); block.id = getVariableId(object.param, Variable.LIST_TYPE);
} else if (runtime.monitorBlockInfo.hasOwnProperty(block.opcode)) { } else if (Object.prototype.hasOwnProperty.call(runtime.monitorBlockInfo, block.opcode)) {
block.id = runtime.monitorBlockInfo[block.opcode].getId(target.id, block.fields); block.id = runtime.monitorBlockInfo[block.opcode].getId(target.id, block.fields);
} else { } else {
// If the opcode can't be found in the runtime monitorBlockInfo, // If the opcode can't be found in the runtime monitorBlockInfo,
@ -405,7 +405,7 @@ const parseMonitorObject = (object, runtime, targets, extensions) => {
* objects. * objects.
*/ */
const parseScratchAssets = function (object, runtime, topLevel, zip) { const parseScratchAssets = function (object, runtime, topLevel, zip) {
if (!object.hasOwnProperty('objName')) { if (!Object.prototype.hasOwnProperty.call(object, 'objName')) {
// Skip parsing monitors. Or any other objects missing objName. // Skip parsing monitors. Or any other objects missing objName.
return null; return null;
} }
@ -419,7 +419,7 @@ const parseScratchAssets = function (object, runtime, topLevel, zip) {
// Costumes from JSON. // Costumes from JSON.
const costumePromises = assets.costumePromises; const costumePromises = assets.costumePromises;
if (object.hasOwnProperty('costumes')) { if (Object.prototype.hasOwnProperty.call(object, 'costumes')) {
for (let i = 0; i < object.costumes.length; i++) { for (let i = 0; i < object.costumes.length; i++) {
const costumeSource = object.costumes[i]; const costumeSource = object.costumes[i];
const bitmapResolution = costumeSource.bitmapResolution || 1; const bitmapResolution = costumeSource.bitmapResolution || 1;
@ -464,7 +464,7 @@ const parseScratchAssets = function (object, runtime, topLevel, zip) {
} }
// Sounds from JSON // Sounds from JSON
const {soundBank, soundPromises} = assets; const {soundBank, soundPromises} = assets;
if (object.hasOwnProperty('sounds')) { if (Object.prototype.hasOwnProperty.call(object, 'sounds')) {
for (let s = 0; s < object.sounds.length; s++) { for (let s = 0; s < object.sounds.length; s++) {
const soundSource = object.sounds[s]; const soundSource = object.sounds[s];
const sound = { const sound = {
@ -523,8 +523,8 @@ const parseScratchAssets = function (object, runtime, topLevel, zip) {
* @return {!Promise.<Array.<Target>>} Promise for the loaded targets when ready, or null for unsupported objects. * @return {!Promise.<Array.<Target>>} Promise for the loaded targets when ready, or null for unsupported objects.
*/ */
const parseScratchObject = function (object, runtime, extensions, topLevel, zip, assets) { const parseScratchObject = function (object, runtime, extensions, topLevel, zip, assets) {
if (!object.hasOwnProperty('objName')) { if (!Object.prototype.hasOwnProperty.call(object, 'objName')) {
if (object.hasOwnProperty('listName')) { if (Object.prototype.hasOwnProperty.call(object, 'listName')) {
// Shim these objects so they can be processed as monitors // Shim these objects so they can be processed as monitors
object.cmd = 'contentsOfList:'; object.cmd = 'contentsOfList:';
object.param = object.listName; object.param = object.listName;
@ -540,10 +540,10 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite. // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
const sprite = new Sprite(blocks, runtime); const sprite = new Sprite(blocks, runtime);
// Sprite/stage name from JSON. // Sprite/stage name from JSON.
if (object.hasOwnProperty('objName')) { if (Object.prototype.hasOwnProperty.call(object, 'objName')) {
if (topLevel && object.objName !== 'Stage') { if (topLevel && object.objName !== 'Stage') {
for (const child of object.children) { for (const child of object.children) {
if (!child.hasOwnProperty('objName') && child.target === object.objName) { if (!Object.prototype.hasOwnProperty.call(child, 'objName') && child.target === object.objName) {
child.target = 'Stage'; child.target = 'Stage';
} }
} }
@ -566,7 +566,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
const addBroadcastMsg = globalBroadcastMsgObj.broadcastMsgMapUpdater; const addBroadcastMsg = globalBroadcastMsgObj.broadcastMsgMapUpdater;
// Load target properties from JSON. // Load target properties from JSON.
if (object.hasOwnProperty('variables')) { if (Object.prototype.hasOwnProperty.call(object, 'variables')) {
for (let j = 0; j < object.variables.length; j++) { for (let j = 0; j < object.variables.length; j++) {
const variable = object.variables[j]; const variable = object.variables[j];
// A variable is a cloud variable if: // A variable is a cloud variable if:
@ -589,7 +589,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
// If included, parse any and all comments on the object (this includes top-level // If included, parse any and all comments on the object (this includes top-level
// workspace comments as well as comments attached to specific blocks) // workspace comments as well as comments attached to specific blocks)
const blockComments = {}; const blockComments = {};
if (object.hasOwnProperty('scriptComments')) { if (Object.prototype.hasOwnProperty.call(object, 'scriptComments')) {
const comments = object.scriptComments.map(commentDesc => { const comments = object.scriptComments.map(commentDesc => {
const [ const [
commentX, commentX,
@ -624,7 +624,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
newComment.blockId = flattenedBlockIndex; newComment.blockId = flattenedBlockIndex;
// Add this comment to the block comments object with its script index // Add this comment to the block comments object with its script index
// as the key // as the key
if (blockComments.hasOwnProperty(flattenedBlockIndex)) { if (Object.prototype.hasOwnProperty.call(blockComments, flattenedBlockIndex)) {
blockComments[flattenedBlockIndex].push(newComment); blockComments[flattenedBlockIndex].push(newComment);
} else { } else {
blockComments[flattenedBlockIndex] = [newComment]; blockComments[flattenedBlockIndex] = [newComment];
@ -641,7 +641,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
} }
// If included, parse any and all scripts/blocks on the object. // If included, parse any and all scripts/blocks on the object.
if (object.hasOwnProperty('scripts')) { if (Object.prototype.hasOwnProperty.call(object, 'scripts')) {
parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions, blockComments); parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions, blockComments);
} }
@ -664,7 +664,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
// Update stage specific blocks (e.g. sprite clicked <=> stage clicked) // Update stage specific blocks (e.g. sprite clicked <=> stage clicked)
blocks.updateTargetSpecificBlocks(topLevel); // topLevel = isStage blocks.updateTargetSpecificBlocks(topLevel); // topLevel = isStage
if (object.hasOwnProperty('lists')) { if (Object.prototype.hasOwnProperty.call(object, 'lists')) {
for (let k = 0; k < object.lists.length; k++) { for (let k = 0; k < object.lists.length; k++) {
const list = object.lists[k]; const list = object.lists[k];
const newVariable = new Variable( const newVariable = new Variable(
@ -677,34 +677,34 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
target.variables[newVariable.id] = newVariable; target.variables[newVariable.id] = newVariable;
} }
} }
if (object.hasOwnProperty('scratchX')) { if (Object.prototype.hasOwnProperty.call(object, 'scratchX')) {
target.x = object.scratchX; target.x = object.scratchX;
} }
if (object.hasOwnProperty('scratchY')) { if (Object.prototype.hasOwnProperty.call(object, 'scratchY')) {
target.y = object.scratchY; target.y = object.scratchY;
} }
if (object.hasOwnProperty('direction')) { if (Object.prototype.hasOwnProperty.call(object, 'direction')) {
// Sometimes the direction can be outside of the range: LLK/scratch-gui#5806 // Sometimes the direction can be outside of the range: LLK/scratch-gui#5806
// wrapClamp it (like we do on RenderedTarget.setDirection) // wrapClamp it (like we do on RenderedTarget.setDirection)
target.direction = MathUtil.wrapClamp(object.direction, -179, 180); target.direction = MathUtil.wrapClamp(object.direction, -179, 180);
} }
if (object.hasOwnProperty('isDraggable')) { if (Object.prototype.hasOwnProperty.call(object, 'isDraggable')) {
target.draggable = object.isDraggable; target.draggable = object.isDraggable;
} }
if (object.hasOwnProperty('scale')) { if (Object.prototype.hasOwnProperty.call(object, 'scale')) {
// SB2 stores as 1.0 = 100%; we use % in the VM. // SB2 stores as 1.0 = 100%; we use % in the VM.
target.size = object.scale * 100; target.size = object.scale * 100;
} }
if (object.hasOwnProperty('visible')) { if (Object.prototype.hasOwnProperty.call(object, 'visible')) {
target.visible = object.visible; target.visible = object.visible;
} }
if (object.hasOwnProperty('currentCostumeIndex')) { if (Object.prototype.hasOwnProperty.call(object, 'currentCostumeIndex')) {
// Current costume index can sometimes be a floating // Current costume index can sometimes be a floating
// point number, use Math.floor to come up with an appropriate index // point number, use Math.floor to come up with an appropriate index
// and clamp it to the actual number of costumes the object has for good measure. // and clamp it to the actual number of costumes the object has for good measure.
target.currentCostume = MathUtil.clamp(Math.floor(object.currentCostumeIndex), 0, object.costumes.length - 1); target.currentCostume = MathUtil.clamp(Math.floor(object.currentCostumeIndex), 0, object.costumes.length - 1);
} }
if (object.hasOwnProperty('rotationStyle')) { if (Object.prototype.hasOwnProperty.call(object, 'rotationStyle')) {
if (object.rotationStyle === 'none') { if (object.rotationStyle === 'none') {
target.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE; target.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE;
} else if (object.rotationStyle === 'leftRight') { } else if (object.rotationStyle === 'leftRight') {
@ -713,16 +713,16 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
target.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND; target.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND;
} }
} }
if (object.hasOwnProperty('tempoBPM')) { if (Object.prototype.hasOwnProperty.call(object, 'tempoBPM')) {
target.tempo = object.tempoBPM; target.tempo = object.tempoBPM;
} }
if (object.hasOwnProperty('videoAlpha')) { if (Object.prototype.hasOwnProperty.call(object, 'videoAlpha')) {
// SB2 stores alpha as opacity, where 1.0 is opaque. // SB2 stores alpha as opacity, where 1.0 is opaque.
// We convert to a percentage, and invert it so 100% is full transparency. // We convert to a percentage, and invert it so 100% is full transparency.
target.videoTransparency = 100 - (100 * object.videoAlpha); target.videoTransparency = 100 - (100 * object.videoAlpha);
} }
if (object.hasOwnProperty('info')) { if (Object.prototype.hasOwnProperty.call(object, 'info')) {
if (object.info.hasOwnProperty('videoOn')) { if (Object.prototype.hasOwnProperty.call(object.info, 'videoOn')) {
if (object.info.videoOn) { if (object.info.videoOn) {
target.videoState = RenderedTarget.VIDEO_STATE.ON; target.videoState = RenderedTarget.VIDEO_STATE.ON;
} else { } else {
@ -730,7 +730,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip,
} }
} }
} }
if (object.hasOwnProperty('indexInLibrary')) { if (Object.prototype.hasOwnProperty.call(object, 'indexInLibrary')) {
// Temporarily store the 'indexInLibrary' property from the sb2 file // Temporarily store the 'indexInLibrary' property from the sb2 file
// so that we can correctly order sprites in the target pane. // so that we can correctly order sprites in the target pane.
// This will be deleted after we are done parsing and ordering the targets list. // This will be deleted after we are done parsing and ordering the targets list.

View file

@ -173,7 +173,7 @@ const serializeFields = function (fields) {
for (const fieldName in fields) { for (const fieldName in fields) {
if (!hasOwnProperty.call(fields, fieldName)) continue; if (!hasOwnProperty.call(fields, fieldName)) continue;
obj[fieldName] = [fields[fieldName].value]; obj[fieldName] = [fields[fieldName].value];
if (fields[fieldName].hasOwnProperty('id')) { if (Object.prototype.hasOwnProperty.call(fields[fieldName], 'id')) {
obj[fieldName].push(fields[fieldName].id); obj[fieldName].push(fields[fieldName].id);
} }
} }
@ -301,7 +301,7 @@ const serializeBlocks = function (blocks) {
const obj = Object.create(null); const obj = Object.create(null);
const extensionIDs = new Set(); const extensionIDs = new Set();
for (const blockID in blocks) { for (const blockID in blocks) {
if (!blocks.hasOwnProperty(blockID)) continue; if (!Object.prototype.hasOwnProperty.call(blocks, blockID)) continue;
obj[blockID] = serializeBlock(blocks[blockID], blocks); obj[blockID] = serializeBlock(blocks[blockID], blocks);
const extensionID = getExtensionIdForOpcode(blocks[blockID].opcode); const extensionID = getExtensionIdForOpcode(blocks[blockID].opcode);
if (extensionID) { if (extensionID) {
@ -428,7 +428,7 @@ const serializeVariables = function (variables) {
const serializeComments = function (comments) { const serializeComments = function (comments) {
const obj = Object.create(null); const obj = Object.create(null);
for (const commentId in comments) { for (const commentId in comments) {
if (!comments.hasOwnProperty(commentId)) continue; if (!Object.prototype.hasOwnProperty.call(comments, commentId)) continue;
const comment = comments[commentId]; const comment = comments[commentId];
const serializedComment = Object.create(null); const serializedComment = Object.create(null);
@ -473,13 +473,21 @@ const serializeTarget = function (target, extensions) {
obj.currentCostume = target.currentCostume; obj.currentCostume = target.currentCostume;
obj.costumes = target.costumes.map(serializeCostume); obj.costumes = target.costumes.map(serializeCostume);
obj.sounds = target.sounds.map(serializeSound); obj.sounds = target.sounds.map(serializeSound);
if (target.hasOwnProperty('volume')) obj.volume = target.volume; if (Object.prototype.hasOwnProperty.call(target, 'volume')) obj.volume = target.volume;
if (target.hasOwnProperty('layerOrder')) obj.layerOrder = target.layerOrder; if (Object.prototype.hasOwnProperty.call(target, 'layerOrder')) obj.layerOrder = target.layerOrder;
if (obj.isStage) { // Only the stage should have these properties if (obj.isStage) { // Only the stage should have these properties
if (target.hasOwnProperty('tempo')) obj.tempo = target.tempo; if (Object.prototype.hasOwnProperty.call(target, 'tempo')) {
if (target.hasOwnProperty('videoTransparency')) obj.videoTransparency = target.videoTransparency; obj.tempo = target.tempo;
if (target.hasOwnProperty('videoState')) obj.videoState = target.videoState; }
if (target.hasOwnProperty('textToSpeechLanguage')) obj.textToSpeechLanguage = target.textToSpeechLanguage; if (Object.prototype.hasOwnProperty.call(target, 'videoTransparency')) {
obj.videoTransparency = target.videoTransparency;
}
if (Object.prototype.hasOwnProperty.call(target, 'videoState')) {
obj.videoState = target.videoState;
}
if (Object.prototype.hasOwnProperty.call(target, 'textToSpeechLanguage')) {
obj.textToSpeechLanguage = target.textToSpeechLanguage;
}
} else { // The stage does not need the following properties, but sprites should } else { // The stage does not need the following properties, but sprites should
obj.visible = target.visible; obj.visible = target.visible;
obj.x = target.x; obj.x = target.x;
@ -852,7 +860,7 @@ const deserializeBlocks = function (blocks) {
* SoundBank for the sound assets. null for unsupported objects. * SoundBank for the sound assets. null for unsupported objects.
*/ */
const parseScratchAssets = function (object, runtime, zip) { const parseScratchAssets = function (object, runtime, zip) {
if (!object.hasOwnProperty('name')) { if (!Object.prototype.hasOwnProperty.call(object, 'name')) {
// Watcher/monitor - skip this object until those are implemented in VM. // Watcher/monitor - skip this object until those are implemented in VM.
// @todo // @todo
return Promise.resolve(null); return Promise.resolve(null);
@ -882,7 +890,7 @@ const parseScratchAssets = function (object, runtime, zip) {
costumeSource.dataFormat || costumeSource.dataFormat ||
(costumeSource.assetType && costumeSource.assetType.runtimeFormat) || // older format (costumeSource.assetType && costumeSource.assetType.runtimeFormat) || // older format
'png'; // if all else fails, guess that it might be a PNG 'png'; // if all else fails, guess that it might be a PNG
const costumeMd5Ext = costumeSource.hasOwnProperty('md5ext') ? const costumeMd5Ext = Object.prototype.hasOwnProperty.call(costumeSource, 'md5ext') ?
costumeSource.md5ext : `${costumeSource.assetId}.${dataFormat}`; costumeSource.md5ext : `${costumeSource.assetId}.${dataFormat}`;
costume.md5 = costumeMd5Ext; costume.md5 = costumeMd5Ext;
costume.dataFormat = dataFormat; costume.dataFormat = dataFormat;
@ -936,7 +944,7 @@ const parseScratchAssets = function (object, runtime, zip) {
* @return {!Promise.<Target>} Promise for the target created (stage or sprite), or null for unsupported objects. * @return {!Promise.<Target>} Promise for the target created (stage or sprite), or null for unsupported objects.
*/ */
const parseScratchObject = function (object, runtime, extensions, zip, assets) { const parseScratchObject = function (object, runtime, extensions, zip, assets) {
if (!object.hasOwnProperty('name')) { if (!Object.prototype.hasOwnProperty.call(object, 'name')) {
// Watcher/monitor - skip this object until those are implemented in VM. // Watcher/monitor - skip this object until those are implemented in VM.
// @todo // @todo
return Promise.resolve(null); return Promise.resolve(null);
@ -948,14 +956,14 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
const sprite = new Sprite(blocks, runtime); const sprite = new Sprite(blocks, runtime);
// Sprite/stage name from JSON. // Sprite/stage name from JSON.
if (object.hasOwnProperty('name')) { if (Object.prototype.hasOwnProperty.call(object, 'name')) {
sprite.name = object.name; sprite.name = object.name;
} }
if (object.hasOwnProperty('blocks')) { if (Object.prototype.hasOwnProperty.call(object, 'blocks')) {
deserializeBlocks(object.blocks); deserializeBlocks(object.blocks);
// Take a second pass to create objects and add extensions // Take a second pass to create objects and add extensions
for (const blockId in object.blocks) { for (const blockId in object.blocks) {
if (!object.blocks.hasOwnProperty(blockId)) continue; if (!Object.prototype.hasOwnProperty.call(object.blocks, blockId)) continue;
const blockJSON = object.blocks[blockId]; const blockJSON = object.blocks[blockId];
blocks.createBlock(blockJSON); blocks.createBlock(blockJSON);
@ -973,22 +981,22 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
// Create the first clone, and load its run-state from JSON. // Create the first clone, and load its run-state from JSON.
const target = sprite.createClone(object.isStage ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER); const target = sprite.createClone(object.isStage ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);
// Load target properties from JSON. // Load target properties from JSON.
if (object.hasOwnProperty('tempo')) { if (Object.prototype.hasOwnProperty.call(object, 'tempo')) {
target.tempo = object.tempo; target.tempo = object.tempo;
} }
if (object.hasOwnProperty('volume')) { if (Object.prototype.hasOwnProperty.call(object, 'volume')) {
target.volume = object.volume; target.volume = object.volume;
} }
if (object.hasOwnProperty('videoTransparency')) { if (Object.prototype.hasOwnProperty.call(object, 'videoTransparency')) {
target.videoTransparency = object.videoTransparency; target.videoTransparency = object.videoTransparency;
} }
if (object.hasOwnProperty('videoState')) { if (Object.prototype.hasOwnProperty.call(object, 'videoState')) {
target.videoState = object.videoState; target.videoState = object.videoState;
} }
if (object.hasOwnProperty('textToSpeechLanguage')) { if (Object.prototype.hasOwnProperty.call(object, 'textToSpeechLanguage')) {
target.textToSpeechLanguage = object.textToSpeechLanguage; target.textToSpeechLanguage = object.textToSpeechLanguage;
} }
if (object.hasOwnProperty('variables')) { if (Object.prototype.hasOwnProperty.call(object, 'variables')) {
for (const varId in object.variables) { for (const varId in object.variables) {
const variable = object.variables[varId]; const variable = object.variables[varId];
// A variable is a cloud variable if: // A variable is a cloud variable if:
@ -1008,7 +1016,7 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
target.variables[newVariable.id] = newVariable; target.variables[newVariable.id] = newVariable;
} }
} }
if (object.hasOwnProperty('lists')) { if (Object.prototype.hasOwnProperty.call(object, 'lists')) {
for (const listId in object.lists) { for (const listId in object.lists) {
const list = object.lists[listId]; const list = object.lists[listId];
const newList = new Variable( const newList = new Variable(
@ -1021,7 +1029,7 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
target.variables[newList.id] = newList; target.variables[newList.id] = newList;
} }
} }
if (object.hasOwnProperty('broadcasts')) { if (Object.prototype.hasOwnProperty.call(object, 'broadcasts')) {
for (const broadcastId in object.broadcasts) { for (const broadcastId in object.broadcasts) {
const broadcast = object.broadcasts[broadcastId]; const broadcast = object.broadcasts[broadcastId];
const newBroadcast = new Variable( const newBroadcast = new Variable(
@ -1035,7 +1043,7 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
target.variables[newBroadcast.id] = newBroadcast; target.variables[newBroadcast.id] = newBroadcast;
} }
} }
if (object.hasOwnProperty('comments')) { if (Object.prototype.hasOwnProperty.call(object, 'comments')) {
for (const commentId in object.comments) { for (const commentId in object.comments) {
const comment = object.comments[commentId]; const comment = object.comments[commentId];
const newComment = new Comment( const newComment = new Comment(
@ -1053,39 +1061,39 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
target.comments[newComment.id] = newComment; target.comments[newComment.id] = newComment;
} }
} }
if (object.hasOwnProperty('x')) { if (Object.prototype.hasOwnProperty.call(object, 'x')) {
target.x = object.x; target.x = object.x;
} }
if (object.hasOwnProperty('y')) { if (Object.prototype.hasOwnProperty.call(object, 'y')) {
target.y = object.y; target.y = object.y;
} }
if (object.hasOwnProperty('direction')) { if (Object.prototype.hasOwnProperty.call(object, 'direction')) {
// Sometimes the direction can be outside of the range: LLK/scratch-gui#5806 // Sometimes the direction can be outside of the range: LLK/scratch-gui#5806
// wrapClamp it (like we do on RenderedTarget.setDirection) // wrapClamp it (like we do on RenderedTarget.setDirection)
target.direction = MathUtil.wrapClamp(object.direction, -179, 180); target.direction = MathUtil.wrapClamp(object.direction, -179, 180);
} }
if (object.hasOwnProperty('size')) { if (Object.prototype.hasOwnProperty.call(object, 'size')) {
target.size = object.size; target.size = object.size;
} }
if (object.hasOwnProperty('visible')) { if (Object.prototype.hasOwnProperty.call(object, 'visible')) {
target.visible = object.visible; target.visible = object.visible;
} }
if (object.hasOwnProperty('currentCostume')) { if (Object.prototype.hasOwnProperty.call(object, 'currentCostume')) {
target.currentCostume = MathUtil.clamp(object.currentCostume, 0, object.costumes.length - 1); target.currentCostume = MathUtil.clamp(object.currentCostume, 0, object.costumes.length - 1);
} }
if (object.hasOwnProperty('rotationStyle')) { if (Object.prototype.hasOwnProperty.call(object, 'rotationStyle')) {
target.rotationStyle = object.rotationStyle; target.rotationStyle = object.rotationStyle;
} }
if (object.hasOwnProperty('isStage')) { if (Object.prototype.hasOwnProperty.call(object, 'isStage')) {
target.isStage = object.isStage; target.isStage = object.isStage;
} }
if (object.hasOwnProperty('targetPaneOrder')) { if (Object.prototype.hasOwnProperty.call(object, 'targetPaneOrder')) {
// Temporarily store the 'targetPaneOrder' property // Temporarily store the 'targetPaneOrder' property
// so that we can correctly order sprites in the target pane. // so that we can correctly order sprites in the target pane.
// This will be deleted after we are done parsing and ordering the targets list. // This will be deleted after we are done parsing and ordering the targets list.
target.targetPaneOrder = object.targetPaneOrder; target.targetPaneOrder = object.targetPaneOrder;
} }
if (object.hasOwnProperty('draggable')) { if (Object.prototype.hasOwnProperty.call(object, 'draggable')) {
target.draggable = object.draggable; target.draggable = object.draggable;
} }
Promise.all(costumePromises).then(costumes => { Promise.all(costumePromises).then(costumes => {

View file

@ -395,7 +395,7 @@ class RenderedTarget extends Target {
* @param {!number} value Numerical magnitude of effect. * @param {!number} value Numerical magnitude of effect.
*/ */
setEffect (effectName, value) { setEffect (effectName, value) {
if (!this.effects.hasOwnProperty(effectName)) return; if (!Object.prototype.hasOwnProperty.call(this.effects, effectName)) return;
this.effects[effectName] = value; this.effects[effectName] = value;
if (this.renderer) { if (this.renderer) {
this.renderer.updateDrawableEffect(this.drawableID, effectName, value); this.renderer.updateDrawableEffect(this.drawableID, effectName, value);
@ -411,12 +411,12 @@ class RenderedTarget extends Target {
*/ */
clearEffects () { clearEffects () {
for (const effectName in this.effects) { for (const effectName in this.effects) {
if (!this.effects.hasOwnProperty(effectName)) continue; if (!Object.prototype.hasOwnProperty.call(this.effects, effectName)) continue;
this.effects[effectName] = 0; this.effects[effectName] = 0;
} }
if (this.renderer) { if (this.renderer) {
for (const effectName in this.effects) { for (const effectName in this.effects) {
if (!this.effects.hasOwnProperty(effectName)) continue; if (!Object.prototype.hasOwnProperty.call(this.effects, effectName)) continue;
this.renderer.updateDrawableEffect(this.drawableID, effectName, 0); this.renderer.updateDrawableEffect(this.drawableID, effectName, 0);
} }
if (this.visible) { if (this.visible) {
@ -682,7 +682,7 @@ class RenderedTarget extends Target {
this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId); this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId);
for (const effectName in this.effects) { for (const effectName in this.effects) {
if (!this.effects.hasOwnProperty(effectName)) continue; if (!Object.prototype.hasOwnProperty.call(this.effects, effectName)) continue;
this.renderer.updateDrawableEffect(this.drawableID, effectName, this.effects[effectName]); this.renderer.updateDrawableEffect(this.drawableID, effectName, this.effects[effectName]);
} }
@ -1020,25 +1020,25 @@ class RenderedTarget extends Target {
* @param {object} data An object with sprite info data to set. * @param {object} data An object with sprite info data to set.
*/ */
postSpriteInfo (data) { postSpriteInfo (data) {
const force = data.hasOwnProperty('force') ? data.force : null; const force = Object.prototype.hasOwnProperty.call(data, 'force') ? data.force : null;
const isXChanged = data.hasOwnProperty('x'); const isXChanged = Object.prototype.hasOwnProperty.call(data, 'x');
const isYChanged = data.hasOwnProperty('y'); const isYChanged = Object.prototype.hasOwnProperty.call(data, 'y');
if (isXChanged || isYChanged) { if (isXChanged || isYChanged) {
this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force); this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force);
} }
if (data.hasOwnProperty('direction')) { if (Object.prototype.hasOwnProperty.call(data, 'direction')) {
this.setDirection(data.direction); this.setDirection(data.direction);
} }
if (data.hasOwnProperty('draggable')) { if (Object.prototype.hasOwnProperty.call(data, 'draggable')) {
this.setDraggable(data.draggable); this.setDraggable(data.draggable);
} }
if (data.hasOwnProperty('rotationStyle')) { if (Object.prototype.hasOwnProperty.call(data, 'rotationStyle')) {
this.setRotationStyle(data.rotationStyle); this.setRotationStyle(data.rotationStyle);
} }
if (data.hasOwnProperty('visible')) { if (Object.prototype.hasOwnProperty.call(data, 'visible')) {
this.setVisible(data.visible); this.setVisible(data.visible);
} }
if (data.hasOwnProperty('size')) { if (Object.prototype.hasOwnProperty.call(data, 'size')) {
this.setSize(data.size); this.setSize(data.size);
} }
} }

View file

@ -62,7 +62,7 @@ class JSONRPC {
if (json.jsonrpc !== '2.0') { if (json.jsonrpc !== '2.0') {
throw new Error(`Bad or missing JSON-RPC version in message: ${json}`); throw new Error(`Bad or missing JSON-RPC version in message: ${json}`);
} }
if (json.hasOwnProperty('method')) { if (Object.prototype.hasOwnProperty.call(json, 'method')) {
this._handleRequest(json); this._handleRequest(json);
} else { } else {
this._handleResponse(json); this._handleResponse(json);

View file

@ -21,8 +21,10 @@ class TaskQueue {
this._maxTokens = maxTokens; this._maxTokens = maxTokens;
this._refillRate = refillRate; this._refillRate = refillRate;
this._pendingTaskRecords = []; this._pendingTaskRecords = [];
this._tokenCount = options.hasOwnProperty('startingTokens') ? options.startingTokens : maxTokens; this._tokenCount = Object.prototype.hasOwnProperty.call(options, 'startingTokens') ?
this._maxTotalCost = options.hasOwnProperty('maxTotalCost') ? options.maxTotalCost : Infinity; options.startingTokens : maxTokens;
this._maxTotalCost = Object.prototype.hasOwnProperty.call(options, 'maxTotalCost') ?
options.maxTotalCost : Infinity;
this._timer = new Timer(); this._timer = new Timer();
this._timer.start(); this._timer.start();
this._timeout = null; this._timeout = null;
@ -53,7 +55,7 @@ class TaskQueue {
if (this._maxTotalCost < Infinity) { if (this._maxTotalCost < Infinity) {
const currentTotalCost = this._pendingTaskRecords.reduce((t, r) => t + r.cost, 0); const currentTotalCost = this._pendingTaskRecords.reduce((t, r) => t + r.cost, 0);
if (currentTotalCost + cost > this._maxTotalCost) { if (currentTotalCost + cost > this._maxTotalCost) {
return Promise.reject('Maximum total cost exceeded'); return Promise.reject(new Error('Maximum total cost exceeded'));
} }
} }
const newRecord = { const newRecord = {

View file

@ -2,7 +2,6 @@ let _TextEncoder;
if (typeof TextEncoder === 'undefined') { if (typeof TextEncoder === 'undefined') {
_TextEncoder = require('text-encoding').TextEncoder; _TextEncoder = require('text-encoding').TextEncoder;
} else { } else {
/* global TextEncoder */
_TextEncoder = TextEncoder; _TextEncoder = TextEncoder;
} }
const EventEmitter = require('events'); const EventEmitter = require('events');
@ -354,7 +353,7 @@ class VirtualMachine extends EventEmitter {
.then(() => this.runtime.handleProjectLoaded()) .then(() => this.runtime.handleProjectLoaded())
.catch(error => { .catch(error => {
// Intentionally rejecting here (want errors to be handled by caller) // Intentionally rejecting here (want errors to be handled by caller)
if (error.hasOwnProperty('validationError')) { if (Object.prototype.hasOwnProperty.call(error, 'validationError')) {
return Promise.reject(JSON.stringify(error)); return Promise.reject(JSON.stringify(error));
} }
return Promise.reject(error); return Promise.reject(error);
@ -503,6 +502,8 @@ class VirtualMachine extends EventEmitter {
const sb3 = require('./serialization/sb3'); const sb3 = require('./serialization/sb3');
return sb3.deserialize(projectJSON, runtime, zip); return sb3.deserialize(projectJSON, runtime, zip);
} }
// TODO: reject with an Error (possible breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('Unable to verify Scratch Project version.'); return Promise.reject('Unable to verify Scratch Project version.');
}; };
return deserializePromise() return deserializePromise()
@ -607,14 +608,18 @@ class VirtualMachine extends EventEmitter {
if (projectVersion === 3) { if (projectVersion === 3) {
return this._addSprite3(validatedInput[0], validatedInput[1]); return this._addSprite3(validatedInput[0], validatedInput[1]);
} }
// TODO: reject with an Error (possible breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject(`${errorPrefix} Unable to verify sprite version.`); return Promise.reject(`${errorPrefix} Unable to verify sprite version.`);
}) })
.then(() => this.runtime.emitProjectChanged()) .then(() => this.runtime.emitProjectChanged())
.catch(error => { .catch(error => {
// Intentionally rejecting here (want errors to be handled by caller) // Intentionally rejecting here (want errors to be handled by caller)
if (error.hasOwnProperty('validationError')) { if (Object.prototype.hasOwnProperty.call(error, 'validationError')) {
return Promise.reject(JSON.stringify(error)); return Promise.reject(JSON.stringify(error));
} }
// TODO: reject with an Error (possible breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject(`${errorPrefix} ${error}`); return Promise.reject(`${errorPrefix} ${error}`);
}); });
} }
@ -673,6 +678,8 @@ class VirtualMachine extends EventEmitter {
}); });
} }
// If the target cannot be found by id, return a rejected promise // If the target cannot be found by id, return a rejected promise
// TODO: reject with an Error (possible breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject(); return Promise.reject();
} }
@ -687,6 +694,8 @@ class VirtualMachine extends EventEmitter {
* @returns {?Promise} - a promise that resolves when the costume has been added * @returns {?Promise} - a promise that resolves when the costume has been added
*/ */
addCostumeFromLibrary (md5ext, costumeObject) { addCostumeFromLibrary (md5ext, costumeObject) {
// TODO: reject with an Error (possible breaking API change!)
// eslint-disable-next-line prefer-promise-reject-errors
if (!this.editingTarget) return Promise.reject(); if (!this.editingTarget) return Promise.reject();
return this.addCostume(md5ext, costumeObject, this.editingTarget.id, 2 /* optVersion */); return this.addCostume(md5ext, costumeObject, this.editingTarget.id, 2 /* optVersion */);
} }
@ -1338,7 +1347,7 @@ class VirtualMachine extends EventEmitter {
targetList: this.runtime.targets targetList: this.runtime.targets
.filter( .filter(
// Don't report clones. // Don't report clones.
target => !target.hasOwnProperty('isOriginal') || target.isOriginal target => !Object.prototype.hasOwnProperty.call(target, 'isOriginal') || target.isOriginal
).map( ).map(
target => target.toJSON() target => target.toJSON()
), ),
@ -1414,7 +1423,10 @@ class VirtualMachine extends EventEmitter {
*/ */
getTargetIdForDrawableId (drawableId) { getTargetIdForDrawableId (drawableId) {
const target = this.runtime.getTargetByDrawableId(drawableId); const target = this.runtime.getTargetByDrawableId(drawableId);
if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) { if (target &&
Object.prototype.hasOwnProperty.call(target, 'id') &&
Object.prototype.hasOwnProperty.call(target, 'isStage') &&
!target.isStage) {
return target.id; return target.id;
} }
return null; return null;

View file

@ -6,6 +6,8 @@ const path = require('path');
const oldRequire = Module.prototype.require; const oldRequire = Module.prototype.require;
Module.prototype.require = function (target) { Module.prototype.require = function (target) {
if (target.indexOf('/') === -1) { if (target.indexOf('/') === -1) {
// we really do just want to forward the arguments here
// eslint-disable-next-line prefer-rest-params
return oldRequire.apply(this, arguments); return oldRequire.apply(this, arguments);
} }

View file

@ -57,7 +57,9 @@ test('importing sb2 project with special chars in message names', t => {
t.equal(allBroadcastFields[ltPerfectMessageId].length, 1); t.equal(allBroadcastFields[ltPerfectMessageId].length, 1);
t.equal(allBroadcastFields[abMessageId].length, 1); t.equal(allBroadcastFields[abMessageId].length, 1);
const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]); const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]);
const catMessageBlocks = catBlocks.filter(block => block.fields.hasOwnProperty('BROADCAST_OPTION')); const catMessageBlocks = catBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'BROADCAST_OPTION')
);
t.equal(catMessageBlocks.length, 2); t.equal(catMessageBlocks.length, 2);
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId); t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId); t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);

View file

@ -57,7 +57,9 @@ test('importing sb3 project with special chars in message names', t => {
t.equal(allBroadcastFields[ltPerfectMessageId].length, 1); t.equal(allBroadcastFields[ltPerfectMessageId].length, 1);
t.equal(allBroadcastFields[abMessageId].length, 1); t.equal(allBroadcastFields[abMessageId].length, 1);
const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]); const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]);
const catMessageBlocks = catBlocks.filter(block => block.fields.hasOwnProperty('BROADCAST_OPTION')); const catMessageBlocks = catBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'BROADCAST_OPTION')
);
t.equal(catMessageBlocks.length, 2); t.equal(catMessageBlocks.length, 2);
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId); t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId); t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);

View file

@ -55,12 +55,12 @@ test('importing sb2 project with special chars in variable names', t => {
// There should be 3 fields, 2 on the stage, and one on the cat // There should be 3 fields, 2 on the stage, and one on the cat
t.equal(allVarListFields[abVarId].length, 3); t.equal(allVarListFields[abVarId].length, 3);
const stageBlocks = Object.keys(stage.blocks._blocks).map(blockId => stage.blocks._blocks[blockId]); const stageBlocks = Object.keys(stage.blocks._blocks).map(blockId => stage.blocks._blocks[blockId]);
const stageListBlocks = stageBlocks.filter(block => block.fields.hasOwnProperty('LIST')); const stageListBlocks = stageBlocks.filter(block => Object.prototype.hasOwnProperty.call(block.fields, 'LIST'));
t.equal(stageListBlocks.length, 2); t.equal(stageListBlocks.length, 2);
t.equal(stageListBlocks[0].fields.LIST.id, abVarId); t.equal(stageListBlocks[0].fields.LIST.id, abVarId);
t.equal(stageListBlocks[1].fields.LIST.id, abVarId); t.equal(stageListBlocks[1].fields.LIST.id, abVarId);
const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]); const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]);
const catListBlocks = catBlocks.filter(block => block.fields.hasOwnProperty('LIST')); const catListBlocks = catBlocks.filter(block => Object.prototype.hasOwnProperty.call(block.fields, 'LIST'));
t.equal(catListBlocks.length, 1); t.equal(catListBlocks.length, 1);
t.equal(catListBlocks[0].fields.LIST.id, abVarId); t.equal(catListBlocks[0].fields.LIST.id, abVarId);
@ -83,10 +83,12 @@ test('importing sb2 project with special chars in variable names', t => {
// Find all the references for this variable, and verify they have the correct ID // Find all the references for this variable, and verify they have the correct ID
// There should be only two, one on the stage and one on bananas // There should be only two, one on the stage and one on bananas
t.equal(allVarListFields[fooVarId].length, 2); t.equal(allVarListFields[fooVarId].length, 2);
const stageVarBlocks = stageBlocks.filter(block => block.fields.hasOwnProperty('VARIABLE')); const stageVarBlocks = stageBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'VARIABLE')
);
t.equal(stageVarBlocks.length, 1); t.equal(stageVarBlocks.length, 1);
t.equal(stageVarBlocks[0].fields.VARIABLE.id, fooVarId); t.equal(stageVarBlocks[0].fields.VARIABLE.id, fooVarId);
const catVarBlocks = catBlocks.filter(block => block.fields.hasOwnProperty('VARIABLE')); const catVarBlocks = catBlocks.filter(block => Object.prototype.hasOwnProperty.call(block.fields, 'VARIABLE'));
t.equal(catVarBlocks.length, 1); t.equal(catVarBlocks.length, 1);
t.equal(catVarBlocks[0].fields.VARIABLE.id, fooVarId); t.equal(catVarBlocks[0].fields.VARIABLE.id, fooVarId);
@ -110,7 +112,9 @@ test('importing sb2 project with special chars in variable names', t => {
// There should be one // There should be one
t.equal(allVarListFields[ltPerfectVarId].length, 1); t.equal(allVarListFields[ltPerfectVarId].length, 1);
const bananasBlocks = Object.keys(bananas.blocks._blocks).map(blockId => bananas.blocks._blocks[blockId]); const bananasBlocks = Object.keys(bananas.blocks._blocks).map(blockId => bananas.blocks._blocks[blockId]);
const bananasVarBlocks = bananasBlocks.filter(block => block.fields.hasOwnProperty('VARIABLE')); const bananasVarBlocks = bananasBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'VARIABLE')
);
t.equal(bananasVarBlocks.length, 1); t.equal(bananasVarBlocks.length, 1);
t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId); t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId);

View file

@ -55,12 +55,12 @@ test('importing sb3 project with special chars in variable names', t => {
// There should be 3 fields, 2 on the stage, and one on the cat // There should be 3 fields, 2 on the stage, and one on the cat
t.equal(allVarListFields[abVarId].length, 3); t.equal(allVarListFields[abVarId].length, 3);
const stageBlocks = Object.keys(stage.blocks._blocks).map(blockId => stage.blocks._blocks[blockId]); const stageBlocks = Object.keys(stage.blocks._blocks).map(blockId => stage.blocks._blocks[blockId]);
const stageListBlocks = stageBlocks.filter(block => block.fields.hasOwnProperty('LIST')); const stageListBlocks = stageBlocks.filter(block => Object.prototype.hasOwnProperty.call(block.fields, 'LIST'));
t.equal(stageListBlocks.length, 2); t.equal(stageListBlocks.length, 2);
t.equal(stageListBlocks[0].fields.LIST.id, abVarId); t.equal(stageListBlocks[0].fields.LIST.id, abVarId);
t.equal(stageListBlocks[1].fields.LIST.id, abVarId); t.equal(stageListBlocks[1].fields.LIST.id, abVarId);
const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]); const catBlocks = Object.keys(cat.blocks._blocks).map(blockId => cat.blocks._blocks[blockId]);
const catListBlocks = catBlocks.filter(block => block.fields.hasOwnProperty('LIST')); const catListBlocks = catBlocks.filter(block => Object.prototype.hasOwnProperty.call(block.fields, 'LIST'));
t.equal(catListBlocks.length, 1); t.equal(catListBlocks.length, 1);
t.equal(catListBlocks[0].fields.LIST.id, abVarId); t.equal(catListBlocks[0].fields.LIST.id, abVarId);
@ -83,10 +83,14 @@ test('importing sb3 project with special chars in variable names', t => {
// Find all the references for this variable, and verify they have the correct ID // Find all the references for this variable, and verify they have the correct ID
// There should be only two, one on the stage and one on bananas // There should be only two, one on the stage and one on bananas
t.equal(allVarListFields[fooVarId].length, 2); t.equal(allVarListFields[fooVarId].length, 2);
const stageVarBlocks = stageBlocks.filter(block => block.fields.hasOwnProperty('VARIABLE')); const stageVarBlocks = stageBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'VARIABLE')
);
t.equal(stageVarBlocks.length, 1); t.equal(stageVarBlocks.length, 1);
t.equal(stageVarBlocks[0].fields.VARIABLE.id, fooVarId); t.equal(stageVarBlocks[0].fields.VARIABLE.id, fooVarId);
const catVarBlocks = catBlocks.filter(block => block.fields.hasOwnProperty('VARIABLE')); const catVarBlocks = catBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'VARIABLE')
);
t.equal(catVarBlocks.length, 1); t.equal(catVarBlocks.length, 1);
t.equal(catVarBlocks[0].fields.VARIABLE.id, fooVarId); t.equal(catVarBlocks[0].fields.VARIABLE.id, fooVarId);
@ -110,7 +114,9 @@ test('importing sb3 project with special chars in variable names', t => {
// There should be one // There should be one
t.equal(allVarListFields[ltPerfectVarId].length, 1); t.equal(allVarListFields[ltPerfectVarId].length, 1);
const bananasBlocks = Object.keys(bananas.blocks._blocks).map(blockId => bananas.blocks._blocks[blockId]); const bananasBlocks = Object.keys(bananas.blocks._blocks).map(blockId => bananas.blocks._blocks[blockId]);
const bananasVarBlocks = bananasBlocks.filter(block => block.fields.hasOwnProperty('VARIABLE')); const bananasVarBlocks = bananasBlocks.filter(
block => Object.prototype.hasOwnProperty.call(block.fields, 'VARIABLE')
);
t.equal(bananasVarBlocks.length, 1); t.equal(bananasVarBlocks.length, 1);
t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId); t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId);

View file

@ -290,8 +290,8 @@ test('wait', t => {
t.equal(yields, 1, 'Second call after timeElapsed does not yield'); t.equal(yields, 1, 'Second call after timeElapsed does not yield');
t.equal(waitTime, mockUtil.stackFrame.duration); t.equal(waitTime, mockUtil.stackFrame.duration);
t.ok(timeElapsed >= (waitTime - thresholdSmall), t.ok(timeElapsed >= (waitTime - thresholdSmall),
'Wait block ended too early: ${timeElapsed} < ${waitTime} - ${thresholdSmall}'); `Wait block ended too early: ${timeElapsed} < ${waitTime} - ${thresholdSmall}`);
t.ok(timeElapsed <= (waitTime + thresholdLarge), t.ok(timeElapsed <= (waitTime + thresholdLarge),
'Wait block ended too late: ${timeElapsed} > ${waitTime} + ${thresholdLarge}'); `Wait block ended too late: ${timeElapsed} > ${waitTime} + ${thresholdLarge}`);
t.end(); t.end();
}); });

View file

@ -402,10 +402,10 @@ test('duplicateVariables duplicates all variables', t => {
// Should be able to find original var IDs in both this target's variables and // Should be able to find original var IDs in both this target's variables and
// the duplicate variables since a blocks container was not specified. // the duplicate variables since a blocks container was not specified.
t.equal(target.variables.hasOwnProperty('var ID 1'), true); t.equal(Object.prototype.hasOwnProperty.call(target.variables, 'var ID 1'), true);
t.equal(target.variables.hasOwnProperty('var ID 2'), true); t.equal(Object.prototype.hasOwnProperty.call(target.variables, 'var ID 2'), true);
t.equal(duplicateVariables.hasOwnProperty('var ID 1'), true); t.equal(Object.prototype.hasOwnProperty.call(duplicateVariables, 'var ID 1'), true);
t.equal(duplicateVariables.hasOwnProperty('var ID 1'), true); t.equal(Object.prototype.hasOwnProperty.call(duplicateVariables, 'var ID 1'), true);
// Values of the duplicate varaiables should match the value of the original values at the time of duplication // Values of the duplicate varaiables should match the value of the original values at the time of duplication
t.equal(target.variables['var ID 1'].value, duplicateVariables['var ID 1'].value); t.equal(target.variables['var ID 1'].value, duplicateVariables['var ID 1'].value);

View file

@ -142,11 +142,11 @@ const testReporter = function (t, reporter) {
t.equal(reporter.json.checkboxInFlyout, true); t.equal(reporter.json.checkboxInFlyout, true);
t.equal(reporter.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_ROUND); t.equal(reporter.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_ROUND);
t.equal(reporter.json.output, 'String'); t.equal(reporter.json.output, 'String');
t.notOk(reporter.json.hasOwnProperty('previousStatement')); t.notOk(Object.prototype.hasOwnProperty.call(reporter.json, 'previousStatement'));
t.notOk(reporter.json.hasOwnProperty('nextStatement')); t.notOk(Object.prototype.hasOwnProperty.call(reporter.json, 'nextStatement'));
t.same(reporter.json.extensions, ['scratch_extension']); t.same(reporter.json.extensions, ['scratch_extension']);
t.equal(reporter.json.message0, '%1 %2simple text'); // "%1 %2" from the block icon t.equal(reporter.json.message0, '%1 %2simple text'); // "%1 %2" from the block icon
t.notOk(reporter.json.hasOwnProperty('message1')); t.notOk(Object.prototype.hasOwnProperty.call(reporter.json, 'message1'));
t.same(reporter.json.args0, [ t.same(reporter.json.args0, [
// %1 in message0: the block icon // %1 in message0: the block icon
{ {
@ -160,7 +160,7 @@ const testReporter = function (t, reporter) {
type: 'field_vertical_separator' type: 'field_vertical_separator'
} }
]); ]);
t.notOk(reporter.json.hasOwnProperty('args1')); t.notOk(Object.prototype.hasOwnProperty.call(reporter.json, 'args1'));
t.equal(reporter.xml, '<block type="test_reporter"></block>'); t.equal(reporter.xml, '<block type="test_reporter"></block>');
}; };
@ -170,11 +170,11 @@ const testInlineImage = function (t, inlineImage) {
t.equal(inlineImage.json.checkboxInFlyout, true); t.equal(inlineImage.json.checkboxInFlyout, true);
t.equal(inlineImage.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_ROUND); t.equal(inlineImage.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_ROUND);
t.equal(inlineImage.json.output, 'String'); t.equal(inlineImage.json.output, 'String');
t.notOk(inlineImage.json.hasOwnProperty('previousStatement')); t.notOk(Object.prototype.hasOwnProperty.call(inlineImage.json, 'previousStatement'));
t.notOk(inlineImage.json.hasOwnProperty('nextStatement')); t.notOk(Object.prototype.hasOwnProperty.call(inlineImage.json, 'nextStatement'));
t.notOk(inlineImage.json.extensions && inlineImage.json.extensions.length); // OK if it's absent or empty t.notOk(inlineImage.json.extensions && inlineImage.json.extensions.length); // OK if it's absent or empty
t.equal(inlineImage.json.message0, 'text and %1'); // block text followed by inline image t.equal(inlineImage.json.message0, 'text and %1'); // block text followed by inline image
t.notOk(inlineImage.json.hasOwnProperty('message1')); t.notOk(Object.prototype.hasOwnProperty.call(inlineImage.json, 'message1'));
t.same(inlineImage.json.args0, [ t.same(inlineImage.json.args0, [
// %1 in message0: the block icon // %1 in message0: the block icon
{ {
@ -185,7 +185,7 @@ const testInlineImage = function (t, inlineImage) {
flip_rtl: false // False by default flip_rtl: false // False by default
} }
]); ]);
t.notOk(inlineImage.json.hasOwnProperty('args1')); t.notOk(Object.prototype.hasOwnProperty.call(inlineImage.json, 'args1'));
t.equal(inlineImage.xml, '<block type="test_inlineImage"></block>'); t.equal(inlineImage.xml, '<block type="test_inlineImage"></block>');
}; };
@ -198,16 +198,16 @@ const testCommand = function (t, command) {
t.equal(command.json.type, 'test_command'); t.equal(command.json.type, 'test_command');
testCategoryInfo(t, command); testCategoryInfo(t, command);
t.equal(command.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE); t.equal(command.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE);
t.assert(command.json.hasOwnProperty('previousStatement')); t.assert(Object.prototype.hasOwnProperty.call(command.json, 'previousStatement'));
t.assert(command.json.hasOwnProperty('nextStatement')); t.assert(Object.prototype.hasOwnProperty.call(command.json, 'nextStatement'));
t.notOk(command.json.extensions && command.json.extensions.length); // OK if it's absent or empty t.notOk(command.json.extensions && command.json.extensions.length); // OK if it's absent or empty
t.equal(command.json.message0, 'text with %1 %2'); t.equal(command.json.message0, 'text with %1 %2');
t.notOk(command.json.hasOwnProperty('message1')); t.notOk(Object.prototype.hasOwnProperty.call(command.json, 'message1'));
t.strictSame(command.json.args0[0], { t.strictSame(command.json.args0[0], {
type: 'input_value', type: 'input_value',
name: 'ARG' name: 'ARG'
}); });
t.notOk(command.json.hasOwnProperty('args1')); t.notOk(Object.prototype.hasOwnProperty.call(command.json, 'args1'));
t.equal(command.xml, t.equal(command.xml,
'<block type="test_command"><value name="ARG"><shadow type="text"></shadow></value>' + '<block type="test_command"><value name="ARG"><shadow type="text"></shadow></value>' +
'<value name="ARG_WITH_DEFAULT"><shadow type="text"><field name="TEXT">' + '<value name="ARG_WITH_DEFAULT"><shadow type="text"><field name="TEXT">' +
@ -218,14 +218,14 @@ const testConditional = function (t, conditional) {
t.equal(conditional.json.type, 'test_ifElse'); t.equal(conditional.json.type, 'test_ifElse');
testCategoryInfo(t, conditional); testCategoryInfo(t, conditional);
t.equal(conditional.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE); t.equal(conditional.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE);
t.ok(conditional.json.hasOwnProperty('previousStatement')); t.ok(Object.prototype.hasOwnProperty.call(conditional.json, 'previousStatement'));
t.ok(conditional.json.hasOwnProperty('nextStatement')); t.ok(Object.prototype.hasOwnProperty.call(conditional.json, 'nextStatement'));
t.notOk(conditional.json.extensions && conditional.json.extensions.length); // OK if it's absent or empty t.notOk(conditional.json.extensions && conditional.json.extensions.length); // OK if it's absent or empty
t.equal(conditional.json.message0, 'test if %1 is spiffy and if so then'); t.equal(conditional.json.message0, 'test if %1 is spiffy and if so then');
t.equal(conditional.json.message1, '%1'); // placeholder for substack #1 t.equal(conditional.json.message1, '%1'); // placeholder for substack #1
t.equal(conditional.json.message2, 'or elsewise'); t.equal(conditional.json.message2, 'or elsewise');
t.equal(conditional.json.message3, '%1'); // placeholder for substack #2 t.equal(conditional.json.message3, '%1'); // placeholder for substack #2
t.notOk(conditional.json.hasOwnProperty('message4')); t.notOk(Object.prototype.hasOwnProperty.call(conditional.json, 'message4'));
t.strictSame(conditional.json.args0[0], { t.strictSame(conditional.json.args0[0], {
type: 'input_value', type: 'input_value',
name: 'THING', name: 'THING',
@ -235,12 +235,12 @@ const testConditional = function (t, conditional) {
type: 'input_statement', type: 'input_statement',
name: 'SUBSTACK' name: 'SUBSTACK'
}); });
t.notOk(conditional.json.hasOwnProperty(conditional.json.args2)); t.notOk(Object.prototype.hasOwnProperty.call(conditional.json, conditional.json.args2));
t.strictSame(conditional.json.args3[0], { t.strictSame(conditional.json.args3[0], {
type: 'input_statement', type: 'input_statement',
name: 'SUBSTACK2' name: 'SUBSTACK2'
}); });
t.notOk(conditional.json.hasOwnProperty('args4')); t.notOk(Object.prototype.hasOwnProperty.call(conditional.json, 'args4'));
t.equal(conditional.xml, '<block type="test_ifElse"><value name="THING"></value></block>'); t.equal(conditional.xml, '<block type="test_ifElse"><value name="THING"></value></block>');
}; };
@ -248,13 +248,13 @@ const testLoop = function (t, loop) {
t.equal(loop.json.type, 'test_loop'); t.equal(loop.json.type, 'test_loop');
testCategoryInfo(t, loop); testCategoryInfo(t, loop);
t.equal(loop.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE); t.equal(loop.json.outputShape, ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE);
t.ok(loop.json.hasOwnProperty('previousStatement')); t.ok(Object.prototype.hasOwnProperty.call(loop.json, 'previousStatement'));
t.notOk(loop.json.hasOwnProperty('nextStatement')); // isTerminal is set on this block t.notOk(Object.prototype.hasOwnProperty.call(loop.json, 'nextStatement')); // isTerminal is set on this block
t.notOk(loop.json.extensions && loop.json.extensions.length); // OK if it's absent or empty t.notOk(loop.json.extensions && loop.json.extensions.length); // OK if it's absent or empty
t.equal(loop.json.message0, 'loopty %1 loops'); t.equal(loop.json.message0, 'loopty %1 loops');
t.equal(loop.json.message1, '%1'); // placeholder for substack t.equal(loop.json.message1, '%1'); // placeholder for substack
t.equal(loop.json.message2, '%1'); // placeholder for loop arrow t.equal(loop.json.message2, '%1'); // placeholder for loop arrow
t.notOk(loop.json.hasOwnProperty('message3')); t.notOk(Object.prototype.hasOwnProperty.call(loop.json, 'message3'));
t.strictSame(loop.json.args0[0], { t.strictSame(loop.json.args0[0], {
type: 'input_value', type: 'input_value',
name: 'MANY' name: 'MANY'
@ -266,7 +266,7 @@ const testLoop = function (t, loop) {
t.equal(loop.json.lastDummyAlign2, 'RIGHT'); // move loop arrow to right side t.equal(loop.json.lastDummyAlign2, 'RIGHT'); // move loop arrow to right side
t.equal(loop.json.args2[0].type, 'field_image'); t.equal(loop.json.args2[0].type, 'field_image');
t.equal(loop.json.args2[0].flip_rtl, true); t.equal(loop.json.args2[0].flip_rtl, true);
t.notOk(loop.json.hasOwnProperty('args3')); t.notOk(Object.prototype.hasOwnProperty.call(loop.json, 'args3'));
t.equal(loop.xml, t.equal(loop.xml,
'<block type="test_loop"><value name="MANY"><shadow type="math_number"></shadow></value></block>'); '<block type="test_loop"><value name="MANY"><shadow type="math_number"></shadow></value></block>');
}; };

View file

@ -40,7 +40,7 @@ test('blocks get new id on duplicate', t => {
rt.blocks.createBlock(block); rt.blocks.createBlock(block);
return rt.duplicate().then(duplicate => { return rt.duplicate().then(duplicate => {
t.notOk(duplicate.blocks._blocks.hasOwnProperty(block.id)); t.notOk(Object.prototype.hasOwnProperty.call(duplicate.blocks._blocks, block.id));
t.end(); t.end();
}); });
}); });