/** * @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 Loop blocks for Blockly. * @author fraser@google.com (Neil Fraser) */ 'use strict'; goog.provide('Blockly.Blocks.loops'); goog.require('Blockly.Blocks'); Blockly.Blocks['controls_repeat'] = { /** * Block for repeat n times (internal number). * @this Blockly.Block */ init: function() { this.setHelpUrl(Blockly.Msg.CONTROLS_REPEAT_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.appendDummyInput() .appendField(Blockly.Msg.CONTROLS_REPEAT_TITLE_REPEAT) .appendField(new Blockly.FieldTextInput('10', Blockly.FieldTextInput.nonnegativeIntegerValidator), 'TIMES') .appendField(Blockly.Msg.CONTROLS_REPEAT_TITLE_TIMES); this.appendStatementInput('DO') .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO); this.setPreviousStatement(true); this.setNextStatement(true); this.setTooltip(Blockly.Msg.CONTROLS_REPEAT_TOOLTIP); } }; Blockly.Blocks['controls_repeat_ext'] = { /** * Block for repeat n times (external number). * @this Blockly.Block */ init: function() { this.setHelpUrl(Blockly.Msg.CONTROLS_REPEAT_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.interpolateMsg(Blockly.Msg.CONTROLS_REPEAT_TITLE, ['TIMES', 'Number', Blockly.ALIGN_RIGHT], Blockly.ALIGN_RIGHT); this.appendStatementInput('DO') .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO); this.setPreviousStatement(true); this.setNextStatement(true); this.setInputsInline(true); this.setTooltip(Blockly.Msg.CONTROLS_REPEAT_TOOLTIP); } }; Blockly.Blocks['controls_whileUntil'] = { /** * Block for 'do while/until' loop. * @this Blockly.Block */ init: function() { var OPERATORS = [[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE, 'WHILE'], [Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL, 'UNTIL']]; this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.appendValueInput('BOOL') .setCheck('Boolean') .appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE'); this.appendStatementInput('DO') .appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO); this.setPreviousStatement(true); this.setNextStatement(true); // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { var op = thisBlock.getFieldValue('MODE'); var TOOLTIPS = { 'WHILE': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE, 'UNTIL': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL }; return TOOLTIPS[op]; }); } }; Blockly.Blocks['controls_for'] = { /** * Block for 'for' loop. * @this Blockly.Block * this blockscad version has typing information */ init: function() { this.category = 'LOOP'; // for Blockscad typing - jayod this.setHelpUrl(Blockly.Msg.CONTROLS_FOR_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.jsonInit({ "message0": Blockly.Msg.CONTROLS_FOR_TITLE, "args0": [ { "type": "field_variable", "name": "VAR", "variable": null }, { "type": "input_value", "name": "FROM", "check": "Number", "align": "RIGHT" }, { "type": "input_value", "name": "TO", "check": "Number", "align": "RIGHT" }, { "type": "input_value", "name": "BY", "check": "Number", "align": "RIGHT" } ], "inputsInline": true, "previousStatement": null, }); this.appendDummyInput() .appendField("(" + Blockscad.Msg.CONVEX_HULL) .appendField(new Blockly.FieldCheckbox('FALSE'), 'HULL') .appendField(")"); this.appendStatementInput('DO') .appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO) .setCheck(['CSG','CAG']); this.setPreviousStatement(true,['CSG','CAG']); // jayod - blockscad typing //this.setNextStatement(true); // jayod - blockscad this.setInputsInline(true); // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { return Blockly.Msg.CONTROLS_FOR_TOOLTIP.replace('%1', thisBlock.getFieldValue('VAR')) + "\n" + Blockscad.Msg.CONTROLS_FOR_TOOLTIP_CHAINHULL; }); }, /** * Return all variables referenced by this block. * @return {!Array.<string>} List of variable names. * @this Blockly.Block */ getVars: function() { return [this.getFieldValue('VAR')]; }, /** * 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) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } }, /** * Add menu option to create getter block for loop variable. * @param {!Array} options List of menu options to add to. * @this Blockly.Block */ customContextMenu: function(options) { if (!this.isCollapsed()) { var option = {enabled: true}; var name = this.getFieldValue('VAR'); option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name); var xmlField = goog.dom.createDom('field', null, name); xmlField.setAttribute('name', 'VAR'); var xmlBlock = goog.dom.createDom('block', null, xmlField); xmlBlock.setAttribute('type', 'variables_get'); option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); options.push(option); } }, setType: function(type) { // for blockscad typing - jayod if (!this.workspace) { // Block has been deleted. return; } this.previousConnection.setCheck(type); this.getInput('DO').connection.setCheck(type); } }; Blockly.Blocks['controls_for_chainhull'] = { /** * Block for 'for' loop. * @this Blockly.Block * this blockscad version has typing information */ init: function() { this.category = 'LOOP'; // for Blockscad typing - jayod this.setHelpUrl(Blockly.Msg.CONTROLS_FOR_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.jsonInit({ "message0": Blockly.Msg.CONTROLS_FOR_TITLE, "args0": [ { "type": "field_variable", "name": "VAR", "variable": null }, { "type": "input_value", "name": "FROM", "check": "Number", "align": "RIGHT" }, { "type": "input_value", "name": "TO", "check": "Number", "align": "RIGHT" }, { "type": "input_value", "name": "BY", "check": "Number", "align": "RIGHT" } ], "inputsInline": true, "previousStatement": null, }); this.appendStatementInput('DO') .appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO) .setCheck(['CSG','CAG']); this.setPreviousStatement(true,['CSG','CAG']); // jayod - blockscad typing // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { return Blockscad.Msg.CONTROLS_FOR_TOOLTIP_CHAINHULL; }); }, /** * Return all variables referenced by this block. * @return {!Array.<string>} List of variable names. * @this Blockly.Block */ getVars: function() { return [this.getFieldValue('VAR')]; }, /** * 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) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } }, /** * Add menu option to create getter block for loop variable. * @param {!Array} options List of menu options to add to. * @this Blockly.Block */ customContextMenu: function(options) { if (!this.isCollapsed()) { var option = {enabled: true}; var name = this.getFieldValue('VAR'); option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name); var xmlField = goog.dom.createDom('field', null, name); xmlField.setAttribute('name', 'VAR'); var xmlBlock = goog.dom.createDom('block', null, xmlField); xmlBlock.setAttribute('type', 'variables_get'); option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); options.push(option); } }, setType: function(type) { // for blockscad typing - jayod if (!this.workspace) { // Block has been deleted. return; } this.previousConnection.setCheck(type); this.getInput('DO').connection.setCheck(type); } }; Blockly.Blocks['controls_forEach'] = { /** * Block for 'for each' loop. * @this Blockly.Block */ init: function() { this.setHelpUrl(Blockly.Msg.CONTROLS_FOREACH_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.appendValueInput('LIST') .setCheck('Array') .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_ITEM) .appendField(new Blockly.FieldVariable(null), 'VAR') .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_INLIST); if (Blockly.Msg.CONTROLS_FOREACH_INPUT_INLIST_TAIL) { this.appendDummyInput() .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_INLIST_TAIL); this.setInputsInline(true); } this.appendStatementInput('DO') .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_DO); this.setPreviousStatement(true); this.setNextStatement(true); // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { return Blockly.Msg.CONTROLS_FOREACH_TOOLTIP.replace('%1', thisBlock.getFieldValue('VAR')); }); }, /** * Return all variables referenced by this block. * @return {!Array.<string>} List of variable names. * @this Blockly.Block */ getVars: function() { return [this.getFieldValue('VAR')]; }, /** * 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) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(newName, 'VAR'); } }, customContextMenu: Blockly.Blocks['controls_for'].customContextMenu }; Blockly.Blocks['controls_flow_statements'] = { /** * Block for flow statements: continue, break. * @this Blockly.Block */ init: function() { var OPERATORS = [[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK, 'BREAK'], [Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE, 'CONTINUE']]; this.setHelpUrl(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL); this.setColour(Blockscad.Toolbox.HEX_LOOP); this.appendDummyInput() .appendField(new Blockly.FieldDropdown(OPERATORS), 'FLOW'); this.setPreviousStatement(true); // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { var op = thisBlock.getFieldValue('FLOW'); var TOOLTIPS = { 'BREAK': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK, 'CONTINUE': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE }; return TOOLTIPS[op]; }); }, /** * Called whenever anything on the workspace changes. * Add warning if this flow block is not nested inside a loop. * @this Blockly.Block */ onchange: function() { var legal = false; // Is the block nested in a loop? var block = this; do { if (block.type == 'controls_repeat' || block.type == 'controls_repeat_ext' || block.type == 'controls_forEach' || block.type == 'controls_for' || block.type == 'controls_whileUntil') { legal = true; break; } block = block.getSurroundParent(); } while (block); if (legal) { this.setWarningText(null); } else { this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING); } } };