loops.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /**
  2. * @license
  3. * Visual Blocks Editor
  4. *
  5. * Copyright 2012 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Loop blocks for Blockly.
  22. * @author fraser@google.com (Neil Fraser)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Blocks.loops');
  26. goog.require('Blockly.Blocks');
  27. /**
  28. * Common HSV hue for all blocks in this category.
  29. */
  30. Blockly.Blocks.loops.HUE = "#9d64fd";
  31. Blockly.Blocks['controls_repeat_ext'] = {
  32. /**
  33. * Block for repeat n times (external number).
  34. * @this Blockly.Block
  35. */
  36. init: function() {
  37. this.jsonInit({
  38. "message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
  39. "args0": [
  40. {
  41. "type": "input_value",
  42. "name": "TIMES",
  43. "check": "Number"
  44. }
  45. ],
  46. "previousStatement": null,
  47. "nextStatement": null,
  48. "colour": Blockly.Blocks.loops.HUE,
  49. "tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
  50. "helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
  51. });
  52. this.appendStatementInput('DO')
  53. .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
  54. }
  55. };
  56. Blockly.Blocks['controls_repeat'] = {
  57. /**
  58. * Block for repeat n times (internal number).
  59. * The 'controls_repeat_ext' block is preferred as it is more flexible.
  60. * @this Blockly.Block
  61. */
  62. init: function() {
  63. this.jsonInit({
  64. "message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
  65. "args0": [
  66. {
  67. "type": "field_number",
  68. "name": "TIMES",
  69. "value": 10,
  70. "min": 0,
  71. "precision": 1
  72. }
  73. ],
  74. "previousStatement": null,
  75. "nextStatement": null,
  76. "colour": Blockly.Blocks.loops.HUE,
  77. "tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
  78. "helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
  79. });
  80. this.appendStatementInput('DO')
  81. .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
  82. }
  83. };
  84. Blockly.Blocks['controls_whileUntil'] = {
  85. /**
  86. * Block for 'do while/until' loop.
  87. * @this Blockly.Block
  88. */
  89. init: function() {
  90. var OPERATORS =
  91. [[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE, 'WHILE'],
  92. [Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL, 'UNTIL']];
  93. this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);
  94. this.setColour(Blockly.Blocks.loops.HUE);
  95. this.appendValueInput('BOOL')
  96. .setCheck('Boolean')
  97. .appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
  98. this.appendStatementInput('DO')
  99. .appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);
  100. this.setPreviousStatement(true);
  101. this.setNextStatement(true);
  102. // Assign 'this' to a variable for use in the tooltip closure below.
  103. var thisBlock = this;
  104. this.setTooltip(function() {
  105. var op = thisBlock.getFieldValue('MODE');
  106. var TOOLTIPS = {
  107. 'WHILE': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE,
  108. 'UNTIL': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL
  109. };
  110. return TOOLTIPS[op];
  111. });
  112. }
  113. };
  114. Blockly.Blocks['controls_while'] = {
  115. /**
  116. * Block for 'while' loop.
  117. * @this Blockly.Block
  118. */
  119. init: function() {
  120. this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);
  121. this.setColour(Blockly.Blocks.loops.HUE);
  122. this.appendValueInput('BOOL')
  123. .setCheck('Boolean')
  124. .appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE);
  125. this.appendStatementInput('DO')
  126. .appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);
  127. this.setPreviousStatement(true);
  128. this.setNextStatement(true);
  129. this.setTooltip(Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE);
  130. }
  131. };
  132. Blockly.Blocks['controls_for'] = {
  133. /**
  134. * Block for 'for' loop.
  135. * @this Blockly.Block
  136. */
  137. init: function() {
  138. this.jsonInit({
  139. "message0": Blockly.Msg.CONTROLS_FOR_TITLE,
  140. "args0": [
  141. {
  142. "type": "field_variable",
  143. "name": "VAR",
  144. "variable": null
  145. },
  146. {
  147. "type": "input_value",
  148. "name": "FROM",
  149. "check": "Number",
  150. "align": "RIGHT"
  151. },
  152. {
  153. "type": "input_value",
  154. "name": "TO",
  155. "check": "Number",
  156. "align": "RIGHT"
  157. },
  158. {
  159. "type": "input_value",
  160. "name": "BY",
  161. "check": "Number",
  162. "align": "RIGHT"
  163. }
  164. ],
  165. "inputsInline": true,
  166. "previousStatement": null,
  167. "nextStatement": null,
  168. "colour": Blockly.Blocks.loops.HUE,
  169. "helpUrl": Blockly.Msg.CONTROLS_FOR_HELPURL
  170. });
  171. this.appendStatementInput('DO')
  172. .appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO);
  173. // Assign 'this' to a variable for use in the tooltip closure below.
  174. var thisBlock = this;
  175. this.setTooltip(function() {
  176. return Blockly.Msg.CONTROLS_FOR_TOOLTIP.replace('%1',
  177. thisBlock.getFieldValue('VAR'));
  178. });
  179. },
  180. /**
  181. * Add menu option to create getter block for loop variable.
  182. * @param {!Array} options List of menu options to add to.
  183. * @this Blockly.Block
  184. */
  185. customContextMenu: function(options) {
  186. if (!this.isCollapsed()) {
  187. var option = {enabled: true};
  188. var name = this.getFieldValue('VAR');
  189. option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
  190. var xmlField = goog.dom.createDom('field', null, name);
  191. xmlField.setAttribute('name', 'VAR');
  192. var xmlBlock = goog.dom.createDom('block', null, xmlField);
  193. xmlBlock.setAttribute('type', 'variables_get');
  194. option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
  195. options.push(option);
  196. }
  197. }
  198. };
  199. Blockly.Blocks['controls_forEach'] = {
  200. /**
  201. * Block for 'for each' loop.
  202. * @this Blockly.Block
  203. */
  204. init: function() {
  205. this.jsonInit({
  206. "message0": Blockly.Msg.CONTROLS_FOREACH_TITLE,
  207. "args0": [
  208. {
  209. "type": "field_variable",
  210. "name": "VAR",
  211. "variable": null
  212. },
  213. {
  214. "type": "input_value",
  215. "name": "LIST",
  216. "check": "Array"
  217. }
  218. ],
  219. "previousStatement": null,
  220. "nextStatement": null,
  221. "colour": Blockly.Blocks.loops.HUE,
  222. "helpUrl": Blockly.Msg.CONTROLS_FOREACH_HELPURL
  223. });
  224. this.appendStatementInput('DO')
  225. .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_DO);
  226. // Assign 'this' to a variable for use in the tooltip closure below.
  227. var thisBlock = this;
  228. this.setTooltip(function() {
  229. return Blockly.Msg.CONTROLS_FOREACH_TOOLTIP.replace('%1',
  230. thisBlock.getFieldValue('VAR'));
  231. });
  232. },
  233. customContextMenu: Blockly.Blocks['controls_for'].customContextMenu
  234. };
  235. Blockly.Blocks['controls_pass'] = {
  236. init: function() {
  237. this.setHelpUrl("");
  238. this.setColour(Blockly.Blocks.loops.HUE);
  239. this.appendDummyInput()
  240. .appendField("do nothing");
  241. this.setPreviousStatement(true);
  242. this.setNextStatement(true);
  243. this.setTooltip("This block does absolutely nothing.");
  244. }
  245. }
  246. Blockly.Blocks['controls_flow_statements'] = {
  247. /**
  248. * Block for flow statements: continue, break.
  249. * @this Blockly.Block
  250. */
  251. init: function() {
  252. var OPERATORS =
  253. [[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK, 'BREAK'],
  254. [Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE, 'CONTINUE']];
  255. this.setHelpUrl(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL);
  256. this.setColour(Blockly.Blocks.loops.HUE);
  257. this.appendDummyInput()
  258. .appendField(new Blockly.FieldDropdown(OPERATORS), 'FLOW');
  259. this.setPreviousStatement(true);
  260. // Assign 'this' to a variable for use in the tooltip closure below.
  261. var thisBlock = this;
  262. this.setTooltip(function() {
  263. var op = thisBlock.getFieldValue('FLOW');
  264. var TOOLTIPS = {
  265. 'BREAK': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK,
  266. 'CONTINUE': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE
  267. };
  268. return TOOLTIPS[op];
  269. });
  270. },
  271. /**
  272. * Called whenever anything on the workspace changes.
  273. * Add warning if this flow block is not nested inside a loop.
  274. * @param {!Blockly.Events.Abstract} e Change event.
  275. * @this Blockly.Block
  276. */
  277. onchange: function(e) {
  278. if (this.workspace.isDragging()) {
  279. return; // Don't change state at the start of a drag.
  280. }
  281. var legal = false;
  282. // Is the block nested in a loop?
  283. var block = this;
  284. do {
  285. if (this.LOOP_TYPES.indexOf(block.type) != -1) {
  286. legal = true;
  287. break;
  288. }
  289. block = block.getSurroundParent();
  290. } while (block);
  291. if (legal) {
  292. this.setWarningText(null);
  293. if (!this.isInFlyout) {
  294. this.setDisabled(false);
  295. }
  296. } else {
  297. this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
  298. if (!this.isInFlyout && !this.getInheritedDisabled()) {
  299. this.setDisabled(true);
  300. }
  301. }
  302. },
  303. /**
  304. * List of block types that are loops and thus do not need warnings.
  305. * To add a new loop type add this to your code:
  306. * Blockly.Blocks['controls_flow_statements'].LOOP_TYPES.push('custom_loop');
  307. */
  308. LOOP_TYPES: ['controls_repeat', 'controls_repeat_ext', 'controls_forEach',
  309. 'controls_for', 'controls_whileUntil', 'controls_while']
  310. };
  311. Blockly.Blocks['loop_forEach'] = {
  312. init: function() {
  313. this.setColour(Blockly.Blocks.loops.HUE)
  314. this.appendValueInput("ITEM")
  315. .appendField(Blockly.Msg.LOOP_FOREACH_ITEM)
  316. this.appendValueInput("LIST")
  317. .appendField(Blockly.Msg.LOOP_FOREACH_LIST)
  318. this.setOutput(true);
  319. this.setInputsInline(true);
  320. }
  321. }