in_progress_blocks.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. Blockly.Blocks['procedures_defnoreturn'] = {
  2. /**
  3. * Block for defining a procedure with no return value.
  4. * @this Blockly.Block
  5. */
  6. init: function() {
  7. var nameField = new Blockly.FieldTextInput(
  8. Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,
  9. Blockly.Procedures.rename);
  10. nameField.setSpellcheck(false);
  11. this.setPreviousStatement(true);
  12. this.setNextStatement(true);
  13. this.updateShape_();
  14. this.setColour(Blockly.Blocks.procedures.HUE);
  15. this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);
  16. this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);
  17. this.arguments_ = [];
  18. this.statementConnection_ = null;
  19. },
  20. /**
  21. * Update the display of parameters for this procedure definition block.
  22. * Display a warning if there are duplicately named parameters.
  23. * @private
  24. * @this Blockly.Block
  25. */
  26. updateParams_: function() {
  27. // Check for duplicated arguments.
  28. var badArg = false;
  29. var hash = {};
  30. for (var i = 0; i < this.arguments_.length; i++) {
  31. if (hash['arg_' + this.arguments_[i].toLowerCase()]) {
  32. badArg = true;
  33. break;
  34. }
  35. hash['arg_' + this.arguments_[i].toLowerCase()] = true;
  36. }
  37. if (badArg) {
  38. this.setWarningText(Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING);
  39. } else {
  40. this.setWarningText(null);
  41. }
  42. },
  43. updateShape_: function() {
  44. var that = this;
  45. function addField(field, block, e) {
  46. var rect = field.fieldGroup_.getBoundingClientRect();
  47. var yPosition = e.clientY;
  48. if (yPosition < rect.top+rect.height/2) {
  49. this.arguments_
  50. that.itemCount_ += 1;
  51. that.addParameter(that.itemCount_);
  52. } else {
  53. if (that.itemCount_ > 0) {
  54. that.removeInput('VALUE' + that.itemCount_);
  55. that.itemCount_ -= 1;
  56. }
  57. }
  58. }
  59. var clickablePlusMinus = new Blockly.FieldClickImage("images/plus_minus_v.png", 12, 24, '+', addField, '-2px');
  60. this.appendDummyInput()
  61. .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE)
  62. .appendField(nameField, 'NAME');
  63. this.appendDummyInput()
  64. .appendField("parameters")
  65. .appendField(clickablePlusMinus)
  66. .appendField('', 'PARAMS');
  67. this.appendStatementInput('STACK')
  68. .appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO);
  69. },
  70. /**
  71. * Create XML to represent the argument inputs.
  72. * @param {=boolean} opt_paramIds If true include the IDs of the parameter
  73. * quarks. Used by Blockly.Procedures.mutateCallers for reconnection.
  74. * @return {!Element} XML storage element.
  75. * @this Blockly.Block
  76. */
  77. mutationToDom: function(opt_paramIds) {
  78. var container = document.createElement('mutation');
  79. if (opt_paramIds) {
  80. container.setAttribute('name', this.getFieldValue('NAME'));
  81. }
  82. for (var i = 0; i < this.arguments_.length; i++) {
  83. var parameter = document.createElement('arg');
  84. parameter.setAttribute('name', this.arguments_[i]);
  85. if (opt_paramIds && this.paramIds_) {
  86. parameter.setAttribute('paramId', this.paramIds_[i]);
  87. }
  88. container.appendChild(parameter);
  89. }
  90. return container;
  91. },
  92. /**
  93. * Parse XML to restore the argument inputs.
  94. * @param {!Element} xmlElement XML storage element.
  95. * @this Blockly.Block
  96. */
  97. domToMutation: function(xmlElement) {
  98. this.arguments_ = [];
  99. for (var i = 0, childNode; childNode = xmlElement.childNodes[i]; i++) {
  100. if (childNode.nodeName.toLowerCase() == 'arg') {
  101. this.arguments_.push(childNode.getAttribute('name'));
  102. }
  103. }
  104. this.updateShape_();
  105. this.updateParams_();
  106. Blockly.Procedures.mutateCallers(this);
  107. },
  108. /**
  109. * Return the signature of this procedure definition.
  110. * @return {!Array} Tuple containing three elements:
  111. * - the name of the defined procedure,
  112. * - a list of all its arguments,
  113. * - that it DOES NOT have a return value.
  114. * @this Blockly.Block
  115. */
  116. getProcedureDef: function() {
  117. return [this.getFieldValue('NAME'), this.arguments_, false];
  118. },
  119. /**
  120. * Return all variables referenced by this block.
  121. * @return {!Array.<string>} List of variable names.
  122. * @this Blockly.Block
  123. */
  124. getVars: function() {
  125. return this.arguments_;
  126. },
  127. /**
  128. * Notification that a variable is renaming.
  129. * If the name matches one of this block's variables, rename it.
  130. * @param {string} oldName Previous name of variable.
  131. * @param {string} newName Renamed variable.
  132. * @this Blockly.Block
  133. */
  134. renameVar: function(oldName, newName) {
  135. var change = false;
  136. for (var i = 0; i < this.arguments_.length; i++) {
  137. if (Blockly.Names.equals(oldName, this.arguments_[i])) {
  138. this.arguments_[i] = newName;
  139. change = true;
  140. }
  141. }
  142. if (change) {
  143. this.updateParams_();
  144. // Update the mutator's variables if the mutator is open.
  145. if (this.mutator.isVisible()) {
  146. var blocks = this.mutator.workspace_.getAllBlocks();
  147. for (var i = 0, block; block = blocks[i]; i++) {
  148. if (block.type == 'procedures_mutatorarg' &&
  149. Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) {
  150. block.setFieldValue(newName, 'NAME');
  151. }
  152. }
  153. }
  154. }
  155. },
  156. /**
  157. * Add custom menu options to this block's context menu.
  158. * @param {!Array} options List of menu options to add to.
  159. * @this Blockly.Block
  160. */
  161. customContextMenu: function(options) {
  162. // Add option to create caller.
  163. var option = {enabled: true};
  164. var name = this.getFieldValue('NAME');
  165. option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name);
  166. var xmlMutation = goog.dom.createDom('mutation');
  167. xmlMutation.setAttribute('name', name);
  168. for (var i = 0; i < this.arguments_.length; i++) {
  169. var xmlArg = goog.dom.createDom('arg');
  170. xmlArg.setAttribute('name', this.arguments_[i]);
  171. xmlMutation.appendChild(xmlArg);
  172. }
  173. var xmlBlock = goog.dom.createDom('block', null, xmlMutation);
  174. xmlBlock.setAttribute('type', this.callType_);
  175. option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
  176. options.push(option);
  177. // Add options to create getters for each parameter.
  178. if (!this.isCollapsed()) {
  179. for (var i = 0; i < this.arguments_.length; i++) {
  180. var option = {enabled: true};
  181. var name = this.arguments_[i];
  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. },
  192. callType_: 'procedures_callnoreturn',
  193. /**
  194. * Called whenever anything on the workspace changes.
  195. * Add warning if this flow block is not nested inside a loop.
  196. * @this Blockly.Block
  197. */
  198. onchange: function() {
  199. if (!this.workspace) {
  200. // Block has been deleted.
  201. return;
  202. }
  203. // Does it have valid returns?
  204. var hasReturns = false;
  205. var descendants = this.getDescendants();
  206. for (var i = 0; i < descendants.length; i++) {
  207. if (descendants[i].type == 'procedures_return') {
  208. hasReturns = true;
  209. }
  210. }
  211. // Iterate through every block and fix the return state
  212. var functionName = this.getFieldValue('NAME');
  213. var blocks = this.workspace.getAllBlocks();
  214. for (var i = 0; i < blocks.length; i++) {
  215. if (blocks[i].type == 'procedures_callreturn' ||
  216. blocks[i].type == 'procedures_callnoreturn') {
  217. var callName = blocks[i].getFieldValue('NAME');
  218. // Variable name may be null if the block is only half-built.
  219. if (callName && Blockly.Names.equals(functionName, callName)) {
  220. blocks[i].setReturn(hasReturns);
  221. }
  222. }
  223. }
  224. }
  225. };
  226. Blockly.Python['procedures_defreturn'] = function(block) {
  227. // Define a procedure with a return value.
  228. // Get the function's name
  229. var funcName = Blockly.Python.variableDB_.getName(block.getFieldValue('NAME'),
  230. Blockly.Procedures.NAME_TYPE);
  231. // Get the stack of code
  232. var branch = Blockly.Python.statementToCode(block, 'STACK');
  233. // Handle prefixing
  234. if (Blockly.Python.STATEMENT_PREFIX) {
  235. branch = Blockly.Python.prefixLines(
  236. Blockly.Python.STATEMENT_PREFIX.replace(/%1/g,
  237. '\'' + block.id + '\''), Blockly.Python.INDENT) + branch;
  238. }
  239. // Handle infinite loop trapping
  240. if (Blockly.Python.INFINITE_LOOP_TRAP) {
  241. branch = Blockly.Python.INFINITE_LOOP_TRAP.replace(/%1/g,
  242. '"' + block.id + '"') + branch;
  243. }
  244. // Handle return value
  245. var returnValue = Blockly.Python.valueToCode(block, 'RETURN',
  246. Blockly.Python.ORDER_NONE) || '';
  247. if (returnValue) {
  248. returnValue = ' return ' + returnValue + '\n';
  249. } else if (!branch) {
  250. branch = Blockly.Python.PASS;
  251. }
  252. var args = [];
  253. for (var i = 0; i < block.arguments_.length; i++) {
  254. args[i] = Blockly.Python.variableDB_.getName(block.arguments_[i],
  255. Blockly.Variables.NAME_TYPE);
  256. }
  257. var code = 'def ' + funcName + '(' + args.join(', ') + '):\n' +
  258. branch + returnValue;
  259. return code;
  260. };