2017-04-17 15:10:04 -04:00
|
|
|
const EventEmitter = require('events');
|
2017-01-19 14:26:38 -05:00
|
|
|
|
2017-04-17 15:10:04 -04:00
|
|
|
const Blocks = require('./blocks');
|
|
|
|
const Variable = require('../engine/variable');
|
|
|
|
const List = require('../engine/list');
|
|
|
|
const uid = require('../util/uid');
|
2016-06-29 13:48:30 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @fileoverview
|
|
|
|
* A Target is an abstract "code-running" object for the Scratch VM.
|
|
|
|
* Examples include sprites/clones or potentially physical-world devices.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
|
|
|
|
* @constructor
|
|
|
|
*/
|
2017-04-17 19:42:48 -04:00
|
|
|
class Target extends EventEmitter {
|
|
|
|
constructor (blocks) {
|
|
|
|
super();
|
2017-01-19 14:26:38 -05:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
if (!blocks) {
|
2017-05-08 09:53:16 -04:00
|
|
|
blocks = new Blocks();
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* A unique ID for this target.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
this.id = uid();
|
|
|
|
/**
|
|
|
|
* Blocks run as code for this target.
|
|
|
|
* @type {!Blocks}
|
|
|
|
*/
|
|
|
|
this.blocks = blocks;
|
|
|
|
/**
|
|
|
|
* Dictionary of variables and their values for this target.
|
|
|
|
* Key is the variable name.
|
|
|
|
* @type {Object.<string,*>}
|
|
|
|
*/
|
|
|
|
this.variables = {};
|
|
|
|
/**
|
|
|
|
* Dictionary of lists and their contents for this target.
|
|
|
|
* Key is the list name.
|
|
|
|
* @type {Object.<string,*>}
|
|
|
|
*/
|
|
|
|
this.lists = {};
|
|
|
|
/**
|
|
|
|
* Dictionary of custom state for this target.
|
|
|
|
* This can be used to store target-specific custom state for blocks which need it.
|
|
|
|
* TODO: do we want to persist this in SB3 files?
|
|
|
|
* @type {Object.<string,*>}
|
|
|
|
*/
|
|
|
|
this._customState = {};
|
2016-06-29 13:48:30 -04:00
|
|
|
}
|
2017-04-17 19:42:48 -04:00
|
|
|
|
2016-08-31 11:39:57 -04:00
|
|
|
/**
|
2017-04-17 19:42:48 -04:00
|
|
|
* Called when the project receives a "green flag."
|
|
|
|
* @abstract
|
2016-09-21 16:38:33 -04:00
|
|
|
*/
|
2017-04-17 19:42:48 -04:00
|
|
|
onGreenFlag () {}
|
|
|
|
|
2016-09-21 16:38:33 -04:00
|
|
|
/**
|
2017-04-17 19:42:48 -04:00
|
|
|
* Return a human-readable name for this target.
|
|
|
|
* Target implementations should override this.
|
|
|
|
* @abstract
|
|
|
|
* @returns {string} Human-readable name for the target.
|
2016-09-21 16:38:33 -04:00
|
|
|
*/
|
2017-04-17 19:42:48 -04:00
|
|
|
getName () {
|
|
|
|
return this.id;
|
|
|
|
}
|
|
|
|
|
2017-01-19 14:26:38 -05:00
|
|
|
/**
|
2017-04-17 19:42:48 -04:00
|
|
|
* Look up a variable object, and create it if one doesn't exist.
|
|
|
|
* Search begins for local variables; then look for globals.
|
2017-06-15 17:29:15 -04:00
|
|
|
* @param {string} id Id of the variable.
|
|
|
|
* @param {string} name Name of the variable.
|
2017-04-17 19:42:48 -04:00
|
|
|
* @return {!Variable} Variable object.
|
2017-01-19 14:26:38 -05:00
|
|
|
*/
|
2017-06-15 17:29:15 -04:00
|
|
|
lookupOrCreateVariable (id, name) {
|
2017-04-17 19:42:48 -04:00
|
|
|
// If we have a local copy, return it.
|
2017-06-15 17:29:15 -04:00
|
|
|
if (this.variables.hasOwnProperty(id)) {
|
|
|
|
return this.variables[id];
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
|
|
|
// If the stage has a global copy, return it.
|
|
|
|
if (this.runtime && !this.isStage) {
|
|
|
|
const stage = this.runtime.getTargetForStage();
|
2017-06-15 17:29:15 -04:00
|
|
|
if (stage.variables.hasOwnProperty(id)) {
|
|
|
|
return stage.variables[id];
|
2017-04-17 19:42:48 -04:00
|
|
|
}
|
2016-09-21 16:38:33 -04:00
|
|
|
}
|
2017-04-17 19:42:48 -04:00
|
|
|
// No variable with this name exists - create it locally.
|
2017-06-15 17:29:15 -04:00
|
|
|
const newVariable = new Variable(id, name, 0, false);
|
|
|
|
this.variables[id] = newVariable;
|
2017-04-17 19:42:48 -04:00
|
|
|
return newVariable;
|
2016-09-21 16:38:33 -04:00
|
|
|
}
|
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* 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} 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];
|
|
|
|
}
|
2016-09-21 16:38:33 -04:00
|
|
|
}
|
2017-04-17 19:42:48 -04:00
|
|
|
// No list with this name exists - create it locally.
|
|
|
|
const newList = new List(name, []);
|
|
|
|
this.lists[name] = newList;
|
|
|
|
return newList;
|
2016-09-21 16:38:33 -04:00
|
|
|
}
|
|
|
|
|
2017-06-15 17:29:15 -04:00
|
|
|
/**
|
|
|
|
* Creates a variable with the given id and name and adds it to the
|
|
|
|
* dictionary of variables.
|
|
|
|
* @param {string} id Id of variable
|
|
|
|
* @param {string} name Name of variable.
|
|
|
|
*/
|
|
|
|
createVariable (id, name) {
|
|
|
|
if (!this.variables.hasOwnProperty(id)) {
|
|
|
|
const newVariable = new Variable(id, name, 0,
|
|
|
|
false);
|
|
|
|
this.variables[id] = newVariable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renames the variable with the given id to newName.
|
|
|
|
* @param {string} id Id of renamed variable.
|
|
|
|
* @param {string} newName New name for the variable.
|
|
|
|
*/
|
|
|
|
renameVariable (id, newName) {
|
|
|
|
if (this.variables.hasOwnProperty(id)) {
|
|
|
|
const variable = this.variables[id];
|
|
|
|
if (variable.id === id) {
|
|
|
|
variable.name = newName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the variable with the given id from the dictionary of variables.
|
|
|
|
* @param {string} id Id of renamed variable.
|
|
|
|
*/
|
|
|
|
deleteVariable (id) {
|
|
|
|
if (this.variables.hasOwnProperty(id)) {
|
|
|
|
delete this.variables[id];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* Post/edit sprite info.
|
|
|
|
* @param {object} data An object with sprite info data to set.
|
|
|
|
* @abstract
|
|
|
|
*/
|
|
|
|
postSpriteInfo () {}
|
2016-10-26 13:27:12 -04:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* Retrieve custom state associated with this target and the provided state ID.
|
|
|
|
* @param {string} stateId - specify which piece of state to retrieve.
|
|
|
|
* @returns {*} the associated state, if any was found.
|
|
|
|
*/
|
|
|
|
getCustomState (stateId) {
|
|
|
|
return this._customState[stateId];
|
|
|
|
}
|
2017-01-19 14:26:38 -05:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* Store custom state associated with this target and the provided state ID.
|
|
|
|
* @param {string} stateId - specify which piece of state to store on this target.
|
|
|
|
* @param {*} newValue - the state value to store.
|
|
|
|
*/
|
|
|
|
setCustomState (stateId, newValue) {
|
|
|
|
this._customState[stateId] = newValue;
|
|
|
|
}
|
2017-01-19 14:26:38 -05:00
|
|
|
|
2017-04-17 19:42:48 -04:00
|
|
|
/**
|
|
|
|
* Call to destroy a target.
|
|
|
|
* @abstract
|
|
|
|
*/
|
|
|
|
dispose () {
|
|
|
|
this._customState = {};
|
|
|
|
}
|
|
|
|
}
|
2016-09-15 19:37:12 -04:00
|
|
|
|
2016-06-29 13:48:30 -04:00
|
|
|
module.exports = Target;
|