Pulling in the latest changes from google/blockly/variables_by_id branch,

starting with commit 6218750 (December 19, 2017) through commit 80b397f (January 10, 2018).
This commit is contained in:
Rachel Fenichel 2017-12-19 11:28:23 -08:00 committed by Karishma Chadha
parent ddf61c146b
commit 855ec2665a
15 changed files with 507 additions and 232 deletions

View file

@ -182,8 +182,21 @@ Blockly.Block = function(workspace, prototypeName, opt_id) {
// Record initial inline state. // Record initial inline state.
/** @type {boolean|undefined} */ /** @type {boolean|undefined} */
this.inputsInlineDefault = this.inputsInline; this.inputsInlineDefault = this.inputsInline;
// Fire a create event.
if (Blockly.Events.isEnabled()) { if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.BlockCreate(this)); var existingGroup = Blockly.Events.getGroup();
if (!existingGroup) {
Blockly.Events.setGroup(true);
}
try {
Blockly.Events.fire(new Blockly.Events.BlockCreate(this));
} finally {
if (!existingGroup) {
Blockly.Events.setGroup(false);
}
}
} }
// Bind an onchange function, if it exists. // Bind an onchange function, if it exists.
if (goog.isFunction(this.onchange)) { if (goog.isFunction(this.onchange)) {
@ -282,6 +295,25 @@ Blockly.Block.prototype.dispose = function(healStack) {
} }
}; };
/**
* Call initModel on all fields on the block.
* May be called more than once.
* Either initModel or initSvg must be called after creating a block and before
* the first interaction with it. Interactions include UI actions
* (e.g. clicking and dragging) and firing events (e.g. create, delete, and
* change).
* @public
*/
Blockly.Block.prototype.initModel = function() {
for (var i = 0, input; input = this.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (field.initModel) {
field.initModel();
}
}
}
};
/** /**
* Unplug this block from its superior block. If this block is a statement, * Unplug this block from its superior block. If this block is a statement,
* optionally reconnect the block underneath with the block on top. * optionally reconnect the block underneath with the block on top.
@ -815,6 +847,7 @@ Blockly.Block.prototype.getField = function(name) {
/** /**
* Return all variables referenced by this block. * Return all variables referenced by this block.
* @return {!Array.<string>} List of variable names. * @return {!Array.<string>} List of variable names.
* @package
*/ */
Blockly.Block.prototype.getVars = function() { Blockly.Block.prototype.getVars = function() {
var vars = []; var vars = [];
@ -832,6 +865,7 @@ Blockly.Block.prototype.getVars = function() {
/** /**
* Return all variables referenced by this block. * Return all variables referenced by this block.
* @return {!Array.<!Blockly.VariableModel>} List of variable models. * @return {!Array.<!Blockly.VariableModel>} List of variable models.
* @package
*/ */
Blockly.Block.prototype.getVarModels = function() { Blockly.Block.prototype.getVarModels = function() {
var vars = []; var vars = [];
@ -850,30 +884,11 @@ Blockly.Block.prototype.getVarModels = function() {
return vars; return vars;
}; };
/**
* Notification that a variable is renaming.
* TODO (#1498): consider deleting this.
* If the name matches one of this block's variables, rename it.
* @param {string} oldName Previous name of variable.
* @param {string} newName Renamed variable.
*/
Blockly.Block.prototype.renameVar = function(oldName, newName) {
for (var i = 0, input; input = this.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if ((field instanceof Blockly.FieldVariable ||
field instanceof Blockly.FieldVariableGetter) &&
Blockly.Names.equals(oldName, field.getValue())) {
field.setValue(newName);
}
}
}
};
/** /**
* Notification that a variable is renaming but keeping the same ID. If the * Notification that a variable is renaming but keeping the same ID. If the
* variable is in use on this block, rerender to show the new name. * variable is in use on this block, rerender to show the new name.
* @param {!Blockly.VariableModel} variable The variable being renamed. * @param {!Blockly.VariableModel} variable The variable being renamed.
* @public * @package
*/ */
Blockly.Block.prototype.updateVarName = function(variable) { Blockly.Block.prototype.updateVarName = function(variable) {
for (var i = 0, input; input = this.inputList[i]; i++) { for (var i = 0, input; input = this.inputList[i]; i++) {
@ -888,7 +903,7 @@ Blockly.Block.prototype.updateVarName = function(variable) {
/** /**
* Notification that a variable is renaming. * Notification that a variable is renaming.
* If the name matches one of this block's variables, rename it. * If the ID matches one of this block's variables, rename it.
* @param {string} oldId ID of variable to rename. * @param {string} oldId ID of variable to rename.
* @param {string} newId ID of new variable. May be the same as oldId, but with * @param {string} newId ID of new variable. May be the same as oldId, but with
* an updated name. * an updated name.
@ -896,7 +911,8 @@ Blockly.Block.prototype.updateVarName = function(variable) {
Blockly.Block.prototype.renameVarById = function(oldId, newId) { Blockly.Block.prototype.renameVarById = function(oldId, newId) {
for (var i = 0, input; input = this.inputList[i]; i++) { for (var i = 0, input; input = this.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) { for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (field instanceof Blockly.FieldVariable && if ((field instanceof Blockly.FieldVariable ||
field instanceof Blockly.FieldVariableGetter) &&
oldId == field.getValue()) { oldId == field.getValue()) {
field.setValue(newId); field.setValue(newId);
} }

View file

@ -103,7 +103,14 @@ Blockly.FieldVariable.prototype.initModel = function() {
} else { } else {
var variable = Blockly.Variables.getOrCreateVariable( var variable = Blockly.Variables.getOrCreateVariable(
this.workspace_, null, this.defaultVariableName, this.defaultType_); this.workspace_, null, this.defaultVariableName, this.defaultType_);
this.setValue(variable.getId()); // Don't fire a change event for this setValue. It would have null as the
// old value, which is not valid.
Blockly.Events.disable();
try {
this.setValue(variable.getId());
} finally {
Blockly.Events.enable();
}
} }
}; };
@ -132,25 +139,36 @@ Blockly.FieldVariable.prototype.setSourceBlock = function(block) {
* @return {string} Current variable's ID. * @return {string} Current variable's ID.
*/ */
Blockly.FieldVariable.prototype.getValue = function() { Blockly.FieldVariable.prototype.getValue = function() {
return this.variable_ ? this.variable_.getId() : ''; return this.variable_ ? this.variable_.getId() : null;
}; };
/** /**
* Get the text from this field. * Get the text from this field, which is the selected variable's name.
* @return {string} Current text. * @return {string} The selected variable's name, or the empty string if no
* variable is selected.
*/ */
Blockly.FieldVariable.prototype.getText = function() { Blockly.FieldVariable.prototype.getText = function() {
return this.variable_ ? this.variable_.name : ''; return this.variable_ ? this.variable_.name : '';
}; };
/**
* Get the variable model for the selected variable.
* Not guaranteed to be in the variable map on the workspace (e.g. if accessed
* after the variable has been deleted).
* @return {?Blockly.VariableModel} the selected variable, or null if none was
* selected.
* @package
*/
Blockly.FieldVariable.prototype.getVariable = function() {
return this.variable_;
};
/** /**
* Set the variable ID. * Set the variable ID.
* @param {string} id New variable ID, which must reference an existing * @param {string} id New variable ID, which must reference an existing
* variable. * variable.
*/ */
Blockly.FieldVariable.prototype.setValue = function(id) { Blockly.FieldVariable.prototype.setValue = function(id) {
// What do I do when id is null? That happens when undoing a change event
// for the first time the value was set.
var workspace = this.sourceBlock_.workspace; var workspace = this.sourceBlock_.workspace;
var variable = Blockly.Variables.getVariable(workspace, id); var variable = Blockly.Variables.getVariable(workspace, id);
@ -167,7 +185,7 @@ Blockly.FieldVariable.prototype.setValue = function(id) {
if (this.sourceBlock_ && Blockly.Events.isEnabled()) { if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
var oldValue = this.variable_ ? this.variable_.getId() : null; var oldValue = this.variable_ ? this.variable_.getId() : null;
Blockly.Events.fire(new Blockly.Events.BlockChange( Blockly.Events.fire(new Blockly.Events.BlockChange(
this.sourceBlock_, 'field', this.name, oldValue, variable.getId())); this.sourceBlock_, 'field', this.name, oldValue, id));
} }
this.variable_ = variable; this.variable_ = variable;
this.value_ = id; this.value_ = id;
@ -200,8 +218,7 @@ Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) {
* @private * @private
*/ */
Blockly.FieldVariable.prototype.getVariableTypes_ = function() { Blockly.FieldVariable.prototype.getVariableTypes_ = function() {
// TODO: Why does this happen every time, instead of once when the workspace // TODO (#1513): Try to avoid calling this every time the field is edited.
// is set? Do we expect the variable types to change that much?
var variableTypes = this.variableTypes; var variableTypes = this.variableTypes;
if (variableTypes === null) { if (variableTypes === null) {
// If variableTypes is null, return all variable types. // If variableTypes is null, return all variable types.
@ -306,12 +323,7 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
return; return;
} }
// TODO: Call any validation function, and allow it to override. // TODO (#1529): Call any validation function, and allow it to override.
// For now it's unclear whether the validator should act on the id.
//var validatedId = this.callValidator(variable.getId());
} }
// if (variable.getId() !== null) {
// this.setValue(validatedId);
// }
this.setValue(id); this.setValue(id);
}; };

View file

@ -47,6 +47,15 @@ Blockly.Names = function(reservedWords, opt_variablePrefix) {
this.reset(); this.reset();
}; };
/**
* Constant to separate developer variable names from user-defined variable
* names when running generators.
* A developer variable will be declared as a global in the generated code, but
* will never be shown to the user in the workspace or stored in the variable
* map.
*/
Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE';
/** /**
* When JavaScript (or most other languages) is generated, variable 'foo' and * When JavaScript (or most other languages) is generated, variable 'foo' and
* procedure 'foo' would collide. However, Blockly has no such problems since * procedure 'foo' would collide. However, Blockly has no such problems since
@ -62,6 +71,41 @@ Blockly.Names = function(reservedWords, opt_variablePrefix) {
Blockly.Names.prototype.reset = function() { Blockly.Names.prototype.reset = function() {
this.db_ = Object.create(null); this.db_ = Object.create(null);
this.dbReverse_ = Object.create(null); this.dbReverse_ = Object.create(null);
this.variableMap_ = null;
};
/**
* Set the variable map that maps from variable name to variable object.
* @param {!Blockly.VariableMap} map The map to track.
* @package
*/
Blockly.Names.prototype.setVariableMap = function(map) {
this.variableMap_ = map;
};
/**
* Get the name for a user-defined variable, based on its ID.
* This should only be used for variables of type Blockly.Variables.NAME_TYPE.
* @param {string} id The ID to look up in the variable map.
* @return {?string} The name of the referenced variable, or null if there was
* no variable map or the variable was not found in the map.
* @private
*/
Blockly.Names.prototype.getNameForUserVariable_ = function(id) {
if (!this.variableMap_) {
console.log('Deprecated call to Blockly.Names.prototype.getName without ' +
'defining a variable map. To fix, add the folowing code in your ' +
'generator\'s init() function:\n' +
'Blockly.YourGeneratorName.variableDB_.setVariableMap(' +
'workspace.getVariableMap());');
return null;
}
var variable = this.variableMap_.getVariableById(id);
if (variable) {
return variable.name;
} else {
return null;
}
}; };
/** /**
@ -69,12 +113,21 @@ Blockly.Names.prototype.reset = function() {
* @param {string} name The Blockly entity name (no constraints). * @param {string} name The Blockly entity name (no constraints).
* @param {string} type The type of entity in Blockly * @param {string} type The type of entity in Blockly
* ('VARIABLE', 'PROCEDURE', 'BUILTIN', etc...). * ('VARIABLE', 'PROCEDURE', 'BUILTIN', etc...).
* @return {string} An entity name legal for the exported language. * @return {string} An entity name that is legal in the exported language.
*/ */
Blockly.Names.prototype.getName = function(name, type) { Blockly.Names.prototype.getName = function(name, type) {
if (type == Blockly.Variables.NAME_TYPE) {
var varName = this.getNameForUserVariable_(name);
if (varName) {
name = varName;
}
}
var normalized = name.toLowerCase() + '_' + type; var normalized = name.toLowerCase() + '_' + type;
var prefix = (type == Blockly.Variables.NAME_TYPE) ?
this.variablePrefix_ : ''; var isVarType = type == Blockly.Variables.NAME_TYPE ||
type == Blockly.Names.DEVELOPER_VARIABLE_TYPE;
var prefix = isVarType ? this.variablePrefix_ : '';
if (normalized in this.db_) { if (normalized in this.db_) {
return prefix + this.db_[normalized]; return prefix + this.db_[normalized];
} }
@ -91,7 +144,7 @@ Blockly.Names.prototype.getName = function(name, type) {
* @param {string} name The Blockly entity name (no constraints). * @param {string} name The Blockly entity name (no constraints).
* @param {string} type The type of entity in Blockly * @param {string} type The type of entity in Blockly
* ('VARIABLE', 'PROCEDURE', 'BUILTIN', etc...). * ('VARIABLE', 'PROCEDURE', 'BUILTIN', etc...).
* @return {string} An entity name legal for the exported language. * @return {string} An entity name that is legal in the exported language.
*/ */
Blockly.Names.prototype.getDistinctName = function(name, type) { Blockly.Names.prototype.getDistinctName = function(name, type) {
var safeName = this.safeName_(name); var safeName = this.safeName_(name);
@ -103,8 +156,9 @@ Blockly.Names.prototype.getDistinctName = function(name, type) {
} }
safeName += i; safeName += i;
this.dbReverse_[safeName] = true; this.dbReverse_[safeName] = true;
var prefix = (type == Blockly.Variables.NAME_TYPE) ? var isVarType = type == Blockly.Variables.NAME_TYPE ||
this.variablePrefix_ : ''; type == Blockly.Names.DEVELOPER_VARIABLE_TYPE;
var prefix = isVarType ? this.variablePrefix_ : '';
return prefix + safeName; return prefix + safeName;
}; };

View file

@ -72,13 +72,16 @@ Blockly.VariableMap.prototype.renameVariable = function(variable, newName) {
var conflictVar = this.getVariable(newName, type); var conflictVar = this.getVariable(newName, type);
var blocks = this.workspace.getAllBlocks(); var blocks = this.workspace.getAllBlocks();
Blockly.Events.setGroup(true); Blockly.Events.setGroup(true);
// The IDs may match if the rename is a simple case change (name1 -> Name1). try {
if (!conflictVar || conflictVar.getId() == variable.getId()) { // The IDs may match if the rename is a simple case change (name1 -> Name1).
this.renameVariableAndUses_(variable, newName, blocks); if (!conflictVar || conflictVar.getId() == variable.getId()) {
} else { this.renameVariableAndUses_(variable, newName, blocks);
this.renameVariableWithConflict_(variable, newName, conflictVar, blocks); } else {
this.renameVariableWithConflict_(variable, newName, conflictVar, blocks);
}
} finally {
Blockly.Events.setGroup(false);
} }
Blockly.Events.setGroup(false);
}; };
/** /**
@ -270,11 +273,14 @@ Blockly.VariableMap.prototype.deleteVariableById = function(id) {
Blockly.VariableMap.prototype.deleteVariableInternal_ = function(variable, Blockly.VariableMap.prototype.deleteVariableInternal_ = function(variable,
uses) { uses) {
Blockly.Events.setGroup(true); Blockly.Events.setGroup(true);
for (var i = 0; i < uses.length; i++) { try {
uses[i].dispose(true, false); for (var i = 0; i < uses.length; i++) {
uses[i].dispose(true, false);
}
this.deleteVariable(variable);
} finally {
Blockly.Events.setGroup(false);
} }
this.deleteVariable(variable);
Blockly.Events.setGroup(false);
}; };
/* End functions for variable deletion. */ /* End functions for variable deletion. */

View file

@ -105,10 +105,42 @@ Blockly.Variables.allVariables = function(root) {
}; };
/** /**
* Return the text that should be used in a field_variable or * Find all developer variables used by blocks in the workspace.
* field_variable_getter when no variable exists. * Developer variables are never shown to the user, but are declared as global
* TODO: #572 * variables in the generated code.
* @return {string} The text to display. * To declare developer variables, define the getDeveloperVariables function on
* your block and return a list of variable names.
* For use by generators.
* @param {!Blockly.Workspace} workspace The workspace to search.
* @return {!Array.<string>} A list of non-duplicated variable names.
* @package
*/
Blockly.Variables.allDeveloperVariables = function(workspace) {
var blocks = workspace.getAllBlocks();
var hash = {};
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
if (block.getDeveloperVars) {
var devVars = block.getDeveloperVars();
for (var j = 0; j < devVars.length; j++) {
hash[devVars[j]] = devVars[j];
}
}
}
// Flatten the hash into a list.
var list = [];
for (var name in hash) {
list.push(hash[name]);
}
return list;
};
/**
* Return the text that should be used in a field_variable or
* field_variable_getter when no variable exists.
* TODO: #572
* @return {string} The text to display.
*/ */
Blockly.Variables.noVariableText = function() { Blockly.Variables.noVariableText = function() {
return "No variable selected"; return "No variable selected";
@ -372,16 +404,20 @@ Blockly.Variables.generateVariableFieldXml_ = function(variableModel, opt_name)
* @param {!Blockly.Workspace} workspace The workspace to search for the * @param {!Blockly.Workspace} workspace The workspace to search for the
* variable. It may be a flyout workspace or main workspace. * variable. It may be a flyout workspace or main workspace.
* @param {string} id The ID to use to look up or create the variable, or null. * @param {string} id The ID to use to look up or create the variable, or null.
* @param {string} name The string to use to look up or create the variable, * @param {string=} opt_name The string to use to look up or create the
* @param {string} type The type to use to look up or create the variable. * variable.
* @param {string=} opt_type The type to use to look up or create the variable.
* @return {!Blockly.VariableModel} The variable corresponding to the given ID * @return {!Blockly.VariableModel} The variable corresponding to the given ID
* or name + type combination. * or name + type combination.
* @package * @package
*/ */
Blockly.Variables.getOrCreateVariable = function(workspace, id, name, type) { Blockly.Variables.getOrCreateVariable = function(workspace, id, opt_name,
var variable = Blockly.Variables.getVariable(workspace, id, name, type); opt_type) {
var variable = Blockly.Variables.getVariable(workspace, id, opt_name,
opt_type);
if (!variable) { if (!variable) {
variable = Blockly.Variables.createVariable_(workspace, id, name, type); variable = Blockly.Variables.createVariable_(workspace, id, opt_name,
opt_type);
} }
return variable; return variable;
}; };
@ -399,7 +435,7 @@ Blockly.Variables.getOrCreateVariable = function(workspace, id, name, type) {
* if lookup by ID fails. * if lookup by ID fails.
* @return {?Blockly.VariableModel} The variable corresponding to the given ID * @return {?Blockly.VariableModel} The variable corresponding to the given ID
* or name + type combination, or null if not found. * or name + type combination, or null if not found.
* @private * @package
*/ */
Blockly.Variables.getVariable = function(workspace, id, opt_name, opt_type) { Blockly.Variables.getVariable = function(workspace, id, opt_name, opt_type) {
var potentialVariableMap = workspace.getPotentialVariableMap(); var potentialVariableMap = workspace.getPotentialVariableMap();
@ -416,6 +452,8 @@ Blockly.Variables.getVariable = function(workspace, id, opt_name, opt_type) {
if (!variable && potentialVariableMap) { if (!variable && potentialVariableMap) {
variable = potentialVariableMap.getVariable(opt_name, opt_type); variable = potentialVariableMap.getVariable(opt_name, opt_type);
} }
} else {
throw new Error('Tried to look up a variable by name without a type');
} }
return variable; return variable;
}; };
@ -425,25 +463,26 @@ Blockly.Variables.getVariable = function(workspace, id, opt_name, opt_type) {
* @param {!Blockly.Workspace} workspace The workspace in which to create the * @param {!Blockly.Workspace} workspace The workspace in which to create the
* variable. It may be a flyout workspace or main workspace. * variable. It may be a flyout workspace or main workspace.
* @param {string} id The ID to use to create the variable, or null. * @param {string} id The ID to use to create the variable, or null.
* @param {string} name The string to use to create the variable. * @param {string=} opt_name The string to use to create the variable.
* @param {string} type The type to use to create the variable. * @param {string=} opt_type The type to use to create the variable.
* @return {!Blockly.VariableModel} The variable corresponding to the given ID * @return {!Blockly.VariableModel} The variable corresponding to the given ID
* or name + type combination. * or name + type combination.
* @private * @private
*/ */
Blockly.Variables.createVariable_ = function(workspace, id, name, type) { Blockly.Variables.createVariable_ = function(workspace, id, opt_name,
opt_type) {
var potentialVariableMap = workspace.getPotentialVariableMap(); var potentialVariableMap = workspace.getPotentialVariableMap();
// Variables without names get uniquely named for this workspace. // Variables without names get uniquely named for this workspace.
if (!name) { if (!opt_name) {
var ws = workspace.isFlyout ? workspace.targetWorkspace : workspace; var ws = workspace.isFlyout ? workspace.targetWorkspace : workspace;
name = Blockly.Variables.generateUniqueName(ws); opt_name = Blockly.Variables.generateUniqueName(ws);
} }
// Create a potential variable if in the flyout. // Create a potential variable if in the flyout.
if (potentialVariableMap) { if (potentialVariableMap) {
var variable = potentialVariableMap.createVariable(name, type, id); var variable = potentialVariableMap.createVariable(opt_name, opt_type, id);
} else { // In the main workspace, create a real variable. } else { // In the main workspace, create a real variable.
var variable = workspace.createVariable(name, type, id); var variable = workspace.createVariable(opt_name, opt_type, id);
} }
return variable; return variable;
}; };

View file

@ -92,7 +92,7 @@ Blockly.Workspace = function(opt_options) {
this.variableMap_ = new Blockly.VariableMap(this); this.variableMap_ = new Blockly.VariableMap(this);
/** /**
* Blocks in the flyout can refer to variables that don't exist in the * Blocks in the flyout can refer to variables that don't exist in the main
* workspace. For instance, the "get item in list" block refers to an "item" * workspace. For instance, the "get item in list" block refers to an "item"
* variable regardless of whether the variable has been created yet. * variable regardless of whether the variable has been created yet.
* A FieldVariable must always refer to a Blockly.VariableModel. We reconcile * A FieldVariable must always refer to a Blockly.VariableModel. We reconcile
@ -141,18 +141,6 @@ Blockly.Workspace.SCAN_ANGLE = 3;
*/ */
Blockly.Workspace.prototype.addTopBlock = function(block) { Blockly.Workspace.prototype.addTopBlock = function(block) {
this.topBlocks_.push(block); this.topBlocks_.push(block);
if (!this.isFlyout) {
return;
}
// This is for the (unlikely) case where you have a variable in a block in
// an always-open flyout. It needs to be possible to edit the block in the
// flyout, so the contents of the dropdown need to be correct.
var variableNames = Blockly.Variables.allUsedVariables(block);
for (var i = 0, name; name = variableNames[i]; i++) {
if (!this.getVariable(name)) {
this.createVariable(name);
}
}
}; };
/** /**
@ -255,7 +243,7 @@ Blockly.Workspace.prototype.createVariable = function(name, opt_type, opt_id) {
}; };
/** /**
* Find all the uses of a named variable. * Find all the uses of the given variable, which is identified by ID.
* @param {string} id ID of the variable to find. * @param {string} id ID of the variable to find.
* @return {!Array.<!Blockly.Block>} Array of block usages. * @return {!Array.<!Blockly.Block>} Array of block usages.
*/ */

View file

@ -87,28 +87,38 @@ Blockly.Xml.blockToDomWithXY = function(block, opt_noId) {
return element; return element;
}; };
Blockly.Xml.fieldToDomVariable_ = function(field, workspace) { /**
* Encode a variable field as XML.
* @param {!Blockly.FieldVariable} field The field to encode.
* @return {?Element} XML element, or null if the field did not need to be
* serialized.
* @private
*/
Blockly.Xml.fieldToDomVariable_ = function(field) {
var id = field.getValue(); var id = field.getValue();
var variable = workspace.getVariableById(id); // The field had not been initialized fully before being serialized.
// This can happen if a block is created directly through a call to
// workspace.newBlock instead of from XML.
// The new block will be serialized for the first time when firing a block
// creation event.
if (id == null) {
field.initModel();
id = field.getValue();
}
// Get the variable directly from the field, instead of doing a lookup. This
// will work even if the variable has already been deleted. This can happen
// because the flyout defers deleting blocks until the next time the flyout is
// opened.
var variable = field.getVariable();
if (!variable) { if (!variable) {
if (workspace.isFlyout && workspace.targetWorkspace) { throw Error('Tried to serialize a variable field with no variable.');
var potentialVariableMap = workspace.getPotentialVariableMap();
if (potentialVariableMap) {
variable = potentialVariableMap.getVariableById(id);
}
}
}
if (variable) {
var container = goog.dom.createDom('field', null, variable.name);
container.setAttribute('name', field.name);
container.setAttribute('id', variable.getId());
container.setAttribute('variabletype', variable.type);
return container;
} else {
// something went wrong?
console.warn('no variable in fieldtodom');
return null;
} }
var container = goog.dom.createDom('field', null, variable.name);
container.setAttribute('name', field.name);
container.setAttribute('id', variable.getId());
container.setAttribute('variabletype', variable.type);
return container;
}; };
/** /**
@ -119,11 +129,11 @@ Blockly.Xml.fieldToDomVariable_ = function(field, workspace) {
* serialized. * serialized.
* @private * @private
*/ */
Blockly.Xml.fieldToDom_ = function(field, workspace) { Blockly.Xml.fieldToDom_ = function(field) {
if (field.name && field.SERIALIZABLE) { if (field.name && field.SERIALIZABLE) {
if (field instanceof Blockly.FieldVariable || if (field instanceof Blockly.FieldVariable ||
field instanceof Blockly.FieldVariableGetter) { field instanceof Blockly.FieldVariableGetter) {
return Blockly.Xml.fieldToDomVariable_(field, workspace); return Blockly.Xml.fieldToDomVariable_(field);
} else { } else {
var container = goog.dom.createDom('field', null, field.getValue()); var container = goog.dom.createDom('field', null, field.getValue());
container.setAttribute('name', field.name); container.setAttribute('name', field.name);
@ -142,10 +152,9 @@ Blockly.Xml.fieldToDom_ = function(field, workspace) {
* @private * @private
*/ */
Blockly.Xml.allFieldsToDom_ = function(block, element) { Blockly.Xml.allFieldsToDom_ = function(block, element) {
var workspace = block.workspace;
for (var i = 0, input; input = block.inputList[i]; i++) { for (var i = 0, input; input = block.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) { for (var j = 0, field; field = input.fieldRow[j]; j++) {
var fieldDom = Blockly.Xml.fieldToDom_(field, workspace); var fieldDom = Blockly.Xml.fieldToDom_(field);
if (fieldDom) { if (fieldDom) {
element.appendChild(fieldDom); element.appendChild(fieldDom);
} }
@ -520,12 +529,11 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) {
var variablesBeforeCreation = workspace.getAllVariables(); var variablesBeforeCreation = workspace.getAllVariables();
try { try {
var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace); var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace);
// Generate list of all blocks.
var blocks = topBlock.getDescendants();
if (workspace.rendered) { if (workspace.rendered) {
// TODO (fenichel): Otherwise call initModel?
// Hide connections to speed up assembly. // Hide connections to speed up assembly.
topBlock.setConnectionsHidden(true); topBlock.setConnectionsHidden(true);
// Generate list of all blocks.
var blocks = topBlock.getDescendants();
// Render each block. // Render each block.
for (var i = blocks.length - 1; i >= 0; i--) { for (var i = blocks.length - 1; i >= 0; i--) {
blocks[i].initSvg(); blocks[i].initSvg();
@ -546,12 +554,15 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) {
// Allow the scrollbars to resize and move based on the new contents. // Allow the scrollbars to resize and move based on the new contents.
// TODO(@picklesrus): #387. Remove when domToBlock avoids resizing. // TODO(@picklesrus): #387. Remove when domToBlock avoids resizing.
workspace.resizeContents(); workspace.resizeContents();
} else {
for (var i = blocks.length - 1; i >= 0; i--) {
blocks[i].initModel();
}
} }
} finally { } finally {
Blockly.Events.enable(); Blockly.Events.enable();
} }
if (Blockly.Events.isEnabled()) { if (Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.BlockCreate(topBlock));
var newVariables = Blockly.Variables.getAddedVariables(workspace, var newVariables = Blockly.Variables.getAddedVariables(workspace,
variablesBeforeCreation); variablesBeforeCreation);
// Fire a VarCreate event for each (if any) new variable created. // Fire a VarCreate event for each (if any) new variable created.
@ -559,6 +570,9 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) {
var thisVariable = newVariables[i]; var thisVariable = newVariables[i];
Blockly.Events.fire(new Blockly.Events.VarCreate(thisVariable)); Blockly.Events.fire(new Blockly.Events.VarCreate(thisVariable));
} }
// Block events come after var events, in case they refer to newly created
// variables.
Blockly.Events.fire(new Blockly.Events.BlockCreate(topBlock));
} }
return topBlock; return topBlock;
}; };

View file

@ -102,13 +102,25 @@ Blockly.Dart.init = function(workspace) {
Blockly.Dart.variableDB_.reset(); Blockly.Dart.variableDB_.reset();
} }
Blockly.Dart.variableDB_.setVariableMap(workspace.getVariableMap());
var defvars = []; var defvars = [];
// Add user variables.
var variables = workspace.getAllVariables(); var variables = workspace.getAllVariables();
if (variables.length) { for (var i = 0; i < variables.length; i++) {
for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].getId(),
defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].name, Blockly.Variables.NAME_TYPE);
Blockly.Variables.NAME_TYPE); }
}
// Add developer variables (not created or named by the user).
var devVarList = Blockly.Variables.allDeveloperVariables(workspace);
for (var i = 0; i < devVarList.length; i++) {
defvars.push(Blockly.Dart.variableDB_.getName(devVarList[i],
Blockly.Names.DEVELOPER_VARIABLE_TYPE));
}
// Declare all of the variables.
if (defvars.length) {
Blockly.Dart.definitions_['variables'] = Blockly.Dart.definitions_['variables'] =
'var ' + defvars.join(', ') + ';'; 'var ' + defvars.join(', ') + ';';
} }

View file

@ -152,13 +152,25 @@ Blockly.JavaScript.init = function(workspace) {
Blockly.JavaScript.variableDB_.reset(); Blockly.JavaScript.variableDB_.reset();
} }
Blockly.JavaScript.variableDB_.setVariableMap(workspace.getVariableMap());
var defvars = []; var defvars = [];
// Add user variables.
var variables = workspace.getAllVariables(); var variables = workspace.getAllVariables();
if (variables.length) { for (var i = 0; i < variables.length; i++) {
for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].getId(),
defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].name, Blockly.Variables.NAME_TYPE);
Blockly.Variables.NAME_TYPE); }
}
// Add developer variables (not created or named by the user).
var devVarList = Blockly.Variables.allDeveloperVariables(workspace);
for (var i = 0; i < devVarList.length; i++) {
defvars.push(Blockly.JavaScript.variableDB_.getName(devVarList[i],
Blockly.Names.DEVELOPER_VARIABLE_TYPE));
}
// Declare all of the variables.
if (defvars.length) {
Blockly.JavaScript.definitions_['variables'] = Blockly.JavaScript.definitions_['variables'] =
'var ' + defvars.join(', ') + ';'; 'var ' + defvars.join(', ') + ';';
} }

View file

@ -109,6 +109,7 @@ Blockly.Lua.init = function(workspace) {
} else { } else {
Blockly.Lua.variableDB_.reset(); Blockly.Lua.variableDB_.reset();
} }
Blockly.Lua.variableDB_.setVariableMap(workspace.getVariableMap());
}; };
/** /**

View file

@ -149,12 +149,23 @@ Blockly.PHP.init = function(workspace) {
Blockly.PHP.variableDB_.reset(); Blockly.PHP.variableDB_.reset();
} }
Blockly.PHP.variableDB_.setVariableMap(workspace.getVariableMap());
var defvars = []; var defvars = [];
var variables = Blockly.Variables.allVariables(workspace); var variables = workspace.getAllVariables();
for (var i = 0; i < variables.length; i++) { for (var i = 0, variable; variable = variables[i]; i++) {
defvars[i] = Blockly.PHP.variableDB_.getName(variables[i], defvars[i] = Blockly.PHP.variableDB_.getName(variable.getId(),
Blockly.Variables.NAME_TYPE) + ';'; Blockly.Variables.NAME_TYPE) + ';';
} }
// Add developer variables (not created or named by the user).
var devVarList = Blockly.Variables.allDeveloperVariables(workspace);
for (var i = 0; i < devVarList.length; i++) {
defvars.push(Blockly.PHP.variableDB_.getName(devVarList[i],
Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ';');
}
// Declare all of the variables.
Blockly.PHP.definitions_['variables'] = defvars.join('\n'); Blockly.PHP.definitions_['variables'] = defvars.join('\n');
}; };

View file

@ -160,12 +160,21 @@ Blockly.Python.init = function(workspace) {
Blockly.Python.variableDB_.reset(); Blockly.Python.variableDB_.reset();
} }
Blockly.Python.variableDB_.setVariableMap(workspace.getVariableMap());
var defvars = []; var defvars = [];
var variables = workspace.getAllVariables(); var variables = workspace.getAllVariables();
for (var i = 0; i < variables.length; i++) { for (var i = 0; i < variables.length; i++) {
defvars[i] = Blockly.Python.variableDB_.getName(variables[i].name, defvars[i] = Blockly.Python.variableDB_.getName(variables[i].getId(),
Blockly.Variables.NAME_TYPE) + ' = None'; Blockly.Variables.NAME_TYPE) + ' = None';
} }
// Add developer variables (not created or named by the user).
var devVarList = Blockly.Variables.allDeveloperVariables(workspace);
for (var i = 0; i < devVarList.length; i++) {
defvars.push(Blockly.Python.variableDB_.getName(devVarList[i],
Blockly.Names.DEVELOPER_VARIABLE_TYPE) + ' = None');
}
Blockly.Python.definitions_['variables'] = defvars.join('\n'); Blockly.Python.definitions_['variables'] = defvars.join('\n');
}; };

View file

@ -29,6 +29,15 @@ goog.require('goog.testing.MockControl');
var mockControl_; var mockControl_;
var workspace; var workspace;
var savedFireFunc = Blockly.Events.fire;
function temporary_fireEvent(event) {
if (!Blockly.Events.isEnabled()) {
return;
}
Blockly.Events.FIRE_QUEUE_.push(event);
Blockly.Events.fireNow_();
}
function eventTest_setUp() { function eventTest_setUp() {
workspace = new Blockly.Workspace(); workspace = new Blockly.Workspace();
@ -37,6 +46,7 @@ function eventTest_setUp() {
function eventTest_setUpWithMockBlocks() { function eventTest_setUpWithMockBlocks() {
eventTest_setUp(); eventTest_setUp();
// TODO: Replace with defineGetVarBlock();
Blockly.defineBlocksWithJsonArray([{ Blockly.defineBlocksWithJsonArray([{
'type': 'field_variable_test_block', 'type': 'field_variable_test_block',
'message0': '%1', 'message0': '%1',
@ -47,10 +57,16 @@ function eventTest_setUpWithMockBlocks() {
'variable': 'item' 'variable': 'item'
} }
], ],
},
{
'type': 'simple_test_block',
'message0': 'simple test block'
}]); }]);
} }
function eventTest_tearDown() { function eventTest_tearDown() {
delete Blockly.Blocks['field_variable_test_block'];
delete Blockly.Blocks['simple_test_block'];
mockControl_.$tearDown(); mockControl_.$tearDown();
workspace.dispose(); workspace.dispose();
} }
@ -63,34 +79,47 @@ function eventTest_tearDownWithMockBlocks() {
function test_abstract_constructor_block() { function test_abstract_constructor_block() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1'); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1');
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.Abstract(block); var block = createSimpleTestBlock(workspace);
assertUndefined(event.varId);
checkExactEventValues(event, {'blockId': '1', 'workspaceId': workspace.id, // Here's the event we care about.
'group': '', 'recordUndo': true}); var event = new Blockly.Events.Abstract(block);
eventTest_tearDownWithMockBlocks(); assertUndefined(event.varId);
checkExactEventValues(event, {'blockId': '1', 'workspaceId': workspace.id,
'group': '', 'recordUndo': true});
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_abstract_constructor_variable() { function test_abstract_constructor_variable() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1'); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, '1');
var variable = workspace.createVariable('name1', 'type1', 'id1'); try {
var event = new Blockly.Events.Abstract(variable); var variable = workspace.createVariable('name1', 'type1', 'id1');
assertUndefined(event.blockId);
checkExactEventValues(event, {'varId': 'id1', var event = new Blockly.Events.Abstract(variable);
'workspaceId': workspace.id, 'group': '', 'recordUndo': true}); assertUndefined(event.blockId);
eventTest_tearDownWithMockBlocks(); checkExactEventValues(event, {'varId': 'id1',
'workspaceId': workspace.id, 'group': '', 'recordUndo': true});
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_abstract_constructor_null() { function test_abstract_constructor_null() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
var event = new Blockly.Events.Abstract(null); try {
assertUndefined(event.blockId); var event = new Blockly.Events.Abstract(null);
assertUndefined(event.workspaceId); assertUndefined(event.blockId);
checkExactEventValues(event, {'group': '', 'recordUndo': true}); assertUndefined(event.workspaceId);
eventTest_tearDownWithMockBlocks(); checkExactEventValues(event, {'group': '', 'recordUndo': true});
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
// Test util
function checkCreateEventValues(event, block, ids, type) { function checkCreateEventValues(event, block, ids, type) {
var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block));
var result_xml = Blockly.Xml.domToText(event.xml); var result_xml = Blockly.Xml.domToText(event.xml);
@ -99,6 +128,7 @@ function checkCreateEventValues(event, block, ids, type) {
assertEquals(type, event.type); assertEquals(type, event.type);
} }
// Test util
function checkDeleteEventValues(event, block, ids, type) { function checkDeleteEventValues(event, block, ids, type) {
var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block));
var result_xml = Blockly.Xml.domToText(event.oldXml); var result_xml = Blockly.Xml.domToText(event.oldXml);
@ -107,6 +137,7 @@ function checkDeleteEventValues(event, block, ids, type) {
assertEquals(type, event.type); assertEquals(type, event.type);
} }
// Test util
function checkExactEventValues(event, values) { function checkExactEventValues(event, values) {
var keys = Object.keys(values); var keys = Object.keys(values);
for (var i = 0, field; field = keys[i]; i++) { for (var i = 0, field; field = keys[i]; i++) {
@ -114,155 +145,212 @@ function checkExactEventValues(event, values) {
} }
} }
// Test util
function createSimpleTestBlock(workspace) {
// Disable events while constructing the block: this is a test of the
// Blockly.Event constructors, not the block constructor.
Blockly.Events.disable();
var block = new Blockly.Block(workspace, 'simple_test_block');
Blockly.Events.enable();
return block;
}
function test_create_constructor() { function test_create_constructor() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.Create(block); var block = createSimpleTestBlock(workspace);
checkCreateEventValues(event, block, ['1'], 'create');
eventTest_tearDownWithMockBlocks(); var event = new Blockly.Events.Create(block);
checkCreateEventValues(event, block, ['1'], 'create');
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_blockCreate_constructor() { function test_blockCreate_constructor() {
// expect that blockCreate behaves the same as create. // expect that blockCreate behaves the same as create.
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.BlockCreate(block); var block = createSimpleTestBlock(workspace);
checkCreateEventValues(event, block, ['1'], 'create');
eventTest_tearDownWithMockBlocks(); var event = new Blockly.Events.BlockCreate(block);
checkCreateEventValues(event, block, ['1'], 'create');
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_delete_constructor() { function test_delete_constructor() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.Delete(block); var block = createSimpleTestBlock(workspace);
checkDeleteEventValues(event, block, ['1'], 'delete'); var event = new Blockly.Events.Delete(block);
eventTest_tearDownWithMockBlocks(); checkDeleteEventValues(event, block, ['1'], 'delete');
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_blockDelete_constructor() { function test_blockDelete_constructor() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.BlockDelete(block); var block = createSimpleTestBlock(workspace);
checkDeleteEventValues(event, block, ['1'], 'delete'); var event = new Blockly.Events.BlockDelete(block);
eventTest_tearDownWithMockBlocks(); checkDeleteEventValues(event, block, ['1'], 'delete');
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_change_constructor() { function test_change_constructor() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.Change(block, 'field', 'VAR', 'item', 'item2'); Blockly.Events.disable();
checkExactEventValues(event, {'element': 'field', 'name': 'VAR', var block = new Blockly.Block(workspace, 'field_variable_test_block');
'oldValue': 'item', 'newValue': 'item2', 'type': 'change'}); Blockly.Events.enable();
eventTest_tearDownWithMockBlocks();
var event = new Blockly.Events.Change(block, 'field', 'VAR', 'id1', 'id2');
checkExactEventValues(event, {'element': 'field', 'name': 'VAR',
'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'});
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_blockChange_constructor() { function test_blockChange_constructor() {
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1']);
var block = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var event = new Blockly.Events.BlockChange(block, 'field', 'VAR', 'item', Blockly.Events.disable();
'item2'); var block = new Blockly.Block(workspace, 'field_variable_test_block');
checkExactEventValues(event, {'element': 'field', 'name': 'VAR', Blockly.Events.enable();
'oldValue': 'item', 'newValue': 'item2', 'type': 'change'});
eventTest_tearDownWithMockBlocks(); var event = new Blockly.Events.BlockChange(block, 'field', 'VAR', 'id1',
'id2');
checkExactEventValues(event, {'element': 'field', 'name': 'VAR',
'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'});
} finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_move_constructorCoordinate() { function test_move_constructorCoordinate() {
// Expect the oldCoordinate to be set. // Expect the oldCoordinate to be set.
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
var block1 = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var coordinate = new goog.math.Coordinate(3,4); var block1 = createSimpleTestBlock(workspace);
block1.xy_ = coordinate; var coordinate = new goog.math.Coordinate(3,4);
block1.xy_ = coordinate;
var event = new Blockly.Events.Move(block1); var event = new Blockly.Events.Move(block1);
checkExactEventValues(event, {'oldCoordinate': coordinate, checkExactEventValues(event, {'oldCoordinate': coordinate,
'type': 'move'}); 'type': 'move'});
eventTest_tearDownWithMockBlocks(); } finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_move_constructoroldParentId() { function test_move_constructoroldParentId() {
// Expect the oldParentId to be set but not the oldCoordinate to be set. // Expect the oldParentId to be set but not the oldCoordinate to be set.
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
var block1 = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var block2 = new Blockly.Block(workspace, 'field_variable_test_block'); var block1 = createSimpleTestBlock(workspace);
block1.parentBlock_ = block2; var block2 = createSimpleTestBlock(workspace);
block1.xy_ = new goog.math.Coordinate(3,4); block1.parentBlock_ = block2;
block1.xy_ = new goog.math.Coordinate(3,4);
var event = new Blockly.Events.Move(block1); var event = new Blockly.Events.Move(block1);
checkExactEventValues(event, {'oldCoordinate': undefined, checkExactEventValues(event, {'oldCoordinate': undefined,
'oldParentId': '2', 'type': 'move'}); 'oldParentId': '2', 'type': 'move'});
block1.parentBlock_ = null; block1.parentBlock_ = null;
eventTest_tearDownWithMockBlocks(); } finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_blockMove_constructorCoordinate() { function test_blockMove_constructorCoordinate() {
// Expect the oldCoordinate to be set. // Expect the oldCoordinate to be set.
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
var block1 = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var coordinate = new goog.math.Coordinate(3,4); var block1 = createSimpleTestBlock(workspace);
block1.xy_ = coordinate; var coordinate = new goog.math.Coordinate(3,4);
block1.xy_ = coordinate;
var event = new Blockly.Events.BlockMove(block1); var event = new Blockly.Events.BlockMove(block1);
checkExactEventValues(event, {'oldCoordinate': coordinate, checkExactEventValues(event, {'oldCoordinate': coordinate,
'type': 'move'}); 'type': 'move'});
eventTest_tearDownWithMockBlocks(); } finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_blockMove_constructoroldParentId() { function test_blockMove_constructoroldParentId() {
// Expect the oldParentId to be set but not the oldCoordinate to be set. // Expect the oldParentId to be set but not the oldCoordinate to be set.
eventTest_setUpWithMockBlocks(); eventTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '2']);
var block1 = new Blockly.Block(workspace, 'field_variable_test_block'); try {
var block2 = new Blockly.Block(workspace, 'field_variable_test_block'); var block1 = createSimpleTestBlock(workspace);
block1.parentBlock_ = block2; var block2 = createSimpleTestBlock(workspace);
block1.xy_ = new goog.math.Coordinate(3,4); block1.parentBlock_ = block2;
block1.xy_ = new goog.math.Coordinate(3,4);
var event = new Blockly.Events.BlockMove(block1); var event = new Blockly.Events.BlockMove(block1);
checkExactEventValues(event, {'oldCoordinate': undefined, checkExactEventValues(event, {'oldCoordinate': undefined,
'oldParentId': '2', 'type': 'move'}); 'oldParentId': '2', 'type': 'move'});
block1.parentBlock_ = null; block1.parentBlock_ = null;
eventTest_tearDownWithMockBlocks(); } finally {
eventTest_tearDownWithMockBlocks();
}
} }
function test_varCreate_constructor() { function test_varCreate_constructor() {
eventTest_setUp(); eventTest_setUp();
var variable = workspace.createVariable('name1', 'type1', 'id1'); try {
var event = new Blockly.Events.VarCreate(variable); var variable = workspace.createVariable('name1', 'type1', 'id1');
checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1', var event = new Blockly.Events.VarCreate(variable);
'type': 'var_create'}); checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1',
eventTest_tearDown(); 'type': 'var_create'});
} finally {
eventTest_tearDown();
}
} }
function test_varCreate_toJson() { function test_varCreate_toJson() {
eventTest_setUp(); eventTest_setUp();
var variable = workspace.createVariable('name1', 'type1', 'id1'); try {
var event = new Blockly.Events.VarCreate(variable); var variable = workspace.createVariable('name1', 'type1', 'id1');
var json = event.toJson(); var event = new Blockly.Events.VarCreate(variable);
var expectedJson = ({type: "var_create", varId: "id1", varType: "type1", var json = event.toJson();
varName: "name1"}); var expectedJson = ({type: "var_create", varId: "id1", varType: "type1",
varName: "name1"});
assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); assertEquals(JSON.stringify(expectedJson), JSON.stringify(json));
eventTest_tearDown(); } finally {
eventTest_tearDown();
}
} }
function test_varCreate_fromJson() { function test_varCreate_fromJson() {
eventTest_setUp(); eventTest_setUp();
var variable = workspace.createVariable('name1', 'type1', 'id1'); try {
var event = new Blockly.Events.VarCreate(variable); var variable = workspace.createVariable('name1', 'type1', 'id1');
var event2 = new Blockly.Events.VarCreate(null); var event = new Blockly.Events.VarCreate(variable);
var json = event.toJson(); var event2 = new Blockly.Events.VarCreate(null);
event2.fromJson(json); var json = event.toJson();
event2.fromJson(json);
assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson()));
eventTest_tearDown(); } finally {
eventTest_tearDown();
}
} }
function test_varCreate_runForward() { function test_varCreate_runForward() {

View file

@ -140,14 +140,13 @@ function test_deleteVariable_InternalTrivial() {
function test_addTopBlock_TrivialFlyoutIsTrue() { function test_addTopBlock_TrivialFlyoutIsTrue() {
workspaceTest_setUp(); workspaceTest_setUp();
workspace.isFlyout = true;
var targetWorkspace = new Blockly.Workspace(); var targetWorkspace = new Blockly.Workspace();
workspace.isFlyout = true;
workspace.targetWorkspace = targetWorkspace; workspace.targetWorkspace = targetWorkspace;
targetWorkspace.createVariable('name1', '', '1'); targetWorkspace.createVariable('name1', '', '1');
// Flyout.init usually does this binding. // Flyout.init usually does this binding.
workspace.getVariableById = workspace.variableMap_ = targetWorkspace.getVariableMap();
targetWorkspace.getVariableById.bind(targetWorkspace);
try { try {
var block = createMockBlock('1'); var block = createMockBlock('1');
@ -155,8 +154,11 @@ function test_addTopBlock_TrivialFlyoutIsTrue() {
workspace.addTopBlock(block); workspace.addTopBlock(block);
checkVariableValues(workspace, 'name1', '', '1'); checkVariableValues(workspace, 'name1', '', '1');
} finally { } finally {
targetWorkspace.dispose();
workspaceTest_tearDown(); workspaceTest_tearDown();
// Have to dispose of the main workspace after the flyout workspace, because
// it holds the variable map.
// Normally the main workspace disposes of the flyout workspace.
targetWorkspace.dispose();
} }
} }

View file

@ -315,13 +315,20 @@ function test_blockToDom_fieldToDom_trivial() {
function test_blockToDom_fieldToDom_defaultCase() { function test_blockToDom_fieldToDom_defaultCase() {
xmlTest_setUpWithMockBlocks(); xmlTest_setUpWithMockBlocks();
setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '1']); setUpMockMethod(mockControl_, Blockly.utils, 'genUid', null, ['1', '1']);
workspace.createVariable('name1'); try {
var block = new Blockly.Block(workspace, 'field_variable_test_block'); workspace.createVariable('name1');
block.inputList[0].fieldRow[0].setValue('1');
var resultFieldDom = Blockly.Xml.blockToDom(block).childNodes[0]; Blockly.Events.disable();
// Expect type is '' and id is '1' since we don't specify type and id. var block = new Blockly.Block(workspace, 'field_variable_test_block');
xmlTest_checkVariableFieldDomValues(resultFieldDom, 'VAR', '', '1', 'name1'); block.inputList[0].fieldRow[0].setValue('1');
xmlTest_tearDownWithMockBlocks(); Blockly.Events.enable();
var resultFieldDom = Blockly.Xml.blockToDom(block).childNodes[0];
// Expect type is '' and id is '1' since we don't specify type and id.
xmlTest_checkVariableFieldDomValues(resultFieldDom, 'VAR', '', '1', 'name1');
} finally {
xmlTest_tearDownWithMockBlocks();
}
} }
function test_blockToDom_fieldToDom_notAFieldVariable() { function test_blockToDom_fieldToDom_notAFieldVariable() {
@ -363,8 +370,12 @@ function test_variablesToDom_twoVariables_oneBlock() {
workspace.createVariable('name1', '', 'id1'); workspace.createVariable('name1', '', 'id1');
workspace.createVariable('name2', 'type2', 'id2'); workspace.createVariable('name2', 'type2', 'id2');
// If events are enabled during block construction, it will create a default
// variable.
Blockly.Events.disable();
var block = new Blockly.Block(workspace, 'field_variable_test_block'); var block = new Blockly.Block(workspace, 'field_variable_test_block');
block.inputList[0].fieldRow[0].setValue('id1'); block.inputList[0].fieldRow[0].setValue('id1');
Blockly.Events.enable();
var resultDom = Blockly.Xml.variablesToDom(workspace.getAllVariables()); var resultDom = Blockly.Xml.variablesToDom(workspace.getAllVariables());
assertEquals(2, resultDom.children.length); assertEquals(2, resultDom.children.length);