Merge pull request #298 from rschamp/scratch-lint

Use shared lint config: eslint-config-scratch
This commit is contained in:
Ray Schamp 2016-10-24 15:43:31 -04:00 committed by GitHub
commit 595be5c193
42 changed files with 1424 additions and 1408 deletions

View file

@ -1,20 +0,0 @@
{
"rules": {
"curly": [2, "multi-line"],
"eol-last": [2],
"indent": [2, 4],
"quotes": [2, "single"],
"linebreak-style": [2, "unix"],
"max-len": [2, 80, 4],
"semi": [2, "always"],
"strict": [2, "never"],
"no-console": [2, {"allow": ["log", "warn", "error", "groupCollapsed", "groupEnd", "time", "timeEnd"]}],
"valid-jsdoc": ["error", {"requireReturn": false}]
},
"env": {
"node": true,
"browser": true,
"worker": true
},
"extends": "eslint:recommended"
}

3
.eslintrc.js Normal file
View file

@ -0,0 +1,3 @@
module.exports = {
extends: ['scratch', 'scratch/node']
};

View file

@ -25,8 +25,10 @@
"version": "./node_modules/.bin/json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"" "version": "./node_modules/.bin/json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\""
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "7.0.0",
"copy-webpack-plugin": "3.0.1", "copy-webpack-plugin": "3.0.1",
"eslint": "2.7.0", "eslint": "3.8.1",
"eslint-config-scratch": "^2.0.0",
"expose-loader": "0.7.1", "expose-loader": "0.7.1",
"gh-pages": "0.11.0", "gh-pages": "0.11.0",
"highlightjs": "8.7.0", "highlightjs": "8.7.0",
@ -34,6 +36,7 @@
"json": "9.0.4", "json": "9.0.4",
"json-loader": "0.5.4", "json-loader": "0.5.4",
"lodash.defaultsdeep": "4.6.0", "lodash.defaultsdeep": "4.6.0",
"minilog": "3.0.1",
"promise": "7.1.1", "promise": "7.1.1",
"scratch-blocks": "latest", "scratch-blocks": "latest",
"scratch-render": "latest", "scratch-render": "latest",

10
src/.eslintrc.js Normal file
View file

@ -0,0 +1,10 @@
module.exports = {
root: true,
extends: 'scratch',
env: {
browser: true
},
globals: {
Promise: true
}
};

View file

@ -1,45 +1,45 @@
var Cast = require('../util/cast'); var Cast = require('../util/cast');
var Timer = require('../util/timer'); var Timer = require('../util/timer');
function Scratch3ControlBlocks(runtime) { var Scratch3ControlBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3ControlBlocks.prototype.getPrimitives = function() { Scratch3ControlBlocks.prototype.getPrimitives = function () {
return { return {
'control_repeat': this.repeat, control_repeat: this.repeat,
'control_repeat_until': this.repeatUntil, control_repeat_until: this.repeatUntil,
'control_forever': this.forever, control_forever: this.forever,
'control_wait': this.wait, control_wait: this.wait,
'control_wait_until': this.waitUntil, control_wait_until: this.waitUntil,
'control_if': this.if, control_if: this.if,
'control_if_else': this.ifElse, control_if_else: this.ifElse,
'control_stop': this.stop, control_stop: this.stop,
'control_create_clone_of': this.createClone, control_create_clone_of: this.createClone,
'control_delete_this_clone': this.deleteClone control_delete_this_clone: this.deleteClone
}; };
}; };
Scratch3ControlBlocks.prototype.getHats = function () { Scratch3ControlBlocks.prototype.getHats = function () {
return { return {
'control_start_as_clone': { control_start_as_clone: {
restartExistingThreads: false restartExistingThreads: false
} }
}; };
}; };
Scratch3ControlBlocks.prototype.repeat = function(args, util) { Scratch3ControlBlocks.prototype.repeat = function (args, util) {
var times = Math.floor(Cast.toNumber(args.TIMES)); var times = Math.floor(Cast.toNumber(args.TIMES));
// Initialize loop // Initialize loop
if (util.stackFrame.loopCounter === undefined) { if (typeof util.stackFrame.loopCounter === 'undefined') {
util.stackFrame.loopCounter = times; util.stackFrame.loopCounter = times;
} }
// Only execute once per frame. // Only execute once per frame.
@ -53,7 +53,7 @@ Scratch3ControlBlocks.prototype.repeat = function(args, util) {
} }
}; };
Scratch3ControlBlocks.prototype.repeatUntil = function(args, util) { Scratch3ControlBlocks.prototype.repeatUntil = function (args, util) {
var condition = Cast.toBoolean(args.CONDITION); var condition = Cast.toBoolean(args.CONDITION);
// If the condition is true, start the branch. // If the condition is true, start the branch.
if (!condition) { if (!condition) {
@ -61,18 +61,18 @@ Scratch3ControlBlocks.prototype.repeatUntil = function(args, util) {
} }
}; };
Scratch3ControlBlocks.prototype.waitUntil = function(args, util) { Scratch3ControlBlocks.prototype.waitUntil = function (args, util) {
var condition = Cast.toBoolean(args.CONDITION); var condition = Cast.toBoolean(args.CONDITION);
if (!condition) { if (!condition) {
util.yield(); util.yield();
} }
}; };
Scratch3ControlBlocks.prototype.forever = function(args, util) { Scratch3ControlBlocks.prototype.forever = function (args, util) {
util.startBranch(1, true); util.startBranch(1, true);
}; };
Scratch3ControlBlocks.prototype.wait = function(args, util) { Scratch3ControlBlocks.prototype.wait = function (args, util) {
if (!util.stackFrame.timer) { if (!util.stackFrame.timer) {
util.stackFrame.timer = new Timer(); util.stackFrame.timer = new Timer();
util.stackFrame.timer.start(); util.stackFrame.timer.start();
@ -86,14 +86,14 @@ Scratch3ControlBlocks.prototype.wait = function(args, util) {
} }
}; };
Scratch3ControlBlocks.prototype.if = function(args, util) { Scratch3ControlBlocks.prototype.if = function (args, util) {
var condition = Cast.toBoolean(args.CONDITION); var condition = Cast.toBoolean(args.CONDITION);
if (condition) { if (condition) {
util.startBranch(1, false); util.startBranch(1, false);
} }
}; };
Scratch3ControlBlocks.prototype.ifElse = function(args, util) { Scratch3ControlBlocks.prototype.ifElse = function (args, util) {
var condition = Cast.toBoolean(args.CONDITION); var condition = Cast.toBoolean(args.CONDITION);
if (condition) { if (condition) {
util.startBranch(1, false); util.startBranch(1, false);
@ -102,21 +102,21 @@ Scratch3ControlBlocks.prototype.ifElse = function(args, util) {
} }
}; };
Scratch3ControlBlocks.prototype.stop = function(args, util) { Scratch3ControlBlocks.prototype.stop = function (args, util) {
var option = args.STOP_OPTION; var option = args.STOP_OPTION;
if (option == 'all') { if (option === 'all') {
util.stopAll(); util.stopAll();
} else if (option == 'other scripts in sprite' || } else if (option === 'other scripts in sprite' ||
option == 'other scripts in stage') { option === 'other scripts in stage') {
util.stopOtherTargetThreads(); util.stopOtherTargetThreads();
} else if (option == 'this script') { } else if (option === 'this script') {
util.stopThread(); util.stopThread();
} }
}; };
Scratch3ControlBlocks.prototype.createClone = function (args, util) { Scratch3ControlBlocks.prototype.createClone = function (args, util) {
var cloneTarget; var cloneTarget;
if (args.CLONE_OPTION == '_myself_') { if (args.CLONE_OPTION === '_myself_') {
cloneTarget = util.target; cloneTarget = util.target;
} else { } else {
cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION); cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION);

View file

@ -1,12 +1,12 @@
var Cast = require('../util/cast'); var Cast = require('../util/cast');
function Scratch3DataBlocks(runtime) { var Scratch3DataBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
@ -14,17 +14,17 @@ function Scratch3DataBlocks(runtime) {
*/ */
Scratch3DataBlocks.prototype.getPrimitives = function () { Scratch3DataBlocks.prototype.getPrimitives = function () {
return { return {
'data_variable': this.getVariable, data_variable: this.getVariable,
'data_setvariableto': this.setVariableTo, data_setvariableto: this.setVariableTo,
'data_changevariableby': this.changeVariableBy, data_changevariableby: this.changeVariableBy,
'data_listcontents': this.getListContents, data_listcontents: this.getListContents,
'data_addtolist': this.addToList, data_addtolist: this.addToList,
'data_deleteoflist': this.deleteOfList, data_deleteoflist: this.deleteOfList,
'data_insertatlist': this.insertAtList, data_insertatlist: this.insertAtList,
'data_replaceitemoflist': this.replaceItemOfList, data_replaceitemoflist: this.replaceItemOfList,
'data_itemoflist': this.getItemOfList, data_itemoflist: this.getItemOfList,
'data_lengthoflist': this.lengthOfList, data_lengthoflist: this.lengthOfList,
'data_listcontainsitem': this.listContainsItem data_listcontainsitem: this.listContainsItem
}; };
}; };
@ -54,7 +54,7 @@ Scratch3DataBlocks.prototype.getListContents = function (args, util) {
for (var i = 0; i < list.contents.length; i++) { for (var i = 0; i < list.contents.length; i++) {
var listItem = list.contents[i]; var listItem = list.contents[i];
if (!((typeof listItem === 'string') && if (!((typeof listItem === 'string') &&
(listItem.length == 1))) { (listItem.length === 1))) {
allSingleLetters = false; allSingleLetters = false;
break; break;
} }
@ -126,7 +126,7 @@ Scratch3DataBlocks.prototype.listContainsItem = function (args, util) {
// Try using Scratch comparison operator on each item. // Try using Scratch comparison operator on each item.
// (Scratch considers the string '123' equal to the number 123). // (Scratch considers the string '123' equal to the number 123).
for (var i = 0; i < list.contents.length; i++) { for (var i = 0; i < list.contents.length; i++) {
if (Cast.compare(list.contents[i], item) == 0) { if (Cast.compare(list.contents[i], item) === 0) {
return true; return true;
} }
} }

View file

@ -1,44 +1,44 @@
var Cast = require('../util/cast'); var Cast = require('../util/cast');
function Scratch3EventBlocks(runtime) { var Scratch3EventBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3EventBlocks.prototype.getPrimitives = function() { Scratch3EventBlocks.prototype.getPrimitives = function () {
return { return {
'event_broadcast': this.broadcast, event_broadcast: this.broadcast,
'event_broadcastandwait': this.broadcastAndWait, event_broadcastandwait: this.broadcastAndWait,
'event_whengreaterthan': this.hatGreaterThanPredicate event_whengreaterthan: this.hatGreaterThanPredicate
}; };
}; };
Scratch3EventBlocks.prototype.getHats = function () { Scratch3EventBlocks.prototype.getHats = function () {
return { return {
'event_whenflagclicked': { event_whenflagclicked: {
restartExistingThreads: true restartExistingThreads: true
}, },
'event_whenkeypressed': { event_whenkeypressed: {
restartExistingThreads: false restartExistingThreads: false
}, },
'event_whenthisspriteclicked': { event_whenthisspriteclicked: {
restartExistingThreads: true restartExistingThreads: true
}, },
'event_whenbackdropswitchesto': { event_whenbackdropswitchesto: {
restartExistingThreads: true restartExistingThreads: true
}, },
'event_whengreaterthan': { event_whengreaterthan: {
restartExistingThreads: false, restartExistingThreads: false,
edgeActivated: true edgeActivated: true
}, },
'event_whenbroadcastreceived': { event_whenbroadcastreceived: {
restartExistingThreads: true restartExistingThreads: true
} }
}; };
@ -48,16 +48,16 @@ Scratch3EventBlocks.prototype.hatGreaterThanPredicate = function (args, util) {
var option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase(); var option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase();
var value = Cast.toNumber(args.VALUE); var value = Cast.toNumber(args.VALUE);
// @todo: Other cases :) // @todo: Other cases :)
if (option == 'timer') { if (option === 'timer') {
return util.ioQuery('clock', 'projectTimer') > value; return util.ioQuery('clock', 'projectTimer') > value;
} }
return false; return false;
}; };
Scratch3EventBlocks.prototype.broadcast = function(args, util) { Scratch3EventBlocks.prototype.broadcast = function (args, util) {
var broadcastOption = Cast.toString(args.BROADCAST_OPTION); var broadcastOption = Cast.toString(args.BROADCAST_OPTION);
util.startHats('event_whenbroadcastreceived', { util.startHats('event_whenbroadcastreceived', {
'BROADCAST_OPTION': broadcastOption BROADCAST_OPTION: broadcastOption
}); });
}; };
@ -68,17 +68,17 @@ Scratch3EventBlocks.prototype.broadcastAndWait = function (args, util) {
// No - start hats for this broadcast. // No - start hats for this broadcast.
util.stackFrame.startedThreads = util.startHats( util.stackFrame.startedThreads = util.startHats(
'event_whenbroadcastreceived', { 'event_whenbroadcastreceived', {
'BROADCAST_OPTION': broadcastOption BROADCAST_OPTION: broadcastOption
} }
); );
if (util.stackFrame.startedThreads.length == 0) { if (util.stackFrame.startedThreads.length === 0) {
// Nothing was started. // Nothing was started.
return; return;
} }
} }
// We've run before; check if the wait is still going on. // We've run before; check if the wait is still going on.
var instance = this; var instance = this;
var waiting = util.stackFrame.startedThreads.some(function(thread) { var waiting = util.stackFrame.startedThreads.some(function (thread) {
return instance.runtime.isActiveThread(thread); return instance.runtime.isActiveThread(thread);
}); });
if (waiting) { if (waiting) {

View file

@ -1,41 +1,41 @@
var Cast = require('../util/cast'); var Cast = require('../util/cast');
function Scratch3LooksBlocks(runtime) { var Scratch3LooksBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3LooksBlocks.prototype.getPrimitives = function() { Scratch3LooksBlocks.prototype.getPrimitives = function () {
return { return {
'looks_say': this.say, looks_say: this.say,
'looks_sayforsecs': this.sayforsecs, looks_sayforsecs: this.sayforsecs,
'looks_think': this.think, looks_think: this.think,
'looks_thinkforsecs': this.sayforsecs, looks_thinkforsecs: this.sayforsecs,
'looks_show': this.show, looks_show: this.show,
'looks_hide': this.hide, looks_hide: this.hide,
'looks_switchcostumeto': this.switchCostume, looks_switchcostumeto: this.switchCostume,
'looks_switchbackdropto': this.switchBackdrop, looks_switchbackdropto: this.switchBackdrop,
'looks_switchbackdroptoandwait': this.switchBackdropAndWait, looks_switchbackdroptoandwait: this.switchBackdropAndWait,
'looks_nextcostume': this.nextCostume, looks_nextcostume: this.nextCostume,
'looks_nextbackdrop': this.nextBackdrop, looks_nextbackdrop: this.nextBackdrop,
'looks_changeeffectby': this.changeEffect, looks_changeeffectby: this.changeEffect,
'looks_seteffectto': this.setEffect, looks_seteffectto: this.setEffect,
'looks_cleargraphiceffects': this.clearEffects, looks_cleargraphiceffects: this.clearEffects,
'looks_changesizeby': this.changeSize, looks_changesizeby: this.changeSize,
'looks_setsizeto': this.setSize, looks_setsizeto: this.setSize,
'looks_gotofront': this.goToFront, looks_gotofront: this.goToFront,
'looks_gobacklayers': this.goBackLayers, looks_gobacklayers: this.goBackLayers,
'looks_size': this.getSize, looks_size: this.getSize,
'looks_costumeorder': this.getCostumeIndex, looks_costumeorder: this.getCostumeIndex,
'looks_backdroporder': this.getBackdropIndex, looks_backdroporder: this.getBackdropIndex,
'looks_backdropname': this.getBackdropName looks_backdropname: this.getBackdropName
}; };
}; };
@ -45,8 +45,8 @@ Scratch3LooksBlocks.prototype.say = function (args, util) {
Scratch3LooksBlocks.prototype.sayforsecs = function (args, util) { Scratch3LooksBlocks.prototype.sayforsecs = function (args, util) {
util.target.setSay('say', args.MESSAGE); util.target.setSay('say', args.MESSAGE);
return new Promise(function(resolve) { return new Promise(function (resolve) {
setTimeout(function() { setTimeout(function () {
// Clear say bubble and proceed. // Clear say bubble and proceed.
util.target.setSay(); util.target.setSay();
resolve(); resolve();
@ -60,8 +60,8 @@ Scratch3LooksBlocks.prototype.think = function (args, util) {
Scratch3LooksBlocks.prototype.thinkforsecs = function (args, util) { Scratch3LooksBlocks.prototype.thinkforsecs = function (args, util) {
util.target.setSay('think', args.MESSAGE); util.target.setSay('think', args.MESSAGE);
return new Promise(function(resolve) { return new Promise(function (resolve) {
setTimeout(function() { setTimeout(function () {
// Clear say bubble and proceed. // Clear say bubble and proceed.
util.target.setSay(); util.target.setSay();
resolve(); resolve();
@ -82,37 +82,37 @@ Scratch3LooksBlocks.prototype.hide = function (args, util) {
* Matches the behavior of Scratch 2.0 for different types of arguments. * Matches the behavior of Scratch 2.0 for different types of arguments.
* @param {!Target} target Target to set costume/backdrop to. * @param {!Target} target Target to set costume/backdrop to.
* @param {Any} requestedCostume Costume requested, e.g., 0, 'name', etc. * @param {Any} requestedCostume Costume requested, e.g., 0, 'name', etc.
* @param {boolean=} opt_zeroIndex Set to zero-index the requestedCostume. * @param {boolean=} optZeroIndex Set to zero-index the requestedCostume.
* @return {Array.<!Thread>} Any threads started by this switch. * @return {Array.<!Thread>} Any threads started by this switch.
*/ */
Scratch3LooksBlocks.prototype._setCostumeOrBackdrop = function (target, Scratch3LooksBlocks.prototype._setCostumeOrBackdrop = function (target,
requestedCostume, opt_zeroIndex) { requestedCostume, optZeroIndex) {
if (typeof requestedCostume === 'number') { if (typeof requestedCostume === 'number') {
target.setCostume(opt_zeroIndex ? target.setCostume(optZeroIndex ?
requestedCostume : requestedCostume - 1); requestedCostume : requestedCostume - 1);
} else { } else {
var costumeIndex = target.getCostumeIndexByName(requestedCostume); var costumeIndex = target.getCostumeIndexByName(requestedCostume);
if (costumeIndex > -1) { if (costumeIndex > -1) {
target.setCostume(costumeIndex); target.setCostume(costumeIndex);
} else if (costumeIndex == 'previous costume' || } else if (costumeIndex === 'previous costume' ||
costumeIndex == 'previous backdrop') { costumeIndex === 'previous backdrop') {
target.setCostume(target.currentCostume - 1); target.setCostume(target.currentCostume - 1);
} else if (costumeIndex == 'next costume' || } else if (costumeIndex === 'next costume' ||
costumeIndex == 'next backdrop') { costumeIndex === 'next backdrop') {
target.setCostume(target.currentCostume + 1); target.setCostume(target.currentCostume + 1);
} else { } else {
var forcedNumber = Cast.toNumber(requestedCostume); var forcedNumber = Cast.toNumber(requestedCostume);
if (!isNaN(forcedNumber)) { if (!isNaN(forcedNumber)) {
target.setCostume(opt_zeroIndex ? target.setCostume(optZeroIndex ?
forcedNumber : forcedNumber - 1); forcedNumber : forcedNumber - 1);
} }
} }
} }
if (target == this.runtime.getTargetForStage()) { if (target === this.runtime.getTargetForStage()) {
// Target is the stage - start hats. // Target is the stage - start hats.
var newName = target.sprite.costumes[target.currentCostume].name; var newName = target.sprite.costumes[target.currentCostume].name;
return this.runtime.startHats('event_whenbackdropswitchesto', { return this.runtime.startHats('event_whenbackdropswitchesto', {
'BACKDROP': newName BACKDROP: newName
}); });
} }
return []; return [];
@ -142,14 +142,14 @@ Scratch3LooksBlocks.prototype.switchBackdropAndWait = function (args, util) {
args.BACKDROP args.BACKDROP
) )
); );
if (util.stackFrame.startedThreads.length == 0) { if (util.stackFrame.startedThreads.length === 0) {
// Nothing was started. // Nothing was started.
return; return;
} }
} }
// We've run before; check if the wait is still going on. // We've run before; check if the wait is still going on.
var instance = this; var instance = this;
var waiting = util.stackFrame.startedThreads.some(function(thread) { var waiting = util.stackFrame.startedThreads.some(function (thread) {
return instance.runtime.isActiveThread(thread); return instance.runtime.isActiveThread(thread);
}); });
if (waiting) { if (waiting) {

View file

@ -2,37 +2,37 @@ var Cast = require('../util/cast');
var MathUtil = require('../util/math-util'); var MathUtil = require('../util/math-util');
var Timer = require('../util/timer'); var Timer = require('../util/timer');
function Scratch3MotionBlocks(runtime) { var Scratch3MotionBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3MotionBlocks.prototype.getPrimitives = function() { Scratch3MotionBlocks.prototype.getPrimitives = function () {
return { return {
'motion_movesteps': this.moveSteps, motion_movesteps: this.moveSteps,
'motion_gotoxy': this.goToXY, motion_gotoxy: this.goToXY,
'motion_goto': this.goTo, motion_goto: this.goTo,
'motion_turnright': this.turnRight, motion_turnright: this.turnRight,
'motion_turnleft': this.turnLeft, motion_turnleft: this.turnLeft,
'motion_pointindirection': this.pointInDirection, motion_pointindirection: this.pointInDirection,
'motion_pointtowards': this.pointTowards, motion_pointtowards: this.pointTowards,
'motion_glidesecstoxy': this.glide, motion_glidesecstoxy: this.glide,
'motion_ifonedgebounce': this.ifOnEdgeBounce, motion_ifonedgebounce: this.ifOnEdgeBounce,
'motion_setrotationstyle': this.setRotationStyle, motion_setrotationstyle: this.setRotationStyle,
'motion_changexby': this.changeX, motion_changexby: this.changeX,
'motion_setx': this.setX, motion_setx: this.setX,
'motion_changeyby': this.changeY, motion_changeyby: this.changeY,
'motion_sety': this.setY, motion_sety: this.setY,
'motion_xposition': this.getX, motion_xposition: this.getX,
'motion_yposition': this.getY, motion_yposition: this.getY,
'motion_direction': this.getDirection motion_direction: this.getDirection
}; };
}; };
@ -149,10 +149,10 @@ Scratch3MotionBlocks.prototype.ifOnEdgeBounce = function (args, util) {
// and clamped to zero when the sprite is beyond. // and clamped to zero when the sprite is beyond.
var stageWidth = this.runtime.constructor.STAGE_WIDTH; var stageWidth = this.runtime.constructor.STAGE_WIDTH;
var stageHeight = this.runtime.constructor.STAGE_HEIGHT; var stageHeight = this.runtime.constructor.STAGE_HEIGHT;
var distLeft = Math.max(0, stageWidth / 2 + bounds.left); var distLeft = Math.max(0, (stageWidth / 2) + bounds.left);
var distTop = Math.max(0, stageHeight / 2 - bounds.top); var distTop = Math.max(0, (stageHeight / 2) - bounds.top);
var distRight = Math.max(0, stageWidth / 2 - bounds.right); var distRight = Math.max(0, (stageWidth / 2) - bounds.right);
var distBottom = Math.max(0, stageHeight / 2 + bounds.bottom); var distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom);
// Find the nearest edge. // Find the nearest edge.
var nearestEdge = ''; var nearestEdge = '';
var minDist = Infinity; var minDist = Infinity;
@ -179,13 +179,13 @@ Scratch3MotionBlocks.prototype.ifOnEdgeBounce = function (args, util) {
var radians = MathUtil.degToRad(90 - util.target.direction); var radians = MathUtil.degToRad(90 - util.target.direction);
var dx = Math.cos(radians); var dx = Math.cos(radians);
var dy = -Math.sin(radians); var dy = -Math.sin(radians);
if (nearestEdge == 'left') { if (nearestEdge === 'left') {
dx = Math.max(0.2, Math.abs(dx)); dx = Math.max(0.2, Math.abs(dx));
} else if (nearestEdge == 'top') { } else if (nearestEdge === 'top') {
dy = Math.max(0.2, Math.abs(dy)); dy = Math.max(0.2, Math.abs(dy));
} else if (nearestEdge == 'right') { } else if (nearestEdge === 'right') {
dx = 0 - Math.max(0.2, Math.abs(dx)); dx = 0 - Math.max(0.2, Math.abs(dx));
} else if (nearestEdge == 'bottom') { } else if (nearestEdge === 'bottom') {
dy = 0 - Math.max(0.2, Math.abs(dy)); dy = 0 - Math.max(0.2, Math.abs(dy));
} }
var newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90; var newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90;

View file

@ -1,36 +1,36 @@
var Cast = require('../util/cast.js'); var Cast = require('../util/cast.js');
function Scratch3OperatorsBlocks(runtime) { var Scratch3OperatorsBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3OperatorsBlocks.prototype.getPrimitives = function() { Scratch3OperatorsBlocks.prototype.getPrimitives = function () {
return { return {
'operator_add': this.add, operator_add: this.add,
'operator_subtract': this.subtract, operator_subtract: this.subtract,
'operator_multiply': this.multiply, operator_multiply: this.multiply,
'operator_divide': this.divide, operator_divide: this.divide,
'operator_lt': this.lt, operator_lt: this.lt,
'operator_equals': this.equals, operator_equals: this.equals,
'operator_gt': this.gt, operator_gt: this.gt,
'operator_and': this.and, operator_and: this.and,
'operator_or': this.or, operator_or: this.or,
'operator_not': this.not, operator_not: this.not,
'operator_random': this.random, operator_random: this.random,
'operator_join': this.join, operator_join: this.join,
'operator_letter_of': this.letterOf, operator_letter_of: this.letterOf,
'operator_length': this.length, operator_length: this.length,
'operator_mod': this.mod, operator_mod: this.mod,
'operator_round': this.round, operator_round: this.round,
'operator_mathop': this.mathop operator_mathop: this.mathop
}; };
}; };
@ -55,7 +55,7 @@ Scratch3OperatorsBlocks.prototype.lt = function (args) {
}; };
Scratch3OperatorsBlocks.prototype.equals = function (args) { Scratch3OperatorsBlocks.prototype.equals = function (args) {
return Cast.compare(args.OPERAND1, args.OPERAND2) == 0; return Cast.compare(args.OPERAND1, args.OPERAND2) === 0;
}; };
Scratch3OperatorsBlocks.prototype.gt = function (args) { Scratch3OperatorsBlocks.prototype.gt = function (args) {
@ -79,10 +79,10 @@ Scratch3OperatorsBlocks.prototype.random = function (args) {
var nTo = Cast.toNumber(args.TO); var nTo = Cast.toNumber(args.TO);
var low = nFrom <= nTo ? nFrom : nTo; var low = nFrom <= nTo ? nFrom : nTo;
var high = nFrom <= nTo ? nTo : nFrom; var high = nFrom <= nTo ? nTo : nFrom;
if (low == high) return low; if (low === high) return low;
// If both arguments are ints, truncate the result to an int. // If both arguments are ints, truncate the result to an int.
if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) { if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) {
return low + parseInt(Math.random() * ((high + 1) - low)); return low + parseInt(Math.random() * ((high + 1) - low), 10);
} }
return (Math.random() * (high - low)) + low; return (Math.random() * (high - low)) + low;
}; };

View file

@ -1,20 +1,20 @@
function Scratch3ProcedureBlocks(runtime) { var Scratch3ProcedureBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3ProcedureBlocks.prototype.getPrimitives = function() { Scratch3ProcedureBlocks.prototype.getPrimitives = function () {
return { return {
'procedures_defnoreturn': this.defNoReturn, procedures_defnoreturn: this.defNoReturn,
'procedures_callnoreturn': this.callNoReturn, procedures_callnoreturn: this.callNoReturn,
'procedures_param': this.param procedures_param: this.param
}; };
}; };

View file

@ -1,41 +1,41 @@
var Cast = require('../util/cast'); var Cast = require('../util/cast');
function Scratch3SensingBlocks(runtime) { var Scratch3SensingBlocks = function (runtime) {
/** /**
* The runtime instantiating this block package. * The runtime instantiating this block package.
* @type {Runtime} * @type {Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Retrieve the block primitives implemented by this package. * Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function. * @return {Object.<string, Function>} Mapping of opcode to Function.
*/ */
Scratch3SensingBlocks.prototype.getPrimitives = function() { Scratch3SensingBlocks.prototype.getPrimitives = function () {
return { return {
'sensing_touchingobject': this.touchingObject, sensing_touchingobject: this.touchingObject,
'sensing_touchingcolor': this.touchingColor, sensing_touchingcolor: this.touchingColor,
'sensing_coloristouchingcolor': this.colorTouchingColor, sensing_coloristouchingcolor: this.colorTouchingColor,
'sensing_distanceto': this.distanceTo, sensing_distanceto: this.distanceTo,
'sensing_timer': this.getTimer, sensing_timer: this.getTimer,
'sensing_resettimer': this.resetTimer, sensing_resettimer: this.resetTimer,
'sensing_mousex': this.getMouseX, sensing_mousex: this.getMouseX,
'sensing_mousey': this.getMouseY, sensing_mousey: this.getMouseY,
'sensing_mousedown': this.getMouseDown, sensing_mousedown: this.getMouseDown,
'sensing_keypressed': this.getKeyPressed, sensing_keypressed: this.getKeyPressed,
'sensing_current': this.current, sensing_current: this.current,
'sensing_dayssince2000': this.daysSince2000 sensing_dayssince2000: this.daysSince2000
}; };
}; };
Scratch3SensingBlocks.prototype.touchingObject = function (args, util) { Scratch3SensingBlocks.prototype.touchingObject = function (args, util) {
var requestedObject = args.TOUCHINGOBJECTMENU; var requestedObject = args.TOUCHINGOBJECTMENU;
if (requestedObject == '_mouse_') { if (requestedObject === '_mouse_') {
var mouseX = util.ioQuery('mouse', 'getX'); var mouseX = util.ioQuery('mouse', 'getX');
var mouseY = util.ioQuery('mouse', 'getY'); var mouseY = util.ioQuery('mouse', 'getY');
return util.target.isTouchingPoint(mouseX, mouseY); return util.target.isTouchingPoint(mouseX, mouseY);
} else if (requestedObject == '_edge_') { } else if (requestedObject === '_edge_') {
return util.target.isTouchingEdge(); return util.target.isTouchingEdge();
} else { } else {
return util.target.isTouchingSprite(requestedObject); return util.target.isTouchingSprite(requestedObject);
@ -114,10 +114,9 @@ Scratch3SensingBlocks.prototype.getKeyPressed = function (args, util) {
return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION); return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION);
}; };
Scratch3SensingBlocks.prototype.daysSince2000 = function() Scratch3SensingBlocks.prototype.daysSince2000 = function () {
{
var msPerDay = 24 * 60 * 60 * 1000; var msPerDay = 24 * 60 * 60 * 1000;
var start = new Date(2000, 1-1, 1); var start = new Date(2000, 1 - 1, 1);
var today = new Date(); var today = new Date();
var dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset(); var dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset();
var mSecsSinceStart = today.valueOf() - start.valueOf(); var mSecsSinceStart = today.valueOf() - start.valueOf();

View file

@ -1,20 +1,6 @@
var mutationAdapter = require('./mutation-adapter'); var mutationAdapter = require('./mutation-adapter');
var html = require('htmlparser2'); var html = require('htmlparser2');
/**
* Adapter between block creation events and block representation which can be
* used by the Scratch runtime.
* @param {Object} e `Blockly.events.create`
* @return {Array.<Object>} List of blocks from this CREATE event.
*/
module.exports = function (e) {
// Validate input
if (typeof e !== 'object') return;
if (typeof e.xml !== 'object') return;
return domToBlocks(html.parseDOM(e.xml.outerHTML));
};
/** /**
* Convert outer blocks DOM from a Blockly CREATE event * Convert outer blocks DOM from a Blockly CREATE event
* to a usable form for the Scratch runtime. * to a usable form for the Scratch runtime.
@ -22,7 +8,7 @@ module.exports = function (e) {
* @param {Element} blocksDOM DOM tree for this event. * @param {Element} blocksDOM DOM tree for this event.
* @return {Array.<Object>} Usable list of blocks from this CREATE event. * @return {Array.<Object>} Usable list of blocks from this CREATE event.
*/ */
function domToBlocks (blocksDOM) { var domToBlocks = function (blocksDOM) {
// At this level, there could be multiple blocks adjacent in the DOM tree. // At this level, there could be multiple blocks adjacent in the DOM tree.
var blocks = {}; var blocks = {};
for (var i = 0; i < blocksDOM.length; i++) { for (var i = 0; i < blocksDOM.length; i++) {
@ -31,7 +17,7 @@ function domToBlocks (blocksDOM) {
continue; continue;
} }
var tagName = block.name.toLowerCase(); var tagName = block.name.toLowerCase();
if (tagName == 'block' || tagName == 'shadow') { if (tagName === 'block' || tagName === 'shadow') {
domToBlock(block, blocks, true, null); domToBlock(block, blocks, true, null);
} }
} }
@ -41,7 +27,21 @@ function domToBlocks (blocksDOM) {
blocksList.push(blocks[b]); blocksList.push(blocks[b]);
} }
return blocksList; return blocksList;
} };
/**
* Adapter between block creation events and block representation which can be
* used by the Scratch runtime.
* @param {Object} e `Blockly.events.create`
* @return {Array.<Object>} List of blocks from this CREATE event.
*/
var adapter = function (e) {
// Validate input
if (typeof e !== 'object') return;
if (typeof e.xml !== 'object') return;
return domToBlocks(html.parseDOM(e.xml.outerHTML));
};
/** /**
* Convert and an individual block DOM to the representation tree. * Convert and an individual block DOM to the representation tree.
@ -50,8 +50,9 @@ function domToBlocks (blocksDOM) {
* @param {Object} blocks Collection of blocks to add to. * @param {Object} blocks Collection of blocks to add to.
* @param {Boolean} isTopBlock Whether blocks at this level are "top blocks." * @param {Boolean} isTopBlock Whether blocks at this level are "top blocks."
* @param {?string} parent Parent block ID. * @param {?string} parent Parent block ID.
* @return {undefined}
*/ */
function domToBlock (blockDOM, blocks, isTopBlock, parent) { var domToBlock = function (blockDOM, blocks, isTopBlock, parent) {
// Block skeleton. // Block skeleton.
var block = { var block = {
id: blockDOM.attribs.id, // Block ID id: blockDOM.attribs.id, // Block ID
@ -61,7 +62,7 @@ function domToBlock (blockDOM, blocks, isTopBlock, parent) {
next: null, // Next block in the stack, if one exists. next: null, // Next block in the stack, if one exists.
topLevel: isTopBlock, // If this block starts a stack. topLevel: isTopBlock, // If this block starts a stack.
parent: parent, // Parent block ID, if available. parent: parent, // Parent block ID, if available.
shadow: blockDOM.name == 'shadow', // If this represents a shadow/slot. shadow: blockDOM.name === 'shadow', // If this represents a shadow/slot.
x: blockDOM.attribs.x, // X position of script, if top-level. x: blockDOM.attribs.x, // X position of script, if top-level.
y: blockDOM.attribs.y // Y position of script, if top-level. y: blockDOM.attribs.y // Y position of script, if top-level.
}; };
@ -82,9 +83,9 @@ function domToBlock (blockDOM, blocks, isTopBlock, parent) {
continue; continue;
} }
var grandChildNodeName = grandChildNode.name.toLowerCase(); var grandChildNodeName = grandChildNode.name.toLowerCase();
if (grandChildNodeName == 'block') { if (grandChildNodeName === 'block') {
childBlockNode = grandChildNode; childBlockNode = grandChildNode;
} else if (grandChildNodeName == 'shadow') { } else if (grandChildNodeName === 'shadow') {
childShadowNode = grandChildNode; childShadowNode = grandChildNode;
} }
} }
@ -117,7 +118,7 @@ function domToBlock (blockDOM, blocks, isTopBlock, parent) {
case 'statement': case 'statement':
// Recursively generate block structure for input block. // Recursively generate block structure for input block.
domToBlock(childBlockNode, blocks, false, block.id); domToBlock(childBlockNode, blocks, false, block.id);
if (childShadowNode && childBlockNode != childShadowNode) { if (childShadowNode && childBlockNode !== childShadowNode) {
// Also generate the shadow block. // Also generate the shadow block.
domToBlock(childShadowNode, blocks, false, block.id); domToBlock(childShadowNode, blocks, false, block.id);
} }
@ -144,4 +145,6 @@ function domToBlock (blockDOM, blocks, isTopBlock, parent) {
break; break;
} }
} }
} };
module.exports = adapter;

View file

@ -8,7 +8,7 @@ var xmlEscape = require('../util/xml-escape');
* and handle updates from Scratch Blocks events. * and handle updates from Scratch Blocks events.
*/ */
function Blocks () { var Blocks = function () {
/** /**
* All blocks in the workspace. * All blocks in the workspace.
* Keys are block IDs, values are metadata about the block. * Keys are block IDs, values are metadata about the block.
@ -22,7 +22,7 @@ function Blocks () {
* @type {Array.<String>} * @type {Array.<String>}
*/ */
this._scripts = []; this._scripts = [];
} };
/** /**
* Blockly inputs that represent statements/branch. * Blockly inputs that represent statements/branch.
@ -109,8 +109,8 @@ Blocks.prototype.getInputs = function (id) {
var inputs = {}; var inputs = {};
for (var input in this._blocks[id].inputs) { for (var input in this._blocks[id].inputs) {
// Ignore blocks prefixed with branch prefix. // Ignore blocks prefixed with branch prefix.
if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !==
!= Blocks.BRANCH_INPUT_PREFIX) { Blocks.BRANCH_INPUT_PREFIX) {
inputs[input] = this._blocks[id].inputs[input]; inputs[input] = this._blocks[id].inputs[input];
} }
} }
@ -149,9 +149,9 @@ Blocks.prototype.getTopLevelScript = function (id) {
Blocks.prototype.getProcedureDefinition = function (name) { Blocks.prototype.getProcedureDefinition = function (name) {
for (var id in this._blocks) { for (var id in this._blocks) {
var block = this._blocks[id]; var block = this._blocks[id];
if ((block.opcode == 'procedures_defnoreturn' || if ((block.opcode === 'procedures_defnoreturn' ||
block.opcode == 'procedures_defreturn') && block.opcode === 'procedures_defreturn') &&
block.mutation.proccode == name) { block.mutation.proccode === name) {
return id; return id;
} }
} }
@ -166,9 +166,9 @@ Blocks.prototype.getProcedureDefinition = function (name) {
Blocks.prototype.getProcedureParamNames = function (name) { Blocks.prototype.getProcedureParamNames = function (name) {
for (var id in this._blocks) { for (var id in this._blocks) {
var block = this._blocks[id]; var block = this._blocks[id];
if ((block.opcode == 'procedures_defnoreturn' || if ((block.opcode === 'procedures_defnoreturn' ||
block.opcode == 'procedures_defreturn') && block.opcode === 'procedures_defreturn') &&
block.mutation.proccode == name) { block.mutation.proccode === name) {
return JSON.parse(block.mutation.argumentnames); return JSON.parse(block.mutation.argumentnames);
} }
} }
@ -181,18 +181,18 @@ Blocks.prototype.getProcedureParamNames = function (name) {
* Create event listener for blocks. Handles validation and serves as a generic * Create event listener for blocks. Handles validation and serves as a generic
* adapter between the blocks and the runtime interface. * adapter between the blocks and the runtime interface.
* @param {Object} e Blockly "block" event * @param {Object} e Blockly "block" event
* @param {?Runtime} opt_runtime Optional runtime to forward click events to. * @param {?Runtime} optRuntime Optional runtime to forward click events to.
*/ */
Blocks.prototype.blocklyListen = function (e, opt_runtime) { Blocks.prototype.blocklyListen = function (e, optRuntime) {
// Validate event // Validate event
if (typeof e !== 'object') return; if (typeof e !== 'object') return;
if (typeof e.blockId !== 'string') return; if (typeof e.blockId !== 'string') return;
// UI event: clicked scripts toggle in the runtime. // UI event: clicked scripts toggle in the runtime.
if (e.element === 'stackclick') { if (e.element === 'stackclick') {
if (opt_runtime) { if (optRuntime) {
opt_runtime.toggleScript(e.blockId); optRuntime.toggleScript(e.blockId);
} }
return; return;
} }
@ -232,8 +232,8 @@ Blocks.prototype.blocklyListen = function (e, opt_runtime) {
return; return;
} }
// Inform any runtime to forget about glows on this script. // Inform any runtime to forget about glows on this script.
if (opt_runtime && this._blocks[e.blockId].topLevel) { if (optRuntime && this._blocks[e.blockId].topLevel) {
opt_runtime.quietGlow(e.blockId); optRuntime.quietGlow(e.blockId);
} }
this.deleteBlock({ this.deleteBlock({
id: e.blockId id: e.blockId
@ -273,11 +273,11 @@ Blocks.prototype.changeBlock = function (args) {
if (args.element !== 'field' && args.element !== 'mutation') return; if (args.element !== 'field' && args.element !== 'mutation') return;
if (typeof this._blocks[args.id] === 'undefined') return; if (typeof this._blocks[args.id] === 'undefined') return;
if (args.element == 'field') { if (args.element === 'field') {
// Update block value // Update block value
if (!this._blocks[args.id].fields[args.name]) return; if (!this._blocks[args.id].fields[args.name]) return;
this._blocks[args.id].fields[args.name].value = args.value; this._blocks[args.id].fields[args.name].value = args.value;
} else if (args.element == 'mutation') { } else if (args.element === 'mutation') {
this._blocks[args.id].mutation = mutationAdapter(args.value); this._blocks[args.id].mutation = mutationAdapter(args.value);
} }
}; };
@ -298,9 +298,9 @@ Blocks.prototype.moveBlock = function (e) {
} }
// Remove from any old parent. // Remove from any old parent.
if (e.oldParent !== undefined) { if (typeof e.oldParent !== 'undefined') {
var oldParent = this._blocks[e.oldParent]; var oldParent = this._blocks[e.oldParent];
if (e.oldInput !== undefined && if (typeof e.oldInput !== 'undefined' &&
oldParent.inputs[e.oldInput].block === e.id) { oldParent.inputs[e.oldInput].block === e.id) {
// This block was connected to the old parent's input. // This block was connected to the old parent's input.
oldParent.inputs[e.oldInput].block = null; oldParent.inputs[e.oldInput].block = null;
@ -312,13 +312,16 @@ Blocks.prototype.moveBlock = function (e) {
} }
// Has the block become a top-level block? // Has the block become a top-level block?
if (e.newParent === undefined) { if (typeof e.newParent === 'undefined') {
this._addScript(e.id); this._addScript(e.id);
} else { } else {
// Remove script, if one exists. // Remove script, if one exists.
this._deleteScript(e.id); this._deleteScript(e.id);
// Otherwise, try to connect it in its new place. // Otherwise, try to connect it in its new place.
if (e.newInput !== undefined) { if (typeof e.newInput === 'undefined') {
// Moved to the new parent's next connection.
this._blocks[e.newParent].next = e.id;
} else {
// 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.
var oldShadow = null; var oldShadow = null;
@ -330,9 +333,6 @@ Blocks.prototype.moveBlock = function (e) {
block: e.id, block: e.id,
shadow: oldShadow shadow: oldShadow
}; };
} else {
// Moved to the new parent's next connection.
this._blocks[e.newParent].next = e.id;
} }
this._blocks[e.id].parent = e.newParent; this._blocks[e.id].parent = e.newParent;
} }
@ -399,7 +399,7 @@ Blocks.prototype.blockToXML = function (blockId) {
// Encode properties of this block. // Encode properties of this block.
var tagName = (block.shadow) ? 'shadow' : 'block'; var tagName = (block.shadow) ? 'shadow' : 'block';
var xy = (block.topLevel) ? var xy = (block.topLevel) ?
' x="' + block.x +'"' + ' y="' + block.y +'"' : ' x="' + block.x + '" y="' + block.y + '"' :
''; '';
var xmlString = ''; var xmlString = '';
xmlString += '<' + tagName + xmlString += '<' + tagName +
@ -420,7 +420,7 @@ Blocks.prototype.blockToXML = function (blockId) {
if (blockInput.block) { if (blockInput.block) {
xmlString += this.blockToXML(blockInput.block); xmlString += this.blockToXML(blockInput.block);
} }
if (blockInput.shadow && blockInput.shadow != blockInput.block) { if (blockInput.shadow && blockInput.shadow !== blockInput.block) {
// Obscured shadow. // Obscured shadow.
xmlString += this.blockToXML(blockInput.shadow); xmlString += this.blockToXML(blockInput.shadow);
} }
@ -453,7 +453,7 @@ Blocks.prototype.blockToXML = function (blockId) {
Blocks.prototype.mutationToXML = function (mutation) { Blocks.prototype.mutationToXML = function (mutation) {
var mutationString = '<' + mutation.tagName; var mutationString = '<' + mutation.tagName;
for (var prop in mutation) { for (var prop in mutation) {
if (prop == 'children' || prop == 'tagName') continue; if (prop === 'children' || prop === 'tagName') continue;
var mutationValue = (typeof mutation[prop] === 'string') ? var mutationValue = (typeof mutation[prop] === 'string') ?
xmlEscape(mutation[prop]) : mutation[prop]; xmlEscape(mutation[prop]) : mutation[prop];
mutationString += ' ' + prop + '="' + mutationValue + '"'; mutationString += ' ' + prop + '="' + mutationValue + '"';

View file

@ -1,3 +1,4 @@
var log = require('../util/log');
var Thread = require('./thread'); var Thread = require('./thread');
/** /**
@ -52,7 +53,7 @@ var execute = function (sequencer, thread) {
if (!opcode) { if (!opcode) {
console.warn('Could not get opcode for block: ' + currentBlockId); log.warn('Could not get opcode for block: ' + currentBlockId);
return; return;
} }
@ -105,14 +106,14 @@ var execute = function (sequencer, thread) {
// Skip through the block (hat with no predicate). // Skip through the block (hat with no predicate).
return; return;
} else { } else {
if (Object.keys(fields).length == 1 && if (Object.keys(fields).length === 1 &&
Object.keys(inputs).length == 0) { Object.keys(inputs).length === 0) {
// One field and no inputs - treat as arg. // One field and no inputs - treat as arg.
for (var fieldKey in fields) { // One iteration. for (var fieldKey in fields) { // One iteration.
handleReport(fields[fieldKey].value); handleReport(fields[fieldKey].value);
} }
} else { } else {
console.warn('Could not get implementation for opcode: ' + log.warn('Could not get implementation for opcode: ' +
opcode); opcode);
} }
thread.requestScriptGlowInFrame = true; thread.requestScriptGlowInFrame = true;
@ -133,8 +134,8 @@ var execute = function (sequencer, thread) {
var input = inputs[inputName]; var input = inputs[inputName];
var inputBlockId = input.block; var inputBlockId = input.block;
// Is there no value for this input waiting in the stack frame? // Is there no value for this input waiting in the stack frame?
if (typeof currentStackFrame.reported[inputName] === 'undefined' if (typeof currentStackFrame.reported[inputName] === 'undefined' &&
&& inputBlockId) { inputBlockId) {
// If there's not, we need to evaluate the block. // If there's not, we need to evaluate the block.
// Push to the stack to evaluate the reporter block. // Push to the stack to evaluate the reporter block.
thread.pushStack(inputBlockId); thread.pushStack(inputBlockId);
@ -170,7 +171,7 @@ var execute = function (sequencer, thread) {
primitiveReportedValue = blockFunction(argValues, { primitiveReportedValue = blockFunction(argValues, {
stackFrame: currentStackFrame.executionContext, stackFrame: currentStackFrame.executionContext,
target: target, target: target,
yield: function() { yield: function () {
thread.status = Thread.STATUS_YIELD; thread.status = Thread.STATUS_YIELD;
}, },
startBranch: function (branchNum, isLoop) { startBranch: function (branchNum, isLoop) {
@ -179,10 +180,10 @@ var execute = function (sequencer, thread) {
stopAll: function () { stopAll: function () {
runtime.stopAll(); runtime.stopAll();
}, },
stopOtherTargetThreads: function() { stopOtherTargetThreads: function () {
runtime.stopForTarget(target, thread); runtime.stopForTarget(target, thread);
}, },
stopThread: function() { stopThread: function () {
sequencer.retireThread(thread); sequencer.retireThread(thread);
}, },
startProcedure: function (procedureCode) { startProcedure: function (procedureCode) {
@ -197,15 +198,20 @@ var execute = function (sequencer, thread) {
getParam: function (paramName) { getParam: function (paramName) {
return thread.getParam(paramName); return thread.getParam(paramName);
}, },
startHats: function(requestedHat, opt_matchFields, opt_target) { startHats: function (requestedHat, optMatchFields, optTarget) {
return ( return (
runtime.startHats(requestedHat, opt_matchFields, opt_target) runtime.startHats(requestedHat, optMatchFields, optTarget)
); );
}, },
ioQuery: function (device, func, args) { ioQuery: function (device, func, args) {
// Find the I/O device and execute the query/function call. // Find the I/O device and execute the query/function call.
if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) { if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) {
var devObject = runtime.ioDevices[device]; var devObject = runtime.ioDevices[device];
// @todo Figure out why eslint complains about no-useless-call
// no-useless-call can't tell if the call is useless for dynamic
// expressions... or something. Not exactly sure why it
// complains here.
// eslint-disable-next-line no-useless-call
return devObject[func].call(devObject, args); return devObject[func].call(devObject, args);
} }
} }
@ -224,19 +230,19 @@ var execute = function (sequencer, thread) {
thread.status = Thread.STATUS_PROMISE_WAIT; thread.status = Thread.STATUS_PROMISE_WAIT;
} }
// Promise handlers // Promise handlers
primitiveReportedValue.then(function(resolvedValue) { primitiveReportedValue.then(function (resolvedValue) {
handleReport(resolvedValue); handleReport(resolvedValue);
if (typeof resolvedValue !== 'undefined') { if (typeof resolvedValue === 'undefined') {
thread.popStack();
} else {
var popped = thread.popStack(); var popped = thread.popStack();
var nextBlockId = thread.target.blocks.getNextBlock(popped); var nextBlockId = thread.target.blocks.getNextBlock(popped);
thread.pushStack(nextBlockId); thread.pushStack(nextBlockId);
} else {
thread.popStack();
} }
}, function(rejectionReason) { }, function (rejectionReason) {
// Promise rejected: the primitive had some error. // Promise rejected: the primitive had some error.
// Log it and proceed. // Log it and proceed.
console.warn('Primitive rejected promise: ', rejectionReason); log.warn('Primitive rejected promise: ', rejectionReason);
thread.status = Thread.STATUS_RUNNING; thread.status = Thread.STATUS_RUNNING;
thread.popStack(); thread.popStack();
}); });

View file

@ -8,9 +8,9 @@
* @param {Array} contents Contents of the list, as an array. * @param {Array} contents Contents of the list, as an array.
* @constructor * @constructor
*/ */
function List (name, contents) { var List = function (name, contents) {
this.name = name; this.name = name;
this.contents = contents; this.contents = contents;
} };
module.exports = List; module.exports = List;

View file

@ -1,12 +1,33 @@
var html = require('htmlparser2'); var html = require('htmlparser2');
/**
* Convert a part of a mutation DOM to a mutation VM object, recursively.
* @param {Object} dom DOM object for mutation tag.
* @return {Object} Object representing useful parts of this mutation.
*/
var mutatorTagToObject = function (dom) {
var obj = Object.create(null);
obj.tagName = dom.name;
obj.children = [];
for (var prop in dom.attribs) {
if (prop === 'xmlns') continue;
obj[prop] = dom.attribs[prop];
}
for (var i = 0; i < dom.children.length; i++) {
obj.children.push(
mutatorTagToObject(dom.children[i])
);
}
return obj;
};
/** /**
* Adapter between mutator XML or DOM and block representation which can be * Adapter between mutator XML or DOM and block representation which can be
* used by the Scratch runtime. * used by the Scratch runtime.
* @param {(Object|string)} mutation Mutation XML string or DOM. * @param {(Object|string)} mutation Mutation XML string or DOM.
* @return {Object} Object representing the mutation. * @return {Object} Object representing the mutation.
*/ */
module.exports = function (mutation) { var mutationAdpater = function (mutation) {
var mutationParsed; var mutationParsed;
// Check if the mutation is already parsed; if not, parse it. // Check if the mutation is already parsed; if not, parse it.
if (typeof mutation === 'object') { if (typeof mutation === 'object') {
@ -17,23 +38,4 @@ module.exports = function (mutation) {
return mutatorTagToObject(mutationParsed); return mutatorTagToObject(mutationParsed);
}; };
/** module.exports = mutationAdpater;
* Convert a part of a mutation DOM to a mutation VM object, recursively.
* @param {Object} dom DOM object for mutation tag.
* @return {Object} Object representing useful parts of this mutation.
*/
function mutatorTagToObject (dom) {
var obj = Object.create(null);
obj.tagName = dom.name;
obj.children = [];
for (var prop in dom.attribs) {
if (prop == 'xmlns') continue;
obj[prop] = dom.attribs[prop];
}
for (var i = 0; i < dom.children.length; i++) {
obj.children.push(
mutatorTagToObject(dom.children[i])
);
}
return obj;
}

View file

@ -10,20 +10,20 @@ var Keyboard = require('../io/keyboard');
var Mouse = require('../io/mouse'); var Mouse = require('../io/mouse');
var defaultBlockPackages = { var defaultBlockPackages = {
'scratch3_control': require('../blocks/scratch3_control'), scratch3_control: require('../blocks/scratch3_control'),
'scratch3_event': require('../blocks/scratch3_event'), scratch3_event: require('../blocks/scratch3_event'),
'scratch3_looks': require('../blocks/scratch3_looks'), scratch3_looks: require('../blocks/scratch3_looks'),
'scratch3_motion': require('../blocks/scratch3_motion'), scratch3_motion: require('../blocks/scratch3_motion'),
'scratch3_operators': require('../blocks/scratch3_operators'), scratch3_operators: require('../blocks/scratch3_operators'),
'scratch3_sensing': require('../blocks/scratch3_sensing'), scratch3_sensing: require('../blocks/scratch3_sensing'),
'scratch3_data': require('../blocks/scratch3_data'), scratch3_data: require('../blocks/scratch3_data'),
'scratch3_procedures': require('../blocks/scratch3_procedures') scratch3_procedures: require('../blocks/scratch3_procedures')
}; };
/** /**
* Manages targets, scripts, and the sequencer. * Manages targets, scripts, and the sequencer.
*/ */
function Runtime () { var Runtime = function () {
// Bind event emitter // Bind event emitter
EventEmitter.call(this); EventEmitter.call(this);
@ -139,11 +139,11 @@ function Runtime () {
// I/O related data. // I/O related data.
/** @type {Object.<string, Object>} */ /** @type {Object.<string, Object>} */
this.ioDevices = { this.ioDevices = {
'clock': new Clock(), clock: new Clock(),
'keyboard': new Keyboard(this), keyboard: new Keyboard(this),
'mouse': new Mouse(this) mouse: new Mouse(this)
}; };
} };
/** /**
* Inherit from EventEmitter * Inherit from EventEmitter
@ -346,7 +346,7 @@ Runtime.prototype.isActiveThread = function (thread) {
Runtime.prototype.toggleScript = function (topBlockId) { Runtime.prototype.toggleScript = function (topBlockId) {
// Remove any existing thread. // Remove any existing thread.
for (var i = 0; i < this.threads.length; i++) { for (var i = 0; i < this.threads.length; i++) {
if (this.threads[i].topBlock == topBlockId) { if (this.threads[i].topBlock === topBlockId) {
this._removeThread(this.threads[i]); this._removeThread(this.threads[i]);
return; return;
} }
@ -361,12 +361,12 @@ Runtime.prototype.toggleScript = function (topBlockId) {
* - the top block ID of the script. * - the top block ID of the script.
* - the target that owns the script. * - the target that owns the script.
* @param {!Function} f Function to call for each script. * @param {!Function} f Function to call for each script.
* @param {Target=} opt_target Optionally, a target to restrict to. * @param {Target=} optTarget Optionally, a target to restrict to.
*/ */
Runtime.prototype.allScriptsDo = function (f, opt_target) { Runtime.prototype.allScriptsDo = function (f, optTarget) {
var targets = this.targets; var targets = this.targets;
if (opt_target) { if (optTarget) {
targets = [opt_target]; targets = [optTarget];
} }
for (var t = 0; t < targets.length; t++) { for (var t = 0; t < targets.length; t++) {
var target = targets[t]; var target = targets[t];
@ -381,12 +381,12 @@ Runtime.prototype.allScriptsDo = function (f, opt_target) {
/** /**
* Start all relevant hats. * Start all relevant hats.
* @param {!string} requestedHatOpcode Opcode of hats to start. * @param {!string} requestedHatOpcode Opcode of hats to start.
* @param {Object=} opt_matchFields Optionally, fields to match on the hat. * @param {Object=} optMatchFields Optionally, fields to match on the hat.
* @param {Target=} opt_target Optionally, a target to restrict to. * @param {Target=} optTarget Optionally, a target to restrict to.
* @return {Array.<Thread>} List of threads started by this function. * @return {Array.<Thread>} List of threads started by this function.
*/ */
Runtime.prototype.startHats = function (requestedHatOpcode, Runtime.prototype.startHats = function (requestedHatOpcode,
opt_matchFields, opt_target) { optMatchFields, optTarget) {
if (!this._hats.hasOwnProperty(requestedHatOpcode)) { if (!this._hats.hasOwnProperty(requestedHatOpcode)) {
// No known hat with this opcode. // No known hat with this opcode.
return; return;
@ -394,7 +394,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
var instance = this; var instance = this;
var newThreads = []; var newThreads = [];
// Consider all scripts, looking for hats with opcode `requestedHatOpcode`. // Consider all scripts, looking for hats with opcode `requestedHatOpcode`.
this.allScriptsDo(function(topBlockId, target) { this.allScriptsDo(function (topBlockId, target) {
var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode; var potentialHatOpcode = target.blocks.getBlock(topBlockId).opcode;
if (potentialHatOpcode !== requestedHatOpcode) { if (potentialHatOpcode !== requestedHatOpcode) {
// Not the right hat. // Not the right hat.
@ -406,10 +406,10 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
// (i.e., before the predicate can be run) because "broadcast and wait" // (i.e., before the predicate can be run) because "broadcast and wait"
// needs to have a precise collection of started threads. // needs to have a precise collection of started threads.
var hatFields = target.blocks.getFields(topBlockId); var hatFields = target.blocks.getFields(topBlockId);
if (opt_matchFields) { if (optMatchFields) {
for (var matchField in opt_matchFields) { for (var matchField in optMatchFields) {
if (hatFields[matchField].value !== if (hatFields[matchField].value !==
opt_matchFields[matchField]) { optMatchFields[matchField]) {
// Field mismatch. // Field mismatch.
return; return;
} }
@ -422,7 +422,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
// any existing threads starting with the top block. // any existing threads starting with the top block.
for (var i = 0; i < instance.threads.length; i++) { for (var i = 0; i < instance.threads.length; i++) {
if (instance.threads[i].topBlock === topBlockId && if (instance.threads[i].topBlock === topBlockId &&
instance.threads[i].target == target) { instance.threads[i].target === target) {
instance._removeThread(instance.threads[i]); instance._removeThread(instance.threads[i]);
} }
} }
@ -431,7 +431,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
// give up if any threads with the top block are running. // give up if any threads with the top block are running.
for (var j = 0; j < instance.threads.length; j++) { for (var j = 0; j < instance.threads.length; j++) {
if (instance.threads[j].topBlock === topBlockId && if (instance.threads[j].topBlock === topBlockId &&
instance.threads[j].target == target) { instance.threads[j].target === target) {
// Some thread is already running. // Some thread is already running.
return; return;
} }
@ -439,7 +439,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
} }
// Start the thread with this top block. // Start the thread with this top block.
newThreads.push(instance._pushThread(topBlockId, target)); newThreads.push(instance._pushThread(topBlockId, target));
}, opt_target); }, optTarget);
return newThreads; return newThreads;
}; };
@ -468,15 +468,15 @@ Runtime.prototype.disposeTarget = function (disposingTarget) {
/** /**
* Stop any threads acting on the target. * Stop any threads acting on the target.
* @param {!Target} target Target to stop threads for. * @param {!Target} target Target to stop threads for.
* @param {Thread=} opt_threadException Optional thread to skip. * @param {Thread=} optThreadException Optional thread to skip.
*/ */
Runtime.prototype.stopForTarget = function (target, opt_threadException) { Runtime.prototype.stopForTarget = function (target, optThreadException) {
// Stop any threads on the target. // Stop any threads on the target.
for (var i = 0; i < this.threads.length; i++) { for (var i = 0; i < this.threads.length; i++) {
if (this.threads[i] === opt_threadException) { if (this.threads[i] === optThreadException) {
continue; continue;
} }
if (this.threads[i].target == target) { if (this.threads[i].target === target) {
this._removeThread(this.threads[i]); this._removeThread(this.threads[i]);
} }
} }
@ -562,13 +562,13 @@ Runtime.prototype.setCompatibilityMode = function (compatibilityModeOn) {
/** /**
* Emit glows/glow clears for scripts after a single tick. * Emit glows/glow clears for scripts after a single tick.
* Looks at `this.threads` and notices which have turned on/off new glows. * Looks at `this.threads` and notices which have turned on/off new glows.
* @param {Array.<Thread>=} opt_extraThreads Optional list of inactive threads. * @param {Array.<Thread>=} optExtraThreads Optional list of inactive threads.
*/ */
Runtime.prototype._updateGlows = function (opt_extraThreads) { Runtime.prototype._updateGlows = function (optExtraThreads) {
var searchThreads = []; var searchThreads = [];
searchThreads.push.apply(searchThreads, this.threads); searchThreads.push.apply(searchThreads, this.threads);
if (opt_extraThreads) { if (optExtraThreads) {
searchThreads.push.apply(searchThreads, opt_extraThreads); searchThreads.push.apply(searchThreads, optExtraThreads);
} }
// Set of scripts that request a glow this frame. // Set of scripts that request a glow this frame.
var requestedGlowsThisFrame = []; var requestedGlowsThisFrame = [];
@ -578,7 +578,7 @@ Runtime.prototype._updateGlows = function (opt_extraThreads) {
for (var i = 0; i < searchThreads.length; i++) { for (var i = 0; i < searchThreads.length; i++) {
var thread = searchThreads[i]; var thread = searchThreads[i];
var target = thread.target; var target = thread.target;
if (target == this._editingTarget) { if (target === this._editingTarget) {
var blockForThread = thread.blockGlowInFrame; var blockForThread = thread.blockGlowInFrame;
if (thread.requestScriptGlowInFrame) { if (thread.requestScriptGlowInFrame) {
var script = target.blocks.getTopLevelScript(blockForThread); var script = target.blocks.getTopLevelScript(blockForThread);
@ -672,7 +672,7 @@ Runtime.prototype.visualReport = function (blockId, value) {
Runtime.prototype.getTargetById = function (targetId) { Runtime.prototype.getTargetById = function (targetId) {
for (var i = 0; i < this.targets.length; i++) { for (var i = 0; i < this.targets.length; i++) {
var target = this.targets[i]; var target = this.targets[i];
if (target.id == targetId) { if (target.id === targetId) {
return target; return target;
} }
} }
@ -686,7 +686,7 @@ Runtime.prototype.getTargetById = function (targetId) {
Runtime.prototype.getSpriteTargetByName = function (spriteName) { Runtime.prototype.getSpriteTargetByName = function (spriteName) {
for (var i = 0; i < this.targets.length; i++) { for (var i = 0; i < this.targets.length; i++) {
var target = this.targets[i]; var target = this.targets[i];
if (target.sprite && target.sprite.name == spriteName) { if (target.sprite && target.sprite.name === spriteName) {
return target; return target;
} }
} }
@ -750,7 +750,7 @@ Runtime.prototype.start = function () {
interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY; interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY;
} }
this.currentStepTime = interval; this.currentStepTime = interval;
this._steppingInterval = self.setInterval(function() { this._steppingInterval = self.setInterval(function () {
this._step(); this._step();
}.bind(this), interval); }.bind(this), interval);
}; };

View file

@ -2,7 +2,7 @@ var Timer = require('../util/timer');
var Thread = require('./thread'); var Thread = require('./thread');
var execute = require('./execute.js'); var execute = require('./execute.js');
function Sequencer (runtime) { var Sequencer = function (runtime) {
/** /**
* A utility timer for timing thread sequencing. * A utility timer for timing thread sequencing.
* @type {!Timer} * @type {!Timer}
@ -14,7 +14,7 @@ function Sequencer (runtime) {
* @type {!Runtime} * @type {!Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Time to run a warp-mode thread, in ms. * Time to run a warp-mode thread, in ms.
@ -78,7 +78,7 @@ Sequencer.prototype.stepThreads = function () {
ranFirstTick = true; ranFirstTick = true;
} }
// Filter inactive threads from `this.runtime.threads`. // Filter inactive threads from `this.runtime.threads`.
this.runtime.threads = this.runtime.threads.filter(function(thread) { this.runtime.threads = this.runtime.threads.filter(function (thread) {
if (inactiveThreads.indexOf(thread) > -1) { if (inactiveThreads.indexOf(thread) > -1) {
return false; return false;
} }

View file

@ -13,7 +13,7 @@ var uid = require('../util/uid');
* @param {?Blocks} blocks Blocks instance for the blocks owned by this target. * @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
* @constructor * @constructor
*/ */
function Target (blocks) { var Target = function (blocks) {
if (!blocks) { if (!blocks) {
blocks = new Blocks(this); blocks = new Blocks(this);
} }
@ -39,7 +39,7 @@ function Target (blocks) {
* @type {Object.<string,*>} * @type {Object.<string,*>}
*/ */
this.lists = {}; this.lists = {};
} };
/** /**
* Called when the project receives a "green flag." * Called when the project receives a "green flag."
@ -109,8 +109,6 @@ Target.prototype.lookupOrCreateList = function (name) {
* Call to destroy a target. * Call to destroy a target.
* @abstract * @abstract
*/ */
Target.prototype.dispose = function () { Target.prototype.dispose = function () {};
};
module.exports = Target; module.exports = Target;

View file

@ -3,7 +3,7 @@
* @param {?string} firstBlock First block to execute in the thread. * @param {?string} firstBlock First block to execute in the thread.
* @constructor * @constructor
*/ */
function Thread (firstBlock) { var Thread = function (firstBlock) {
/** /**
* ID of top block of the thread * ID of top block of the thread
* @type {!string} * @type {!string}
@ -53,7 +53,7 @@ function Thread (firstBlock) {
* @type {?Timer} * @type {?Timer}
*/ */
this.warpTimer = null; this.warpTimer = null;
} };
/** /**
* Thread status for initialized or running thread. * Thread status for initialized or running thread.
@ -225,8 +225,8 @@ Thread.prototype.isRecursiveCall = function (procedureCode) {
var sp = this.stack.length - 1; var sp = this.stack.length - 1;
for (var i = sp - 1; i >= 0; i--) { for (var i = sp - 1; i >= 0; i--) {
var block = this.target.blocks.getBlock(this.stack[i]); var block = this.target.blocks.getBlock(this.stack[i]);
if (block.opcode == 'procedures_callnoreturn' && if (block.opcode === 'procedures_callnoreturn' &&
block.mutation.proccode == procedureCode) { block.mutation.proccode === procedureCode) {
return true; return true;
} }
if (--callCount < 0) return false; if (--callCount < 0) return false;

View file

@ -9,10 +9,10 @@
* @param {boolean} isCloud Whether the variable is stored in the cloud. * @param {boolean} isCloud Whether the variable is stored in the cloud.
* @constructor * @constructor
*/ */
function Variable (name, value, isCloud) { var Variable = function (name, value, isCloud) {
this.name = name; this.name = name;
this.value = value; this.value = value;
this.isCloud = isCloud; this.isCloud = isCloud;
} };
module.exports = Variable; module.exports = Variable;

View file

@ -9,27 +9,12 @@ var Blocks = require('../engine/blocks');
var Clone = require('../sprites/clone'); var Clone = require('../sprites/clone');
var Sprite = require('../sprites/sprite'); var Sprite = require('../sprites/sprite');
var Color = require('../util/color.js'); var Color = require('../util/color.js');
var log = require('../util/log');
var uid = require('../util/uid'); var uid = require('../util/uid');
var specMap = require('./sb2specmap'); var specMap = require('./sb2specmap');
var Variable = require('../engine/variable'); var Variable = require('../engine/variable');
var List = require('../engine/list'); var List = require('../engine/list');
/**
* Top-level handler. Parse provided JSON,
* and process the top-level object (the stage object).
* @param {!string} json SB2-format JSON to load.
* @param {!Runtime} runtime Runtime object to load all structures into.
* @param {Boolean=} opt_forceSprite If set, treat as sprite (Sprite2).
* @return {?Target} Top-level target created (stage or sprite).
*/
function sb2import (json, runtime, opt_forceSprite) {
return parseScratchObject(
JSON.parse(json),
runtime,
!opt_forceSprite
);
}
/** /**
* Parse a single "Scratch object" and create all its in-memory VM objects. * Parse a single "Scratch object" and create all its in-memory VM objects.
* @param {!Object} object From-JSON "Scratch object:" sprite, stage, watcher. * @param {!Object} object From-JSON "Scratch object:" sprite, stage, watcher.
@ -37,7 +22,7 @@ function sb2import (json, runtime, opt_forceSprite) {
* @param {boolean} topLevel Whether this is the top-level object (stage). * @param {boolean} topLevel Whether this is the top-level object (stage).
* @return {?Target} Target created (stage or sprite). * @return {?Target} Target created (stage or sprite).
*/ */
function parseScratchObject (object, runtime, topLevel) { var parseScratchObject = function (object, runtime, topLevel) {
if (!object.hasOwnProperty('objName')) { if (!object.hasOwnProperty('objName')) {
// Watcher/monitor - skip this object until those are implemented in VM. // Watcher/monitor - skip this object until those are implemented in VM.
// @todo // @todo
@ -57,8 +42,8 @@ function parseScratchObject (object, runtime, topLevel) {
var costume = object.costumes[i]; var costume = object.costumes[i];
// @todo: Make sure all the relevant metadata is being pulled out. // @todo: Make sure all the relevant metadata is being pulled out.
sprite.costumes.push({ sprite.costumes.push({
skin: 'https://cdn.assets.scratch.mit.edu/internalapi/asset/' skin: 'https://cdn.assets.scratch.mit.edu/internalapi/asset/' +
+ costume.baseLayerMD5 + '/get/', costume.baseLayerMD5 + '/get/',
name: costume.costumeName, name: costume.costumeName,
bitmapResolution: costume.bitmapResolution, bitmapResolution: costume.bitmapResolution,
rotationCenterX: costume.rotationCenterX, rotationCenterX: costume.rotationCenterX,
@ -115,11 +100,11 @@ function parseScratchObject (object, runtime, topLevel) {
target.currentCostume = Math.round(object.currentCostumeIndex); target.currentCostume = Math.round(object.currentCostumeIndex);
} }
if (object.hasOwnProperty('rotationStyle')) { if (object.hasOwnProperty('rotationStyle')) {
if (object.rotationStyle == 'none') { if (object.rotationStyle === 'none') {
target.rotationStyle = Clone.ROTATION_STYLE_NONE; target.rotationStyle = Clone.ROTATION_STYLE_NONE;
} else if (object.rotationStyle == 'leftRight') { } else if (object.rotationStyle === 'leftRight') {
target.rotationStyle = Clone.ROTATION_STYLE_LEFT_RIGHT; target.rotationStyle = Clone.ROTATION_STYLE_LEFT_RIGHT;
} else if (object.rotationStyle == 'normal') { } else if (object.rotationStyle === 'normal') {
target.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND; target.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND;
} }
} }
@ -132,7 +117,23 @@ function parseScratchObject (object, runtime, topLevel) {
} }
} }
return target; return target;
} };
/**
* Top-level handler. Parse provided JSON,
* and process the top-level object (the stage object).
* @param {!string} json SB2-format JSON to load.
* @param {!Runtime} runtime Runtime object to load all structures into.
* @param {Boolean=} optForceSprite If set, treat as sprite (Sprite2).
* @return {?Target} Top-level target created (stage or sprite).
*/
var sb2import = function (json, runtime, optForceSprite) {
return parseScratchObject(
JSON.parse(json),
runtime,
!optForceSprite
);
};
/** /**
* Parse a Scratch object's scripts into VM blocks. * Parse a Scratch object's scripts into VM blocks.
@ -140,7 +141,7 @@ function parseScratchObject (object, runtime, topLevel) {
* @param {!Object} scripts Scripts object from SB2 JSON. * @param {!Object} scripts Scripts object from SB2 JSON.
* @param {!Blocks} blocks Blocks object to load parsed blocks into. * @param {!Blocks} blocks Blocks object to load parsed blocks into.
*/ */
function parseScripts (scripts, blocks) { var parseScripts = function (scripts, blocks) {
for (var i = 0; i < scripts.length; i++) { for (var i = 0; i < scripts.length; i++) {
var script = scripts[i]; var script = scripts[i];
var scriptX = script[0]; var scriptX = script[0];
@ -162,7 +163,7 @@ function parseScripts (scripts, blocks) {
blocks.createBlock(convertedBlocks[j]); blocks.createBlock(convertedBlocks[j]);
} }
} }
} };
/** /**
* Parse any list of blocks from SB2 JSON into a list of VM-format blocks. * Parse any list of blocks from SB2 JSON into a list of VM-format blocks.
@ -172,7 +173,7 @@ function parseScripts (scripts, blocks) {
* @param {Array.<Object>} blockList SB2 JSON-format block list. * @param {Array.<Object>} blockList SB2 JSON-format block list.
* @return {Array.<Object>} Scratch VM-format block list. * @return {Array.<Object>} Scratch VM-format block list.
*/ */
function parseBlockList (blockList) { var parseBlockList = function (blockList) {
var resultingList = []; var resultingList = [];
var previousBlock = null; // For setting next. var previousBlock = null; // For setting next.
for (var i = 0; i < blockList.length; i++) { for (var i = 0; i < blockList.length; i++) {
@ -186,7 +187,7 @@ function parseBlockList (blockList) {
resultingList.push(parsedBlock); resultingList.push(parsedBlock);
} }
return resultingList; return resultingList;
} };
/** /**
* Flatten a block tree into a block list. * Flatten a block tree into a block list.
@ -194,7 +195,7 @@ function parseBlockList (blockList) {
* @param {Array.<Object>} blocks list generated by `parseBlockList`. * @param {Array.<Object>} blocks list generated by `parseBlockList`.
* @return {Array.<Object>} Flattened list to be passed to `blocks.createBlock`. * @return {Array.<Object>} Flattened list to be passed to `blocks.createBlock`.
*/ */
function flatten (blocks) { var flatten = function (blocks) {
var finalBlocks = []; var finalBlocks = [];
for (var i = 0; i < blocks.length; i++) { for (var i = 0; i < blocks.length; i++) {
var block = blocks[i]; var block = blocks[i];
@ -205,7 +206,7 @@ function flatten (blocks) {
delete block.children; delete block.children;
} }
return finalBlocks; return finalBlocks;
} };
/** /**
* Convert a Scratch 2.0 procedure string (e.g., "my_procedure %s %b %n") * Convert a Scratch 2.0 procedure string (e.g., "my_procedure %s %b %n")
@ -214,44 +215,44 @@ function flatten (blocks) {
* @param {string} procCode Scratch 2.0 procedure string. * @param {string} procCode Scratch 2.0 procedure string.
* @return {Object} Argument map compatible with those in sb2specmap. * @return {Object} Argument map compatible with those in sb2specmap.
*/ */
function parseProcedureArgMap (procCode) { var parseProcedureArgMap = function (procCode) {
var argMap = [ var argMap = [
{} // First item in list is op string. {} // First item in list is op string.
]; ];
var INPUT_PREFIX = 'input'; var INPUT_PREFIX = 'input';
var inputCount = 0; var inputCount = 0;
// Split by %n, %b, %s. // Split by %n, %b, %s.
var parts = procCode.split(/(?=[^\\]\%[nbs])/); var parts = procCode.split(/(?=[^\\]%[nbs])/);
for (var i = 0; i < parts.length; i++) { for (var i = 0; i < parts.length; i++) {
var part = parts[i].trim(); var part = parts[i].trim();
if (part.substring(0, 1) == '%') { if (part.substring(0, 1) === '%') {
var argType = part.substring(1, 2); var argType = part.substring(1, 2);
var arg = { var arg = {
type: 'input', type: 'input',
inputName: INPUT_PREFIX + (inputCount++) inputName: INPUT_PREFIX + (inputCount++)
}; };
if (argType == 'n') { if (argType === 'n') {
arg.inputOp = 'math_number'; arg.inputOp = 'math_number';
} else if (argType == 's') { } else if (argType === 's') {
arg.inputOp = 'text'; arg.inputOp = 'text';
} }
argMap.push(arg); argMap.push(arg);
} }
} }
return argMap; return argMap;
} };
/** /**
* Parse a single SB2 JSON-formatted block and its children. * Parse a single SB2 JSON-formatted block and its children.
* @param {!Object} sb2block SB2 JSON-formatted block. * @param {!Object} sb2block SB2 JSON-formatted block.
* @return {Object} Scratch VM format block. * @return {Object} Scratch VM format block.
*/ */
function parseBlock (sb2block) { var parseBlock = function (sb2block) {
// First item in block object is the old opcode (e.g., 'forward:'). // First item in block object is the old opcode (e.g., 'forward:').
var oldOpcode = sb2block[0]; var oldOpcode = sb2block[0];
// Convert the block using the specMap. See sb2specmap.js. // Convert the block using the specMap. See sb2specmap.js.
if (!oldOpcode || !specMap[oldOpcode]) { if (!oldOpcode || !specMap[oldOpcode]) {
console.warn('Couldn\'t find SB2 block: ', oldOpcode); log.warn('Couldn\'t find SB2 block: ', oldOpcode);
return; return;
} }
var blockMetadata = specMap[oldOpcode]; var blockMetadata = specMap[oldOpcode];
@ -266,7 +267,7 @@ function parseBlock (sb2block) {
children: [] // Store any generated children, flattened in `flatten`. children: [] // Store any generated children, flattened in `flatten`.
}; };
// For a procedure call, generate argument map from proc string. // For a procedure call, generate argument map from proc string.
if (oldOpcode == 'call') { if (oldOpcode === 'call') {
blockMetadata.argMap = parseProcedureArgMap(sb2block[1]); blockMetadata.argMap = parseProcedureArgMap(sb2block[1]);
} }
// Look at the expected arguments in `blockMetadata.argMap.` // Look at the expected arguments in `blockMetadata.argMap.`
@ -278,7 +279,7 @@ function parseBlock (sb2block) {
// Whether the input is obscuring a shadow. // Whether the input is obscuring a shadow.
var shadowObscured = false; var shadowObscured = false;
// Positional argument is an input. // Positional argument is an input.
if (expectedArg.type == 'input') { if (expectedArg.type === 'input') {
// Create a new block and input metadata. // Create a new block and input metadata.
var inputUid = uid(); var inputUid = uid();
activeBlock.inputs[expectedArg.inputName] = { activeBlock.inputs[expectedArg.inputName] = {
@ -286,10 +287,10 @@ function parseBlock (sb2block) {
block: null, block: null,
shadow: null shadow: null
}; };
if (typeof providedArg == 'object' && providedArg) { if (typeof providedArg === 'object' && providedArg) {
// Block or block list occupies the input. // Block or block list occupies the input.
var innerBlocks; var innerBlocks;
if (typeof providedArg[0] == 'object' && providedArg[0]) { if (typeof providedArg[0] === 'object' && providedArg[0]) {
// Block list occupies the input. // Block list occupies the input.
innerBlocks = parseBlockList(providedArg); innerBlocks = parseBlockList(providedArg);
} else { } else {
@ -298,7 +299,7 @@ function parseBlock (sb2block) {
} }
var previousBlock = null; var previousBlock = null;
for (var j = 0; j < innerBlocks.length; j++) { for (var j = 0; j < innerBlocks.length; j++) {
if (j == 0) { if (j === 0) {
innerBlocks[j].parent = activeBlock.id; innerBlocks[j].parent = activeBlock.id;
} else { } else {
innerBlocks[j].parent = previousBlock; innerBlocks[j].parent = previousBlock;
@ -324,22 +325,22 @@ function parseBlock (sb2block) {
var fieldValue = providedArg; var fieldValue = providedArg;
// Shadows' field names match the input name, except for these: // Shadows' field names match the input name, except for these:
var fieldName = expectedArg.inputName; var fieldName = expectedArg.inputName;
if (expectedArg.inputOp == 'math_number' || if (expectedArg.inputOp === 'math_number' ||
expectedArg.inputOp == 'math_whole_number' || expectedArg.inputOp === 'math_whole_number' ||
expectedArg.inputOp == 'math_positive_number' || expectedArg.inputOp === 'math_positive_number' ||
expectedArg.inputOp == 'math_integer' || expectedArg.inputOp === 'math_integer' ||
expectedArg.inputOp == 'math_angle') { expectedArg.inputOp === 'math_angle') {
fieldName = 'NUM'; fieldName = 'NUM';
// Fields are given Scratch 2.0 default values if obscured. // Fields are given Scratch 2.0 default values if obscured.
if (shadowObscured) { if (shadowObscured) {
fieldValue = 10; fieldValue = 10;
} }
} else if (expectedArg.inputOp == 'text') { } else if (expectedArg.inputOp === 'text') {
fieldName = 'TEXT'; fieldName = 'TEXT';
if (shadowObscured) { if (shadowObscured) {
fieldValue = ''; fieldValue = '';
} }
} else if (expectedArg.inputOp == 'colour_picker') { } else if (expectedArg.inputOp === 'colour_picker') {
// Convert SB2 color to hex. // Convert SB2 color to hex.
fieldValue = Color.decimalToHex(providedArg); fieldValue = Color.decimalToHex(providedArg);
fieldName = 'COLOUR'; fieldName = 'COLOUR';
@ -367,7 +368,7 @@ function parseBlock (sb2block) {
if (!activeBlock.inputs[expectedArg.inputName].block) { if (!activeBlock.inputs[expectedArg.inputName].block) {
activeBlock.inputs[expectedArg.inputName].block = inputUid; activeBlock.inputs[expectedArg.inputName].block = inputUid;
} }
} else if (expectedArg.type == 'field') { } else if (expectedArg.type === 'field') {
// Add as a field on this block. // Add as a field on this block.
activeBlock.fields[expectedArg.fieldName] = { activeBlock.fields[expectedArg.fieldName] = {
name: expectedArg.fieldName, name: expectedArg.fieldName,
@ -376,18 +377,18 @@ function parseBlock (sb2block) {
} }
} }
// Special cases to generate mutations. // Special cases to generate mutations.
if (oldOpcode == 'stopScripts') { if (oldOpcode === 'stopScripts') {
// Mutation for stop block: if the argument is 'other scripts', // Mutation for stop block: if the argument is 'other scripts',
// the block needs a next connection. // the block needs a next connection.
if (sb2block[1] == 'other scripts in sprite' || if (sb2block[1] === 'other scripts in sprite' ||
sb2block[1] == 'other scripts in stage') { sb2block[1] === 'other scripts in stage') {
activeBlock.mutation = { activeBlock.mutation = {
tagName: 'mutation', tagName: 'mutation',
hasnext: 'true', hasnext: 'true',
children: [] children: []
}; };
} }
} else if (oldOpcode == 'procDef') { } else if (oldOpcode === 'procDef') {
// Mutation for procedure definition: // Mutation for procedure definition:
// store all 2.0 proc data. // store all 2.0 proc data.
var procData = sb2block.slice(1); var procData = sb2block.slice(1);
@ -399,7 +400,7 @@ function parseBlock (sb2block) {
warp: procData[3], // Warp mode, e.g., true/false. warp: procData[3], // Warp mode, e.g., true/false.
children: [] children: []
}; };
} else if (oldOpcode == 'call') { } else if (oldOpcode === 'call') {
// Mutation for procedure call: // Mutation for procedure call:
// string for proc code (e.g., "abc %n %b %s"). // string for proc code (e.g., "abc %n %b %s").
activeBlock.mutation = { activeBlock.mutation = {
@ -407,7 +408,7 @@ function parseBlock (sb2block) {
children: [], children: [],
proccode: sb2block[1] proccode: sb2block[1]
}; };
} else if (oldOpcode == 'getParam') { } else if (oldOpcode === 'getParam') {
// Mutation for procedure parameter. // Mutation for procedure parameter.
activeBlock.mutation = { activeBlock.mutation = {
tagName: 'mutation', tagName: 'mutation',
@ -417,6 +418,6 @@ function parseBlock (sb2block) {
}; };
} }
return activeBlock; return activeBlock;
} };
module.exports = sb2import; module.exports = sb2import;

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ var Blocks = require('./engine/blocks');
* *
* @author Andrew Sliwinski <ascii@media.mit.edu> * @author Andrew Sliwinski <ascii@media.mit.edu>
*/ */
function VirtualMachine () { var VirtualMachine = function () {
var instance = this; var instance = this;
// Bind event emitter and runtime to VM instance // Bind event emitter and runtime to VM instance
EventEmitter.call(instance); EventEmitter.call(instance);
@ -45,7 +45,7 @@ function VirtualMachine () {
this.blockListener = this.blockListener.bind(this); this.blockListener = this.blockListener.bind(this);
this.flyoutBlockListener = this.flyoutBlockListener.bind(this); this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
} };
/** /**
* Inherit from EventEmitter * Inherit from EventEmitter
@ -106,12 +106,12 @@ VirtualMachine.prototype.clear = function () {
VirtualMachine.prototype.getPlaygroundData = function () { VirtualMachine.prototype.getPlaygroundData = function () {
var instance = this; var instance = this;
// Only send back thread data for the current editingTarget. // Only send back thread data for the current editingTarget.
var threadData = this.runtime.threads.filter(function(thread) { var threadData = this.runtime.threads.filter(function (thread) {
return thread.target == instance.editingTarget; return thread.target === instance.editingTarget;
}); });
// Remove the target key, since it's a circular reference. // Remove the target key, since it's a circular reference.
var filteredThreadData = JSON.stringify(threadData, function(key, value) { var filteredThreadData = JSON.stringify(threadData, function (key, value) {
if (key == 'target') return undefined; if (key === 'target') return;
return value; return value;
}, 2); }, 2);
this.emit('playgroundData', { this.emit('playgroundData', {
@ -273,7 +273,7 @@ VirtualMachine.prototype.flyoutBlockListener = function (e) {
*/ */
VirtualMachine.prototype.setEditingTarget = function (targetId) { VirtualMachine.prototype.setEditingTarget = function (targetId) {
// Has the target id changed? If not, exit. // Has the target id changed? If not, exit.
if (targetId == this.editingTarget.id) { if (targetId === this.editingTarget.id) {
return; return;
} }
var target = this.runtime.getTargetById(targetId); var target = this.runtime.getTargetById(targetId);
@ -297,7 +297,7 @@ VirtualMachine.prototype.emitTargetsUpdate = function () {
targetList: this.runtime.targets.filter(function (target) { targetList: this.runtime.targets.filter(function (target) {
// Don't report clones. // Don't report clones.
return !target.hasOwnProperty('isOriginal') || target.isOriginal; return !target.hasOwnProperty('isOriginal') || target.isOriginal;
}).map(function(target) { }).map(function (target) {
return [target.id, target.getName()]; return [target.id, target.getName()];
}), }),
// Currently editing target id. // Currently editing target id.
@ -311,7 +311,7 @@ VirtualMachine.prototype.emitTargetsUpdate = function () {
*/ */
VirtualMachine.prototype.emitWorkspaceUpdate = function () { VirtualMachine.prototype.emitWorkspaceUpdate = function () {
this.emit('workspaceUpdate', { this.emit('workspaceUpdate', {
'xml': this.editingTarget.blocks.toXML() xml: this.editingTarget.blocks.toXML()
}); });
}; };

View file

@ -1,6 +1,6 @@
var Timer = require('../util/timer'); var Timer = require('../util/timer');
function Clock (runtime) { var Clock = function (runtime) {
this._projectTimer = new Timer(); this._projectTimer = new Timer();
this._projectTimer.start(); this._projectTimer.start();
this._pausedTime = null; this._pausedTime = null;
@ -10,7 +10,7 @@ function Clock (runtime) {
* @type{!Runtime} * @type{!Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
Clock.prototype.projectTimer = function () { Clock.prototype.projectTimer = function () {
if (this._paused) { if (this._paused) {

View file

@ -1,6 +1,6 @@
var Cast = require('../util/cast'); var Cast = require('../util/cast');
function Keyboard (runtime) { var Keyboard = function (runtime) {
/** /**
* List of currently pressed keys. * List of currently pressed keys.
* @type{Array.<number>} * @type{Array.<number>}
@ -12,7 +12,7 @@ function Keyboard (runtime) {
* @type{!Runtime} * @type{!Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
/** /**
* Convert a Scratch key name to a DOM keyCode. * Convert a Scratch key name to a DOM keyCode.
@ -20,7 +20,7 @@ function Keyboard (runtime) {
* @return {number} Key code corresponding to a DOM event. * @return {number} Key code corresponding to a DOM event.
*/ */
Keyboard.prototype._scratchKeyToKeyCode = function (keyName) { Keyboard.prototype._scratchKeyToKeyCode = function (keyName) {
if (typeof keyName == 'number') { if (typeof keyName === 'number') {
// Key codes placed in with number blocks. // Key codes placed in with number blocks.
return keyName; return keyName;
} }
@ -62,10 +62,10 @@ Keyboard.prototype.postData = function (data) {
} }
// Always trigger hats, even if it was already pressed. // Always trigger hats, even if it was already pressed.
this.runtime.startHats('event_whenkeypressed', { this.runtime.startHats('event_whenkeypressed', {
'KEY_OPTION': this._keyCodeToScratchKey(data.keyCode) KEY_OPTION: this._keyCodeToScratchKey(data.keyCode)
}); });
this.runtime.startHats('event_whenkeypressed', { this.runtime.startHats('event_whenkeypressed', {
'KEY_OPTION': 'any' KEY_OPTION: 'any'
}); });
} else if (index > -1) { } else if (index > -1) {
// If already present, remove from the list. // If already present, remove from the list.
@ -75,7 +75,7 @@ Keyboard.prototype.postData = function (data) {
}; };
Keyboard.prototype.getKeyIsDown = function (key) { Keyboard.prototype.getKeyIsDown = function (key) {
if (key == 'any') { if (key === 'any') {
return this._keysPressed.length > 0; return this._keysPressed.length > 0;
} }
var keyCode = this._scratchKeyToKeyCode(key); var keyCode = this._scratchKeyToKeyCode(key);

View file

@ -1,6 +1,6 @@
var MathUtil = require('../util/math-util'); var MathUtil = require('../util/math-util');
function Mouse (runtime) { var Mouse = function (runtime) {
this._x = 0; this._x = 0;
this._y = 0; this._y = 0;
this._isDown = false; this._isDown = false;
@ -10,14 +10,14 @@ function Mouse (runtime) {
* @type{!Runtime} * @type{!Runtime}
*/ */
this.runtime = runtime; this.runtime = runtime;
} };
Mouse.prototype.postData = function(data) { Mouse.prototype.postData = function (data) {
if (data.x) { if (data.x) {
this._x = data.x - data.canvasWidth / 2; this._x = data.x - (data.canvasWidth / 2);
} }
if (data.y) { if (data.y) {
this._y = data.y - data.canvasHeight / 2; this._y = data.y - (data.canvasHeight / 2);
} }
if (typeof data.isDown !== 'undefined') { if (typeof data.isDown !== 'undefined') {
this._isDown = data.isDown; this._isDown = data.isDown;
@ -33,7 +33,7 @@ Mouse.prototype._activateClickHats = function (x, y) {
for (var i = 0; i < this.runtime.targets.length; i++) { for (var i = 0; i < this.runtime.targets.length; i++) {
var target = this.runtime.targets[i]; var target = this.runtime.targets[i];
if (target.hasOwnProperty('drawableID') && if (target.hasOwnProperty('drawableID') &&
target.drawableID == drawableID) { target.drawableID === drawableID) {
this.runtime.startHats('event_whenthisspriteclicked', this.runtime.startHats('event_whenthisspriteclicked',
null, target); null, target);
return; return;

View file

@ -1,4 +1,6 @@
var util = require('util'); var util = require('util');
var log = require('../util/log');
var MathUtil = require('../util/math-util'); var MathUtil = require('../util/math-util');
var Target = require('../engine/target'); var Target = require('../engine/target');
@ -8,7 +10,7 @@ var Target = require('../engine/target');
* @param {Runtime} runtime Reference to the runtime. * @param {Runtime} runtime Reference to the runtime.
* @constructor * @constructor
*/ */
function Clone(sprite, runtime) { var Clone = function (sprite, runtime) {
Target.call(this, sprite.blocks); Target.call(this, sprite.blocks);
this.runtime = runtime; this.runtime = runtime;
/** /**
@ -35,15 +37,15 @@ function Clone(sprite, runtime) {
* @type {!Object.<string, number>} * @type {!Object.<string, number>}
*/ */
this.effects = { this.effects = {
'color': 0, color: 0,
'fisheye': 0, fisheye: 0,
'whirl': 0, whirl: 0,
'pixelate': 0, pixelate: 0,
'mosaic': 0, mosaic: 0,
'brightness': 0, brightness: 0,
'ghost': 0 ghost: 0
}; };
} };
util.inherits(Clone, Target); util.inherits(Clone, Target);
/** /**
@ -166,7 +168,7 @@ Clone.prototype._getRenderedDirectionAndScale = function () {
// Default: no changes to `this.direction` or `this.scale`. // Default: no changes to `this.direction` or `this.scale`.
var finalDirection = this.direction; var finalDirection = this.direction;
var finalScale = [this.size, this.size]; var finalScale = [this.size, this.size];
if (this.rotationStyle == Clone.ROTATION_STYLE_NONE) { if (this.rotationStyle === Clone.ROTATION_STYLE_NONE) {
// Force rendered direction to be 90. // Force rendered direction to be 90.
finalDirection = 90; finalDirection = 90;
} else if (this.rotationStyle === Clone.ROTATION_STYLE_LEFT_RIGHT) { } else if (this.rotationStyle === Clone.ROTATION_STYLE_LEFT_RIGHT) {
@ -211,10 +213,10 @@ Clone.prototype.setSay = function (type, message) {
} }
// @todo: Render to stage. // @todo: Render to stage.
if (!type || !message) { if (!type || !message) {
console.log('Clearing say bubble'); log.info('Clearing say bubble');
return; return;
} }
console.log('Setting say bubble:', type, message); log.info('Setting say bubble:', type, message);
}; };
/** /**
@ -319,11 +321,11 @@ Clone.prototype.setCostume = function (index) {
* @param {!string} rotationStyle New rotation style. * @param {!string} rotationStyle New rotation style.
*/ */
Clone.prototype.setRotationStyle = function (rotationStyle) { Clone.prototype.setRotationStyle = function (rotationStyle) {
if (rotationStyle == Clone.ROTATION_STYLE_NONE) { if (rotationStyle === Clone.ROTATION_STYLE_NONE) {
this.rotationStyle = Clone.ROTATION_STYLE_NONE; this.rotationStyle = Clone.ROTATION_STYLE_NONE;
} else if (rotationStyle == Clone.ROTATION_STYLE_ALL_AROUND) { } else if (rotationStyle === Clone.ROTATION_STYLE_ALL_AROUND) {
this.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND; this.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND;
} else if (rotationStyle == Clone.ROTATION_STYLE_LEFT_RIGHT) { } else if (rotationStyle === Clone.ROTATION_STYLE_LEFT_RIGHT) {
this.rotationStyle = Clone.ROTATION_STYLE_LEFT_RIGHT; this.rotationStyle = Clone.ROTATION_STYLE_LEFT_RIGHT;
} }
if (this.renderer) { if (this.renderer) {
@ -345,7 +347,7 @@ Clone.prototype.setRotationStyle = function (rotationStyle) {
*/ */
Clone.prototype.getCostumeIndexByName = function (costumeName) { Clone.prototype.getCostumeIndexByName = function (costumeName) {
for (var i = 0; i < this.sprite.costumes.length; i++) { for (var i = 0; i < this.sprite.costumes.length; i++) {
if (this.sprite.costumes[i].name == costumeName) { if (this.sprite.costumes[i].name === costumeName) {
return i; return i;
} }
} }
@ -408,8 +410,8 @@ Clone.prototype.isTouchingPoint = function (x, y) {
// Limits test to this Drawable, so this will return true // Limits test to this Drawable, so this will return true
// even if the clone is obscured by another Drawable. // even if the clone is obscured by another Drawable.
var pickResult = this.runtime.renderer.pick( var pickResult = this.runtime.renderer.pick(
x + this.runtime.constructor.STAGE_WIDTH / 2, x + (this.runtime.constructor.STAGE_WIDTH / 2),
-y + this.runtime.constructor.STAGE_HEIGHT / 2, -y + (this.runtime.constructor.STAGE_HEIGHT / 2),
null, null, null, null,
[this.drawableID] [this.drawableID]
); );
@ -447,7 +449,7 @@ Clone.prototype.isTouchingSprite = function (spriteName) {
if (!firstClone || !this.renderer) { if (!firstClone || !this.renderer) {
return false; return false;
} }
var drawableCandidates = firstClone.sprite.clones.map(function(clone) { var drawableCandidates = firstClone.sprite.clones.map(function (clone) {
return clone.drawableID; return clone.drawableID;
}); });
return this.renderer.isTouchingDrawables( return this.renderer.isTouchingDrawables(
@ -506,11 +508,11 @@ Clone.prototype.goBackLayers = function (nLayers) {
* Keep a desired position within a fence. * Keep a desired position within a fence.
* @param {number} newX New desired X position. * @param {number} newX New desired X position.
* @param {number} newY New desired Y position. * @param {number} newY New desired Y position.
* @param {Object=} opt_fence Optional fence with left, right, top bottom. * @param {Object=} optFence Optional fence with left, right, top bottom.
* @return {Array.<number>} Fenced X and Y coordinates. * @return {Array.<number>} Fenced X and Y coordinates.
*/ */
Clone.prototype.keepInFence = function (newX, newY, opt_fence) { Clone.prototype.keepInFence = function (newX, newY, optFence) {
var fence = opt_fence; var fence = optFence;
if (!fence) { if (!fence) {
fence = { fence = {
left: -this.runtime.constructor.STAGE_WIDTH / 2, left: -this.runtime.constructor.STAGE_WIDTH / 2,

View file

@ -8,7 +8,7 @@ var Blocks = require('../engine/blocks');
* @param {Runtime} runtime Reference to the runtime. * @param {Runtime} runtime Reference to the runtime.
* @constructor * @constructor
*/ */
function Sprite (blocks, runtime) { var Sprite = function (blocks, runtime) {
this.runtime = runtime; this.runtime = runtime;
if (!blocks) { if (!blocks) {
// Shared set of blocks for all clones. // Shared set of blocks for all clones.
@ -38,7 +38,7 @@ function Sprite (blocks, runtime) {
* @type {Array.<!Clone>} * @type {Array.<!Clone>}
*/ */
this.clones = []; this.clones = [];
} };
/** /**
* Create a clone of this sprite. * Create a clone of this sprite.
@ -46,7 +46,7 @@ function Sprite (blocks, runtime) {
*/ */
Sprite.prototype.createClone = function () { Sprite.prototype.createClone = function () {
var newClone = new Clone(this, this.runtime); var newClone = new Clone(this, this.runtime);
newClone.isOriginal = this.clones.length == 0; newClone.isOriginal = this.clones.length === 0;
this.clones.push(newClone); this.clones.push(newClone);
if (newClone.isOriginal) { if (newClone.isOriginal) {
newClone.initDrawable(); newClone.initDrawable();

View file

@ -1,6 +1,6 @@
var Color = require('../util/color'); var Color = require('../util/color');
function Cast () {} var Cast = function () {};
/** /**
* @fileoverview * @fileoverview
@ -44,9 +44,9 @@ Cast.toBoolean = function (value) {
} }
if (typeof value === 'string') { if (typeof value === 'string') {
// These specific strings are treated as false in Scratch. // These specific strings are treated as false in Scratch.
if ((value == '') || if ((value === '') ||
(value == '0') || (value === '0') ||
(value.toLowerCase() == 'false')) { (value.toLowerCase() === 'false')) {
return false; return false;
} }
// All other strings treated as true. // All other strings treated as true.
@ -72,7 +72,7 @@ Cast.toString = function (value) {
*/ */
Cast.toRgbColorList = function (value) { Cast.toRgbColorList = function (value) {
var color; var color;
if (typeof value == 'string' && value.substring(0, 1) == '#') { if (typeof value === 'string' && value.substring(0, 1) === '#') {
color = Color.hexToRgb(value); color = Color.hexToRgb(value);
} else { } else {
color = Color.decimalToRgb(Cast.toNumber(value)); color = Color.decimalToRgb(Cast.toNumber(value));
@ -114,7 +114,7 @@ Cast.isInt = function (val) {
return true; return true;
} }
// True if it's "round" (e.g., 2.0 and 2). // True if it's "round" (e.g., 2.0 and 2).
return val == parseInt(val); return val === parseInt(val, 10);
} else if (typeof val === 'boolean') { } else if (typeof val === 'boolean') {
// `True` and `false` always represent integer after Scratch cast. // `True` and `false` always represent integer after Scratch cast.
return true; return true;
@ -138,15 +138,15 @@ Cast.LIST_ALL = 'ALL';
*/ */
Cast.toListIndex = function (index, length) { Cast.toListIndex = function (index, length) {
if (typeof index !== 'number') { if (typeof index !== 'number') {
if (index == 'all') { if (index === 'all') {
return Cast.LIST_ALL; return Cast.LIST_ALL;
} }
if (index == 'last') { if (index === 'last') {
if (length > 0) { if (length > 0) {
return length; return length;
} }
return Cast.LIST_INVALID; return Cast.LIST_INVALID;
} else if (index == 'random' || index == 'any') { } else if (index === 'random' || index === 'any') {
if (length > 0) { if (length > 0) {
return 1 + Math.floor(Math.random() * length); return 1 + Math.floor(Math.random() * length);
} }

View file

@ -1,4 +1,4 @@
function Color () {} var Color = function () {};
/** /**
* Convert a Scratch decimal color to a hex string, #RRGGBB. * Convert a Scratch decimal color to a hex string, #RRGGBB.
@ -35,7 +35,7 @@ Color.decimalToRgb = function (decimal) {
*/ */
Color.hexToRgb = function (hex) { Color.hexToRgb = function (hex) {
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) { hex = hex.replace(shorthandRegex, function (m, r, g, b) {
return r + r + g + g + b + b; return r + r + g + g + b + b;
}); });
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

4
src/util/log.js Normal file
View file

@ -0,0 +1,4 @@
var minilog = require('minilog');
minilog.enable();
module.exports = minilog('vm');

View file

@ -1,4 +1,4 @@
function MathUtil () {} var MathUtil = function () {};
/** /**
* Convert a value from degrees to radians. * Convert a value from degrees to radians.
@ -42,7 +42,7 @@ MathUtil.clamp = function (n, min, max) {
*/ */
MathUtil.wrapClamp = function (n, min, max) { MathUtil.wrapClamp = function (n, min, max) {
var range = (max - min) + 1; var range = (max - min) + 1;
return n - Math.floor((n - min) / range) * range; return n - (Math.floor((n - min) / range) * range);
}; };
module.exports = MathUtil; module.exports = MathUtil;

View file

@ -15,7 +15,7 @@
/** /**
* @constructor * @constructor
*/ */
function Timer () {} var Timer = function () {};
/** /**
* Used to store the start time of a timer action. * Used to store the start time of a timer action.

5
test/.eslintrc.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
rules: {
'no-undefined': [0]
}
};

View file

@ -9,75 +9,75 @@ test('getPrimitives', function (t) {
}); });
test('add', function (t) { test('add', function (t) {
t.strictEqual(blocks.add({NUM1:'1', NUM2:'1'}), 2); t.strictEqual(blocks.add({NUM1: '1', NUM2: '1'}), 2);
t.strictEqual(blocks.add({NUM1:'foo', NUM2:'bar'}), 0); t.strictEqual(blocks.add({NUM1: 'foo', NUM2: 'bar'}), 0);
t.end(); t.end();
}); });
test('subtract', function (t) { test('subtract', function (t) {
t.strictEqual(blocks.subtract({NUM1:'1', NUM2:'1'}), 0); t.strictEqual(blocks.subtract({NUM1: '1', NUM2: '1'}), 0);
t.strictEqual(blocks.subtract({NUM1:'foo', NUM2:'bar'}), 0); t.strictEqual(blocks.subtract({NUM1: 'foo', NUM2: 'bar'}), 0);
t.end(); t.end();
}); });
test('multiply', function (t) { test('multiply', function (t) {
t.strictEqual(blocks.multiply({NUM1:'2', NUM2:'2'}), 4); t.strictEqual(blocks.multiply({NUM1: '2', NUM2: '2'}), 4);
t.strictEqual(blocks.multiply({NUM1:'foo', NUM2:'bar'}), 0); t.strictEqual(blocks.multiply({NUM1: 'foo', NUM2: 'bar'}), 0);
t.end(); t.end();
}); });
test('divide', function (t) { test('divide', function (t) {
t.strictEqual(blocks.divide({NUM1:'2', NUM2:'2'}), 1); t.strictEqual(blocks.divide({NUM1: '2', NUM2: '2'}), 1);
t.strictEqual(blocks.divide({NUM1:'1', NUM2:'0'}), Infinity); // @todo t.strictEqual(blocks.divide({NUM1: '1', NUM2: '0'}), Infinity); // @todo
t.ok(isNaN(blocks.divide({NUM1:'foo', NUM2:'bar'}))); // @todo t.ok(isNaN(blocks.divide({NUM1: 'foo', NUM2: 'bar'}))); // @todo
t.end(); t.end();
}); });
test('lt', function (t) { test('lt', function (t) {
t.strictEqual(blocks.lt({OPERAND1:'1', OPERAND2:'2'}), true); t.strictEqual(blocks.lt({OPERAND1: '1', OPERAND2: '2'}), true);
t.strictEqual(blocks.lt({OPERAND1:'2', OPERAND2:'1'}), false); t.strictEqual(blocks.lt({OPERAND1: '2', OPERAND2: '1'}), false);
t.strictEqual(blocks.lt({OPERAND1:'1', OPERAND2:'1'}), false); t.strictEqual(blocks.lt({OPERAND1: '1', OPERAND2: '1'}), false);
t.end(); t.end();
}); });
test('equals', function (t) { test('equals', function (t) {
t.strictEqual(blocks.equals({OPERAND1:'1', OPERAND2:'2'}), false); t.strictEqual(blocks.equals({OPERAND1: '1', OPERAND2: '2'}), false);
t.strictEqual(blocks.equals({OPERAND1:'2', OPERAND2:'1'}), false); t.strictEqual(blocks.equals({OPERAND1: '2', OPERAND2: '1'}), false);
t.strictEqual(blocks.equals({OPERAND1:'1', OPERAND2:'1'}), true); t.strictEqual(blocks.equals({OPERAND1: '1', OPERAND2: '1'}), true);
t.end(); t.end();
}); });
test('gt', function (t) { test('gt', function (t) {
t.strictEqual(blocks.gt({OPERAND1:'1', OPERAND2:'2'}), false); t.strictEqual(blocks.gt({OPERAND1: '1', OPERAND2: '2'}), false);
t.strictEqual(blocks.gt({OPERAND1:'2', OPERAND2:'1'}), true); t.strictEqual(blocks.gt({OPERAND1: '2', OPERAND2: '1'}), true);
t.strictEqual(blocks.gt({OPERAND1:'1', OPERAND2:'1'}), false); t.strictEqual(blocks.gt({OPERAND1: '1', OPERAND2: '1'}), false);
t.end(); t.end();
}); });
test('and', function (t) { test('and', function (t) {
t.strictEqual(blocks.and({OPERAND1:true, OPERAND2:true}), true); t.strictEqual(blocks.and({OPERAND1: true, OPERAND2: true}), true);
t.strictEqual(blocks.and({OPERAND1:true, OPERAND2:false}), false); t.strictEqual(blocks.and({OPERAND1: true, OPERAND2: false}), false);
t.strictEqual(blocks.and({OPERAND1:false, OPERAND2:false}), false); t.strictEqual(blocks.and({OPERAND1: false, OPERAND2: false}), false);
t.end(); t.end();
}); });
test('or', function (t) { test('or', function (t) {
t.strictEqual(blocks.or({OPERAND1:true, OPERAND2:true}), true); t.strictEqual(blocks.or({OPERAND1: true, OPERAND2: true}), true);
t.strictEqual(blocks.or({OPERAND1:true, OPERAND2:false}), true); t.strictEqual(blocks.or({OPERAND1: true, OPERAND2: false}), true);
t.strictEqual(blocks.or({OPERAND1:false, OPERAND2:false}), false); t.strictEqual(blocks.or({OPERAND1: false, OPERAND2: false}), false);
t.end(); t.end();
}); });
test('not', function (t) { test('not', function (t) {
t.strictEqual(blocks.not({OPERAND:true}), false); t.strictEqual(blocks.not({OPERAND: true}), false);
t.strictEqual(blocks.not({OPERAND:false}), true); t.strictEqual(blocks.not({OPERAND: false}), true);
t.end(); t.end();
}); });
test('random', function (t) { test('random', function (t) {
var min = 0; var min = 0;
var max = 100; var max = 100;
var result = blocks.random({FROM:min, TO:max}); var result = blocks.random({FROM: min, TO: max});
t.ok(result >= min); t.ok(result >= min);
t.ok(result <= max); t.ok(result <= max);
t.end(); t.end();
@ -86,14 +86,14 @@ test('random', function (t) {
test('random - equal', function (t) { test('random - equal', function (t) {
var min = 1; var min = 1;
var max = 1; var max = 1;
t.strictEqual(blocks.random({FROM:min, TO:max}), min); t.strictEqual(blocks.random({FROM: min, TO: max}), min);
t.end(); t.end();
}); });
test('random - decimal', function (t) { test('random - decimal', function (t) {
var min = 0.1; var min = 0.1;
var max = 10; var max = 10;
var result = blocks.random({FROM:min, TO:max}); var result = blocks.random({FROM: min, TO: max});
t.ok(result >= min); t.ok(result >= min);
t.ok(result <= max); t.ok(result <= max);
t.end(); t.end();
@ -102,7 +102,7 @@ test('random - decimal', function (t) {
test('random - int', function (t) { test('random - int', function (t) {
var min = 0; var min = 0;
var max = 10; var max = 10;
var result = blocks.random({FROM:min, TO:max}); var result = blocks.random({FROM: min, TO: max});
t.ok(result >= min); t.ok(result >= min);
t.ok(result <= max); t.ok(result <= max);
t.end(); t.end();
@ -111,65 +111,65 @@ test('random - int', function (t) {
test('random - reverse', function (t) { test('random - reverse', function (t) {
var min = 0; var min = 0;
var max = 10; var max = 10;
var result = blocks.random({FROM:max, TO:min}); var result = blocks.random({FROM: max, TO: min});
t.ok(result >= min); t.ok(result >= min);
t.ok(result <= max); t.ok(result <= max);
t.end(); t.end();
}); });
test('join', function (t) { test('join', function (t) {
t.strictEqual(blocks.join({STRING1:'foo', STRING2:'bar'}), 'foobar'); t.strictEqual(blocks.join({STRING1: 'foo', STRING2: 'bar'}), 'foobar');
t.strictEqual(blocks.join({STRING1:'1', STRING2:'2'}), '12'); t.strictEqual(blocks.join({STRING1: '1', STRING2: '2'}), '12');
t.end(); t.end();
}); });
test('letterOf', function (t) { test('letterOf', function (t) {
t.strictEqual(blocks.letterOf({STRING:'foo', LETTER:0}), ''); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 0}), '');
t.strictEqual(blocks.letterOf({STRING:'foo', LETTER:1}), 'f'); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 1}), 'f');
t.strictEqual(blocks.letterOf({STRING:'foo', LETTER:2}), 'o'); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 2}), 'o');
t.strictEqual(blocks.letterOf({STRING:'foo', LETTER:3}), 'o'); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 3}), 'o');
t.strictEqual(blocks.letterOf({STRING:'foo', LETTER:4}), ''); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 4}), '');
t.strictEqual(blocks.letterOf({STRING:'foo', LETTER:'bar'}), ''); t.strictEqual(blocks.letterOf({STRING: 'foo', LETTER: 'bar'}), '');
t.end(); t.end();
}); });
test('length', function (t) { test('length', function (t) {
t.strictEqual(blocks.length({STRING:''}), 0); t.strictEqual(blocks.length({STRING: ''}), 0);
t.strictEqual(blocks.length({STRING:'foo'}), 3); t.strictEqual(blocks.length({STRING: 'foo'}), 3);
t.strictEqual(blocks.length({STRING:'1'}), 1); t.strictEqual(blocks.length({STRING: '1'}), 1);
t.strictEqual(blocks.length({STRING:'100'}), 3); t.strictEqual(blocks.length({STRING: '100'}), 3);
t.end(); t.end();
}); });
test('mod', function (t) { test('mod', function (t) {
t.strictEqual(blocks.mod({NUM1:1, NUM2:1}), 0); t.strictEqual(blocks.mod({NUM1: 1, NUM2: 1}), 0);
t.strictEqual(blocks.mod({NUM1:3, NUM2:6}), 3); t.strictEqual(blocks.mod({NUM1: 3, NUM2: 6}), 3);
t.strictEqual(blocks.mod({NUM1:-3, NUM2:6}), 3); t.strictEqual(blocks.mod({NUM1: -3, NUM2: 6}), 3);
t.end(); t.end();
}); });
test('round', function (t) { test('round', function (t) {
t.strictEqual(blocks.round({NUM:1}), 1); t.strictEqual(blocks.round({NUM: 1}), 1);
t.strictEqual(blocks.round({NUM:1.1}), 1); t.strictEqual(blocks.round({NUM: 1.1}), 1);
t.strictEqual(blocks.round({NUM:1.5}), 2); t.strictEqual(blocks.round({NUM: 1.5}), 2);
t.end(); t.end();
}); });
test('mathop', function (t) { test('mathop', function (t) {
t.strictEqual(blocks.mathop({OPERATOR:'abs', NUM:-1}), 1); t.strictEqual(blocks.mathop({OPERATOR: 'abs', NUM: -1}), 1);
t.strictEqual(blocks.mathop({OPERATOR:'floor', NUM:1.5}), 1); t.strictEqual(blocks.mathop({OPERATOR: 'floor', NUM: 1.5}), 1);
t.strictEqual(blocks.mathop({OPERATOR:'ceiling', NUM:0.1}), 1); t.strictEqual(blocks.mathop({OPERATOR: 'ceiling', NUM: 0.1}), 1);
t.strictEqual(blocks.mathop({OPERATOR:'sqrt', NUM:1}), 1); t.strictEqual(blocks.mathop({OPERATOR: 'sqrt', NUM: 1}), 1);
t.strictEqual(blocks.mathop({OPERATOR:'sin', NUM:1}), 0.01745240643728351); t.strictEqual(blocks.mathop({OPERATOR: 'sin', NUM: 1}), 0.01745240643728351);
t.strictEqual(blocks.mathop({OPERATOR:'cos', NUM:1}), 0.9998476951563913); t.strictEqual(blocks.mathop({OPERATOR: 'cos', NUM: 1}), 0.9998476951563913);
t.strictEqual(blocks.mathop({OPERATOR:'tan', NUM:1}), 0.017455064928217585); t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 1}), 0.017455064928217585);
t.strictEqual(blocks.mathop({OPERATOR:'asin', NUM:1}), 90); t.strictEqual(blocks.mathop({OPERATOR: 'asin', NUM: 1}), 90);
t.strictEqual(blocks.mathop({OPERATOR:'acos', NUM:1}), 0); t.strictEqual(blocks.mathop({OPERATOR: 'acos', NUM: 1}), 0);
t.strictEqual(blocks.mathop({OPERATOR:'atan', NUM:1}), 45); t.strictEqual(blocks.mathop({OPERATOR: 'atan', NUM: 1}), 45);
t.strictEqual(blocks.mathop({OPERATOR:'ln', NUM:1}), 0); t.strictEqual(blocks.mathop({OPERATOR: 'ln', NUM: 1}), 0);
t.strictEqual(blocks.mathop({OPERATOR:'log', NUM:1}), 0); t.strictEqual(blocks.mathop({OPERATOR: 'log', NUM: 1}), 0);
t.strictEqual(blocks.mathop({OPERATOR:'e ^', NUM:1}), 2.718281828459045); t.strictEqual(blocks.mathop({OPERATOR: 'e ^', NUM: 1}), 2.718281828459045);
t.strictEqual(blocks.mathop({OPERATOR:'10 ^', NUM:1}), 10); t.strictEqual(blocks.mathop({OPERATOR: '10 ^', NUM: 1}), 10);
t.strictEqual(blocks.mathop({OPERATOR:'undefined', NUM:1}), 0); t.strictEqual(blocks.mathop({OPERATOR: 'undefined', NUM: 1}), 0);
t.end(); t.end();
}); });

View file

@ -7,10 +7,10 @@ test('spec', function (t) {
t.end(); t.end();
}); });
test('invalid inputs', function(t) { test('invalid inputs', function (t) {
var nothing = adapter('not an object'); var nothing = adapter('not an object');
t.type(nothing, 'undefined'); t.type(nothing, 'undefined');
nothing = adapter({noxmlproperty:true}); nothing = adapter({noxmlproperty: true});
t.type(nothing, 'undefined'); t.type(nothing, 'undefined');
t.end(); t.end();
}); });
@ -26,7 +26,7 @@ test('create event', function (t) {
t.type(result[0].opcode, 'string'); t.type(result[0].opcode, 'string');
t.type(result[0].fields, 'object'); t.type(result[0].fields, 'object');
t.type(result[0].inputs, 'object'); t.type(result[0].inputs, 'object');
t.type(result[0].inputs['DURATION'], 'object'); t.type(result[0].inputs.DURATION, 'object');
t.type(result[0].topLevel, 'boolean'); t.type(result[0].topLevel, 'boolean');
t.equal(result[0].topLevel, true); t.equal(result[0].topLevel, true);
@ -35,8 +35,8 @@ test('create event', function (t) {
t.type(result[1].opcode, 'string'); t.type(result[1].opcode, 'string');
t.type(result[1].fields, 'object'); t.type(result[1].fields, 'object');
t.type(result[1].inputs, 'object'); t.type(result[1].inputs, 'object');
t.type(result[1].fields['NUM'], 'object'); t.type(result[1].fields.NUM, 'object');
t.type(result[1].fields['NUM'].value, '10'); t.type(result[1].fields.NUM.value, '10');
t.type(result[1].topLevel, 'boolean'); t.type(result[1].topLevel, 'boolean');
t.equal(result[1].topLevel, false); t.equal(result[1].topLevel, false);
@ -50,18 +50,18 @@ test('create with branch', function (t) {
t.type(result[0].opcode, 'string'); t.type(result[0].opcode, 'string');
t.type(result[0].fields, 'object'); t.type(result[0].fields, 'object');
t.type(result[0].inputs, 'object'); t.type(result[0].inputs, 'object');
t.type(result[0].inputs['SUBSTACK'], 'object'); t.type(result[0].inputs.SUBSTACK, 'object');
t.type(result[0].topLevel, 'boolean'); t.type(result[0].topLevel, 'boolean');
t.equal(result[0].topLevel, true); t.equal(result[0].topLevel, true);
// In branch // In branch
var branchBlockId = result[0].inputs['SUBSTACK']['block']; var branchBlockId = result[0].inputs.SUBSTACK.block;
var branchShadowId = result[0].inputs['SUBSTACK']['shadow']; var branchShadowId = result[0].inputs.SUBSTACK.shadow;
t.type(branchBlockId, 'string'); t.type(branchBlockId, 'string');
t.equal(branchShadowId, null); t.equal(branchShadowId, null);
// Find actual branch block // Find actual branch block
var branchBlock = null; var branchBlock = null;
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
if (result[i].id == branchBlockId) { if (result[i].id === branchBlockId) {
branchBlock = result[i]; branchBlock = result[i];
} }
} }
@ -76,27 +76,27 @@ test('create with two branches', function (t) {
t.type(result[0].opcode, 'string'); t.type(result[0].opcode, 'string');
t.type(result[0].fields, 'object'); t.type(result[0].fields, 'object');
t.type(result[0].inputs, 'object'); t.type(result[0].inputs, 'object');
t.type(result[0].inputs['SUBSTACK'], 'object'); t.type(result[0].inputs.SUBSTACK, 'object');
t.type(result[0].inputs['SUBSTACK2'], 'object'); t.type(result[0].inputs.SUBSTACK2, 'object');
t.type(result[0].topLevel, 'boolean'); t.type(result[0].topLevel, 'boolean');
t.equal(result[0].topLevel, true); t.equal(result[0].topLevel, true);
// In branchs // In branchs
var firstBranchBlockId = result[0].inputs['SUBSTACK']['block']; var firstBranchBlockId = result[0].inputs.SUBSTACK.block;
var secondBranchBlockId = result[0].inputs['SUBSTACK2']['block']; var secondBranchBlockId = result[0].inputs.SUBSTACK2.block;
t.type(firstBranchBlockId, 'string'); t.type(firstBranchBlockId, 'string');
t.type(secondBranchBlockId, 'string'); t.type(secondBranchBlockId, 'string');
var firstBranchShadowBlockId = result[0].inputs['SUBSTACK']['shadow']; var firstBranchShadowBlockId = result[0].inputs.SUBSTACK.shadow;
var secondBranchShadowBlockId = result[0].inputs['SUBSTACK2']['shadow']; var secondBranchShadowBlockId = result[0].inputs.SUBSTACK2.shadow;
t.equal(firstBranchShadowBlockId, null); t.equal(firstBranchShadowBlockId, null);
t.equal(secondBranchShadowBlockId, null); t.equal(secondBranchShadowBlockId, null);
// Find actual branch blocks // Find actual branch blocks
var firstBranchBlock = null; var firstBranchBlock = null;
var secondBranchBlock = null; var secondBranchBlock = null;
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
if (result[i].id == firstBranchBlockId) { if (result[i].id === firstBranchBlockId) {
firstBranchBlock = result[i]; firstBranchBlock = result[i];
} }
if (result[i].id == secondBranchBlockId) { if (result[i].id === secondBranchBlockId) {
secondBranchBlock = result[i]; secondBranchBlock = result[i];
} }
} }

View file

@ -245,8 +245,8 @@ test('create', function (t) {
topLevel: true topLevel: true
}); });
t.type(b._blocks['foo'], 'object'); t.type(b._blocks.foo, 'object');
t.equal(b._blocks['foo'].opcode, 'TEST_BLOCK'); t.equal(b._blocks.foo.opcode, 'TEST_BLOCK');
t.notEqual(b._scripts.indexOf('foo'), -1); t.notEqual(b._scripts.indexOf('foo'), -1);
t.end(); t.end();
}); });
@ -277,7 +277,7 @@ test('move', function (t) {
}); });
t.equal(b._scripts.length, 1); t.equal(b._scripts.length, 1);
t.equal(Object.keys(b._blocks).length, 2); t.equal(Object.keys(b._blocks).length, 2);
t.equal(b._blocks['foo'].next, 'bar'); t.equal(b._blocks.foo.next, 'bar');
// Detach 'bar' from 'foo' // Detach 'bar' from 'foo'
b.moveBlock({ b.moveBlock({
@ -286,7 +286,7 @@ test('move', function (t) {
}); });
t.equal(b._scripts.length, 2); t.equal(b._scripts.length, 2);
t.equal(Object.keys(b._blocks).length, 2); t.equal(Object.keys(b._blocks).length, 2);
t.equal(b._blocks['foo'].next, null); t.equal(b._blocks.foo.next, null);
t.end(); t.end();
}); });
@ -314,7 +314,7 @@ test('move into empty', function (t) {
newInput: 'fooInput', newInput: 'fooInput',
newParent: 'foo' newParent: 'foo'
}); });
t.equal(b._blocks['foo'].inputs['fooInput'].block, 'bar'); t.equal(b._blocks.foo.inputs.fooInput.block, 'bar');
t.end(); t.end();
}); });
@ -326,7 +326,7 @@ test('move no obscure shadow', function (t) {
next: null, next: null,
fields: {}, fields: {},
inputs: { inputs: {
'fooInput': { fooInput: {
name: 'fooInput', name: 'fooInput',
block: 'x', block: 'x',
shadow: 'y' shadow: 'y'
@ -347,8 +347,8 @@ test('move no obscure shadow', function (t) {
newInput: 'fooInput', newInput: 'fooInput',
newParent: 'foo' newParent: 'foo'
}); });
t.equal(b._blocks['foo'].inputs['fooInput'].block, 'bar'); t.equal(b._blocks.foo.inputs.fooInput.block, 'bar');
t.equal(b._blocks['foo'].inputs['fooInput'].shadow, 'y'); t.equal(b._blocks.foo.inputs.fooInput.shadow, 'y');
t.end(); t.end();
}); });
@ -369,7 +369,7 @@ test('change', function (t) {
}); });
// Test that the field is updated // Test that the field is updated
t.equal(b._blocks['foo'].fields.someField.value, 'initial-value'); t.equal(b._blocks.foo.fields.someField.value, 'initial-value');
b.changeBlock({ b.changeBlock({
element: 'field', element: 'field',
@ -378,7 +378,7 @@ test('change', function (t) {
value: 'final-value' value: 'final-value'
}); });
t.equal(b._blocks['foo'].fields.someField.value, 'final-value'); t.equal(b._blocks.foo.fields.someField.value, 'final-value');
// Invalid cases // Invalid cases
// No `element` // No `element`
@ -387,7 +387,7 @@ test('change', function (t) {
name: 'someField', name: 'someField',
value: 'invalid-value' value: 'invalid-value'
}); });
t.equal(b._blocks['foo'].fields.someField.value, 'final-value'); t.equal(b._blocks.foo.fields.someField.value, 'final-value');
// No block ID // No block ID
b.changeBlock({ b.changeBlock({
@ -395,7 +395,7 @@ test('change', function (t) {
name: 'someField', name: 'someField',
value: 'invalid-value' value: 'invalid-value'
}); });
t.equal(b._blocks['foo'].fields.someField.value, 'final-value'); t.equal(b._blocks.foo.fields.someField.value, 'final-value');
// No such field // No such field
b.changeBlock({ b.changeBlock({
@ -404,7 +404,7 @@ test('change', function (t) {
name: 'someWrongField', name: 'someWrongField',
value: 'final-value' value: 'final-value'
}); });
t.equal(b._blocks['foo'].fields.someField.value, 'final-value'); t.equal(b._blocks.foo.fields.someField.value, 'final-value');
t.end(); t.end();
}); });
@ -423,7 +423,7 @@ test('delete', function (t) {
id: 'foo' id: 'foo'
}); });
t.type(b._blocks['foo'], 'undefined'); t.type(b._blocks.foo, 'undefined');
t.equal(b._scripts.indexOf('foo'), -1); t.equal(b._scripts.indexOf('foo'), -1);
t.end(); t.end();
}); });
@ -459,9 +459,9 @@ test('delete chain', function (t) {
b.deleteBlock({ b.deleteBlock({
id: 'foo' id: 'foo'
}); });
t.type(b._blocks['foo'], 'undefined'); t.type(b._blocks.foo, 'undefined');
t.type(b._blocks['foo2'], 'undefined'); t.type(b._blocks.foo2, 'undefined');
t.type(b._blocks['foo3'], 'undefined'); t.type(b._blocks.foo3, 'undefined');
t.equal(b._scripts.indexOf('foo'), -1); t.equal(b._scripts.indexOf('foo'), -1);
t.equal(Object.keys(b._blocks).length, 0); t.equal(Object.keys(b._blocks).length, 0);
t.equal(b._scripts.length, 0); t.equal(b._scripts.length, 0);
@ -532,11 +532,11 @@ test('delete inputs', function (t) {
b.deleteBlock({ b.deleteBlock({
id: 'foo' id: 'foo'
}); });
t.type(b._blocks['foo'], 'undefined'); t.type(b._blocks.foo, 'undefined');
t.type(b._blocks['foo2'], 'undefined'); t.type(b._blocks.foo2, 'undefined');
t.type(b._blocks['foo3'], 'undefined'); t.type(b._blocks.foo3, 'undefined');
t.type(b._blocks['foo4'], 'undefined'); t.type(b._blocks.foo4, 'undefined');
t.type(b._blocks['foo5'], 'undefined'); t.type(b._blocks.foo5, 'undefined');
t.equal(b._scripts.indexOf('foo'), -1); t.equal(b._scripts.indexOf('foo'), -1);
t.equal(Object.keys(b._blocks).length, 0); t.equal(Object.keys(b._blocks).length, 0);
t.equal(b._scripts.length, 0); t.equal(b._scripts.length, 0);

View file

@ -75,19 +75,19 @@ test('toString', function (t) {
test('toRbgColorList', function (t) { test('toRbgColorList', function (t) {
// Hex (minimal, see "color" util tests) // Hex (minimal, see "color" util tests)
t.deepEqual(cast.toRgbColorList('#000'), [0,0,0]); t.deepEqual(cast.toRgbColorList('#000'), [0, 0, 0]);
t.deepEqual(cast.toRgbColorList('#000000'), [0,0,0]); t.deepEqual(cast.toRgbColorList('#000000'), [0, 0, 0]);
t.deepEqual(cast.toRgbColorList('#fff'), [255,255,255]); t.deepEqual(cast.toRgbColorList('#fff'), [255, 255, 255]);
t.deepEqual(cast.toRgbColorList('#ffffff'), [255,255,255]); t.deepEqual(cast.toRgbColorList('#ffffff'), [255, 255, 255]);
// Decimal (minimal, see "color" util tests) // Decimal (minimal, see "color" util tests)
t.deepEqual(cast.toRgbColorList(0), [0,0,0]); t.deepEqual(cast.toRgbColorList(0), [0, 0, 0]);
t.deepEqual(cast.toRgbColorList(1), [0,0,1]); t.deepEqual(cast.toRgbColorList(1), [0, 0, 1]);
t.deepEqual(cast.toRgbColorList(16777215), [255,255,255]); t.deepEqual(cast.toRgbColorList(16777215), [255, 255, 255]);
// Malformed // Malformed
t.deepEqual(cast.toRgbColorList('ffffff'), [0,0,0]); t.deepEqual(cast.toRgbColorList('ffffff'), [0, 0, 0]);
t.deepEqual(cast.toRgbColorList('foobar'), [0,0,0]); t.deepEqual(cast.toRgbColorList('foobar'), [0, 0, 0]);
t.end(); t.end();
}); });
@ -144,7 +144,7 @@ test('isInt', function (t) {
}); });
test('toListIndex', function (t) { test('toListIndex', function (t) {
var list = [0,1,2,3,4,5]; var list = [0, 1, 2, 3, 4, 5];
var empty = []; var empty = [];
// Valid // Valid

View file

@ -11,25 +11,25 @@ test('decimalToHex', function (t) {
}); });
test('decimalToRgb', function (t) { test('decimalToRgb', function (t) {
t.deepEqual(color.decimalToRgb(0), {r:0,g:0,b:0}); t.deepEqual(color.decimalToRgb(0), {r: 0, g: 0, b: 0});
t.deepEqual(color.decimalToRgb(1), {r:0,g:0,b:1}); t.deepEqual(color.decimalToRgb(1), {r: 0, g: 0, b: 1});
t.deepEqual(color.decimalToRgb(16777215), {r:255,g:255,b:255}); t.deepEqual(color.decimalToRgb(16777215), {r: 255, g: 255, b: 255});
t.deepEqual(color.decimalToRgb(-16777215), {r:0,g:0,b:1}); t.deepEqual(color.decimalToRgb(-16777215), {r: 0, g: 0, b: 1});
t.deepEqual(color.decimalToRgb(99999999), {r:245,g:224,b:255}); t.deepEqual(color.decimalToRgb(99999999), {r: 245, g: 224, b: 255});
t.end(); t.end();
}); });
test('hexToRgb', function (t) { test('hexToRgb', function (t) {
t.deepEqual(color.hexToRgb('#000'), {r:0,g:0,b:0}); t.deepEqual(color.hexToRgb('#000'), {r: 0, g: 0, b: 0});
t.deepEqual(color.hexToRgb('#000000'), {r:0,g:0,b:0}); t.deepEqual(color.hexToRgb('#000000'), {r: 0, g: 0, b: 0});
t.deepEqual(color.hexToRgb('#fff'), {r:255,g:255,b:255}); t.deepEqual(color.hexToRgb('#fff'), {r: 255, g: 255, b: 255});
t.deepEqual(color.hexToRgb('#ffffff'), {r:255,g:255,b:255}); t.deepEqual(color.hexToRgb('#ffffff'), {r: 255, g: 255, b: 255});
t.deepEqual(color.hexToRgb('#0fa'), {r:0,g:255,b:170}); t.deepEqual(color.hexToRgb('#0fa'), {r: 0, g: 255, b: 170});
t.deepEqual(color.hexToRgb('#00ffaa'), {r:0,g:255,b:170}); t.deepEqual(color.hexToRgb('#00ffaa'), {r: 0, g: 255, b: 170});
t.deepEqual(color.hexToRgb('000'), {r:0,g:0,b:0}); t.deepEqual(color.hexToRgb('000'), {r: 0, g: 0, b: 0});
t.deepEqual(color.hexToRgb('fff'), {r:255,g:255,b:255}); t.deepEqual(color.hexToRgb('fff'), {r: 255, g: 255, b: 255});
t.deepEqual(color.hexToRgb('00ffaa'), {r:0,g:255,b:170}); t.deepEqual(color.hexToRgb('00ffaa'), {r: 0, g: 255, b: 170});
t.deepEqual(color.hexToRgb('0'), null); t.deepEqual(color.hexToRgb('0'), null);
t.deepEqual(color.hexToRgb('hello world'), null); t.deepEqual(color.hexToRgb('hello world'), null);
@ -38,16 +38,16 @@ test('hexToRgb', function (t) {
}); });
test('rgbToHex', function (t) { test('rgbToHex', function (t) {
t.strictEqual(color.rgbToHex({r:0,g:0,b:0}), '#000000'); t.strictEqual(color.rgbToHex({r: 0, g: 0, b: 0}), '#000000');
t.strictEqual(color.rgbToHex({r:255,g:255,b:255}), '#ffffff'); t.strictEqual(color.rgbToHex({r: 255, g: 255, b: 255}), '#ffffff');
t.strictEqual(color.rgbToHex({r:0,g:255,b:170}), '#00ffaa'); t.strictEqual(color.rgbToHex({r: 0, g: 255, b: 170}), '#00ffaa');
t.end(); t.end();
}); });
test('rgbToDecimal', function (t) { test('rgbToDecimal', function (t) {
t.strictEqual(color.rgbToDecimal({r:0,g:0,b:0}), 0); t.strictEqual(color.rgbToDecimal({r: 0, g: 0, b: 0}), 0);
t.strictEqual(color.rgbToDecimal({r:255,g:255,b:255}), 16777215); t.strictEqual(color.rgbToDecimal({r: 255, g: 255, b: 255}), 16777215);
t.strictEqual(color.rgbToDecimal({r:0,g:255,b:170}), 65450); t.strictEqual(color.rgbToDecimal({r: 0, g: 255, b: 170}), 65450);
t.end(); t.end();
}); });

View file

@ -50,7 +50,7 @@ module.exports = [
// Webpack-compatible // Webpack-compatible
defaultsDeep({}, base, { defaultsDeep({}, base, {
entry: { entry: {
'dist': './src/index.js' dist: './src/index.js'
}, },
output: { output: {
@ -63,8 +63,8 @@ module.exports = [
// Playground // Playground
defaultsDeep({}, base, { defaultsDeep({}, base, {
entry: { entry: {
'vm': './src/index.js', vm: './src/index.js',
'vendor': [ vendor: [
// FPS counter // FPS counter
'stats.js/build/stats.min.js', 'stats.js/build/stats.min.js',
// Syntax highlighter // Syntax highlighter