UNPKG

cloud-blocks

Version:

Cloud Blocks is a library for building scratch computing interfaces with Luxrobo MODI.

1,047 lines (976 loc) 33.8 kB
/** * @license * Visual Blocks Editor * * Copyright 2012 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @fileoverview Procedure blocks for Scratch. */ 'use strict'; goog.provide('Blockly.ScratchBlocks.ProcedureUtils'); goog.require('Blockly.Blocks'); goog.require('Blockly.Colours'); goog.require('Blockly.constants'); goog.require('Blockly.ScratchBlocks.VerticalExtensions'); // Serialization and deserialization. /** * Create XML to represent the (non-editable) name and arguments of a procedure * call block. * @return {!Element} XML storage element. * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom = function () { var container = document.createElement('mutation'); container.setAttribute('proccode', this.procCode_); container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); container.setAttribute('warp', JSON.stringify(this.warp_)); return container; }; /** * Parse XML to restore the (non-editable) name and arguments of a procedure * call block. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation = function ( xmlElement ) { this.procCode_ = xmlElement.getAttribute('proccode'); this.generateShadows_ = JSON.parse( xmlElement.getAttribute('generateshadows') ); this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); this.warp_ = JSON.parse(xmlElement.getAttribute('warp')); this.updateDisplay_(); }; /** * Create XML to represent the (non-editable) name and arguments of a * procedures_prototype block or a procedures_declaration block. * @param {boolean=} opt_generateShadows Whether to include the generateshadows * flag in the generated XML. False if not provided. * @return {!Element} XML storage element. * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom = function ( opt_generateShadows ) { var container = document.createElement('mutation'); if (opt_generateShadows) { container.setAttribute('generateshadows', true); } container.setAttribute('proccode', this.procCode_); container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); container.setAttribute('argumentnames', JSON.stringify(this.displayNames_)); container.setAttribute( 'argumentdefaults', JSON.stringify(this.argumentDefaults_) ); container.setAttribute('warp', JSON.stringify(this.warp_)); return container; }; /** * Parse XML to restore the (non-editable) name and arguments of a * procedures_prototype block or a procedures_declaration block. * @param {!Element} xmlElement XML storage element. * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function ( xmlElement ) { this.procCode_ = xmlElement.getAttribute('proccode'); this.warp_ = JSON.parse(xmlElement.getAttribute('warp')); var prevArgIds = this.argumentIds_; var prevDisplayNames = this.displayNames_; this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); this.displayNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')); this.argumentDefaults_ = JSON.parse( xmlElement.getAttribute('argumentdefaults') ); this.updateDisplay_(); if (this.updateArgumentReporterNames_) { this.updateArgumentReporterNames_(prevArgIds, prevDisplayNames); } }; // End of serialization and deserialization. // Shared by all three procedure blocks (procedures_declaration, // procedures_call, and procedures_prototype). /** * Returns the name of the procedure this block calls, or the empty string if * it has not yet been set. * @return {string} Procedure name. * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.getProcCode = function () { return this.procCode_; }; /** * Update the block's structure and appearance to match the internally stored * mutation. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function () { var wasRendered = this.rendered; this.rendered = false; var connectionMap = this.disconnectOldBlocks_(); this.removeAllInputs_(); this.createAllInputs_(connectionMap); this.deleteShadows_(connectionMap); this.rendered = wasRendered; if (wasRendered && !this.isInsertionMarker()) { this.initSvg(); this.render(); } }; /** * Disconnect old blocks from all value inputs on this block, but hold onto them * in case they can be reattached later. Also save the shadow DOM if it exists. * The result is a map from argument ID to information that was associated with * that argument at the beginning of the mutation. * @return {!Object.<string, {shadow: Element, block: Blockly.Block}>} An object * mapping argument IDs to blocks and shadow DOMs. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_ = function () { // Remove old stuff var connectionMap = {}; for (var i = 0, input; (input = this.inputList[i]); i++) { if (input.connection) { var target = input.connection.targetBlock(); var saveInfo = { shadow: input.connection.getShadowDom(), block: target }; connectionMap[input.name] = saveInfo; // Remove the shadow DOM, then disconnect the block. Otherwise a shadow // block will respawn instantly, and we'd have to remove it when we remove // the input. input.connection.setShadowDom(null); if (target) { input.connection.disconnect(); } } } return connectionMap; }; /** * Remove all inputs on the block, including dummy inputs. * Assumes no input has shadow DOM set. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function () { // Delete inputs directly instead of with block.removeInput to avoid splicing // out of the input list at every index. for (var i = 0, input; (input = this.inputList[i]); i++) { input.dispose(); } this.inputList = []; }; /** * Create all inputs specified by the new procCode, and populate them with * shadow blocks or reconnected old blocks as appropriate. * @param {!Object.<string, {shadow: Element, block: Blockly.Block}>} * connectionMap An object mapping argument IDs to blocks and shadow DOMs. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function ( connectionMap ) { // Split the proc into components, by %n, %b, and %s (ignoring escaped). var procComponents = this.procCode_.split(/(?=[^\\]%[nbs])/); procComponents = procComponents.map(function (c) { return c.trim(); // Strip whitespace. }); // Create arguments and labels as appropriate. var argumentCount = 0; for (var i = 0, component; (component = procComponents[i]); i++) { var labelText; if (component.substring(0, 1) == '%') { var argumentType = component.substring(1, 2); if ( !(argumentType == 'n' || argumentType == 'b' || argumentType == 's') ) { throw new Error( 'Found an custom procedure with an invalid type: ' + argumentType ); } labelText = component.substring(2).trim(); var id = this.argumentIds_[argumentCount]; var input = this.appendValueInput(id); if (argumentType == 'b') { input.setCheck('Boolean'); } this.populateArgument_( argumentType, argumentCount, connectionMap, id, input ); argumentCount++; } else { labelText = component.trim(); } this.addProcedureLabel_(labelText.replace(/\\%/, '%')); } }; /** * Delete all shadow blocks in the given map. * @param {!Object.<string, Blockly.Block>} connectionMap An object mapping * argument IDs to the blocks that were connected to those IDs at the * beginning of the mutation. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function (connectionMap) { // Get rid of all of the old shadow blocks if they aren't connected. if (connectionMap) { for (var id in connectionMap) { var saveInfo = connectionMap[id]; if (saveInfo) { var block = saveInfo['block']; if (block && block.isShadow()) { block.dispose(); connectionMap[id] = null; // At this point we know which shadow DOMs are about to be orphaned in // the VM. What do we do with that information? } } } } }; // End of shared code. /** * Add a label field with the given text to a procedures_call or * procedures_prototype block. * @param {string} text The label text. * @private */ Blockly.ScratchBlocks.ProcedureUtils.addLabelField_ = function (text) { this.appendDummyInput().appendField(text); }; /** * Add a label editor with the given text to a procedures_declaration * block. Editing the text in the label editor updates the text of the * corresponding label fields on function calls. * @param {string} text The label text. * @private */ Blockly.ScratchBlocks.ProcedureUtils.addLabelEditor_ = function (text) { if (text) { this.appendDummyInput(Blockly.utils.genUid()).appendField( new Blockly.FieldTextInputRemovable(text) ); } }; /** * Build a DOM node representing a shadow block of the given type. * @param {string} type One of 's' (string) or 'n' (number). * @return {!Element} The DOM node representing the new shadow block. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ = function (type) { var shadowDom = goog.dom.createDom('shadow'); if (type == 'n') { var shadowType = 'math_number'; var fieldName = 'NUM'; var fieldValue = '1'; } else { var shadowType = 'text'; var fieldName = 'TEXT'; var fieldValue = ''; } shadowDom.setAttribute('type', shadowType); var fieldDom = goog.dom.createDom('field', null, fieldValue); fieldDom.setAttribute('name', fieldName); shadowDom.appendChild(fieldDom); return shadowDom; }; /** * Create a new shadow block and attach it to the given input. * @param {!Blockly.Input} input The value input to attach a block to. * @param {string} argumentType One of 'b' (boolean), 's' (string) or * 'n' (number). * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function ( input, argumentType ) { if (argumentType == 'n' || argumentType == 's') { var blockType = argumentType == 'n' ? 'math_number' : 'text'; Blockly.Events.disable(); try { var newBlock = this.workspace.newBlock(blockType); if (argumentType == 'n') { newBlock.setFieldValue('1', 'NUM'); } else { newBlock.setFieldValue('', 'TEXT'); } newBlock.setShadow(true); if (!this.isInsertionMarker()) { newBlock.initSvg(); newBlock.render(false); } } finally { Blockly.Events.enable(); } if (Blockly.Events.isEnabled()) { Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); } newBlock.outputConnection.connect(input.connection); } }; /** * Create a new argument reporter block. * @param {string} argumentType One of 'b' (boolean), 's' (string) or * 'n' (number). * @param {string} displayName The name of the argument as provided by the * user, which becomes the text of the label on the argument reporter block. * @return {!Blockly.BlockSvg} The newly created argument reporter block. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ = function ( argumentType, displayName ) { if (argumentType == 'n' || argumentType == 's') { var blockType = 'argument_reporter_string_number'; } else { var blockType = 'argument_reporter_boolean'; } Blockly.Events.disable(); try { var newBlock = this.workspace.newBlock(blockType); newBlock.setShadow(true); newBlock.setFieldValue(displayName, 'VALUE'); if (!this.isInsertionMarker()) { newBlock.initSvg(); newBlock.render(false); } } finally { Blockly.Events.enable(); } if (Blockly.Events.isEnabled()) { Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); } return newBlock; }; /** * Populate the argument by attaching the correct child block or shadow to the * given input. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). * @param {number} index The index of this argument into the argument id array. * @param {!Object.<string, {shadow: Element, block: Blockly.Block}>} * connectionMap An object mapping argument IDs to blocks and shadow DOMs. * @param {string} id The ID of the input to populate. * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_ = function ( type, index, connectionMap, id, input ) { var oldBlock = null; var oldShadow = null; if (connectionMap && id in connectionMap) { var saveInfo = connectionMap[id]; oldBlock = saveInfo['block']; oldShadow = saveInfo['shadow']; } if (connectionMap && oldBlock) { // Reattach the old block and shadow DOM. connectionMap[input.name] = null; oldBlock.outputConnection.connect(input.connection); if (type != 'b' && this.generateShadows_) { var shadowDom = oldShadow || this.buildShadowDom_(type); input.connection.setShadowDom(shadowDom); } } else if (this.generateShadows_) { this.attachShadow_(input, type); } }; /** * Populate the argument by attaching the correct argument reporter to the given * input. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). * @param {number} index The index of this argument into the argument ID and * argument display name arrays. * @param {!Object.<string, {shadow: Element, block: Blockly.Block}>} * connectionMap An object mapping argument IDs to blocks and shadow DOMs. * @param {string} id The ID of the input to populate. * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_ = function ( type, index, connectionMap, id, input ) { var oldBlock = null; if (connectionMap && id in connectionMap) { var saveInfo = connectionMap[id]; oldBlock = saveInfo['block']; } var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_( oldBlock, type ); var displayName = this.displayNames_[index]; // Decide which block to attach. if (connectionMap && oldBlock && oldTypeMatches) { // Update the text if needed. The old argument reporter is the same type, // and on the same input, but the argument's display name may have changed. var argumentReporter = oldBlock; argumentReporter.setFieldValue(displayName, 'VALUE'); connectionMap[input.name] = null; } else { var argumentReporter = this.createArgumentReporter_(type, displayName); } // Attach the block. input.connection.connect(argumentReporter.outputConnection); }; /** * Populate the argument by attaching the correct argument editor to the given * input. * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). * @param {number} index The index of this argument into the argument id and * argument display name arrays. * @param {!Object.<string, {shadow: Element, block: Blockly.Block}>} * connectionMap An object mapping argument IDs to blocks and shadow DOMs. * @param {string} id The ID of the input to populate. * @param {!Blockly.Input} input The newly created input to populate. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_ = function ( type, index, connectionMap, id, input ) { var oldBlock = null; if (connectionMap && id in connectionMap) { var saveInfo = connectionMap[id]; oldBlock = saveInfo['block']; } // TODO: This always returns false, because it checks for argument reporter // blocks instead of argument editor blocks. Create a new version for argument // editors. var oldTypeMatches = Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_( oldBlock, type ); var displayName = this.displayNames_[index]; // Decide which block to attach. if (oldBlock && oldTypeMatches) { var argumentEditor = oldBlock; oldBlock.setFieldValue(displayName, 'TEXT'); connectionMap[input.name] = null; } else { var argumentEditor = this.createArgumentEditor_(type, displayName); } // Attach the block. input.connection.connect(argumentEditor.outputConnection); }; /** * Check whether the type of the old block corresponds to the given argument * type. * @param {Blockly.BlockSvg} oldBlock The old block to check. * @param {string} type The argument type. One of 'n', 'n', or 's'. * @return {boolean} True if the type matches, false otherwise. */ Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function ( oldBlock, type ) { if (!oldBlock) { return false; } if ( (type == 'n' || type == 's') && oldBlock.type == 'argument_reporter_string_number' ) { return true; } if (type == 'b' && oldBlock.type == 'argument_reporter_boolean') { return true; } return false; }; /** * Create an argument editor. * An argument editor is a shadow block with a single text field, which is used * to set the display name of the argument. * @param {string} argumentType One of 'b' (boolean), 's' (string) or * 'n' (number). * @param {string} displayName The display name of this argument, which is the * text of the field on the shadow block. * @return {!Blockly.BlockSvg} The newly created argument editor block. * @private * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_ = function ( argumentType, displayName ) { Blockly.Events.disable(); try { if (argumentType == 'n' || argumentType == 's') { var newBlock = this.workspace.newBlock('argument_editor_string_number'); } else { var newBlock = this.workspace.newBlock('argument_editor_boolean'); } newBlock.setFieldValue(displayName, 'TEXT'); newBlock.setShadow(true); if (!this.isInsertionMarker()) { newBlock.initSvg(); newBlock.render(false); } } finally { Blockly.Events.enable(); } if (Blockly.Events.isEnabled()) { Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); } return newBlock; }; /** * Update the serializable information on the block based on the existing inputs * and their text. */ Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function () { this.procCode_ = ''; this.displayNames_ = []; this.argumentIds_ = []; for (var i = 0; i < this.inputList.length; i++) { if (i != 0) { this.procCode_ += ' '; } var input = this.inputList[i]; if (input.type == Blockly.DUMMY_INPUT) { this.procCode_ += input.fieldRow[0].getValue(); } else if (input.type == Blockly.INPUT_VALUE) { // Inspect the argument editor. var target = input.connection.targetBlock(); this.displayNames_.push(target.getFieldValue('TEXT')); this.argumentIds_.push(input.name); if (target.type == 'argument_editor_boolean') { this.procCode_ += '%b'; } else { this.procCode_ += '%s'; } } else { throw new Error( 'Unexpected input type on a procedure mutator root: ' + input.type ); } } }; /** * Focus on the last argument editor or label editor on the block. * @private */ Blockly.ScratchBlocks.ProcedureUtils.focusLastEditor_ = function () { if (this.inputList.length > 0) { var newInput = this.inputList[this.inputList.length - 1]; if (newInput.type == Blockly.DUMMY_INPUT) { newInput.fieldRow[0].showEditor_(); } else if (newInput.type == Blockly.INPUT_VALUE) { // Inspect the argument editor. var target = newInput.connection.targetBlock(); target.getField('TEXT').showEditor_(); } } }; /** * Externally-visible function to add a label to the procedure declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function () { Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' label text'; this.updateDisplay_(); this.focusLastEditor_(); }; /** * Externally-visible function to add a boolean argument to the procedure * declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function () { Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' %b'; this.displayNames_.push('boolean'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push('false'); this.updateDisplay_(); this.focusLastEditor_(); }; /** * Externally-visible function to add a string/number argument to the procedure * declaration. * @public */ Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function () { Blockly.WidgetDiv.hide(true); this.procCode_ = this.procCode_ + ' %s'; this.displayNames_.push('number or text'); this.argumentIds_.push(Blockly.utils.genUid()); this.argumentDefaults_.push(''); this.updateDisplay_(); this.focusLastEditor_(); }; /** * Externally-visible function to get the warp on procedure declaration. * @return {boolean} The value of the warp_ property. * @public */ Blockly.ScratchBlocks.ProcedureUtils.getWarp = function () { return this.warp_; }; /** * Externally-visible function to set the warp on procedure declaration. * @param {boolean} warp The value of the warp_ property. * @public */ Blockly.ScratchBlocks.ProcedureUtils.setWarp = function (warp) { this.warp_ = warp; }; /** * Callback to remove a field, only for the declaration block. * @param {Blockly.Field} field The field being removed. * @public */ Blockly.ScratchBlocks.ProcedureUtils.removeFieldCallback = function (field) { // Do not delete if there is only one input if (this.inputList.length === 1) { return; } var inputNameToRemove = null; for (var n = 0; n < this.inputList.length; n++) { var input = this.inputList[n]; if (input.connection) { var target = input.connection.targetBlock(); if (target.getField(field.name) == field) { inputNameToRemove = input.name; } } else { for (var j = 0; j < input.fieldRow.length; j++) { if (input.fieldRow[j] == field) { inputNameToRemove = input.name; } } } } if (inputNameToRemove) { Blockly.WidgetDiv.hide(true); this.removeInput(inputNameToRemove); this.onChangeFn(); this.updateDisplay_(); } }; /** * Callback to pass removeField up to the declaration block from arguments. * @param {Blockly.Field} field The field being removed. * @public */ Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ = function ( field ) { if (this.parentBlock_ && this.parentBlock_.removeFieldCallback) { this.parentBlock_.removeFieldCallback(field); } }; /** * Update argument reporter field values after an edit to the prototype mutation * using previous argument ids and names. * Because the argument reporters only store names and not which argument ids they * are linked to, it would not be safe to update all argument reporters on the workspace * since they may be argument reporters with the same name from a different procedure. * Until there is a more explicit way of identifying argument reporter blocks using ids, * be conservative and only update argument reporters that are used in the * stack below the prototype, ie the definition. * @param {!Array<string>} prevArgIds The previous ordering of argument ids. * @param {!Array<string>} prevDisplayNames The previous argument names. * @this Blockly.Block */ Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ = function ( prevArgIds, prevDisplayNames ) { var nameChanges = []; var argReporters = []; var definitionBlock = this.getParent(); if (!definitionBlock) return; // Create a list of argument reporters that are descendants of the definition stack (see above comment) var allBlocks = definitionBlock.getDescendants(false); for (var i = 0; i < allBlocks.length; i++) { var block = allBlocks[i]; if ( (block.type === 'argument_reporter_string_number' || block.type === 'argument_reporter_boolean') && !block.isShadow() ) { // Exclude arg reporters in the prototype block, which are shadows. argReporters.push(block); } } // Create a list of "name changes", including the new name and blocks matching the old name // Only search over the current set of argument ids, ignore args that have been removed for (var i = 0, id; (id = this.argumentIds_[i]); i++) { // Find the previous index of this argument id. Could be -1 if it is newly added. var prevIndex = prevArgIds.indexOf(id); if (prevIndex == -1) continue; // Newly added argument, no corresponding previous argument to update. var prevName = prevDisplayNames[prevIndex]; if (prevName != this.displayNames_[i]) { nameChanges.push({ newName: this.displayNames_[i], blocks: argReporters.filter(function (block) { return block.getFieldValue('VALUE') == prevName; }) }); } } // Finally update the blocks for each name change. // Do this after creating the lists to avoid cycles of renaming. for (var j = 0, nameChange; (nameChange = nameChanges[j]); j++) { for (var k = 0, block; (block = nameChange.blocks[k]); k++) { block.setFieldValue(nameChange.newName, 'VALUE'); } } }; Blockly.Blocks['procedures_definition'] = { /** * Block for defining a procedure with no return value. * @this Blockly.Block */ init: function () { this.jsonInit({ message0: Blockly.Msg.PROCEDURES_DEFINITION, args0: [ { type: 'input_statement', name: 'custom_block' } ], extensions: ['colours_more', 'shape_hat', 'procedure_def_contextmenu'] }); } }; Blockly.Blocks['procedures_call'] = { /** * Block for calling a procedure with no return value. * @this Blockly.Block */ init: function () { this.jsonInit({ extensions: [ 'colours_more', 'shape_statement', 'procedure_call_contextmenu' ] }); this.procCode_ = ''; this.argumentIds_ = []; this.warp_ = false; }, // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, // Exist on all three blocks, but have different implementations. mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation, populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_, addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, // Only exists on the external caller. attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ }; Blockly.Blocks['procedures_prototype'] = { /** * Block for calling a procedure with no return value, for rendering inside * define block. * @this Blockly.Block */ init: function () { this.jsonInit({ extensions: ['colours_more', 'shape_statement'] }); /* Data known about the procedure. */ this.procCode_ = ''; this.displayNames_ = []; this.argumentIds_ = []; this.argumentDefaults_ = []; this.warp_ = false; }, // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, // Exist on all three blocks, but have different implementations. mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_, addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, // Only exists on procedures_prototype. createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_, updateArgumentReporterNames_: Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ }; Blockly.Blocks['procedures_declaration'] = { /** * The root block in the procedure declaration editor. * @this Blockly.Block */ init: function () { this.jsonInit({ extensions: ['colours_more', 'shape_statement'] }); /* Data known about the procedure. */ this.procCode_ = ''; this.displayNames_ = []; this.argumentIds_ = []; this.argumentDefaults_ = []; this.warp_ = false; }, // Shared. getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, // Exist on all three blocks, but have different implementations. mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_, addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelEditor_, // Exist on declaration and arguments editors, with different implementations. removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeFieldCallback, // Only exist on procedures_declaration. createArgumentEditor_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_, focusLastEditor_: Blockly.ScratchBlocks.ProcedureUtils.focusLastEditor_, getWarp: Blockly.ScratchBlocks.ProcedureUtils.getWarp, setWarp: Blockly.ScratchBlocks.ProcedureUtils.setWarp, addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ }; Blockly.Blocks['argument_reporter_boolean'] = { init: function () { this.jsonInit({ message0: ' %1', args0: [ { type: 'field_label_serializable', name: 'VALUE', text: '' } ], extensions: ['colours_more', 'output_boolean'] }); } }; Blockly.Blocks['argument_reporter_string_number'] = { init: function () { this.jsonInit({ message0: ' %1', args0: [ { type: 'field_label_serializable', name: 'VALUE', text: '' } ], extensions: ['colours_more', 'output_number', 'output_string'] }); } }; Blockly.Blocks['argument_editor_boolean'] = { init: function () { this.jsonInit({ message0: ' %1', args0: [ { type: 'field_input_removable', name: 'TEXT', text: 'foo' } ], colour: Blockly.Colours.textField, colourSecondary: Blockly.Colours.textField, colourTertiary: Blockly.Colours.textField, extensions: ['output_boolean'] }); }, // Exist on declaration and arguments editors, with different implementations. removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ }; Blockly.Blocks['argument_editor_string_number'] = { init: function () { this.jsonInit({ message0: ' %1', args0: [ { type: 'field_input_removable', name: 'TEXT', text: 'foo' } ], colour: Blockly.Colours.textField, colourSecondary: Blockly.Colours.textField, colourTertiary: Blockly.Colours.textField, extensions: ['output_number', 'output_string'] }); }, // Exist on declaration and arguments editors, with different implementations. removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ };