loops.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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. Blockly.Blocks['controls_repeat'] = {
  28. /**
  29. * Block for repeat n times (internal number).
  30. * @this Blockly.Block
  31. */
  32. init: function() {
  33. this.setHelpUrl(Blockly.Msg.CONTROLS_REPEAT_HELPURL);
  34. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  35. this.appendDummyInput()
  36. .appendField(Blockly.Msg.CONTROLS_REPEAT_TITLE_REPEAT)
  37. .appendField(new Blockly.FieldTextInput('10',
  38. Blockly.FieldTextInput.nonnegativeIntegerValidator), 'TIMES')
  39. .appendField(Blockly.Msg.CONTROLS_REPEAT_TITLE_TIMES);
  40. this.appendStatementInput('DO')
  41. .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
  42. this.setPreviousStatement(true);
  43. this.setNextStatement(true);
  44. this.setTooltip(Blockly.Msg.CONTROLS_REPEAT_TOOLTIP);
  45. }
  46. };
  47. Blockly.Blocks['controls_repeat_ext'] = {
  48. /**
  49. * Block for repeat n times (external number).
  50. * @this Blockly.Block
  51. */
  52. init: function() {
  53. this.setHelpUrl(Blockly.Msg.CONTROLS_REPEAT_HELPURL);
  54. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  55. this.interpolateMsg(Blockly.Msg.CONTROLS_REPEAT_TITLE,
  56. ['TIMES', 'Number', Blockly.ALIGN_RIGHT],
  57. Blockly.ALIGN_RIGHT);
  58. this.appendStatementInput('DO')
  59. .appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
  60. this.setPreviousStatement(true);
  61. this.setNextStatement(true);
  62. this.setInputsInline(true);
  63. this.setTooltip(Blockly.Msg.CONTROLS_REPEAT_TOOLTIP);
  64. }
  65. };
  66. Blockly.Blocks['controls_whileUntil'] = {
  67. /**
  68. * Block for 'do while/until' loop.
  69. * @this Blockly.Block
  70. */
  71. init: function() {
  72. var OPERATORS =
  73. [[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE, 'WHILE'],
  74. [Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL, 'UNTIL']];
  75. this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);
  76. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  77. this.appendValueInput('BOOL')
  78. .setCheck('Boolean')
  79. .appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
  80. this.appendStatementInput('DO')
  81. .appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);
  82. this.setPreviousStatement(true);
  83. this.setNextStatement(true);
  84. // Assign 'this' to a variable for use in the tooltip closure below.
  85. var thisBlock = this;
  86. this.setTooltip(function() {
  87. var op = thisBlock.getFieldValue('MODE');
  88. var TOOLTIPS = {
  89. 'WHILE': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE,
  90. 'UNTIL': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL
  91. };
  92. return TOOLTIPS[op];
  93. });
  94. }
  95. };
  96. Blockly.Blocks['controls_for'] = {
  97. /**
  98. * Block for 'for' loop.
  99. * @this Blockly.Block
  100. * this blockscad version has typing information
  101. */
  102. init: function() {
  103. this.category = 'LOOP'; // for Blockscad typing - jayod
  104. this.setHelpUrl(Blockly.Msg.CONTROLS_FOR_HELPURL);
  105. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  106. this.jsonInit({
  107. "message0": Blockly.Msg.CONTROLS_FOR_TITLE,
  108. "args0": [
  109. {
  110. "type": "field_variable",
  111. "name": "VAR",
  112. "variable": null
  113. },
  114. {
  115. "type": "input_value",
  116. "name": "FROM",
  117. "check": "Number",
  118. "align": "RIGHT"
  119. },
  120. {
  121. "type": "input_value",
  122. "name": "TO",
  123. "check": "Number",
  124. "align": "RIGHT"
  125. },
  126. {
  127. "type": "input_value",
  128. "name": "BY",
  129. "check": "Number",
  130. "align": "RIGHT"
  131. }
  132. ],
  133. "inputsInline": true,
  134. "previousStatement": null,
  135. });
  136. this.appendDummyInput()
  137. .appendField("(" + Blockscad.Msg.CONVEX_HULL)
  138. .appendField(new Blockly.FieldCheckbox('FALSE'), 'HULL')
  139. .appendField(")");
  140. this.appendStatementInput('DO')
  141. .appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO)
  142. .setCheck(['CSG','CAG']);
  143. this.setPreviousStatement(true,['CSG','CAG']); // jayod - blockscad typing
  144. //this.setNextStatement(true); // jayod - blockscad
  145. this.setInputsInline(true);
  146. // Assign 'this' to a variable for use in the tooltip closure below.
  147. var thisBlock = this;
  148. this.setTooltip(function() {
  149. return Blockly.Msg.CONTROLS_FOR_TOOLTIP.replace('%1',
  150. thisBlock.getFieldValue('VAR')) + "\n" + Blockscad.Msg.CONTROLS_FOR_TOOLTIP_CHAINHULL;
  151. });
  152. },
  153. /**
  154. * Return all variables referenced by this block.
  155. * @return {!Array.<string>} List of variable names.
  156. * @this Blockly.Block
  157. */
  158. getVars: function() {
  159. return [this.getFieldValue('VAR')];
  160. },
  161. /**
  162. * Notification that a variable is renaming.
  163. * If the name matches one of this block's variables, rename it.
  164. * @param {string} oldName Previous name of variable.
  165. * @param {string} newName Renamed variable.
  166. * @this Blockly.Block
  167. */
  168. renameVar: function(oldName, newName) {
  169. if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
  170. this.setFieldValue(newName, 'VAR');
  171. }
  172. },
  173. /**
  174. * Add menu option to create getter block for loop variable.
  175. * @param {!Array} options List of menu options to add to.
  176. * @this Blockly.Block
  177. */
  178. customContextMenu: function(options) {
  179. if (!this.isCollapsed()) {
  180. var option = {enabled: true};
  181. var name = this.getFieldValue('VAR');
  182. option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
  183. var xmlField = goog.dom.createDom('field', null, name);
  184. xmlField.setAttribute('name', 'VAR');
  185. var xmlBlock = goog.dom.createDom('block', null, xmlField);
  186. xmlBlock.setAttribute('type', 'variables_get');
  187. option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
  188. options.push(option);
  189. }
  190. },
  191. setType: function(type) { // for blockscad typing - jayod
  192. if (!this.workspace) {
  193. // Block has been deleted.
  194. return;
  195. }
  196. this.previousConnection.setCheck(type);
  197. this.getInput('DO').connection.setCheck(type);
  198. }
  199. };
  200. Blockly.Blocks['controls_for_chainhull'] = {
  201. /**
  202. * Block for 'for' loop.
  203. * @this Blockly.Block
  204. * this blockscad version has typing information
  205. */
  206. init: function() {
  207. this.category = 'LOOP'; // for Blockscad typing - jayod
  208. this.setHelpUrl(Blockly.Msg.CONTROLS_FOR_HELPURL);
  209. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  210. this.jsonInit({
  211. "message0": Blockly.Msg.CONTROLS_FOR_TITLE,
  212. "args0": [
  213. {
  214. "type": "field_variable",
  215. "name": "VAR",
  216. "variable": null
  217. },
  218. {
  219. "type": "input_value",
  220. "name": "FROM",
  221. "check": "Number",
  222. "align": "RIGHT"
  223. },
  224. {
  225. "type": "input_value",
  226. "name": "TO",
  227. "check": "Number",
  228. "align": "RIGHT"
  229. },
  230. {
  231. "type": "input_value",
  232. "name": "BY",
  233. "check": "Number",
  234. "align": "RIGHT"
  235. }
  236. ],
  237. "inputsInline": true,
  238. "previousStatement": null,
  239. });
  240. this.appendStatementInput('DO')
  241. .appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO)
  242. .setCheck(['CSG','CAG']);
  243. this.setPreviousStatement(true,['CSG','CAG']); // jayod - blockscad typing
  244. // Assign 'this' to a variable for use in the tooltip closure below.
  245. var thisBlock = this;
  246. this.setTooltip(function() {
  247. return Blockscad.Msg.CONTROLS_FOR_TOOLTIP_CHAINHULL;
  248. });
  249. },
  250. /**
  251. * Return all variables referenced by this block.
  252. * @return {!Array.<string>} List of variable names.
  253. * @this Blockly.Block
  254. */
  255. getVars: function() {
  256. return [this.getFieldValue('VAR')];
  257. },
  258. /**
  259. * Notification that a variable is renaming.
  260. * If the name matches one of this block's variables, rename it.
  261. * @param {string} oldName Previous name of variable.
  262. * @param {string} newName Renamed variable.
  263. * @this Blockly.Block
  264. */
  265. renameVar: function(oldName, newName) {
  266. if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
  267. this.setFieldValue(newName, 'VAR');
  268. }
  269. },
  270. /**
  271. * Add menu option to create getter block for loop variable.
  272. * @param {!Array} options List of menu options to add to.
  273. * @this Blockly.Block
  274. */
  275. customContextMenu: function(options) {
  276. if (!this.isCollapsed()) {
  277. var option = {enabled: true};
  278. var name = this.getFieldValue('VAR');
  279. option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
  280. var xmlField = goog.dom.createDom('field', null, name);
  281. xmlField.setAttribute('name', 'VAR');
  282. var xmlBlock = goog.dom.createDom('block', null, xmlField);
  283. xmlBlock.setAttribute('type', 'variables_get');
  284. option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
  285. options.push(option);
  286. }
  287. },
  288. setType: function(type) { // for blockscad typing - jayod
  289. if (!this.workspace) {
  290. // Block has been deleted.
  291. return;
  292. }
  293. this.previousConnection.setCheck(type);
  294. this.getInput('DO').connection.setCheck(type);
  295. }
  296. };
  297. Blockly.Blocks['controls_forEach'] = {
  298. /**
  299. * Block for 'for each' loop.
  300. * @this Blockly.Block
  301. */
  302. init: function() {
  303. this.setHelpUrl(Blockly.Msg.CONTROLS_FOREACH_HELPURL);
  304. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  305. this.appendValueInput('LIST')
  306. .setCheck('Array')
  307. .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_ITEM)
  308. .appendField(new Blockly.FieldVariable(null), 'VAR')
  309. .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_INLIST);
  310. if (Blockly.Msg.CONTROLS_FOREACH_INPUT_INLIST_TAIL) {
  311. this.appendDummyInput()
  312. .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_INLIST_TAIL);
  313. this.setInputsInline(true);
  314. }
  315. this.appendStatementInput('DO')
  316. .appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_DO);
  317. this.setPreviousStatement(true);
  318. this.setNextStatement(true);
  319. // Assign 'this' to a variable for use in the tooltip closure below.
  320. var thisBlock = this;
  321. this.setTooltip(function() {
  322. return Blockly.Msg.CONTROLS_FOREACH_TOOLTIP.replace('%1',
  323. thisBlock.getFieldValue('VAR'));
  324. });
  325. },
  326. /**
  327. * Return all variables referenced by this block.
  328. * @return {!Array.<string>} List of variable names.
  329. * @this Blockly.Block
  330. */
  331. getVars: function() {
  332. return [this.getFieldValue('VAR')];
  333. },
  334. /**
  335. * Notification that a variable is renaming.
  336. * If the name matches one of this block's variables, rename it.
  337. * @param {string} oldName Previous name of variable.
  338. * @param {string} newName Renamed variable.
  339. * @this Blockly.Block
  340. */
  341. renameVar: function(oldName, newName) {
  342. if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
  343. this.setFieldValue(newName, 'VAR');
  344. }
  345. },
  346. customContextMenu: Blockly.Blocks['controls_for'].customContextMenu
  347. };
  348. Blockly.Blocks['controls_flow_statements'] = {
  349. /**
  350. * Block for flow statements: continue, break.
  351. * @this Blockly.Block
  352. */
  353. init: function() {
  354. var OPERATORS =
  355. [[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK, 'BREAK'],
  356. [Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE, 'CONTINUE']];
  357. this.setHelpUrl(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL);
  358. this.setColour(Blockscad.Toolbox.HEX_LOOP);
  359. this.appendDummyInput()
  360. .appendField(new Blockly.FieldDropdown(OPERATORS), 'FLOW');
  361. this.setPreviousStatement(true);
  362. // Assign 'this' to a variable for use in the tooltip closure below.
  363. var thisBlock = this;
  364. this.setTooltip(function() {
  365. var op = thisBlock.getFieldValue('FLOW');
  366. var TOOLTIPS = {
  367. 'BREAK': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK,
  368. 'CONTINUE': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE
  369. };
  370. return TOOLTIPS[op];
  371. });
  372. },
  373. /**
  374. * Called whenever anything on the workspace changes.
  375. * Add warning if this flow block is not nested inside a loop.
  376. * @this Blockly.Block
  377. */
  378. onchange: function() {
  379. var legal = false;
  380. // Is the block nested in a loop?
  381. var block = this;
  382. do {
  383. if (block.type == 'controls_repeat' ||
  384. block.type == 'controls_repeat_ext' ||
  385. block.type == 'controls_forEach' ||
  386. block.type == 'controls_for' ||
  387. block.type == 'controls_whileUntil') {
  388. legal = true;
  389. break;
  390. }
  391. block = block.getSurroundParent();
  392. } while (block);
  393. if (legal) {
  394. this.setWarningText(null);
  395. } else {
  396. this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
  397. }
  398. }
  399. };