blocks.js 25 KB

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