iobroker.javascript
Version:
Rules Engine for ioBroker
688 lines (637 loc) • 29.3 kB
JavaScript
if (Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.indexOf('procedures_defcustomreturn') === -1) {
Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('procedures_defcustomreturn');
}
if (Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.indexOf('procedures_defcustomnoreturn') === -1) {
Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('procedures_defcustomnoreturn');
}
// derived from core/procedures.js
/**
* Find all user-created procedure definitions in a workspace.
* @param {!Blockly.Workspace} root Root workspace.
* @return {!Array.<!Array.<!Array>>} Pair of arrays, the
* first contains procedures without return variables, the second with.
* Each procedure is defined by a three-element list of name, parameter
* list, and return value boolean.
*/
Blockly.Procedures.allProceduresNew = function (root) {
const result = Blockly.Procedures.allProcedures(root);
const proceduresCustomNoReturn = root
.getProcedureMap()
.getProcedures()
.filter((p) => !!p.getReturnTypes())
.map((p) => [
p.getName(),
p.getParameters().map((pa) => pa.getName()),
true,
]);
root.getBlocksByType('procedures_defcustomnoreturn', false).forEach((b) => {
if (!Blockly.Procedures.isProcedureBlock(b)) {
proceduresCustomNoReturn.push(b.getProcedureDef());
}
});
const proceduresCustomReturn = root
.getProcedureMap()
.getProcedures()
.filter((p) => !!p.getReturnTypes())
.map((p) => [
p.getName(),
p.getParameters().map((pa) => pa.getName()),
true,
]);
root.getBlocksByType('procedures_defcustomreturn', false).forEach((b) => {
if (!Blockly.Procedures.isProcedureBlock(b)) {
proceduresCustomReturn.push(b.getProcedureDef());
}
});
return result.concat([proceduresCustomNoReturn, proceduresCustomReturn]);
};
/**
* Construct the blocks required by the flyout for the procedure category.
* @param {!Blockly.Workspace} workspace The workspace containing procedures.
* @return {!Array.<!Element>} Array of XML block elements.
*/
Blockly.Procedures.flyoutCategoryNew = function (workspace) {
const xmlList = [];
const utils = (Blockly.Xml.utils ? Blockly.Xml.utils : Blockly.utils.xml);
if (Blockly.Blocks['procedures_defnoreturn']) {
// <block type="procedures_defnoreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
const block = utils.createElement('block');
block.setAttribute('type', 'procedures_defnoreturn');
block.setAttribute('gap', '16');
const nameField = utils.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(
utils.createTextNode(Blockly.Msg['PROCEDURES_DEFNORETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_defreturn']) {
// <block type="procedures_defreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
const block = utils.createElement('block');
block.setAttribute('type', 'procedures_defreturn');
block.setAttribute('gap', '16');
const nameField = utils.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(
utils.createTextNode(Blockly.Msg['PROCEDURES_DEFRETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_ifreturn']) {
// <block type="procedures_ifreturn" gap="16"></block>
const block = utils.createElement('block');
block.setAttribute('type', 'procedures_ifreturn');
block.setAttribute('gap', '16');
xmlList.push(block);
}
if (Blockly.Blocks['procedures_return']) {
// <block type="procedures_ifreturn" gap="16"></block>
const block = utils.createElement('block');
block.setAttribute('type', 'procedures_return');
block.setAttribute('gap', '16');
xmlList.push(block);
}
if (Blockly.Blocks['procedures_defcustomnoreturn']) {
// <block type="procedures_defcustomnoreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
const block = utils.createElement('block');
block.setAttribute('type', 'procedures_defcustomnoreturn');
block.setAttribute('gap', '16');
const nameField = utils.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(utils.createTextNode(
Blockly.Msg['PROCEDURES_DEFNORETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (Blockly.Blocks['procedures_defcustomreturn']) {
// <block type="procedures_defcustomreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
const block = utils.createElement('block');
block.setAttribute('type', 'procedures_defcustomreturn');
block.setAttribute('gap', '16');
const nameField = utils.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(utils.createTextNode(
Blockly.Msg['PROCEDURES_DEFRETURN_PROCEDURE']));
block.appendChild(nameField);
xmlList.push(block);
}
if (xmlList.length) {
// Add slightly larger gap between system blocks and user calls.
xmlList[xmlList.length - 1].setAttribute('gap', '24');
}
/**
* Add items to xmlList for each listed procedure.
*
* @param procedureList A list of procedures, each of which is defined by a
* three-element list of name, parameter list, and return value boolean.
* @param templateName The type of the block to generate.
*/
function populateProcedures(procedureList, templateName) {
const utils = (Blockly.Xml.utils ? Blockly.Xml.utils : Blockly.utils.xml);
for (let i = 0; i < procedureList.length; i++) {
const name = procedureList[i][0];
const args = procedureList[i][1];
// <block type="procedures_callnoreturn" gap="16">
// <mutation name="do something">
// <arg name="x"></arg>
// </mutation>
// </block>
const block = utils.createElement('block');
block.setAttribute('type', templateName);
block.setAttribute('gap', '16');
const mutation = utils.createElement('mutation');
mutation.setAttribute('name', name);
block.appendChild(mutation);
for (let j = 0; j < args.length; j++) {
const arg = utils.createElement('arg');
arg.setAttribute('name', args[j]);
mutation.appendChild(arg);
}
xmlList.push(block);
}
}
const tuple = Blockly.Procedures.allProceduresNew(workspace);
populateProcedures(tuple[0], 'procedures_callnoreturn');
populateProcedures(tuple[1], 'procedures_callreturn');
populateProcedures(tuple[2], 'procedures_callcustomnoreturn');
populateProcedures(tuple[3], 'procedures_callcustomreturn');
return xmlList;
};
// ---------------------- patch for async functions ------------------------------
// taken from javascript/procedures.js https://github.com/google/blockly/blob/blockly-v10.1.3/generators/javascript/procedures.js
Blockly.JavaScript.forBlock['procedures_defreturn'] = function (block) {
// Define a procedure with a return value.
const funcName = Blockly.JavaScript.nameDB_.getName(
block.getFieldValue('NAME'), Blockly.PROCEDURE_CATEGORY_NAME);
let xfix1 = '';
if (Blockly.JavaScript.STATEMENT_PREFIX) {
xfix1 += Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_PREFIX, block);
}
if (Blockly.JavaScript.STATEMENT_SUFFIX) {
xfix1 += Blockly.JavaScript.injectId(Blockly.JavaScript.STATEMENT_SUFFIX, block);
}
if (xfix1) {
xfix1 = Blockly.JavaScript.prefixLines(xfix1, Blockly.JavaScript.INDENT);
}
let loopTrap = '';
if (Blockly.JavaScript.INFINITE_LOOP_TRAP) {
loopTrap = Blockly.JavaScript.prefixLines(
Blockly.JavaScript.injectId(Blockly.JavaScript.INFINITE_LOOP_TRAP, block),
Blockly.JavaScript.INDENT,
);
}
let returnValue = '';
let xfix2 = '';
const branch = Blockly.JavaScript.statementToCode(block, 'STACK');
try {
returnValue = Blockly.JavaScript.valueToCode(block, 'RETURN', Blockly.JavaScript.ORDER_NONE) || '';
if (branch && returnValue) {
// After executing the function body, revisit this block for the return.
xfix2 = xfix1;
}
if (returnValue) {
returnValue = Blockly.JavaScript.INDENT + 'return ' + returnValue + ';\n';
}
} catch (e) {
// This function is without a return value.
}
const args = [];
const variables = block.getVars();
for (let i = 0; i < variables.length; i++) {
args[i] = Blockly.JavaScript.nameDB_.getName(variables[i], Blockly.VARIABLE_CATEGORY_NAME);
}
let code = 'async function ' + funcName + '(' + args.join(', ') + ') {\n' + xfix1 +
loopTrap + branch + xfix2 + returnValue + '}';
code = Blockly.JavaScript.scrub_(block, code);
// Add % so as not to collide with helper functions in the definitions list.
Blockly.JavaScript.definitions_['%' + funcName] = code;
return null;
};
Blockly.JavaScript.forBlock['procedures_defnoreturn'] = Blockly.JavaScript.forBlock['procedures_defreturn'];
Blockly.JavaScript.forBlock['procedures_callreturn'] = function (block) {
// Call a procedure with a return value.
const funcName = Blockly.JavaScript.nameDB_.getName(
block.getFieldValue('NAME'), Blockly.PROCEDURE_CATEGORY_NAME);
const args = [];
const variables = block.getVars();
for (let i = 0; i < variables.length; i++) {
args[i] = Blockly.JavaScript.valueToCode(block, 'ARG' + i, Blockly.JavaScript.ORDER_NONE) || 'null';
}
const code = 'await ' + funcName + '(' + args.join(', ') + ')';
return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
};
// ---------------------- custom function with return ------------------------------
Blockly.Blocks['procedures_defcustomreturn'] = {
getProcedureModel() {
return this.model;
},
isProcedureDef() {
return true;
},
/**
* Block for defining a procedure with a return value.
* @this Blockly.Block
*/
init: function () {
const nameField = new Blockly.FieldTextInput('',
Blockly.Procedures.rename);
nameField.setSpellcheck(false);
this.appendDummyInput()
// .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_TITLE'])
.appendField(Blockly.Translate('procedures_defcustomreturn_name'))
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
/*this.appendValueInput('RETURN')
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']);*/
this.setMutator(new Blockly.icons.MutatorIcon(['procedures_mutatorarg'], this));
if ((this.workspace.options.comments ||
(this.workspace.options.parentWorkspace &&
this.workspace.options.parentWorkspace.options.comments)) &&
Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']) {
this.setCommentText(Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']);
}
this.setStyle('procedure_blocks');
this.setTooltip(Blockly.Msg['PROCEDURES_DEFRETURN_TOOLTIP']);
this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFRETURN_HELPURL']);
this.arguments_ = [];
this.argumentVarModels_ = [];
this.setStatements_(true);
this.statementConnection_ = null;
this.appendDummyInput('SCRIPT')
.appendField(new Blockly.FieldScript(btoa('return 0;')), 'SCRIPT');
this.setInputsInline(true);
this.setStatements_(false);
},
setStatements_: Blockly.Blocks['procedures_defreturn'].setStatements_,
updateParams_: Blockly.Blocks['procedures_defreturn'].updateParams_,
mutationToDom: Blockly.Blocks['procedures_defreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_defreturn'].domToMutation,
/**
* Populate the mutator's dialog with this block's components.
* @param {!Blockly.Workspace} workspace Mutator's workspace.
* @return {!Blockly.Block} Root block in mutator.
* @this Blockly.Block
*/
decompose: function (workspace) {
const containerBlock = workspace.newBlock('procedures_mutatorcontainer');
containerBlock.initSvg();
// Check/uncheck the allow statement box.
/*
if (this.getInput('RETURN')) {
containerBlock.setFieldValue(
this.hasStatements_ ? 'TRUE' : 'FALSE', 'STATEMENTS');
} else {
containerBlock.getInput('STATEMENT_INPUT').setVisible(false);
}
*/
containerBlock.getInput('STATEMENT_INPUT').setVisible(false);
// Parameter list.
let connection = containerBlock.getInput('STACK').connection;
for (let i = 0; i < this.arguments_.length; i++) {
const paramBlock = workspace.newBlock('procedures_mutatorarg');
paramBlock.initSvg();
paramBlock.setFieldValue(this.arguments_[i], 'NAME');
// Store the old location.
paramBlock.oldLocation = i;
connection.connect(paramBlock.previousConnection);
connection = paramBlock.nextConnection;
}
// Initialize procedure's callers with blank IDs.
Blockly.Procedures.mutateCallers(this);
return containerBlock;
},
/**
* Reconfigure this block based on the mutator dialog's components.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this Blockly.Block
*/
compose: function (containerBlock) {
// Parameter list.
this.arguments_ = [];
this.paramIds_ = [];
this.argumentVarModels_ = [];
let paramBlock = containerBlock.getInputTargetBlock('STACK');
while (paramBlock) {
const varName = paramBlock.getFieldValue('NAME');
this.arguments_.push(varName);
const variable = this.workspace.getVariable(varName, '');
if (variable != null) {
this.argumentVarModels_.push(variable);
} else {
console.log('Failed to get variable named ' + varName + ', ignoring.');
}
this.paramIds_.push(paramBlock.id);
paramBlock = paramBlock.nextConnection &&
paramBlock.nextConnection.targetBlock();
}
this.updateParams_();
Blockly.Procedures.mutateCallers(this);
/*
// Show/hide the statement input.
let hasStatements = containerBlock.getFieldValue('STATEMENTS');
if (hasStatements !== null) {
hasStatements = hasStatements == 'TRUE';
if (this.hasStatements_ != hasStatements) {
if (hasStatements) {
this.setStatements_(true);
// Restore the stack, if one was saved.
Blockly.icons.MutatorIcon.reconnect(this.statementConnection_, this, 'STACK');
this.statementConnection_ = null;
} else {
// Save the stack, then disconnect it.
const stackConnection = this.getInput('STACK').connection;
this.statementConnection_ = stackConnection.targetConnection;
if (this.statementConnection_) {
const stackBlock = stackConnection.targetBlock();
stackBlock.unplug();
stackBlock.bumpNeighbours_();
}
this.setStatements_(false);
}
}
}
*/
},
/**
* Return the signature of this procedure definition.
* @return {!Array} Tuple containing three elements:
* - the name of the defined procedure,
* - a list of all its arguments,
* - that it DOES NOT have a return value.
* @this Blockly.Block
*/
getProcedureDef: function () {
return [this.getFieldValue('NAME'), this.arguments_, true, true];
},
getVars: Blockly.Blocks['procedures_defreturn'].getVars,
getVarModels: Blockly.Blocks['procedures_defreturn'].getVarModels,
renameVarById: Blockly.Blocks['procedures_defreturn'].renameVarById,
updateVarName: Blockly.Blocks['procedures_defreturn'].updateVarName,
displayRenamedVar_: Blockly.Blocks['procedures_defreturn'].displayRenamedVar_,
customContextMenu: Blockly.Blocks['procedures_defreturn'].customContextMenu,
callType_: 'procedures_callcustomreturn'
};
Blockly.JavaScript.forBlock['procedures_defcustomreturn'] = function (block) {
// Define a procedure with a return value.
const funcName = Blockly.JavaScript.nameDB_.getName(
block.getFieldValue('NAME'), Blockly.PROCEDURE_CATEGORY_NAME);
const args = [];
for (let i = 0; i < block.arguments_.length; i++) {
args[i] = Blockly.JavaScript.nameDB_.getName(block.arguments_[i], Blockly.VARIABLE_CATEGORY_NAME);
}
const script = Blockly.b64DecodeUnicode(block.getFieldValue('SCRIPT') || '');
const lines = script.split('\n');
for (let l = 0; l < lines.length; l++) {
lines[l] = ' ' + lines[l];
}
let code = 'async function ' + funcName + '(' + args.join(', ') + ') {\n' +
lines.join('\n') + '\n}';
code = Blockly.JavaScript.scrub_(block, code);
// Add % so as not to collide with helper functions in definitions list.
Blockly.JavaScript.definitions_['%' + funcName] = code;
return null;
};
Blockly.Blocks['procedures_callcustomreturn'] = {
init: Blockly.Blocks['procedures_callreturn'].init,
getProcedureCall: Blockly.Blocks['procedures_callreturn'].getProcedureCall,
renameProcedure: Blockly.Blocks['procedures_callreturn'].renameProcedure,
setProcedureParameters_: Blockly.Blocks['procedures_callreturn'].setProcedureParameters_,
updateShape_: Blockly.Blocks['procedures_callreturn'].updateShape_,
mutationToDom: Blockly.Blocks['procedures_callreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_callreturn'].domToMutation,
getVars: Blockly.Blocks['procedures_callreturn'].getVars,
getVarModels: Blockly.Blocks['procedures_callreturn'].getVarModels,
onchange: Blockly.Blocks['procedures_callreturn'].onchange,
customContextMenu: Blockly.Blocks['procedures_callreturn'].customContextMenu,
defType_: 'procedures_defcustomreturn'
};
Blockly.JavaScript.forBlock['procedures_callcustomreturn'] = Blockly.JavaScript.forBlock['procedures_callreturn'];
// ---------------------- custom function with no return ------------------------------
// This was modified, so the shadow condition block is created in the init function
Blockly.Blocks['procedures_ifreturn'].init = function () {
const __input = this.appendValueInput('CONDITION')
.setCheck('Boolean')
.appendField(Blockly.Msg['CONTROLS_IF_MSG_IF']);
const _shadow = this.workspace.newBlock('logic_boolean');
_shadow.setShadow(true);
_shadow.setFieldValue('TRUE', 'BOOL');
_shadow.outputConnection.connect(__input.connection);
this.appendValueInput('VALUE').appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']);
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setStyle('procedure_blocks');
this.setTooltip(Blockly.Msg['PROCEDURES_IFRETURN_TOOLTIP']);
this.setHelpUrl(Blockly.Msg['PROCEDURES_IFRETURN_HELPURL']);
this.hasReturnValue_ = true;
};
Blockly.Blocks['procedures_defcustomnoreturn'] = {
getProcedureModel() {
return this.model;
},
isProcedureDef() {
return true;
},
init: function () {
const nameField = new Blockly.FieldTextInput('', Blockly.Procedures.rename);
nameField.setSpellcheck(false);
this.appendDummyInput()
.appendField(Blockly.Translate('procedures_defcustomnoreturn_name'))
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
this.setMutator(new Blockly.icons.MutatorIcon(['procedures_mutatorarg'], this));
if ((this.workspace.options.comments ||
(this.workspace.options.parentWorkspace && this.workspace.options.parentWorkspace.options.comments)) &&
Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']) {
this.setCommentText(Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']);
}
this.setStyle('procedure_blocks');
this.setColour(Blockly.Msg['PROCEDURES_HUE']);
this.setTooltip(Blockly.Msg['PROCEDURES_DEFNORETURN_TOOLTIP']);
this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFNORETURN_HELPURL']);
this.arguments_ = [];
this.argumentVarModels_ = [];
this.setStatements_(true);
this.statementConnection_ = null;
this.appendDummyInput('SCRIPT')
.appendField(new Blockly.FieldScript(''), 'SCRIPT');
this.setInputsInline(true);
this.setStatements_(false);
},
setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_,
updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_,
mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation,
decompose: Blockly.Blocks['procedures_defcustomreturn'].decompose,
compose: Blockly.Blocks['procedures_defcustomreturn'].compose,
getProcedureDef: function () {
return [this.getFieldValue('NAME'), this.arguments_, false, true];
},
getVars: Blockly.Blocks['procedures_defnoreturn'].getVars,
getVarModels: Blockly.Blocks['procedures_defnoreturn'].getVarModels,
renameVarById: Blockly.Blocks['procedures_defnoreturn'].renameVarById,
updateVarName: Blockly.Blocks['procedures_defnoreturn'].updateVarName,
displayRenamedVar_: Blockly.Blocks['procedures_defnoreturn'].displayRenamedVar_,
customContextMenu: Blockly.Blocks['procedures_defnoreturn'].customContextMenu,
callType_: 'procedures_callcustomnoreturn'
};
Blockly.JavaScript.forBlock['procedures_defcustomnoreturn'] = Blockly.JavaScript.forBlock['procedures_defcustomreturn'];
Blockly.Blocks['procedures_callcustomnoreturn'] = {
init: Blockly.Blocks['procedures_callnoreturn'].init,
getProcedureCall: Blockly.Blocks['procedures_callnoreturn'].getProcedureCall,
renameProcedure: Blockly.Blocks['procedures_callnoreturn'].renameProcedure,
setProcedureParameters_: Blockly.Blocks['procedures_callnoreturn'].setProcedureParameters_,
updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_,
mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom,
domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation,
getVars: Blockly.Blocks['procedures_callnoreturn'].getVars,
getVarModels: Blockly.Blocks['procedures_callnoreturn'].getVarModels,
onchange: Blockly.Blocks['procedures_callnoreturn'].onchange,
customContextMenu: Blockly.Blocks['procedures_callnoreturn'].customContextMenu,
defType_: 'procedures_defcustomnoreturn'
};
Blockly.JavaScript.forBlock['procedures_callcustomnoreturn'] = function (block) {
// Call a procedure with no return value.
// Generated code is for a function call as a statement is the same as a
// function call as a value, with the addition of line ending.
const tuple = Blockly.JavaScript.forBlock['procedures_callcustomreturn'](block);
return tuple[0] + ';\n';
};
Blockly.Blocks['procedures_return'] = {
/**
* Block for conditionally returning a value from a procedure.
*/
init: function () {
this.appendValueInput('VALUE').appendField(
Blockly.Msg['PROCEDURES_DEFRETURN_RETURN'],
);
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setStyle('procedure_blocks');
this.setTooltip(Blockly.Msg['PROCEDURES_IFRETURN_TOOLTIP']);
this.setHelpUrl(Blockly.Msg['PROCEDURES_IFRETURN_HELPURL']);
this.hasReturnValue_ = true;
},
/**
* Create XML to represent whether this block has a return value.
*
* @returns XML storage element.
*/
mutationToDom: function () {
const utils = Blockly.Xml.utils ? Blockly.Xml.utils : Blockly.utils.xml;
const container = utils.createElement('mutation');
container.setAttribute('value', String(Number(this.hasReturnValue_)));
return container;
},
/**
* Parse XML to restore whether this block has a return value.
*
* @param xmlElement XML storage element.
*/
domToMutation: function (xmlElement) {
const value = xmlElement.getAttribute('value');
this.hasReturnValue_ = value === '1';
if (!this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendDummyInput('VALUE').appendField(
Blockly.Msg['PROCEDURES_DEFRETURN_RETURN'],
);
}
},
// This block does not need JSO serialization hooks (saveExtraState and
// loadExtraState) because the state of this block is already encoded in the
// block's position in the workspace.
// XML hooks are kept for backwards compatibility.
/**
* Called whenever anything on the workspace changes.
* Add warning if this flow block is not nested inside a loop.
*
* @param e Move event.
*/
onchange: function (e) {
if (
(this.workspace.isDragging && this.workspace.isDragging()) ||
(e.type !== 'move' && e.type !== 'create')
) {
return; // Don't change state at the start of a drag.
}
let legal = false;
// Is the block nested in a procedure?
let block = this; // eslint-disable-line @typescript-eslint/no-this-alias
do {
if (this.FUNCTION_TYPES.includes(block.type)) {
legal = true;
break;
}
block = block.getSurroundParent();
} while (block);
if (legal) {
// If needed, toggle whether this block has a return value.
if (block.type === 'procedures_defnoreturn' && this.hasReturnValue_) {
this.removeInput('VALUE');
this.appendDummyInput('VALUE').appendField(
Blockly.Msg['PROCEDURES_DEFRETURN_RETURN'],
);
this.hasReturnValue_ = false;
} else if (
block.type === 'procedures_defreturn' &&
!this.hasReturnValue_
) {
this.removeInput('VALUE');
this.appendValueInput('VALUE').appendField(
Blockly.Msg['PROCEDURES_DEFRETURN_RETURN'],
);
this.hasReturnValue_ = true;
}
this.setWarningText(null);
} else {
this.setWarningText(Blockly.Msg['PROCEDURES_IFRETURN_WARNING']);
}
if (!this.isInFlyout) {
// const utils = Blockly.Xml.utils ? Blockly.Xml.utils : Blockly.utils.xml;
try {
// There is no need to record the enable/disable change on the undo/redo
// list since the change will be automatically recreated when replayed.
//utils.setRecordUndo(false);
this.setDisabledReason(!legal, 'UNPARENTED_IFRETURN');
} finally {
//utils.setRecordUndo(true);
}
}
},
/**
* List of block types that are functions and thus do not need warnings.
* To add a new function type add this to your code:
* Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('custom_func');
*/
FUNCTION_TYPES: ['procedures_defnoreturn', 'procedures_defreturn', 'procedures_defcustomreturn', 'procedures_defcustomnoreturn'],
};
Blockly.JavaScript.forBlock['procedures_return'] = function (block, generator) {
// Conditionally return value from a procedure.
let code = '';
if (generator.STATEMENT_SUFFIX) {
// Inject any statement suffix here since the regular one at the end
// will not get executed if the return is triggered.
code += generator.prefixLines(
generator.injectId(generator.STATEMENT_SUFFIX, block),
generator.INDENT,
);
}
if (block.hasReturnValue_) {
const value = generator.valueToCode(block, 'VALUE', 99 /* Order.NONE */) || 'null';
code += generator.INDENT + 'return ' + value + ';\n';
} else {
code += generator.INDENT + 'return;\n';
}
return code;
};