mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
Break out variable validation into separate functions by type. Other refactoring/cleanup, especially w/regards to trimming names of user-provided input). Variables/Lists/Messages now all have separate alert messages and modal titles by type for both creation and renaming of a variable.
This commit is contained in:
parent
8a86ac6953
commit
20d1c1f028
2 changed files with 207 additions and 92 deletions
|
@ -353,6 +353,16 @@ Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE = 'broadcast_msg';
|
|||
*/
|
||||
Blockly.LIST_VARIABLE_TYPE = 'list';
|
||||
|
||||
// TODO (#1251) Replace '' below with 'scalar', and start using this constant
|
||||
// everywhere.
|
||||
/**
|
||||
* String representing the variable type of scalar variables.
|
||||
* This string, for use in differentiating between types of variables,
|
||||
* indicates that the current variable is a scalar variable.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.SCALAR_VARIABLE_TYPE = '';
|
||||
|
||||
/**
|
||||
* The type of all procedure definition blocks.
|
||||
* @const {string}
|
||||
|
|
|
@ -259,62 +259,57 @@ Blockly.Variables.realizePotentialVar = function(varName, varType, potentialVarW
|
|||
* Create a new variable on the given workspace.
|
||||
* @param {!Blockly.Workspace} workspace The workspace on which to create the
|
||||
* variable.
|
||||
* @param {function(?string=)=} opt_callback A callback. It will
|
||||
* be passed the id of the variable to be created, or null if change is to be
|
||||
* aborted (cancel button), or undefined if an existing variable was chosen.
|
||||
* @param {string} opt_type Optional type of variable, like 'string' or 'list'.
|
||||
* @param {function(?string=)=} opt_callback An optional callback function to act
|
||||
* on the id of the variable that is created from the user's input, or null
|
||||
* if the change is to be aborted (cancel button or an invalid name was provided).
|
||||
* @param {string} opt_type Optional type of the variable to be created,
|
||||
* like 'string' or 'list'.
|
||||
*/
|
||||
Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) {
|
||||
// Decide on a modal message based on the opt_type. If opt_type was not
|
||||
// provided, default to the original message for scalar variables.
|
||||
var newMsg = '';
|
||||
var modalTitle = '';
|
||||
if (opt_type === Blockly.LIST_VARIABLE_TYPE) {
|
||||
newMsg = Blockly.Msg.NEW_LIST_TITLE;
|
||||
modalTitle = Blockly.Msg.LIST_MODAL_TITLE;
|
||||
|
||||
} else if (opt_type === Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) {
|
||||
var newMsg, modalTitle;
|
||||
opt_type = opt_type || '';
|
||||
if (opt_type === Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) {
|
||||
newMsg = Blockly.Msg.NEW_BROADCAST_MESSAGE_TITLE;
|
||||
modalTitle = Blockly.Msg.BROADCAST_MODAL_TITLE;
|
||||
} else if (opt_type === Blockly.LIST_VARIABLE_TYPE) {
|
||||
newMsg = Blockly.Msg.NEW_LIST_TITLE;
|
||||
modalTitle = Blockly.Msg.LIST_MODAL_TITLE;
|
||||
} else {
|
||||
newMsg = Blockly.Msg.NEW_VARIABLE_TITLE;
|
||||
modalTitle = Blockly.Msg.VARIABLE_MODAL_TITLE;
|
||||
}
|
||||
var validate = Blockly.Variables.nameValidator_.bind(null, opt_type);
|
||||
// This function needs to be named so it can be called recursively.
|
||||
var promptAndCheckWithAlert = function(defaultName) {
|
||||
Blockly.Variables.promptName(newMsg, defaultName,
|
||||
Blockly.prompt(newMsg, defaultName,
|
||||
function(text) {
|
||||
if (text) {
|
||||
if (workspace.getVariable(text, opt_type)) {
|
||||
Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
|
||||
text),
|
||||
function() {
|
||||
promptAndCheckWithAlert(text); // Recurse
|
||||
});
|
||||
} else {
|
||||
var potentialVarMap = workspace.getPotentialVariableMap();
|
||||
var variable;
|
||||
// This check ensures that if a new variable is being created from a
|
||||
// workspace that already has a variable of the same name and type as
|
||||
// a potential variable, that potential variable gets turned into a
|
||||
// real variable and thus there aren't duplicate options in the field_variable
|
||||
// dropdown.
|
||||
if (potentialVarMap && opt_type) {
|
||||
variable = Blockly.Variables.realizePotentialVar(text, opt_type, workspace, false);
|
||||
}
|
||||
if (!variable) {
|
||||
variable = workspace.createVariable(text, opt_type);
|
||||
}
|
||||
var validatedText = validate(text, workspace, promptAndCheckWithAlert, opt_callback);
|
||||
if (validatedText) {
|
||||
// The name is valid according to the type, create the variable
|
||||
var potentialVarMap = workspace.getPotentialVariableMap();
|
||||
var variable;
|
||||
// This check ensures that if a new variable is being created from a
|
||||
// workspace that already has a variable of the same name and type as
|
||||
// a potential variable, that potential variable gets turned into a
|
||||
// real variable and thus there aren't duplicate options in the field_variable
|
||||
// dropdown.
|
||||
if (potentialVarMap && opt_type) {
|
||||
variable = Blockly.Variables.realizePotentialVar(validatedText, opt_type, workspace, false);
|
||||
}
|
||||
if (!variable) {
|
||||
variable = workspace.createVariable(validatedText, opt_type);
|
||||
}
|
||||
|
||||
var flyout = workspace.isFlyout ? workspace : workspace.getFlyout();
|
||||
var variableBlockId = variable.getId();
|
||||
if (flyout.setCheckboxState) {
|
||||
flyout.setCheckboxState(variableBlockId, true);
|
||||
}
|
||||
var flyout = workspace.isFlyout ? workspace : workspace.getFlyout();
|
||||
var variableBlockId = variable.getId();
|
||||
if (flyout.setCheckboxState) {
|
||||
flyout.setCheckboxState(variableBlockId, true);
|
||||
}
|
||||
|
||||
if (opt_callback) {
|
||||
opt_callback(variableBlockId);
|
||||
}
|
||||
if (opt_callback) {
|
||||
opt_callback(variableBlockId);
|
||||
}
|
||||
} else {
|
||||
// User canceled prompt without a value.
|
||||
|
@ -322,11 +317,136 @@ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) {
|
|||
opt_callback(null);
|
||||
}
|
||||
}
|
||||
}, opt_type, modalTitle);
|
||||
}, modalTitle);
|
||||
};
|
||||
promptAndCheckWithAlert('');
|
||||
};
|
||||
|
||||
/**
|
||||
* This function provides a common interface for variable name validation agnostic
|
||||
* of type. This is so that functions like Blockly.Variables.createVariable and
|
||||
* Blockly.Variables.renameVariable can call a single function (with a single type
|
||||
* type signature) to validate the user-provided name for the variable.
|
||||
* @param {string} type The type of the variable for which the provided name
|
||||
* should be validated.
|
||||
* @param {string} text The user-provided text that should be validated as a
|
||||
* variable name.
|
||||
* @param {!Blockly.Workspace} workspace The workspace on which to validate the
|
||||
* variable name. This is the workspace used to check whether the variable
|
||||
* already exists.
|
||||
* @param {function(!string=)=} retry A function to handle re-prompting the user
|
||||
* if the name they supplied is invalid.
|
||||
* @param {function(?string=)=} opt_callback An optional function to be called on
|
||||
* a pre-existing variable of the user-provided name.
|
||||
* @return {string} The result of validating the given text according to the given
|
||||
* type. This will be the validated name if it is determined to be valid (or
|
||||
* possibly after the user has been prompted for a retry), or null if the name
|
||||
* is determined to be invalid/in-use, and the calling function should not
|
||||
* proceed with creating or renaming the variable.
|
||||
*/
|
||||
Blockly.Variables.nameValidator_ = function(type, text, workspace, retry, opt_callback) {
|
||||
// The validators for the different variable types require slightly different arguments.
|
||||
// For broadcast messages, if a broadcast message of the provided name already exists,
|
||||
// the validator needs to call a function that updates the selected
|
||||
// field option of the dropdown menu of the block that was used to create the new message.
|
||||
// For scalar variables and lists, the validator needs a retry function that is responsible
|
||||
// for popping up an alert and prompting the user for a new name.
|
||||
|
||||
if (type == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) {
|
||||
return Blockly.Variables.validateBroadcastMessageName_(text, workspace, opt_callback);
|
||||
} else if (type == Blockly.LIST_VARIABLE_TYPE) {
|
||||
return Blockly.Variables.validateListName_(text, workspace, retry);
|
||||
} else {
|
||||
return Blockly.Variables.validateScalarVariableName_(text, workspace, retry);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the given name as a broadcast message type.
|
||||
* @param {string} name The name to validate
|
||||
* @param {!Blockly.Workspace} workspace The workspace the name should be validated
|
||||
* against.
|
||||
* @param {function(?string=)=} opt_callback An optional function to call if a broadcast
|
||||
* message already exists with the given name. This function will be called on the id
|
||||
* of the existing variable.
|
||||
* @return {string} The validated name, or null if invalid.
|
||||
*/
|
||||
Blockly.Variables.validateBroadcastMessageName_ = function(name, workspace, opt_callback) {
|
||||
if (!name) {
|
||||
// If there was no name provided (e.g. the user hit enter w/an empty string, or
|
||||
// used the cancel button to exit the prompt)
|
||||
return null;
|
||||
}
|
||||
var variable = workspace.getVariable(name, Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE);
|
||||
if (variable) {
|
||||
// If the user provided a name for a broadcast message that already exists,
|
||||
// the only action should be to use the provided callback function to update
|
||||
// the selected option in the field of the block that was used to create
|
||||
// this message.
|
||||
if (opt_callback) {
|
||||
opt_callback(variable.getId());
|
||||
}
|
||||
// Return null to signal to the calling function that we do not want to create
|
||||
// a new variable.
|
||||
return null;
|
||||
} else {
|
||||
// The name provided is actually a new name, so the calling
|
||||
// function should go ahead and create it as a new variable.
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the given name as a scalar variable type.
|
||||
* This function is also responsible for any user facing error-handling/retry.
|
||||
* @param {string} name The name to validate
|
||||
* @param {!Blockly.Workspace} workspace The workspace the name should be validated
|
||||
* against.
|
||||
* @param {function(?string=)=} retry A function to handle an invalid name.
|
||||
* @param {function(?string=)=} opt_callback An optional function to call on the existing
|
||||
* variable if a scalar variable of the validated name already exists.
|
||||
* @return {string} The validated name, or null if invalid.
|
||||
*/
|
||||
Blockly.Variables.validateScalarVariableName_ = function(name, workspace, retry) {
|
||||
// For scalar variables, we don't want leading or trailing white space
|
||||
name = Blockly.Variables.trimName_(name);
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
if (!workspace.getVariable(name, Blockly.SCALAR_VARIABLE_TYPE)) {
|
||||
return name;
|
||||
} else {
|
||||
// error & retry
|
||||
Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1', name),
|
||||
function() {retry(name);});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the given name as a list type.
|
||||
* This function is also responsible for any user facing error-handling/retry.
|
||||
* @param {string} name The name to validate
|
||||
* @param {!Blockly.Workspace} workspace The workspace the name should be validated
|
||||
* against.
|
||||
* @param {function(?string=)=} retry The function to call if a list
|
||||
* of the provided, trimmed name already exists.
|
||||
* @return {string} The validated name or null if invalid or if user cancelled.
|
||||
*/
|
||||
Blockly.Variables.validateListName_ = function(name, workspace, retry) {
|
||||
// For lists, we don't want leading or trailing white space
|
||||
name = Blockly.Variables.trimName_(name);
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
if (!workspace.getVariable(name, Blockly.LIST_VARIABLE_TYPE)) {
|
||||
return name;
|
||||
} else {
|
||||
// error & retry
|
||||
Blockly.alert(Blockly.Msg.LIST_ALREADY_EXISTS.replace('%1', name),
|
||||
function() {retry(name);});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a variable with the given workspace, variableType, and oldName.
|
||||
* @param {!Blockly.Workspace} workspace The workspace on which to rename the
|
||||
|
@ -337,25 +457,35 @@ Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) {
|
|||
* aborted (cancel button), or undefined if an existing variable was chosen.
|
||||
*/
|
||||
Blockly.Variables.renameVariable = function(workspace, variable,
|
||||
opt_callback) {
|
||||
// (karishma) TODO (#1244) Modal message should change depending on what type
|
||||
// of variable is getting renamed.
|
||||
opt_callback) {
|
||||
// Validation and modal message/title depends on the variable type
|
||||
var promptMsg, modalTitle, validate;
|
||||
var varType = variable.type;
|
||||
if (varType == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) {
|
||||
// Only lists and variables can be renamed... this is an error
|
||||
console.warn('Encountered unexpected variable type ' + varType +
|
||||
'when attempting to rename a variable.');
|
||||
return;
|
||||
}
|
||||
if (varType == Blockly.LIST_VARIABLE_TYPE) {
|
||||
promptMsg = Blockly.Msg.RENAME_LIST_TITLE;
|
||||
modalTitle = Blockly.Msg.RENAME_LIST_MODAL_TITLE;
|
||||
validate = Blockly.Variables.validateListName_;
|
||||
} else { // Default to everthing for scalar variables
|
||||
promptMsg = Blockly.Msg.RENAME_VARIABLE_TITLE;
|
||||
modalTitle = Blockly.Msg.RENAME_VARIABLE_MODAL_TITLE;
|
||||
validate = Blockly.Variables.validateScalarVariableName_;
|
||||
}
|
||||
|
||||
// This function needs to be named so it can be called recursively.
|
||||
var promptAndCheckWithAlert = function(defaultName) {
|
||||
var promptText =
|
||||
Blockly.Msg.RENAME_VARIABLE_TITLE.replace('%1', variable.name);
|
||||
Blockly.Variables.promptName(promptText, defaultName,
|
||||
promptMsg.replace('%1', variable.name);
|
||||
Blockly.prompt(promptText, defaultName,
|
||||
function(newName) {
|
||||
if (newName) {
|
||||
if (workspace.getVariable(newName, variable.type)) {
|
||||
Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
|
||||
newName),
|
||||
function() {
|
||||
promptAndCheckWithAlert(newName); // Recurse
|
||||
});
|
||||
} else {
|
||||
workspace.renameVariableById(variable.getId(), newName);
|
||||
}
|
||||
var validatedText = validate(newName, workspace, promptAndCheckWithAlert);
|
||||
if (validatedText) {
|
||||
workspace.renameVariableById(variable.getId(), validatedText);
|
||||
if (opt_callback) {
|
||||
opt_callback(newName);
|
||||
}
|
||||
|
@ -365,49 +495,24 @@ Blockly.Variables.renameVariable = function(workspace, variable,
|
|||
opt_callback(null);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
}, modalTitle);
|
||||
};
|
||||
promptAndCheckWithAlert('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Prompt the user for a new variable name.
|
||||
* @param {string} promptText The string of the prompt.
|
||||
* @param {string} defaultText The default value to show in the prompt's field.
|
||||
* @param {function(?string)} callback A callback. It will be passed the new
|
||||
* variable name, or null if the user picked something illegal.
|
||||
* @param {string} opt_type Optional type of variable, like 'string' or 'list'.
|
||||
* @param {string} opt_modal_title Optional title for the prompt.
|
||||
*/
|
||||
Blockly.Variables.promptName = function(promptText, defaultText, callback, opt_type,
|
||||
opt_modal_title) {
|
||||
var modalTitle = opt_modal_title ? opt_modal_title : Blockly.Msg.VARIABLE_MODAL_TITLE;
|
||||
Blockly.prompt(promptText, defaultText, function(newVar) {
|
||||
// Merge runs of whitespace. Strip leading and trailing whitespace.
|
||||
// Beyond this, all names are legal.
|
||||
if (newVar) {
|
||||
newVar = Blockly.Variables.validateName_(newVar, opt_type);
|
||||
}
|
||||
callback(newVar);
|
||||
}, modalTitle);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate the variable name provided by the user.
|
||||
* Strip leading and trailing whitespace from the given name, for use with
|
||||
* user provided name for scalar variables and lists.
|
||||
* @param {string} name The user-provided name of the variable.
|
||||
* @param {string} opt_type Optional type of variable, like 'string' or 'list'.
|
||||
* @return {string} The validated and possibly transformed name of the variable.
|
||||
* @return {string} The trimmed name, or whatever falsey value was originally provided.
|
||||
*/
|
||||
Blockly.Variables.validateName_ = function(name, opt_type) {
|
||||
if (!opt_type || opt_type != Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) {
|
||||
name = name.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, '');
|
||||
if (name == Blockly.Msg.RENAME_VARIABLE ||
|
||||
name == Blockly.Msg.NEW_VARIABLE) {
|
||||
// Ok, not ALL names are legal...
|
||||
name = null;
|
||||
}
|
||||
Blockly.Variables.trimName_ = function(name) {
|
||||
if (name) {
|
||||
return goog.string.trim(name);
|
||||
} else {
|
||||
// Return whatever was provided
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue