lists.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  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 List blocks for Blockly.
  22. * @author fraser@google.com (Neil Fraser)
  23. */
  24. 'use strict';
  25. goog.provide('Blockly.Blocks.lists');
  26. goog.require('Blockly.Blocks');
  27. Blockscad.Toolbox = Blockscad.Toolbox || {};
  28. Blockly.Blocks.lists.HUE = Blockscad.Toolbox.HEX_LOGIC;
  29. Blockly.Blocks['lists_create_empty'] = {
  30. /**
  31. * Block for creating an empty list.
  32. * @this Blockly.Block
  33. */
  34. init: function() {
  35. this.setHelpUrl(Blockly.Msg.LISTS_CREATE_EMPTY_HELPURL);
  36. this.setColour(Blockly.Blocks.lists.HUE);
  37. this.setOutput(true, 'Array');
  38. this.appendDummyInput()
  39. .appendField(Blockly.Msg.LISTS_CREATE_EMPTY_TITLE);
  40. this.setTooltip(Blockly.Msg.LISTS_CREATE_EMPTY_TOOLTIP);
  41. }
  42. };
  43. Blockly.Blocks['lists_create_with'] = {
  44. /**
  45. * Block for creating a list with any number of elements of any type.
  46. * @this Blockly.Block
  47. */
  48. init: function() {
  49. this.setHelpUrl(Blockly.Msg.LISTS_CREATE_WITH_HELPURL);
  50. this.setColour(Blockly.Blocks.lists.HUE);
  51. this.itemCount_ = 3;
  52. this.updateShape_();
  53. this.setOutput(true, 'Array');
  54. this.setMutator(new Blockly.Mutator(['lists_create_with_item']));
  55. this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP);
  56. },
  57. /**
  58. * Create XML to represent list inputs.
  59. * @return {!Element} XML storage element.
  60. * @this Blockly.Block
  61. */
  62. mutationToDom: function() {
  63. var container = document.createElement('mutation');
  64. container.setAttribute('items', this.itemCount_);
  65. return container;
  66. },
  67. /**
  68. * Parse XML to restore the list inputs.
  69. * @param {!Element} xmlElement XML storage element.
  70. * @this Blockly.Block
  71. */
  72. domToMutation: function(xmlElement) {
  73. this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  74. this.updateShape_();
  75. },
  76. /**
  77. * Populate the mutator's dialog with this block's components.
  78. * @param {!Blockly.Workspace} workspace Mutator's workspace.
  79. * @return {!Blockly.Block} Root block in mutator.
  80. * @this Blockly.Block
  81. */
  82. decompose: function(workspace) {
  83. var containerBlock =
  84. Blockly.Block.obtain(workspace, 'lists_create_with_container');
  85. containerBlock.initSvg();
  86. var connection = containerBlock.getInput('STACK').connection;
  87. for (var i = 0; i < this.itemCount_; i++) {
  88. var itemBlock = Blockly.Block.obtain(workspace, 'lists_create_with_item');
  89. itemBlock.initSvg();
  90. connection.connect(itemBlock.previousConnection);
  91. connection = itemBlock.nextConnection;
  92. }
  93. return containerBlock;
  94. },
  95. /**
  96. * Reconfigure this block based on the mutator dialog's components.
  97. * @param {!Blockly.Block} containerBlock Root block in mutator.
  98. * @this Blockly.Block
  99. */
  100. compose: function(containerBlock) {
  101. var itemBlock = containerBlock.getInputTargetBlock('STACK');
  102. // Count number of inputs.
  103. var connections = [];
  104. var i = 0;
  105. while (itemBlock) {
  106. connections[i] = itemBlock.valueConnection_;
  107. itemBlock = itemBlock.nextConnection &&
  108. itemBlock.nextConnection.targetBlock();
  109. i++;
  110. }
  111. this.itemCount_ = i;
  112. this.updateShape_();
  113. // Reconnect any child blocks.
  114. for (var i = 0; i < this.itemCount_; i++) {
  115. if (connections[i]) {
  116. this.getInput('ADD' + i).connection.connect(connections[i]);
  117. }
  118. }
  119. },
  120. /**
  121. * Store pointers to any connected child blocks.
  122. * @param {!Blockly.Block} containerBlock Root block in mutator.
  123. * @this Blockly.Block
  124. */
  125. saveConnections: function(containerBlock) {
  126. var itemBlock = containerBlock.getInputTargetBlock('STACK');
  127. var i = 0;
  128. while (itemBlock) {
  129. var input = this.getInput('ADD' + i);
  130. itemBlock.valueConnection_ = input && input.connection.targetConnection;
  131. i++;
  132. itemBlock = itemBlock.nextConnection &&
  133. itemBlock.nextConnection.targetBlock();
  134. }
  135. },
  136. /**
  137. * Modify this block to have the correct number of inputs.
  138. * @private
  139. * @this Blockly.Block
  140. */
  141. updateShape_: function() {
  142. // Delete everything.
  143. if (this.getInput('EMPTY')) {
  144. this.removeInput('EMPTY');
  145. } else {
  146. var i = 0;
  147. while (this.getInput('ADD' + i)) {
  148. this.removeInput('ADD' + i);
  149. i++;
  150. }
  151. }
  152. // Rebuild block.
  153. if (this.itemCount_ == 0) {
  154. this.appendDummyInput('EMPTY')
  155. .appendField(Blockly.Msg.LISTS_CREATE_EMPTY_TITLE);
  156. } else {
  157. for (var i = 0; i < this.itemCount_; i++) {
  158. var input = this.appendValueInput('ADD' + i);
  159. if (i == 0) {
  160. input.appendField(Blockly.Msg.LISTS_CREATE_WITH_INPUT_WITH);
  161. }
  162. }
  163. }
  164. }
  165. };
  166. Blockly.Blocks['lists_create_with_container'] = {
  167. /**
  168. * Mutator block for list container.
  169. * @this Blockly.Block
  170. */
  171. init: function() {
  172. this.setColour(Blockly.Blocks.lists.HUE);
  173. this.appendDummyInput()
  174. .appendField(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD);
  175. this.appendStatementInput('STACK');
  176. this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP);
  177. this.contextMenu = false;
  178. }
  179. };
  180. Blockly.Blocks['lists_create_with_item'] = {
  181. /**
  182. * Mutator bolck for adding items.
  183. * @this Blockly.Block
  184. */
  185. init: function() {
  186. this.setColour(Blockly.Blocks.lists.HUE);
  187. this.appendDummyInput()
  188. .appendField(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TITLE);
  189. this.setPreviousStatement(true);
  190. this.setNextStatement(true);
  191. this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);
  192. this.contextMenu = false;
  193. }
  194. };
  195. Blockly.Blocks['lists_repeat'] = {
  196. /**
  197. * Block for creating a list with one element repeated.
  198. * @this Blockly.Block
  199. */
  200. init: function() {
  201. this.jsonInit({
  202. "message0": Blockly.Msg.LISTS_REPEAT_TITLE,
  203. "args0": [
  204. {
  205. "type": "input_value",
  206. "name": "ITEM"
  207. },
  208. {
  209. "type": "input_value",
  210. "name": "NUM",
  211. "check": "Number"
  212. }
  213. ],
  214. "output": "Array",
  215. "colour": Blockly.Blocks.lists.HUE,
  216. "tooltip": Blockly.Msg.LISTS_REPEAT_TOOLTIP,
  217. "helpUrl": Blockly.Msg.LISTS_REPEAT_HELPURL
  218. });
  219. }
  220. };
  221. Blockly.Blocks['lists_length'] = {
  222. /**
  223. * Block for list length.
  224. * @this Blockly.Block
  225. */
  226. init: function() {
  227. this.jsonInit({
  228. "message0": Blockly.Msg.LISTS_LENGTH_TITLE,
  229. "args0": [
  230. {
  231. "type": "input_value",
  232. "name": "VALUE",
  233. "check": ['String', 'Array']
  234. }
  235. ],
  236. "output": 'Number',
  237. "colour": Blockly.Blocks.lists.HUE,
  238. "tooltip": Blockly.Msg.LISTS_LENGTH_TOOLTIP,
  239. "helpUrl": Blockly.Msg.LISTS_LENGTH_HELPURL
  240. });
  241. }
  242. };
  243. Blockly.Blocks['lists_isEmpty'] = {
  244. /**
  245. * Block for is the list empty?
  246. * @this Blockly.Block
  247. */
  248. init: function() {
  249. this.jsonInit({
  250. "message0": Blockly.Msg.LISTS_ISEMPTY_TITLE,
  251. "args0": [
  252. {
  253. "type": "input_value",
  254. "name": "VALUE",
  255. "check": ['String', 'Array']
  256. }
  257. ],
  258. "output": 'Boolean',
  259. "colour": Blockly.Blocks.lists.HUE,
  260. "tooltip": Blockly.Msg.LISTS_ISEMPTY_TOOLTIP,
  261. "helpUrl": Blockly.Msg.LISTS_ISEMPTY_HELPURL
  262. });
  263. }
  264. };
  265. Blockly.Blocks['lists_indexOf'] = {
  266. /**
  267. * Block for finding an item in the list.
  268. * @this Blockly.Block
  269. */
  270. init: function() {
  271. var OPERATORS =
  272. [[Blockly.Msg.LISTS_INDEX_OF_FIRST, 'FIRST'],
  273. [Blockly.Msg.LISTS_INDEX_OF_LAST, 'LAST']];
  274. this.setHelpUrl(Blockly.Msg.LISTS_INDEX_OF_HELPURL);
  275. this.setColour(Blockly.Blocks.lists.HUE);
  276. this.setOutput(true, 'Number');
  277. this.appendValueInput('VALUE')
  278. .setCheck('Array')
  279. .appendField(Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST);
  280. this.appendValueInput('FIND')
  281. .appendField(new Blockly.FieldDropdown(OPERATORS), 'END');
  282. this.setInputsInline(true);
  283. this.setTooltip(Blockly.Msg.LISTS_INDEX_OF_TOOLTIP);
  284. }
  285. };
  286. Blockly.Blocks['lists_getIndex'] = {
  287. /**
  288. * Block for getting element at index.
  289. * @this Blockly.Block
  290. */
  291. init: function() {
  292. var MODE =
  293. [[Blockly.Msg.LISTS_GET_INDEX_GET, 'GET'],
  294. [Blockly.Msg.LISTS_GET_INDEX_GET_REMOVE, 'GET_REMOVE'],
  295. [Blockly.Msg.LISTS_GET_INDEX_REMOVE, 'REMOVE']];
  296. this.WHERE_OPTIONS =
  297. [[Blockly.Msg.LISTS_GET_INDEX_FROM_START, 'FROM_START'],
  298. [Blockly.Msg.LISTS_GET_INDEX_FROM_END, 'FROM_END'],
  299. [Blockly.Msg.LISTS_GET_INDEX_FIRST, 'FIRST'],
  300. [Blockly.Msg.LISTS_GET_INDEX_LAST, 'LAST'],
  301. [Blockly.Msg.LISTS_GET_INDEX_RANDOM, 'RANDOM']];
  302. this.setHelpUrl(Blockly.Msg.LISTS_GET_INDEX_HELPURL);
  303. this.setColour(Blockly.Blocks.lists.HUE);
  304. var modeMenu = new Blockly.FieldDropdown(MODE, function(value) {
  305. var isStatement = (value == 'REMOVE');
  306. this.sourceBlock_.updateStatement_(isStatement);
  307. });
  308. this.appendValueInput('VALUE')
  309. .setCheck('Array')
  310. .appendField(Blockly.Msg.LISTS_GET_INDEX_INPUT_IN_LIST);
  311. this.appendDummyInput()
  312. .appendField(modeMenu, 'MODE')
  313. .appendField('', 'SPACE');
  314. this.appendDummyInput('AT');
  315. if (Blockly.Msg.LISTS_GET_INDEX_TAIL) {
  316. this.appendDummyInput('TAIL')
  317. .appendField(Blockly.Msg.LISTS_GET_INDEX_TAIL);
  318. }
  319. this.setInputsInline(true);
  320. this.setOutput(true);
  321. this.updateAt_(true);
  322. // Assign 'this' to a variable for use in the tooltip closure below.
  323. var thisBlock = this;
  324. this.setTooltip(function() {
  325. var combo = thisBlock.getFieldValue('MODE') + '_' +
  326. thisBlock.getFieldValue('WHERE');
  327. return Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_' + combo];
  328. });
  329. },
  330. /**
  331. * Create XML to represent whether the block is a statement or a value.
  332. * Also represent whether there is an 'AT' input.
  333. * @return {Element} XML storage element.
  334. * @this Blockly.Block
  335. */
  336. mutationToDom: function() {
  337. var container = document.createElement('mutation');
  338. var isStatement = !this.outputConnection;
  339. container.setAttribute('statement', isStatement);
  340. var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
  341. container.setAttribute('at', isAt);
  342. return container;
  343. },
  344. /**
  345. * Parse XML to restore the 'AT' input.
  346. * @param {!Element} xmlElement XML storage element.
  347. * @this Blockly.Block
  348. */
  349. domToMutation: function(xmlElement) {
  350. // Note: Until January 2013 this block did not have mutations,
  351. // so 'statement' defaults to false and 'at' defaults to true.
  352. var isStatement = (xmlElement.getAttribute('statement') == 'true');
  353. this.updateStatement_(isStatement);
  354. var isAt = (xmlElement.getAttribute('at') != 'false');
  355. this.updateAt_(isAt);
  356. },
  357. /**
  358. * Switch between a value block and a statement block.
  359. * @param {boolean} newStatement True if the block should be a statement.
  360. * False if the block should be a value.
  361. * @private
  362. * @this Blockly.Block
  363. */
  364. updateStatement_: function(newStatement) {
  365. var oldStatement = !this.outputConnection;
  366. if (newStatement != oldStatement) {
  367. this.unplug(true, true);
  368. if (newStatement) {
  369. this.setOutput(false);
  370. this.setPreviousStatement(true);
  371. this.setNextStatement(true);
  372. } else {
  373. this.setPreviousStatement(false);
  374. this.setNextStatement(false);
  375. this.setOutput(true);
  376. }
  377. }
  378. },
  379. /**
  380. * Create or delete an input for the numeric index.
  381. * @param {boolean} isAt True if the input should exist.
  382. * @private
  383. * @this Blockly.Block
  384. */
  385. updateAt_: function(isAt) {
  386. // Destroy old 'AT' and 'ORDINAL' inputs.
  387. this.removeInput('AT');
  388. this.removeInput('ORDINAL', true);
  389. // Create either a value 'AT' input or a dummy input.
  390. if (isAt) {
  391. this.appendValueInput('AT').setCheck('Number');
  392. if (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
  393. this.appendDummyInput('ORDINAL')
  394. .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
  395. }
  396. } else {
  397. this.appendDummyInput('AT');
  398. }
  399. var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
  400. var newAt = (value == 'FROM_START') || (value == 'FROM_END');
  401. // The 'isAt' variable is available due to this function being a closure.
  402. if (newAt != isAt) {
  403. var block = this.sourceBlock_;
  404. block.updateAt_(newAt);
  405. // This menu has been destroyed and replaced. Update the replacement.
  406. block.setFieldValue(value, 'WHERE');
  407. return null;
  408. }
  409. return undefined;
  410. });
  411. this.getInput('AT').appendField(menu, 'WHERE');
  412. if (Blockly.Msg.LISTS_GET_INDEX_TAIL) {
  413. this.moveInputBefore('TAIL', null);
  414. }
  415. }
  416. };
  417. Blockly.Blocks['lists_setIndex'] = {
  418. /**
  419. * Block for setting the element at index.
  420. * @this Blockly.Block
  421. */
  422. init: function() {
  423. var MODE =
  424. [[Blockly.Msg.LISTS_SET_INDEX_SET, 'SET'],
  425. [Blockly.Msg.LISTS_SET_INDEX_INSERT, 'INSERT']];
  426. this.WHERE_OPTIONS =
  427. [[Blockly.Msg.LISTS_GET_INDEX_FROM_START, 'FROM_START'],
  428. [Blockly.Msg.LISTS_GET_INDEX_FROM_END, 'FROM_END'],
  429. [Blockly.Msg.LISTS_GET_INDEX_FIRST, 'FIRST'],
  430. [Blockly.Msg.LISTS_GET_INDEX_LAST, 'LAST'],
  431. [Blockly.Msg.LISTS_GET_INDEX_RANDOM, 'RANDOM']];
  432. this.setHelpUrl(Blockly.Msg.LISTS_SET_INDEX_HELPURL);
  433. this.setColour(Blockly.Blocks.lists.HUE);
  434. this.appendValueInput('LIST')
  435. .setCheck('Array')
  436. .appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
  437. this.appendDummyInput()
  438. .appendField(new Blockly.FieldDropdown(MODE), 'MODE')
  439. .appendField('', 'SPACE');
  440. this.appendDummyInput('AT');
  441. this.appendValueInput('TO')
  442. .appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_TO);
  443. this.setInputsInline(true);
  444. this.setPreviousStatement(true);
  445. this.setNextStatement(true);
  446. this.setTooltip(Blockly.Msg.LISTS_SET_INDEX_TOOLTIP);
  447. this.updateAt_(true);
  448. // Assign 'this' to a variable for use in the tooltip closure below.
  449. var thisBlock = this;
  450. this.setTooltip(function() {
  451. var combo = thisBlock.getFieldValue('MODE') + '_' +
  452. thisBlock.getFieldValue('WHERE');
  453. return Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_' + combo];
  454. });
  455. },
  456. /**
  457. * Create XML to represent whether there is an 'AT' input.
  458. * @return {Element} XML storage element.
  459. * @this Blockly.Block
  460. */
  461. mutationToDom: function() {
  462. var container = document.createElement('mutation');
  463. var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
  464. container.setAttribute('at', isAt);
  465. return container;
  466. },
  467. /**
  468. * Parse XML to restore the 'AT' input.
  469. * @param {!Element} xmlElement XML storage element.
  470. * @this Blockly.Block
  471. */
  472. domToMutation: function(xmlElement) {
  473. // Note: Until January 2013 this block did not have mutations,
  474. // so 'at' defaults to true.
  475. var isAt = (xmlElement.getAttribute('at') != 'false');
  476. this.updateAt_(isAt);
  477. },
  478. /**
  479. * Create or delete an input for the numeric index.
  480. * @param {boolean} isAt True if the input should exist.
  481. * @private
  482. * @this Blockly.Block
  483. */
  484. updateAt_: function(isAt) {
  485. // Destroy old 'AT' and 'ORDINAL' input.
  486. this.removeInput('AT');
  487. this.removeInput('ORDINAL', true);
  488. // Create either a value 'AT' input or a dummy input.
  489. if (isAt) {
  490. this.appendValueInput('AT').setCheck('Number');
  491. if (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
  492. this.appendDummyInput('ORDINAL')
  493. .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
  494. }
  495. } else {
  496. this.appendDummyInput('AT');
  497. }
  498. var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
  499. var newAt = (value == 'FROM_START') || (value == 'FROM_END');
  500. // The 'isAt' variable is available due to this function being a closure.
  501. if (newAt != isAt) {
  502. var block = this.sourceBlock_;
  503. block.updateAt_(newAt);
  504. // This menu has been destroyed and replaced. Update the replacement.
  505. block.setFieldValue(value, 'WHERE');
  506. return null;
  507. }
  508. return undefined;
  509. });
  510. this.moveInputBefore('AT', 'TO');
  511. if (this.getInput('ORDINAL')) {
  512. this.moveInputBefore('ORDINAL', 'TO');
  513. }
  514. this.getInput('AT').appendField(menu, 'WHERE');
  515. }
  516. };
  517. Blockly.Blocks['lists_getSublist'] = {
  518. /**
  519. * Block for getting sublist.
  520. * @this Blockly.Block
  521. */
  522. init: function() {
  523. this['WHERE_OPTIONS_1'] =
  524. [[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START, 'FROM_START'],
  525. [Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END, 'FROM_END'],
  526. [Blockly.Msg.LISTS_GET_SUBLIST_START_FIRST, 'FIRST']];
  527. this['WHERE_OPTIONS_2'] =
  528. [[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_START, 'FROM_START'],
  529. [Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END, 'FROM_END'],
  530. [Blockly.Msg.LISTS_GET_SUBLIST_END_LAST, 'LAST']];
  531. this.setHelpUrl(Blockly.Msg.LISTS_GET_SUBLIST_HELPURL);
  532. this.setColour(Blockly.Blocks.lists.HUE);
  533. this.appendValueInput('LIST')
  534. .setCheck('Array')
  535. .appendField(Blockly.Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST);
  536. this.appendDummyInput('AT1');
  537. this.appendDummyInput('AT2');
  538. if (Blockly.Msg.LISTS_GET_SUBLIST_TAIL) {
  539. this.appendDummyInput('TAIL')
  540. .appendField(Blockly.Msg.LISTS_GET_SUBLIST_TAIL);
  541. }
  542. this.setInputsInline(true);
  543. this.setOutput(true, 'Array');
  544. this.updateAt_(1, true);
  545. this.updateAt_(2, true);
  546. this.setTooltip(Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP);
  547. },
  548. /**
  549. * Create XML to represent whether there are 'AT' inputs.
  550. * @return {Element} XML storage element.
  551. * @this Blockly.Block
  552. */
  553. mutationToDom: function() {
  554. var container = document.createElement('mutation');
  555. var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE;
  556. container.setAttribute('at1', isAt1);
  557. var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE;
  558. container.setAttribute('at2', isAt2);
  559. return container;
  560. },
  561. /**
  562. * Parse XML to restore the 'AT' inputs.
  563. * @param {!Element} xmlElement XML storage element.
  564. * @this Blockly.Block
  565. */
  566. domToMutation: function(xmlElement) {
  567. var isAt1 = (xmlElement.getAttribute('at1') == 'true');
  568. var isAt2 = (xmlElement.getAttribute('at2') == 'true');
  569. this.updateAt_(1, isAt1);
  570. this.updateAt_(2, isAt2);
  571. },
  572. /**
  573. * Create or delete an input for a numeric index.
  574. * This block has two such inputs, independant of each other.
  575. * @param {number} n Specify first or second input (1 or 2).
  576. * @param {boolean} isAt True if the input should exist.
  577. * @private
  578. * @this Blockly.Block
  579. */
  580. updateAt_: function(n, isAt) {
  581. // Create or delete an input for the numeric index.
  582. // Destroy old 'AT' and 'ORDINAL' inputs.
  583. this.removeInput('AT' + n);
  584. this.removeInput('ORDINAL' + n, true);
  585. // Create either a value 'AT' input or a dummy input.
  586. if (isAt) {
  587. this.appendValueInput('AT' + n).setCheck('Number');
  588. if (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
  589. this.appendDummyInput('ORDINAL' + n)
  590. .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
  591. }
  592. } else {
  593. this.appendDummyInput('AT' + n);
  594. }
  595. var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n],
  596. function(value) {
  597. var newAt = (value == 'FROM_START') || (value == 'FROM_END');
  598. // The 'isAt' variable is available due to this function being a closure.
  599. if (newAt != isAt) {
  600. var block = this.sourceBlock_;
  601. block.updateAt_(n, newAt);
  602. // This menu has been destroyed and replaced. Update the replacement.
  603. block.setFieldValue(value, 'WHERE' + n);
  604. return null;
  605. }
  606. return undefined;
  607. });
  608. this.getInput('AT' + n)
  609. .appendField(menu, 'WHERE' + n);
  610. if (n == 1) {
  611. this.moveInputBefore('AT1', 'AT2');
  612. if (this.getInput('ORDINAL1')) {
  613. this.moveInputBefore('ORDINAL1', 'AT2');
  614. }
  615. }
  616. if (Blockly.Msg.LISTS_GET_SUBLIST_TAIL) {
  617. this.moveInputBefore('TAIL', null);
  618. }
  619. }
  620. };
  621. Blockly.Blocks['lists_split'] = {
  622. /**
  623. * Block for splitting text into a list, or joining a list into text.
  624. * @this Blockly.Block
  625. */
  626. init: function() {
  627. // Assign 'this' to a variable for use in the closures below.
  628. var thisBlock = this;
  629. var dropdown = new Blockly.FieldDropdown(
  630. [[Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT, 'SPLIT'],
  631. [Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST, 'JOIN']],
  632. function(newMode) {
  633. thisBlock.updateType_(newMode);
  634. });
  635. this.setHelpUrl(Blockly.Msg.LISTS_SPLIT_HELPURL);
  636. this.setColour(Blockly.Blocks.lists.HUE);
  637. this.appendValueInput('INPUT')
  638. .setCheck('String')
  639. .appendField(dropdown, 'MODE');
  640. this.appendValueInput('DELIM')
  641. .setCheck('String')
  642. .appendField(Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER);
  643. this.setInputsInline(true);
  644. this.setOutput(true, 'Array');
  645. this.setTooltip(function() {
  646. var mode = thisBlock.getFieldValue('MODE');
  647. if (mode == 'SPLIT') {
  648. return Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT;
  649. } else if (mode == 'JOIN') {
  650. return Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN;
  651. }
  652. throw 'Unknown mode: ' + mode;
  653. });
  654. },
  655. /**
  656. * Modify this block to have the correct input and output types.
  657. * @param {string} newMode Either 'SPLIT' or 'JOIN'.
  658. * @private
  659. * @this Blockly.Block
  660. */
  661. updateType_: function(newMode) {
  662. if (newMode == 'SPLIT') {
  663. this.outputConnection.setCheck('Array');
  664. this.getInput('INPUT').setCheck('String');
  665. } else {
  666. this.outputConnection.setCheck('String');
  667. this.getInput('INPUT').setCheck('Array');
  668. }
  669. },
  670. /**
  671. * Create XML to represent the input and output types.
  672. * @return {!Element} XML storage element.
  673. * @this Blockly.Block
  674. */
  675. mutationToDom: function() {
  676. var container = document.createElement('mutation');
  677. container.setAttribute('mode', this.getFieldValue('MODE'));
  678. return container;
  679. },
  680. /**
  681. * Parse XML to restore the input and output types.
  682. * @param {!Element} xmlElement XML storage element.
  683. * @this Blockly.Block
  684. */
  685. domToMutation: function(xmlElement) {
  686. this.updateType_(xmlElement.getAttribute('mode'));
  687. }
  688. };