blocks.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. /**
  2. * Blockly Demos: Block Factory Blocks
  3. *
  4. * Copyright 2012 Google Inc.
  5. * https://developers.google.com/blockly/
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. /**
  20. * @fileoverview Blocks for Blockly's Block Factory application.
  21. * @author fraser@google.com (Neil Fraser)
  22. */
  23. 'use strict';
  24. Blockly.Blocks['factory_base'] = {
  25. // Base of new block.
  26. init: function() {
  27. this.setColour(120);
  28. this.appendDummyInput()
  29. .appendField('name')
  30. .appendField(new Blockly.FieldTextInput('math_foo'), 'NAME');
  31. this.appendStatementInput('INPUTS')
  32. .setCheck('Input')
  33. .appendField('inputs');
  34. var dropdown = new Blockly.FieldDropdown([
  35. ['automatic inputs', 'AUTO'],
  36. ['external inputs', 'EXT'],
  37. ['inline inputs', 'INT']]);
  38. this.appendDummyInput()
  39. .appendField(dropdown, 'INLINE');
  40. dropdown = new Blockly.FieldDropdown([
  41. ['no connections', 'NONE'],
  42. ['← left output', 'LEFT'],
  43. ['↕ top+bottom connections', 'BOTH'],
  44. ['↑ top connection', 'TOP'],
  45. ['↓ bottom connection', 'BOTTOM']],
  46. function(option) {
  47. this.sourceBlock_.updateShape_(option);
  48. });
  49. this.appendDummyInput()
  50. .appendField(dropdown, 'CONNECTIONS');
  51. this.appendValueInput('COLOUR')
  52. .setCheck('Colour')
  53. .appendField('colour');
  54. this.setTooltip('Build a custom block by plugging\n' +
  55. 'fields, inputs and other blocks here.');
  56. this.setHelpUrl(
  57. 'https://developers.google.com/blockly/guides/create-custom-blocks/block-factory');
  58. },
  59. mutationToDom: function() {
  60. var container = document.createElement('mutation');
  61. container.setAttribute('connections', this.getFieldValue('CONNECTIONS'));
  62. return container;
  63. },
  64. domToMutation: function(xmlElement) {
  65. var connections = xmlElement.getAttribute('connections');
  66. this.updateShape_(connections);
  67. },
  68. updateShape_: function(option) {
  69. var outputExists = this.getInput('OUTPUTTYPE');
  70. var topExists = this.getInput('TOPTYPE');
  71. var bottomExists = this.getInput('BOTTOMTYPE');
  72. if (option == 'LEFT') {
  73. if (!outputExists) {
  74. this.addTypeInput_('OUTPUTTYPE', 'output type');
  75. }
  76. } else if (outputExists) {
  77. this.removeInput('OUTPUTTYPE');
  78. }
  79. if (option == 'TOP' || option == 'BOTH') {
  80. if (!topExists) {
  81. this.addTypeInput_('TOPTYPE', 'top type');
  82. }
  83. } else if (topExists) {
  84. this.removeInput('TOPTYPE');
  85. }
  86. if (option == 'BOTTOM' || option == 'BOTH') {
  87. if (!bottomExists) {
  88. this.addTypeInput_('BOTTOMTYPE', 'bottom type');
  89. }
  90. } else if (bottomExists) {
  91. this.removeInput('BOTTOMTYPE');
  92. }
  93. },
  94. addTypeInput_: function(name, label) {
  95. this.appendValueInput(name)
  96. .setCheck('Type')
  97. .appendField(label);
  98. this.moveInputBefore(name, 'COLOUR');
  99. var type = this.workspace.newBlock('type_null');
  100. type.setShadow(true);
  101. type.outputConnection.connect(this.getInput(name).connection);
  102. type.initSvg();
  103. type.render();
  104. }
  105. };
  106. var FIELD_MESSAGE = 'fields %1 %2';
  107. var FIELD_ARGS = [
  108. {
  109. "type": "field_dropdown",
  110. "name": "ALIGN",
  111. "options": [['left', 'LEFT'], ['right', 'RIGHT'], ['centre', 'CENTRE']],
  112. },
  113. {
  114. "type": "input_statement",
  115. "name": "FIELDS",
  116. "check": "Field"
  117. }
  118. ];
  119. var TYPE_MESSAGE = 'type %1';
  120. var TYPE_ARGS = [
  121. {
  122. "type": "input_value",
  123. "name": "TYPE",
  124. "check": "Type",
  125. "align": "RIGHT"
  126. }
  127. ];
  128. Blockly.Blocks['input_value'] = {
  129. // Value input.
  130. init: function() {
  131. this.jsonInit({
  132. "message0": "value input %1 %2",
  133. "args0": [
  134. {
  135. "type": "field_input",
  136. "name": "INPUTNAME",
  137. "text": "NAME"
  138. },
  139. {
  140. "type": "input_dummy"
  141. }
  142. ],
  143. "message1": FIELD_MESSAGE,
  144. "args1": FIELD_ARGS,
  145. "message2": TYPE_MESSAGE,
  146. "args2": TYPE_ARGS,
  147. "previousStatement": "Input",
  148. "nextStatement": "Input",
  149. "colour": 210,
  150. "tooltip": "A value socket for horizontal connections.",
  151. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=71"
  152. });
  153. },
  154. onchange: function() {
  155. inputNameCheck(this);
  156. }
  157. };
  158. Blockly.Blocks['input_statement'] = {
  159. // Statement input.
  160. init: function() {
  161. this.jsonInit({
  162. "message0": "statement input %1 %2",
  163. "args0": [
  164. {
  165. "type": "field_input",
  166. "name": "INPUTNAME",
  167. "text": "NAME"
  168. },
  169. {
  170. "type": "input_dummy"
  171. },
  172. ],
  173. "message1": FIELD_MESSAGE,
  174. "args1": FIELD_ARGS,
  175. "message2": TYPE_MESSAGE,
  176. "args2": TYPE_ARGS,
  177. "previousStatement": "Input",
  178. "nextStatement": "Input",
  179. "colour": 210,
  180. "tooltip": "A statement socket for enclosed vertical stacks.",
  181. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=246"
  182. });
  183. },
  184. onchange: function() {
  185. inputNameCheck(this);
  186. }
  187. };
  188. Blockly.Blocks['input_dummy'] = {
  189. // Dummy input.
  190. init: function() {
  191. this.jsonInit({
  192. "message0": "dummy input",
  193. "message1": FIELD_MESSAGE,
  194. "args1": FIELD_ARGS,
  195. "previousStatement": "Input",
  196. "nextStatement": "Input",
  197. "colour": 210,
  198. "tooltip": "For adding fields on a separate row with no " +
  199. "connections. Alignment options (left, right, centre) " +
  200. "apply only to multi-line fields.",
  201. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293"
  202. });
  203. }
  204. };
  205. Blockly.Blocks['field_static'] = {
  206. // Text value.
  207. init: function() {
  208. this.setColour(160);
  209. this.appendDummyInput()
  210. .appendField('text')
  211. .appendField(new Blockly.FieldTextInput(''), 'TEXT');
  212. this.setPreviousStatement(true, 'Field');
  213. this.setNextStatement(true, 'Field');
  214. this.setTooltip('Static text that serves as a label.');
  215. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=88');
  216. }
  217. };
  218. Blockly.Blocks['field_input'] = {
  219. // Text input.
  220. init: function() {
  221. this.setColour(160);
  222. this.appendDummyInput()
  223. .appendField('text input')
  224. .appendField(new Blockly.FieldTextInput('default'), 'TEXT')
  225. .appendField(',')
  226. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  227. this.setPreviousStatement(true, 'Field');
  228. this.setNextStatement(true, 'Field');
  229. this.setTooltip('An input field for the user to enter text.');
  230. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
  231. },
  232. onchange: function() {
  233. fieldNameCheck(this);
  234. }
  235. };
  236. Blockly.Blocks['field_number'] = {
  237. // Numeric input.
  238. init: function() {
  239. this.setColour(160);
  240. this.appendDummyInput()
  241. .appendField('numeric input')
  242. .appendField(new Blockly.FieldNumber(0), 'VALUE')
  243. .appendField(',')
  244. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  245. this.appendDummyInput()
  246. .appendField('min')
  247. .appendField(new Blockly.FieldNumber(-Infinity), 'MIN')
  248. .appendField('max')
  249. .appendField(new Blockly.FieldNumber(Infinity), 'MAX')
  250. .appendField('precision')
  251. .appendField(new Blockly.FieldNumber(0, 0), 'PRECISION');
  252. this.setPreviousStatement(true, 'Field');
  253. this.setNextStatement(true, 'Field');
  254. this.setTooltip('An input field for the user to enter a number.');
  255. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
  256. },
  257. onchange: function() {
  258. fieldNameCheck(this);
  259. }
  260. };
  261. Blockly.Blocks['field_angle'] = {
  262. // Angle input.
  263. init: function() {
  264. this.setColour(160);
  265. this.appendDummyInput()
  266. .appendField('angle input')
  267. .appendField(new Blockly.FieldAngle('90'), 'ANGLE')
  268. .appendField(',')
  269. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  270. this.setPreviousStatement(true, 'Field');
  271. this.setNextStatement(true, 'Field');
  272. this.setTooltip('An input field for the user to enter an angle.');
  273. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=372');
  274. },
  275. onchange: function() {
  276. fieldNameCheck(this);
  277. }
  278. };
  279. Blockly.Blocks['field_dropdown'] = {
  280. // Dropdown menu.
  281. init: function() {
  282. this.appendDummyInput()
  283. .appendField('dropdown')
  284. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  285. this.optionCount_ = 3;
  286. this.updateShape_();
  287. this.setPreviousStatement(true, 'Field');
  288. this.setNextStatement(true, 'Field');
  289. this.setMutator(new Blockly.Mutator(['field_dropdown_option']));
  290. this.setColour(160);
  291. this.setTooltip('Dropdown menu with a list of options.');
  292. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
  293. },
  294. mutationToDom: function(workspace) {
  295. // Create XML to represent menu options.
  296. var container = document.createElement('mutation');
  297. container.setAttribute('options', this.optionCount_);
  298. return container;
  299. },
  300. domToMutation: function(container) {
  301. // Parse XML to restore the menu options.
  302. this.optionCount_ = parseInt(container.getAttribute('options'), 10);
  303. this.updateShape_();
  304. },
  305. decompose: function(workspace) {
  306. // Populate the mutator's dialog with this block's components.
  307. var containerBlock = workspace.newBlock('field_dropdown_container');
  308. containerBlock.initSvg();
  309. var connection = containerBlock.getInput('STACK').connection;
  310. for (var i = 0; i < this.optionCount_; i++) {
  311. var optionBlock = workspace.newBlock('field_dropdown_option');
  312. optionBlock.initSvg();
  313. connection.connect(optionBlock.previousConnection);
  314. connection = optionBlock.nextConnection;
  315. }
  316. return containerBlock;
  317. },
  318. compose: function(containerBlock) {
  319. // Reconfigure this block based on the mutator dialog's components.
  320. var optionBlock = containerBlock.getInputTargetBlock('STACK');
  321. // Count number of inputs.
  322. var data = [];
  323. while (optionBlock) {
  324. data.push([optionBlock.userData_, optionBlock.cpuData_]);
  325. optionBlock = optionBlock.nextConnection &&
  326. optionBlock.nextConnection.targetBlock();
  327. }
  328. this.optionCount_ = data.length;
  329. this.updateShape_();
  330. // Restore any data.
  331. for (var i = 0; i < this.optionCount_; i++) {
  332. this.setFieldValue(data[i][0] || 'option', 'USER' + i);
  333. this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
  334. }
  335. },
  336. saveConnections: function(containerBlock) {
  337. // Store names and values for each option.
  338. var optionBlock = containerBlock.getInputTargetBlock('STACK');
  339. var i = 0;
  340. while (optionBlock) {
  341. optionBlock.userData_ = this.getFieldValue('USER' + i);
  342. optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
  343. i++;
  344. optionBlock = optionBlock.nextConnection &&
  345. optionBlock.nextConnection.targetBlock();
  346. }
  347. },
  348. updateShape_: function() {
  349. // Modify this block to have the correct number of options.
  350. // Add new options.
  351. for (var i = 0; i < this.optionCount_; i++) {
  352. if (!this.getInput('OPTION' + i)) {
  353. this.appendDummyInput('OPTION' + i)
  354. .appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
  355. .appendField(',')
  356. .appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
  357. }
  358. }
  359. // Remove deleted options.
  360. while (this.getInput('OPTION' + i)) {
  361. this.removeInput('OPTION' + i);
  362. i++;
  363. }
  364. },
  365. onchange: function() {
  366. if (this.workspace && this.optionCount_ < 1) {
  367. this.setWarningText('Drop down menu must\nhave at least one option.');
  368. } else {
  369. fieldNameCheck(this);
  370. }
  371. }
  372. };
  373. Blockly.Blocks['field_dropdown_container'] = {
  374. // Container.
  375. init: function() {
  376. this.setColour(160);
  377. this.appendDummyInput()
  378. .appendField('add options');
  379. this.appendStatementInput('STACK');
  380. this.setTooltip('Add, remove, or reorder options\n' +
  381. 'to reconfigure this dropdown menu.');
  382. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
  383. this.contextMenu = false;
  384. }
  385. };
  386. Blockly.Blocks['field_dropdown_option'] = {
  387. // Add option.
  388. init: function() {
  389. this.setColour(160);
  390. this.appendDummyInput()
  391. .appendField('option');
  392. this.setPreviousStatement(true);
  393. this.setNextStatement(true);
  394. this.setTooltip('Add a new option to the dropdown menu.');
  395. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
  396. this.contextMenu = false;
  397. }
  398. };
  399. Blockly.Blocks['field_checkbox'] = {
  400. // Checkbox.
  401. init: function() {
  402. this.setColour(160);
  403. this.appendDummyInput()
  404. .appendField('checkbox')
  405. .appendField(new Blockly.FieldCheckbox('TRUE'), 'CHECKED')
  406. .appendField(',')
  407. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  408. this.setPreviousStatement(true, 'Field');
  409. this.setNextStatement(true, 'Field');
  410. this.setTooltip('Checkbox field.');
  411. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=485');
  412. },
  413. onchange: function() {
  414. fieldNameCheck(this);
  415. }
  416. };
  417. Blockly.Blocks['field_colour'] = {
  418. // Colour input.
  419. init: function() {
  420. this.setColour(160);
  421. this.appendDummyInput()
  422. .appendField('colour')
  423. .appendField(new Blockly.FieldColour('#ff0000'), 'COLOUR')
  424. .appendField(',')
  425. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  426. this.setPreviousStatement(true, 'Field');
  427. this.setNextStatement(true, 'Field');
  428. this.setTooltip('Colour input field.');
  429. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=495');
  430. },
  431. onchange: function() {
  432. fieldNameCheck(this);
  433. }
  434. };
  435. Blockly.Blocks['field_date'] = {
  436. // Date input.
  437. init: function() {
  438. this.setColour(160);
  439. this.appendDummyInput()
  440. .appendField('date')
  441. .appendField(new Blockly.FieldDate(), 'DATE')
  442. .appendField(',')
  443. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  444. this.setPreviousStatement(true, 'Field');
  445. this.setNextStatement(true, 'Field');
  446. this.setTooltip('Date input field.');
  447. },
  448. onchange: function() {
  449. fieldNameCheck(this);
  450. }
  451. };
  452. Blockly.Blocks['field_variable'] = {
  453. // Dropdown for variables.
  454. init: function() {
  455. this.setColour(160);
  456. this.appendDummyInput()
  457. .appendField('variable')
  458. .appendField(new Blockly.FieldTextInput('item'), 'TEXT')
  459. .appendField(',')
  460. .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
  461. this.setPreviousStatement(true, 'Field');
  462. this.setNextStatement(true, 'Field');
  463. this.setTooltip('Dropdown menu for variable names.');
  464. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=510');
  465. },
  466. onchange: function() {
  467. fieldNameCheck(this);
  468. }
  469. };
  470. Blockly.Blocks['field_image'] = {
  471. // Image.
  472. init: function() {
  473. this.setColour(160);
  474. var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
  475. this.appendDummyInput()
  476. .appendField('image')
  477. .appendField(new Blockly.FieldTextInput(src), 'SRC');
  478. this.appendDummyInput()
  479. .appendField('width')
  480. .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH')
  481. .appendField('height')
  482. .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT')
  483. .appendField('alt text')
  484. .appendField(new Blockly.FieldTextInput('*'), 'ALT');
  485. this.setPreviousStatement(true, 'Field');
  486. this.setNextStatement(true, 'Field');
  487. this.setTooltip('Static image (JPEG, PNG, GIF, SVG, BMP).\n' +
  488. 'Retains aspect ratio regardless of height and width.\n' +
  489. 'Alt text is for when collapsed.');
  490. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=567');
  491. }
  492. };
  493. Blockly.Blocks['type_group'] = {
  494. // Group of types.
  495. init: function() {
  496. this.typeCount_ = 2;
  497. this.updateShape_();
  498. this.setOutput(true, 'Type');
  499. this.setMutator(new Blockly.Mutator(['type_group_item']));
  500. this.setColour(230);
  501. this.setTooltip('Allows more than one type to be accepted.');
  502. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677');
  503. },
  504. mutationToDom: function(workspace) {
  505. // Create XML to represent a group of types.
  506. var container = document.createElement('mutation');
  507. container.setAttribute('types', this.typeCount_);
  508. return container;
  509. },
  510. domToMutation: function(container) {
  511. // Parse XML to restore the group of types.
  512. this.typeCount_ = parseInt(container.getAttribute('types'), 10);
  513. this.updateShape_();
  514. for (var i = 0; i < this.typeCount_; i++) {
  515. this.removeInput('TYPE' + i);
  516. }
  517. for (var i = 0; i < this.typeCount_; i++) {
  518. var input = this.appendValueInput('TYPE' + i)
  519. .setCheck('Type');
  520. if (i == 0) {
  521. input.appendField('any of');
  522. }
  523. }
  524. },
  525. decompose: function(workspace) {
  526. // Populate the mutator's dialog with this block's components.
  527. var containerBlock = workspace.newBlock('type_group_container');
  528. containerBlock.initSvg();
  529. var connection = containerBlock.getInput('STACK').connection;
  530. for (var i = 0; i < this.typeCount_; i++) {
  531. var typeBlock = workspace.newBlock('type_group_item');
  532. typeBlock.initSvg();
  533. connection.connect(typeBlock.previousConnection);
  534. connection = typeBlock.nextConnection;
  535. }
  536. return containerBlock;
  537. },
  538. compose: function(containerBlock) {
  539. // Reconfigure this block based on the mutator dialog's components.
  540. var typeBlock = containerBlock.getInputTargetBlock('STACK');
  541. // Count number of inputs.
  542. var connections = [];
  543. while (typeBlock) {
  544. connections.push(typeBlock.valueConnection_);
  545. typeBlock = typeBlock.nextConnection &&
  546. typeBlock.nextConnection.targetBlock();
  547. }
  548. // Disconnect any children that don't belong.
  549. for (var i = 0; i < this.typeCount_; i++) {
  550. var connection = this.getInput('TYPE' + i).connection.targetConnection;
  551. if (connection && connections.indexOf(connection) == -1) {
  552. connection.disconnect();
  553. }
  554. }
  555. this.typeCount_ = connections.length;
  556. this.updateShape_();
  557. // Reconnect any child blocks.
  558. for (var i = 0; i < this.typeCount_; i++) {
  559. Blockly.Mutator.reconnect(connections[i], this, 'TYPE' + i);
  560. }
  561. },
  562. saveConnections: function(containerBlock) {
  563. // Store a pointer to any connected child blocks.
  564. var typeBlock = containerBlock.getInputTargetBlock('STACK');
  565. var i = 0;
  566. while (typeBlock) {
  567. var input = this.getInput('TYPE' + i);
  568. typeBlock.valueConnection_ = input && input.connection.targetConnection;
  569. i++;
  570. typeBlock = typeBlock.nextConnection &&
  571. typeBlock.nextConnection.targetBlock();
  572. }
  573. },
  574. updateShape_: function() {
  575. // Modify this block to have the correct number of inputs.
  576. // Add new inputs.
  577. for (var i = 0; i < this.typeCount_; i++) {
  578. if (!this.getInput('TYPE' + i)) {
  579. var input = this.appendValueInput('TYPE' + i);
  580. if (i == 0) {
  581. input.appendField('any of');
  582. }
  583. }
  584. }
  585. // Remove deleted inputs.
  586. while (this.getInput('TYPE' + i)) {
  587. this.removeInput('TYPE' + i);
  588. i++;
  589. }
  590. }
  591. };
  592. Blockly.Blocks['type_group_container'] = {
  593. // Container.
  594. init: function() {
  595. this.jsonInit({
  596. "message0": "add types %1 %2",
  597. "args0": [
  598. {"type": "input_dummy"},
  599. {"type": "input_statement", "name": "STACK"}
  600. ],
  601. "colour": 230,
  602. "tooltip": "Add, or remove allowed type.",
  603. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
  604. });
  605. }
  606. };
  607. Blockly.Blocks['type_group_item'] = {
  608. // Add type.
  609. init: function() {
  610. this.jsonInit({
  611. "message0": "type",
  612. "previousStatement": null,
  613. "nextStatement": null,
  614. "colour": 230,
  615. "tooltip": "Add a new allowed type.",
  616. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
  617. });
  618. }
  619. };
  620. Blockly.Blocks['type_null'] = {
  621. // Null type.
  622. valueType: null,
  623. init: function() {
  624. this.jsonInit({
  625. "message0": "any",
  626. "output": "Type",
  627. "colour": 230,
  628. "tooltip": "Any type is allowed.",
  629. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
  630. });
  631. }
  632. };
  633. Blockly.Blocks['type_boolean'] = {
  634. // Boolean type.
  635. valueType: 'Boolean',
  636. init: function() {
  637. this.jsonInit({
  638. "message0": "Boolean",
  639. "output": "Type",
  640. "colour": 230,
  641. "tooltip": "Booleans (true/false) are allowed.",
  642. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
  643. });
  644. }
  645. };
  646. Blockly.Blocks['type_number'] = {
  647. // Number type.
  648. valueType: 'Number',
  649. init: function() {
  650. this.jsonInit({
  651. "message0": "Number",
  652. "output": "Type",
  653. "colour": 230,
  654. "tooltip": "Numbers (int/float) are allowed.",
  655. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
  656. });
  657. }
  658. };
  659. Blockly.Blocks['type_string'] = {
  660. // String type.
  661. valueType: 'String',
  662. init: function() {
  663. this.jsonInit({
  664. "message0": "String",
  665. "output": "Type",
  666. "colour": 230,
  667. "tooltip": "Strings (text) are allowed.",
  668. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
  669. });
  670. }
  671. };
  672. Blockly.Blocks['type_list'] = {
  673. // List type.
  674. valueType: 'Array',
  675. init: function() {
  676. this.jsonInit({
  677. "message0": "Array",
  678. "output": "Type",
  679. "colour": 230,
  680. "tooltip": "Arrays (lists) are allowed.",
  681. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
  682. });
  683. }
  684. };
  685. Blockly.Blocks['type_other'] = {
  686. // Other type.
  687. init: function() {
  688. this.jsonInit({
  689. "message0": "other %1",
  690. "args0": [{"type": "field_input", "name": "TYPE", "text": ""}],
  691. "output": "Type",
  692. "colour": 230,
  693. "tooltip": "Custom type to allow.",
  694. "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=702"
  695. });
  696. }
  697. };
  698. Blockly.Blocks['colour_hue'] = {
  699. // Set the colour of the block.
  700. init: function() {
  701. this.appendDummyInput()
  702. .appendField('hue:')
  703. .appendField(new Blockly.FieldAngle('0', this.validator), 'HUE');
  704. this.setOutput(true, 'Colour');
  705. this.setTooltip('Paint the block with this colour.');
  706. this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55');
  707. },
  708. validator: function(text) {
  709. // Update the current block's colour to match.
  710. var hue = parseInt(text, 10);
  711. if (!isNaN(hue)) {
  712. this.sourceBlock_.setColour(hue);
  713. }
  714. },
  715. mutationToDom: function(workspace) {
  716. var container = document.createElement('mutation');
  717. container.setAttribute('colour', this.getColour());
  718. return container;
  719. },
  720. domToMutation: function(container) {
  721. this.setColour(container.getAttribute('colour'));
  722. }
  723. };
  724. /**
  725. * Check to see if more than one field has this name.
  726. * Highly inefficient (On^2), but n is small.
  727. * @param {!Blockly.Block} referenceBlock Block to check.
  728. */
  729. function fieldNameCheck(referenceBlock) {
  730. if (!referenceBlock.workspace) {
  731. // Block has been deleted.
  732. return;
  733. }
  734. var name = referenceBlock.getFieldValue('FIELDNAME').toLowerCase();
  735. var count = 0;
  736. var blocks = referenceBlock.workspace.getAllBlocks();
  737. for (var i = 0, block; block = blocks[i]; i++) {
  738. var otherName = block.getFieldValue('FIELDNAME');
  739. if (!block.disabled && !block.getInheritedDisabled() &&
  740. otherName && otherName.toLowerCase() == name) {
  741. count++;
  742. }
  743. }
  744. var msg = (count > 1) ?
  745. 'There are ' + count + ' field blocks\n with this name.' : null;
  746. referenceBlock.setWarningText(msg);
  747. }
  748. /**
  749. * Check to see if more than one input has this name.
  750. * Highly inefficient (On^2), but n is small.
  751. * @param {!Blockly.Block} referenceBlock Block to check.
  752. */
  753. function inputNameCheck(referenceBlock) {
  754. if (!referenceBlock.workspace) {
  755. // Block has been deleted.
  756. return;
  757. }
  758. var name = referenceBlock.getFieldValue('INPUTNAME').toLowerCase();
  759. var count = 0;
  760. var blocks = referenceBlock.workspace.getAllBlocks();
  761. for (var i = 0, block; block = blocks[i]; i++) {
  762. var otherName = block.getFieldValue('INPUTNAME');
  763. if (!block.disabled && !block.getInheritedDisabled() &&
  764. otherName && otherName.toLowerCase() == name) {
  765. count++;
  766. }
  767. }
  768. var msg = (count > 1) ?
  769. 'There are ' + count + ' input blocks\n with this name.' : null;
  770. referenceBlock.setWarningText(msg);
  771. }