diff --git a/src/blocks/scratch3_data.js b/src/blocks/scratch3_data.js index 551f717f7..8ec4f124e 100644 --- a/src/blocks/scratch3_data.js +++ b/src/blocks/scratch3_data.js @@ -52,8 +52,8 @@ class Scratch3DataBlocks { // If it is, report contents joined together with no separator. // If it's not, report contents joined together with a space. let allSingleLetters = true; - for (let i = 0; i < list.contents.length; i++) { - const listItem = list.contents[i]; + for (let i = 0; i < list.value.length; i++) { + const listItem = list.value[i]; if (!((typeof listItem === 'string') && (listItem.length === 1))) { allSingleLetters = false; @@ -61,73 +61,73 @@ class Scratch3DataBlocks { } } if (allSingleLetters) { - return list.contents.join(''); + return list.value.join(''); } - return list.contents.join(' '); + return list.value.join(' '); } addToList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - list.contents.push(args.ITEM); + list.value.push(args.ITEM); } deleteOfList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); + const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return; } else if (index === Cast.LIST_ALL) { - list.contents = []; + list.value = []; return; } - list.contents.splice(index - 1, 1); + list.value.splice(index - 1, 1); } insertAtList (args, util) { const item = args.ITEM; const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length + 1); + const index = Cast.toListIndex(args.INDEX, list.value.length + 1); if (index === Cast.LIST_INVALID) { return; } - list.contents.splice(index - 1, 0, item); + list.value.splice(index - 1, 0, item); } replaceItemOfList (args, util) { const item = args.ITEM; const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); + const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return; } - list.contents.splice(index - 1, 1, item); + list.value.splice(index - 1, 1, item); } getItemOfList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - const index = Cast.toListIndex(args.INDEX, list.contents.length); + const index = Cast.toListIndex(args.INDEX, list.value.length); if (index === Cast.LIST_INVALID) { return ''; } - return list.contents[index - 1]; + return list.value[index - 1]; } lengthOfList (args, util) { const list = util.target.lookupOrCreateList(args.LIST); - return list.contents.length; + return list.value.length; } listContainsItem (args, util) { const item = args.ITEM; const list = util.target.lookupOrCreateList(args.LIST); - if (list.contents.indexOf(item) >= 0) { + if (list.value.indexOf(item) >= 0) { return true; } // Try using Scratch comparison operator on each item. // (Scratch considers the string '123' equal to the number 123). - for (let i = 0; i < list.contents.length; i++) { - if (Cast.compare(list.contents[i], item) === 0) { + for (let i = 0; i < list.value.length; i++) { + if (Cast.compare(list.value[i], item) === 0) { return true; } } diff --git a/src/engine/blocks.js b/src/engine/blocks.js index ba9077fc7..7a9ecbb88 100644 --- a/src/engine/blocks.js +++ b/src/engine/blocks.js @@ -260,7 +260,7 @@ class Blocks { // If not, create it on the stage. // TODO create global and local variables when UI provides a way. if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) { - stage.createVariable(e.varId, e.varName); + stage.createVariable(e.varId, e.varName, e.varType); } break; case 'var_rename': @@ -310,7 +310,7 @@ class Blocks { case 'field': // Update block value if (!block.fields[args.name]) return; - if (args.name === 'VARIABLE') { + if (args.name === 'VARIABLE' || args.name === 'LIST') { // Get variable name using the id in args.value. const variable = optRuntime.getEditingTarget().lookupVariableById(args.value); if (variable) { diff --git a/src/engine/execute.js b/src/engine/execute.js index a4bf76b2a..8093a346d 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -138,7 +138,7 @@ const execute = function (sequencer, thread) { // Add all fields on this block to the argValues. for (const fieldName in fields) { if (!fields.hasOwnProperty(fieldName)) continue; - if (fieldName === 'VARIABLE') { + if (fieldName === 'VARIABLE' || fieldName === 'LIST') { argValues[fieldName] = fields[fieldName].id; } else { argValues[fieldName] = fields[fieldName].value; diff --git a/src/engine/list.js b/src/engine/list.js deleted file mode 100644 index 0f3ec0f2b..000000000 --- a/src/engine/list.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @fileoverview - * Object representing a Scratch list. - */ - -/** - * @param {!string} name Name of the list. - * @param {Array} contents Contents of the list, as an array. - * @constructor - */ -class List { - constructor (name, contents) { - this.name = name; - this.contents = contents; - } -} - -module.exports = List; diff --git a/src/engine/target.js b/src/engine/target.js index 2a6be81a6..764094daa 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -2,7 +2,6 @@ const EventEmitter = require('events'); const Blocks = require('./blocks'); const Variable = require('../engine/variable'); -const List = require('../engine/list'); const uid = require('../util/uid'); const {Map} = require('immutable'); @@ -88,7 +87,7 @@ class Target extends EventEmitter { const variable = this.lookupVariableById(id); if (variable) return variable; // No variable with this name exists - create it locally. - const newVariable = new Variable(id, name, 0, false); + const newVariable = new Variable(id, name, "", false); this.variables[id] = newVariable; return newVariable; } @@ -117,24 +116,16 @@ class Target extends EventEmitter { /** * Look up a list object for this target, and create it if one doesn't exist. * Search begins for local lists; then look for globals. + * @param {!string} id Id of the list. * @param {!string} name Name of the list. * @return {!List} List object. */ - lookupOrCreateList (name) { - // If we have a local copy, return it. - if (this.lists.hasOwnProperty(name)) { - return this.lists[name]; - } - // If the stage has a global copy, return it. - if (this.runtime && !this.isStage) { - const stage = this.runtime.getTargetForStage(); - if (stage.lists.hasOwnProperty(name)) { - return stage.lists[name]; - } - } - // No list with this name exists - create it locally. - const newList = new List(name, []); - this.lists[name] = newList; + lookupOrCreateList (id, name) { + const list = this.lookupVariableById(id); + if (list) return list; + // No variable with this name exists - create it locally. + const newList = new Variable(id, name, "list", false); + this.variables[id] = newList; return newList; } @@ -143,11 +134,11 @@ class Target extends EventEmitter { * dictionary of variables. * @param {string} id Id of variable * @param {string} name Name of variable. + * @param {string} type Type of variable, one of string, number, list */ - createVariable (id, name) { + createVariable (id, name, type) { if (!this.variables.hasOwnProperty(id)) { - const newVariable = new Variable(id, name, 0, - false); + const newVariable = new Variable(id, name, type, false); this.variables[id] = newVariable; } } diff --git a/src/engine/variable.js b/src/engine/variable.js index b79ab2975..9f047a73b 100644 --- a/src/engine/variable.js +++ b/src/engine/variable.js @@ -9,19 +9,27 @@ class Variable { /** * @param {string} id Id of the variable. * @param {string} name Name of the variable. - * @param {(string|number)} value Value of the variable. + * @param {(string|number)} type Type of the variable, one of "" or "list" * @param {boolean} isCloud Whether the variable is stored in the cloud. * @constructor */ - constructor (id, name, value, isCloud) { + constructor (id, name, type, isCloud) { this.id = id || uid(); this.name = name; - this.value = value; + this.type = type; this.isCloud = isCloud; + switch (this.type) { + case "": + this.value = 0; + break; + case "list": + this.value = []; + break; + } } toXML () { - return `${this.name}`; + return `${this.name}`; } } diff --git a/src/serialization/sb2.js b/src/serialization/sb2.js index 2a54d40a7..e76d6ee2b 100644 --- a/src/serialization/sb2.js +++ b/src/serialization/sb2.js @@ -13,7 +13,6 @@ const log = require('../util/log'); const uid = require('../util/uid'); const specMap = require('./sb2_specmap'); const Variable = require('../engine/variable'); -const List = require('../engine/list'); const {loadCostume} = require('../import/load-costume.js'); const {loadSound} = require('../import/load-sound.js'); @@ -235,9 +234,10 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { const newVariable = new Variable( getVariableId(variable.name), variable.name, - variable.value, + "", variable.isPersistent ); + newVariable.value = variable.value; target.variables[newVariable.id] = newVariable; } } @@ -251,10 +251,14 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) { for (let k = 0; k < object.lists.length; k++) { const list = object.lists[k]; // @todo: monitor properties. - target.lists[list.listName] = new List( + const newVariable = new Variable( + getVariableId(list.listName), list.listName, - list.contents + "list", + list.isPersistent ); + newVariable.value = list.contents; + target.variables[newVariable.id] = newVariable; } } if (object.hasOwnProperty('scratchX')) { diff --git a/src/serialization/sb2_specmap.js b/src/serialization/sb2_specmap.js index cf5be1c89..59200a06f 100644 --- a/src/serialization/sb2_specmap.js +++ b/src/serialization/sb2_specmap.js @@ -1252,7 +1252,7 @@ const specMap = { ] }, 'contentsOfList:': { - opcode: 'data_list', + opcode: 'data_listcontents', argMap: [ { type: 'field', diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 426f1004a..9e8bcbf4e 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -8,7 +8,6 @@ const vmPackage = require('../../package.json'); const Blocks = require('../engine/blocks'); const Sprite = require('../sprites/sprite'); const Variable = require('../engine/variable'); -const List = require('../engine/list'); const {loadCostume} = require('../import/load-costume.js'); const {loadSound} = require('../import/load-sound.js'); @@ -125,22 +124,13 @@ const parseScratchObject = function (object, runtime, extensions) { const newVariable = new Variable( variable.id, variable.name, - variable.value, + variable.type, variable.isPersistent ); + newVariable.value = variable.value; target.variables[newVariable.id] = newVariable; } } - if (object.hasOwnProperty('lists')) { - for (let k = 0; k < object.lists.length; k++) { - const list = object.lists[k]; - // @todo: monitor properties. - target.lists[list.listName] = new List( - list.listName, - list.contents - ); - } - } if (object.hasOwnProperty('x')) { target.x = object.x; }