import * as Blockly from 'blockly/core'; const CLASS_HUE = 345;//'#af5180'//330; const PROPERTY_HUE = 345; const METHOD_HUE = 345; const OBJECT_HUE = 345; export const class_make = { init: function () { this.appendDummyInput() .appendField(Blockly.Msg.CREATE_CLASS) .appendField(new Blockly.FieldTextInput(""), "VAR"); this.appendStatementInput("data") .setCheck(null) .setAlign(Blockly.inputs.Align.RIGHT); this.setColour(CLASS_HUE); this.setTooltip(""); this.setHelpUrl(""); }, class_getVars: function () { var varValue = this.getFieldValue('VAR'); if (varValue == null) { return []; } return varValue.split(","); }, class_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } } }; export const class_make_with_base = { init: function () { this.appendValueInput("NAME") .appendField(Blockly.Msg.CREATE_CLASS) .appendField(new Blockly.FieldTextInput(""), "VAR") .appendField(" " + Blockly.Msg.FATHER_CLASS + ":"); this.appendStatementInput("data") .setCheck(null) .setAlign(Blockly.inputs.Align.RIGHT); this.setColour(CLASS_HUE); this.setTooltip(""); this.setHelpUrl(""); }, class_getVars: function () { var varValue = this.getFieldValue('VAR'); if (varValue == null) { return []; } return varValue.split(","); }, class_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } } }; export const class_get = { init: function () { this.setColour(CLASS_HUE); this.appendDummyInput() .appendField(Blockly.Msg.MIXPY_CLASS) .appendField(new Blockly.FieldTextInput(''), 'VAR'); this.setOutput(true); this.setTooltip(Blockly.Msg.VARIABLES_GET_TOOLTIP); }, class_getVars: function () { return [this.getFieldValue('VAR')]; }, class_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } } }; export const property_set = { init: function () { this.setColour(PROPERTY_HUE); this.appendValueInput('VALUE'); this.appendValueInput('DATA') .appendField(Blockly.Msg.MIXPY_ATTRIBUTE_OF) .appendField(new Blockly.FieldTextInput(''), 'VAR') .appendField(" " + Blockly.Msg.MIXLY_VALUE2 + " "); this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); this.setTooltip(Blockly.Msg.VARIABLES_SET_TOOLTIP); }, property_getVars: function () { var varValue = this.getFieldValue('VAR'); if (varValue == null) { return []; } return varValue.split(","); }, property_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } } }; export const property_get = { init: function () { this.setColour(PROPERTY_HUE); this.appendValueInput('VALUE'); this.appendDummyInput() .appendField(Blockly.Msg.MIXPY_ATTRIBUTE_OF) .appendField(new Blockly.FieldTextInput(''), 'VAR'); this.setInputsInline(true); this.setOutput(true); this.setTooltip(Blockly.Msg.VARIABLES_GET_TOOLTIP); }, property_getVars: function () { return [this.getFieldValue('VAR')]; }, property_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } } }; /** * Common HSV hue for all blocks in this category. */ export const method_procedures_defnoreturn = { /** * Block for defining a procedure with no return value. * @this Blockly.Block */ init: function () { this.setColour(METHOD_HUE); var nameField = new Blockly.FieldTextInput( Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE, Blockly.Class.prorename); nameField.setSpellcheck(false); this.appendDummyInput() .appendField(Blockly.Msg.MIXPY_CREATE_METHOD) .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE) .appendField(nameField, 'NAME') .appendField('', 'PARAMS'); this.setPreviousStatement(true); this.setNextStatement(true); this.setMutator(new Blockly.icons.MutatorIcon(['method_procedures_mutatorarg'], this)); this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP); this.arguments_ = []; this.argumentstype_ = [];//新增 this.setStatements_(true); this.statementConnection_ = null; }, /** * Add or remove the statement block from this function definition. * @param {boolean} hasStatements True if a statement block is needed. * @this Blockly.Block */ setStatements_: function (hasStatements) { if (this.hasStatements_ === hasStatements) { return; } if (hasStatements) { this.appendStatementInput('STACK') .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO); if (this.getInput('RETURN')) { this.moveInputBefore('STACK', 'RETURN'); } } else { this.removeInput('STACK', true); } this.hasStatements_ = hasStatements; }, /** * Update the display of parameters for this procedure definition block. * Display a warning if there are duplicately named parameters. * @private * @this Blockly.Block */ updateParams_: function () { // Check for duplicated arguments. var badArg = false; var hash = {}; for (var i = 0; i < this.arguments_.length; i++) { if (hash['arg_' + this.arguments_[i].toLowerCase()]) { badArg = true; break; } hash['arg_' + this.arguments_[i].toLowerCase()] = true; } if (badArg) { this.setWarningText(Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING); } else { this.setWarningText(null); } // Merge the arguments into a human-readable list. var paramString = ''; if (this.arguments_.length) { paramString = Blockly.Msg.PROCEDURES_BEFORE_PARAMS + ' ' + this.arguments_.join(', '); } // The params field is deterministic based on the mutation, // no need to fire a change event. Blockly.Events.disable(); this.setFieldValue(paramString, 'PARAMS'); Blockly.Events.enable(); }, /** * Create XML to represent the argument inputs. * @param {=boolean} opt_paramIds If true include the IDs of the parameter * quarks. Used by Blockly.Procedures.mutateCallers for reconnection. * @return {!Element} XML storage element. * @this Blockly.Block */ mutationToDom: function () { var container = document.createElement('mutation'); for (var i = 0; i < this.arguments_.length; i++) { var parameter = document.createElement('arg'); parameter.setAttribute('name', this.arguments_[i]); //parameter.setAttribute('vartype', this.argumentstype_[i]);//新增 container.appendChild(parameter); } // Save whether the statement input is visible. if (!this.hasStatements_) { container.setAttribute('statements', 'false'); } return container; }, /** * Parse XML to restore the argument inputs. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ domToMutation: function (xmlElement) { this.arguments_ = []; this.argumentstype_ = [];//新增 for (var i = 0; xmlElement.childNodes[i]; i++) { let childNode = xmlElement.childNodes[i]; if (childNode.nodeName.toLowerCase() == 'arg') { this.arguments_.push(childNode.getAttribute('name')); //this.argumentstype_.push(childNode.getAttribute('vartype'));//新增 } } this.updateParams_(); Blockly.Class.mutateCallers(this); // Show or hide the statement input. this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); }, /** * 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) { var containerBlock = workspace.newBlock('method_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); } // Parameter list. var connection = containerBlock.getInput('STACK').connection; for (var i = 0; i < this.arguments_.length; i++) { var paramBlock = workspace.newBlock('method_procedures_mutatorarg'); paramBlock.initSvg(); paramBlock.setFieldValue(this.arguments_[i], 'NAME'); //paramBlock.setFieldValue(this.argumentstype_[i], 'TYPEVAR');//新增 // Store the old location. paramBlock.oldLocation = i; connection.connect(paramBlock.previousConnection); connection = paramBlock.nextConnection; } // Initialize procedure's callers with blank IDs. //Blockly.Class.promutateCallers(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.argumentstype_ = [];//新增 var paramBlock = containerBlock.getInputTargetBlock('STACK'); while (paramBlock) { this.arguments_.push(paramBlock.getFieldValue('NAME')); //this.argumentstype_.push(paramBlock.getFieldValue('TYPEVAR'));//新增 this.paramIds_.push(paramBlock.id); paramBlock = paramBlock.nextConnection && paramBlock.nextConnection.targetBlock(); } this.updateParams_(); Blockly.Class.promutateCallers(this); // Show/hide the statement input. var 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. this.statementConnection_ && this.statementConnection_.reconnect(this, 'STACK'); this.statementConnection_ = null; } else { // Save the stack, then disconnect it. var stackConnection = this.getInput('STACK').connection; this.statementConnection_ = stackConnection.targetConnection; if (this.statementConnection_) { var stackBlock = stackConnection.targetBlock(); stackBlock.unplug(); stackBlock.bumpNeighbours_(); } this.setStatements_(false); } } } }, /** * Dispose of any callers. * @this Blockly.Block */ /* dispose: function() { var name = this.getFieldValue('NAME'); Blockly.Class.prodisposeCallers(name, this.workspace); // Call parent's destructor. this.constructor.prototype.dispose.apply(this, arguments); },*/ /** * 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 */ method_getProcedureDef: function () { var surround_parent = this.getSurroundParent(); var arg_data = ''; for (var i = 0; i < this.arguments_.length; i++) { arg_data = arg_data + '_' + this.arguments_[i]; } if (surround_parent && ((surround_parent.type == 'class_make') || (surround_parent.type == 'class_make_with_base'))) { var class_name = surround_parent.getFieldValue('VAR'); return [this.getFieldValue('NAME'), this.arguments_, false, this.getFieldValue('NAME') + '_' + class_name, this.getFieldValue('NAME') + arg_data]; } return [this.getFieldValue('NAME'), this.arguments_, false, this.getFieldValue('NAME'), this.getFieldValue('NAME') + arg_data]; }, /** * Return all variables referenced by this block. * @return {!Array.} List of variable names. * @this Blockly.Block */ getVars: function () { return this.arguments_; }, /** * Notification that a variable is renaming. * If the name matches one of this block's variables, rename it. * @param {string} oldName Previous name of variable. * @param {string} newName Renamed variable. * @this Blockly.Block */ renameVar: function (oldName, newName) { var change = false; for (var i = 0; i < this.arguments_.length; i++) { if (Blockly.Names.equals(oldName, this.arguments_[i])) { this.arguments_[i] = newName; change = true; } } if (change) { this.updateParams_(); // Update the mutator's variables if the mutator is open. if (this.mutator.isVisible()) { var blocks = this.mutator.workspace_.getAllBlocks(); for (var i = 0; blocks[i]; i++) { let block = blocks[i]; if (block.type == 'method_procedures_mutatorarg' && Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) { block.setFieldValue(newName, 'NAME'); } } } } }, /** * Add custom menu options to this block's context menu. * @param {!Array} options List of menu options to add to. * @this Blockly.Block */ customContextMenu: function (options) { // Add option to create caller. var option = { enabled: true }; var name = this.getFieldValue('NAME'); option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name); var xmlMutation = Blockly.utils.xml.createElement('mutation'); xmlMutation.setAttribute('name', name); for (var i = 0; i < this.arguments_.length; i++) { var xmlArg = Blockly.utils.xml.createElement('arg'); xmlArg.setAttribute('name', this.arguments_[i]); //xmlArg.setAttribute('type', this.argumentstype_[i]);//新增 xmlMutation.appendChild(xmlArg); } var xmlBlock = Blockly.utils.xml.createElement('block', null, xmlMutation); xmlBlock.setAttribute('type', this.callType_); option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); options.push(option); // Add options to create getters for each parameter. if (!this.isCollapsed()) { for (var i = 0; i < this.arguments_.length; i++) { var option = { enabled: true }; var name = this.arguments_[i]; option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name); var xmlField = Blockly.utils.xml.createElement('field', null, name); xmlField.setAttribute('name', 'VAR'); xmlField.setAttribute('type', 'TYPEVAR');//新增 var xmlBlock = Blockly.utils.xml.createElement('block', null, xmlField); xmlBlock.setAttribute('type', 'variables_get'); option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); options.push(option); } } }, callType_: 'method_procedures_callnoreturn' }; export const method_procedures_defreturn = { /** * Block for defining a procedure with a return value. * @this Blockly.Block */ init: function () { this.setColour(METHOD_HUE); var nameField = new Blockly.FieldTextInput( Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE, Blockly.Class.prorename); nameField.setSpellcheck(false); this.appendDummyInput() .appendField(Blockly.Msg.MIXPY_CREATE_METHOD) .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE) .appendField(nameField, 'NAME') .appendField('', 'PARAMS'); this.appendValueInput('RETURN') .setAlign(Blockly.inputs.Align.RIGHT) .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); this.setPreviousStatement(true); this.setNextStatement(true); this.setMutator(new Blockly.icons.MutatorIcon(['method_procedures_mutatorarg'], this)); this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP); this.arguments_ = []; this.setStatements_(true); this.statementConnection_ = null; }, setStatements_: method_procedures_defnoreturn.setStatements_, updateParams_: method_procedures_defnoreturn.updateParams_, mutationToDom: method_procedures_defnoreturn.mutationToDom, domToMutation: method_procedures_defnoreturn.domToMutation, decompose: method_procedures_defnoreturn.decompose, compose: method_procedures_defnoreturn.compose, dispose: method_procedures_defnoreturn.dispose, /** * 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 have a return value. * @this Blockly.Block */ method_getProcedureDef: function () { var surround_parent = this.getSurroundParent(); var arg_data = ''; for (var i = 0; i < this.arguments_.length; i++) { arg_data = arg_data + '_' + this.arguments_[i]; } if (surround_parent && ((surround_parent.type == 'class_make') || (surround_parent.type == 'class_make_with_base'))) { var class_name = surround_parent.getFieldValue('VAR'); return [this.getFieldValue('NAME'), this.arguments_, true, this.getFieldValue('NAME') + '_' + class_name, this.getFieldValue('NAME') + arg_data]; } return [this.getFieldValue('NAME'), this.arguments_, true, this.getFieldValue('NAME'), this.getFieldValue('NAME') + arg_data]; }, getVars: method_procedures_defnoreturn.getVars, renameVar: method_procedures_defnoreturn.renameVar, customContextMenu: method_procedures_defnoreturn.customContextMenu, callType_: 'method_procedures_callreturn' }; export const method_procedures_mutatorcontainer = { /** * Mutator block for procedure container. * @this Blockly.Block */ init: function () { this.setColour(METHOD_HUE); this.appendDummyInput() .appendField(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE); this.appendStatementInput('STACK'); this.appendDummyInput('STATEMENT_INPUT') .appendField(Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS) .appendField(new Blockly.FieldCheckbox('TRUE'), 'STATEMENTS'); this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP); this.contextMenu = false; } }; export const method_procedures_mutatorarg = { /** * Mutator block for procedure argument. * @this Blockly.Block */ init: function () { this.setColour(METHOD_HUE); this.appendDummyInput() .appendField(Blockly.Msg.PROCEDURES_BEFORE_PARAMS) //.appendField(new Blockly.FieldDropdown([[Blockly.Msg.MIXLY_NUMBER, 'number'], [Blockly.Msg.LANG_MATH_STRING, 'string'], [Blockly.Msg.LANG_MATH_BOOLEAN, 'boolean'], [Blockly.Msg.MIXLY_MICROBIT_JS_TYPE_ARRAY_NUMBER, 'Array'], [Blockly.Msg.MIXLY_MICROBIT_JS_TYPE_ARRAY_STRING, 'Array']]), 'TYPEVAR') .appendField(new Blockly.FieldTextInput('x', this.validator_), 'NAME'); this.setPreviousStatement(true); this.setNextStatement(true); this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP); this.contextMenu = false; }, /** * Obtain a valid name for the procedure. * Merge runs of whitespace. Strip leading and trailing whitespace. * Beyond this, all names are legal. * @param {string} newVar User-supplied name. * @return {?string} Valid name, or null if a name was not specified. * @private * @this Blockly.Block */ validator_: function (newVar) { newVar = newVar.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); return newVar || null; } }; export const method_procedures_callnoreturn = { /** * Block for calling a procedure with no return value. * @this Blockly.Block */ init: function () { this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL); this.setColour(METHOD_HUE); this.appendValueInput('DATA'); this.appendDummyInput('TOPROW') .appendField(Blockly.Msg.MIXPY_EXECUTION_METHOD) .appendField(this.id, 'NAME'); this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); // Tooltip is set in method_renameProcedure. this.arguments_ = []; this.quarkConnections_ = {}; this.quarkIds_ = null; }, /** * Returns the name of the procedure this block calls. * @return {string} Procedure name. * @this Blockly.Block */ method_getProcedureCall: function () { // The NAME field is guaranteed to exist, null will never be returned. return /** @type {string} */ (this.getFieldValue('NAME')); }, /** * Notification that a procedure is renaming. * If the name matches this block's procedure, rename it. * @param {string} oldName Previous name of procedure. * @param {string} newName Renamed procedure. * @this Blockly.Block */ method_renameProcedure: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.method_getProcedureCall())) { this.setFieldValue(newName, 'NAME'); this.setTooltip( (this.outputConnection ? Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP : Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP) .replace('%1', newName)); } }, /** * Notification that the procedure's parameters have changed. * @param {!Array.} paramNames New param names, e.g. ['x', 'y', 'z']. * @param {!Array.} paramIds IDs of params (consistent for each * parameter through the life of a mutator, regardless of param renaming), * e.g. ['piua', 'f8b_', 'oi.o']. * @private * @this Blockly.Block */ setProcedureParameters_: function (paramNames, paramIds) { // Data structures: // this.arguments = ['x', 'y'] // Existing param names. // this.quarkConnections_ {piua: null, f8b_: Blockly.Connection} // Look-up of paramIds to connections plugged into the call block. // this.quarkIds_ = ['piua', 'f8b_'] // Existing param IDs. // Note that quarkConnections_ may include IDs that no longer exist, but // which might reappear if a param is reattached in the mutator. var defBlock = Blockly.Class.progetDefinition(this.method_getProcedureCall(), this.workspace); const mutatorIcon = defBlock && defBlock.getIcon(Blockly.icons.MutatorIcon.TYPE); const mutatorOpen = mutatorIcon && mutatorIcon.bubbleIsVisible(); if (!mutatorOpen) { this.quarkConnections_ = {}; this.quarkIds_ = null; } if (!paramIds) { // Reset the quarks (a mutator is about to open). return; } if (this.arguments_.length) { if (Blockly.Names.equals(this.arguments_, paramNames)) { // No change. this.quarkIds_ = paramIds; return; } } if (paramIds.length != paramNames.length) { throw 'Error: paramNames and paramIds must be the same length.'; } this.setCollapsed(false); if (!this.quarkIds_) { // Initialize tracking for this block. this.quarkConnections_ = {}; if (paramNames.join('\n') == this.arguments_.join('\n')) { // No change to the parameters, allow quarkConnections_ to be // populated with the existing connections. this.quarkIds_ = paramIds; } else { this.quarkIds_ = []; } } // Switch off rendering while the block is rebuilt. var savedRendered = this.rendered; this.rendered = false; // Update the quarkConnections_ with existing connections. for (var i = 1; i < this.arguments_.length; i++) { var input = this.getInput('ARG' + i); if (input) { var connection = input.connection.targetConnection; this.quarkConnections_[this.quarkIds_[i]] = connection; if (mutatorOpen && connection && paramIds.indexOf(this.quarkIds_[i]) == -1) { // This connection should no longer be attached to this block. connection.disconnect(); connection.getSourceBlock().bumpNeighbours_(); } } } // Rebuild the block's arguments. this.arguments_ = [].concat(paramNames); this.updateShape_(); this.quarkIds_ = paramIds; // Reconnect any child blocks. if (this.quarkIds_) { for (var i = 1; i < this.arguments_.length; i++) { var quarkId = this.quarkIds_[i]; if (quarkId in this.quarkConnections_) { var connection = this.quarkConnections_[quarkId]; if (connection && !connection.reconnect(this, 'ARG' + i)) { // Block no longer exists or has been attached elsewhere. delete this.quarkConnections_[quarkId]; } } } } // Restore rendering and show the changes. this.rendered = savedRendered; if (this.rendered) { this.render(); } }, /** * Modify this block to have the correct number of arguments. * @private * @this Blockly.Block */ updateShape_: function () { for (var i = 1; i < this.arguments_.length; i++) { var field = this.getField('ARGNAME' + i); if (field) { // Ensure argument name is up to date. // The argument name field is deterministic based on the mutation, // no need to fire a change event. Blockly.Events.disable(); field.setValue(this.arguments_[i]); Blockly.Events.enable(); } else { // Add new input. field = new Blockly.FieldLabel(this.arguments_[i]); var input = this.appendValueInput('ARG' + i) .setAlign(Blockly.inputs.Align.RIGHT) .appendField(field, 'ARGNAME' + i); input.init(); } } // Remove deleted inputs. while (this.getInput('ARG' + i)) { this.removeInput('ARG' + i); i++; } // Add 'with:' if there are parameters, remove otherwise. var topRow = this.getInput('TOPROW'); if (topRow) { if (this.arguments_.length - 1) { if (!this.getField('WITH')) { topRow.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS, 'WITH'); topRow.init(); } } else { if (this.getField('WITH')) { topRow.removeField('WITH'); } } } }, /** * Create XML to represent the (non-editable) name and arguments. * @return {!Element} XML storage element. * @this Blockly.Block */ mutationToDom: function () { var container = document.createElement('mutation'); container.setAttribute('name', this.method_getProcedureCall()); for (var i = 0; i < this.arguments_.length; i++) { var parameter = document.createElement('arg'); parameter.setAttribute('name', this.arguments_[i]); container.appendChild(parameter); } return container; }, /** * Parse XML to restore the (non-editable) name and parameters. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ domToMutation: function (xmlElement) { var name = xmlElement.getAttribute('name'); this.method_renameProcedure(this.method_getProcedureCall(), name); var args = []; var paramIds = []; for (var i = 0; xmlElement.childNodes[i]; i++) { let childNode = xmlElement.childNodes[i]; if (childNode.nodeName.toLowerCase() == 'arg') { args.push(childNode.getAttribute('name')); paramIds.push(childNode.getAttribute('paramId')); } } this.setProcedureParameters_(args, paramIds); }, /** * Notification that a variable is renaming. * If the name matches one of this block's variables, rename it. * @param {string} oldName Previous name of variable. * @param {string} newName Renamed variable. * @this Blockly.Block */ renameVar: function (oldName, newName) { for (var i = 1; i < this.arguments_.length; i++) { if (Blockly.Names.equals(oldName, this.arguments_[i])) { this.arguments_[i] = newName; this.getField('ARGNAME' + i).setValue(newName); } } }, /** * Add menu option to find the definition block for this call. * @param {!Array} options List of menu options to add to. * @this Blockly.Block */ customContextMenu: function (options) { var option = { enabled: true }; option.text = Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF; var name = this.method_getProcedureCall(); var workspace = this.workspace; option.callback = function () { var def = Blockly.Class.progetDefinition(name, workspace); def && def.select(); }; options.push(option); } }; export const method_procedures_callreturn = { /** * Block for calling a procedure with a return value. * @this Blockly.Block */ init: function () { this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL); this.setColour(METHOD_HUE); this.appendValueInput('DATA'); this.appendDummyInput('TOPROW') .appendField(Blockly.Msg.MIXPY_EXECUTION_METHOD) .appendField(Blockly.Msg.PROCEDURES_CALLRETURN_CALL) .appendField('', 'NAME'); this.setOutput(true); this.setInputsInline(true); // Tooltip is set in domToMutation. this.arguments_ = []; this.quarkConnections_ = {}; this.quarkIds_ = null; }, method_getProcedureCall: method_procedures_callnoreturn.method_getProcedureCall, method_renameProcedure: method_procedures_callnoreturn.method_renameProcedure, setProcedureParameters_: method_procedures_callnoreturn.setProcedureParameters_, updateShape_: method_procedures_callnoreturn.updateShape_, mutationToDom: method_procedures_callnoreturn.mutationToDom, domToMutation: method_procedures_callnoreturn.domToMutation, renameVar: method_procedures_callnoreturn.renameVar, customContextMenu: method_procedures_callnoreturn.customContextMenu }; export const method_procedures_ifreturn = { /** * Block for conditionally returning a value from a procedure. * @this Blockly.Block */ init: function () { this.setColour(METHOD_HUE); this.appendValueInput('CONDITION') .setCheck(Boolean) .appendField(Blockly.Msg.CONTROLS_IF_MSG_IF); this.appendValueInput('VALUE') .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP); this.hasReturnValue_ = true; }, /** * Create XML to represent whether this block has a return value. * @return {!Element} XML storage element. * @this Blockly.Block */ mutationToDom: function () { var container = document.createElement('mutation'); container.setAttribute('value', Number(this.hasReturnValue_)); return container; }, /** * Parse XML to restore whether this block has a return value. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ domToMutation: function (xmlElement) { var value = xmlElement.getAttribute('value'); this.hasReturnValue_ = (value == 1); if (!this.hasReturnValue_) { this.removeInput('VALUE'); this.appendDummyInput('VALUE') .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); } }, /** * Called whenever anything on the workspace changes. * Add warning if this flow block is not nested inside a loop. * @param {!Blockly.Events.Abstract} e Change event. * @this Blockly.Block */ onchange: function () { var legal = false; // Is the block nested in a procedure? var block = this; do { if (this.FUNCTION_TYPES.indexOf(block.type) != -1) { legal = true; break; } block = block.getSurroundParent(); } while (block); if (legal) { // If needed, toggle whether this block has a return value. if (block.type == 'method_procedures_defnoreturn' && this.hasReturnValue_) { this.removeInput('VALUE'); this.appendDummyInput('VALUE') .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); this.hasReturnValue_ = false; } else if (block.type == 'method_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); } }, /** * List of block types that are functions and thus do not need warnings. * To add a new function type add this to your code: * procedures_ifreturn.FUNCTION_TYPES.push('custom_func'); */ FUNCTION_TYPES: ['method_procedures_defnoreturn', 'method_procedures_defreturn'] }; export const method_procedures_return = { /** * Block for conditionally returning a value from a procedure. * @this Blockly.Block */ init: function () { this.setColour(METHOD_HUE); // this.appendValueInput('CONDITION') // .setCheck(Boolean) // .appendField(Blockly.Msg.CONTROLS_IF_MSG_IF); this.appendValueInput('VALUE') .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP); this.hasReturnValue_ = true; }, /** * Create XML to represent whether this block has a return value. * @return {!Element} XML storage element. * @this Blockly.Block */ mutationToDom: function () { var container = document.createElement('mutation'); container.setAttribute('value', Number(this.hasReturnValue_)); return container; }, /** * Parse XML to restore whether this block has a return value. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ domToMutation: function (xmlElement) { var value = xmlElement.getAttribute('value'); this.hasReturnValue_ = (value == 1); if (!this.hasReturnValue_) { this.removeInput('VALUE'); this.appendDummyInput('VALUE') .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); } }, /** * Called whenever anything on the workspace changes. * Add warning if this flow block is not nested inside a loop. * @param {!Blockly.Events.Abstract} e Change event. * @this Blockly.Block */ onchange: function () { var legal = false; // Is the block nested in a procedure? var block = this; do { if (this.FUNCTION_TYPES.indexOf(block.type) != -1) { legal = true; break; } block = block.getSurroundParent(); } while (block); if (legal) { // If needed, toggle whether this block has a return value. if (block.type == 'method_procedures_defnoreturn' && this.hasReturnValue_) { this.removeInput('VALUE'); this.appendDummyInput('VALUE') .appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN); this.hasReturnValue_ = false; } else if (block.type == 'method_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); } }, /** * List of block types that are functions and thus do not need warnings. * To add a new function type add this to your code: * procedures_ifreturn.FUNCTION_TYPES.push('custom_func'); */ FUNCTION_TYPES: ['method_procedures_defnoreturn', 'method_procedures_defreturn'] }; export const object_set = { init: function () { this.appendDummyInput("EMPTY") .appendField(Blockly.Msg.MIXPY_OBJECT) .appendField(new Blockly.FieldTextInput(""), "VAR11") .appendField(" " + Blockly.Msg.MIXLY_VALUE2 + " " + Blockly.Msg.MIXPY_CLASS) .appendField(new Blockly.FieldTextInput(""), "VAR10"); this.itemCount_ = 0; this.updateShape_(); this.setPreviousStatement(true, null); this.setNextStatement(true, null); this.setColour(OBJECT_HUE); this.setMutator(new Blockly.icons.MutatorIcon(['object_set_with_item'], this)); this.setTooltip(""); this.setHelpUrl(""); }, class_getVars: function () { return [this.getFieldValue('VAR10')]; }, class_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR10'))) { this.setFieldValue(newName, 'VAR10'); } }, object_getVars: function () { var varValue = this.getFieldValue('VAR11'); if (varValue == null) { return []; } return varValue.split(","); }, object_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR11'))) { this.setFieldValue(newName, 'VAR11'); } }, mutationToDom: function () { var container = document.createElement('mutation'); container.setAttribute('items', this.itemCount_); return container; }, domToMutation: function (xmlElement) { this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10); this.updateShape_(); }, decompose: function (workspace) { var containerBlock = workspace.newBlock('object_set_with_container'); containerBlock.initSvg(); var connection = containerBlock.getInput('STACK').connection; for (var i = 0; i < this.itemCount_; i++) { var itemBlock = workspace.newBlock('object_set_with_item'); itemBlock.initSvg(); connection.connect(itemBlock.previousConnection); connection = itemBlock.nextConnection; } return containerBlock; }, compose: function (containerBlock) { var itemBlock = containerBlock.getInputTargetBlock('STACK'); // Count number of inputs. var connections = []; var i = 0; while (itemBlock) { connections[i] = itemBlock.valueConnection_; itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock(); i++; } this.itemCount_ = i; this.updateShape_(); // Reconnect any child blocks. for (var i = 0; i < this.itemCount_; i++) { if (connections[i]) { this.getInput('ADD' + i).connection.connect(connections[i]); } } }, saveConnections: function (containerBlock) { var itemBlock = containerBlock.getInputTargetBlock('STACK'); var i = 0; while (itemBlock) { var input = this.getInput('ADD' + i); itemBlock.valueConnection_ = input && input.connection.targetConnection; i++; itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock(); } }, updateShape_: function () { // Delete everything. var i = 0; while (this.getInput('ADD' + i)) { this.removeInput('ADD' + i); i++; } // Rebuild block. if (this.itemCount_ != 0) { for (var i = 0; i < this.itemCount_; i++) { var input = this.appendValueInput('ADD' + i); input.setAlign(Blockly.inputs.Align.RIGHT) input.appendField(Blockly.Msg.MIXLY_PARAMS + (i + 1) + ":"); } } } }; export const object_set_with_item = { /** * Mutator bolck for adding items. * @this Blockly.Block */ init: function () { this.setColour(OBJECT_HUE); this.appendDummyInput() .appendField(Blockly.Msg.MIXPY_ADD_PARAMETERS); this.setPreviousStatement(true); this.setNextStatement(true); this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP); this.contextMenu = false; } }; export const object_set_with_container = { /** * Mutator block for list container. * @this Blockly.Block */ init: function () { this.setColour(OBJECT_HUE); this.appendDummyInput() .appendField(Blockly.Msg.MIXLY_PARAMS); this.appendStatementInput('STACK'); this.setTooltip(""); this.contextMenu = false; } }; export const object_get = { init: function () { this.setColour(OBJECT_HUE); this.appendDummyInput() .appendField(Blockly.Msg.MIXPY_OBJECT) .appendField(new Blockly.FieldTextInput(''), 'VAR'); this.setOutput(true); this.setTooltip(Blockly.Msg.VARIABLES_GET_TOOLTIP); }, object_getVars: function () { return [this.getFieldValue('VAR')]; }, object_renameVar: function (oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } } };