source: EcnlProtoTool/trunk/webapp/webmrbc/Blocks/Lists.cs@ 270

Last change on this file since 270 was 270, checked in by coas-nagasima, 7 years ago

mruby版ECNLプロトタイピング・ツールを追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csharp
File size: 27.7 KB
Line 
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/**
22 * @fileoverview List blocks for Blockly.
23 * @author fraser@google.com (Neil Fraser)
24 */
25using System;
26using System.Linq;
27using System.Text;
28using Bridge;
29using Bridge.Html5;
30using Bridge.jQuery2;
31
32namespace WebMrbc
33{
34 public class Lists
35 {
36 /**
37 * Common HSV hue for all blocks in this category.
38 */
39 public static int HUE = 260;
40 }
41
42 public class ListsCreateEmptyBlock : Block
43 {
44 public const string type_name = "lists_create_empty";
45
46 public ListsCreateEmptyBlock()
47 : base(type_name)
48 {
49 }
50
51 /**
52 * Block for creating an empty list.
53 * The "list_create_with" block is preferred as it is more flexible.
54 * <block type="lists_create_with">
55 * <mutation items="0"></mutation>
56 * </block>
57 * @this Blockly.Block
58 */
59 public void init()
60 {
61 this.jsonInit(new {
62 message0 = Msg.LISTS_CREATE_EMPTY_TITLE,
63 output = "Array",
64 colour = Lists.HUE,
65 tooltip = Msg.LISTS_CREATE_EMPTY_TOOLTIP,
66 helpUrl = Msg.LISTS_CREATE_EMPTY_HELPURL
67 });
68 }
69 }
70
71 public class ListsCreateWithBlock : Block
72 {
73 public const string type_name = "lists_create_with";
74 internal int itemCount_;
75
76 public ListsCreateWithBlock()
77 : base(type_name)
78 {
79 }
80
81 /**
82 * Block for creating a list with any number of elements of any type.
83 * @this Blockly.Block
84 */
85 public void init()
86 {
87 this.setHelpUrl(Msg.LISTS_CREATE_WITH_HELPURL);
88 this.setColour(Lists.HUE);
89 this.itemCount_ = 3;
90 this.updateShape_();
91 this.setOutput(true, "Array");
92 this.setMutator(new Mutator(new[] { ListsCreateWithItemBlock.type_name }));
93 this.setTooltip(Msg.LISTS_CREATE_WITH_TOOLTIP);
94 }
95
96 /**
97 * Create XML to represent list inputs.
98 * @return {!Element} XML storage element.
99 * @this Blockly.Block
100 */
101 public Element mutationToDom()
102 {
103 var container = Document.CreateElement("mutation");
104 container.SetAttribute("items", this.itemCount_.ToString());
105 return container;
106 }
107
108 /**
109 * Parse XML to restore the list inputs.
110 * @param {!Element} xmlElement XML storage element.
111 * @this Blockly.Block
112 */
113 public void domToMutation(Element xmlElement)
114 {
115 var times = xmlElement.GetAttribute("items");
116 this.itemCount_ = times == null ? 0 : Script.ParseInt(times, 10);
117 this.updateShape_();
118 }
119
120 /**
121 * Populate the mutator's dialog with this block's components.
122 * @param {!Workspace} workspace Mutator's workspace.
123 * @return {!Blockly.Block} Root block in mutator.
124 * @this Blockly.Block
125 */
126 public Block decompose(Workspace workspace)
127 {
128 var containerBlock = workspace.newBlock(ListsCreateWithContainerBlock.type_name);
129 containerBlock.initSvg();
130 var connection = containerBlock.getInput("STACK").connection;
131 for (var i = 0; i < this.itemCount_; i++) {
132 var itemBlock = workspace.newBlock(ListsCreateWithItemBlock.type_name);
133 itemBlock.initSvg();
134 connection.connect(itemBlock.previousConnection);
135 connection = itemBlock.nextConnection;
136 }
137 return containerBlock;
138 }
139
140 /**
141 * Reconfigure this block based on the mutator dialog's components.
142 * @param {!Blockly.Block} containerBlock Root block in mutator.
143 * @this Blockly.Block
144 */
145 public void compose(Block containerBlock)
146 {
147 var itemBlock = (ListsCreateWithItemBlock)containerBlock.getInputTargetBlock("STACK");
148 // Count number of inputs.
149 var connections = new Connection[0];
150 while (itemBlock != null) {
151 connections.Push(itemBlock.valueConnection_);
152 itemBlock = (itemBlock.nextConnection != null) ?
153 (ListsCreateWithItemBlock)itemBlock.nextConnection.targetBlock() : null;
154 }
155 // Disconnect any children that don"t belong.
156 for (var i = 0; i < this.itemCount_; i++) {
157 var connection = this.getInput("ADD" + i).connection.targetConnection;
158 if (connection != null && Array.IndexOf(connections, connection) == -1) {
159 connection.disconnect();
160 }
161 }
162 this.itemCount_ = connections.Length;
163 this.updateShape_();
164 // Reconnect any child blocks.
165 for (var i = 0; i < this.itemCount_; i++) {
166 Mutator.reconnect(connections[i], this, "ADD" + i);
167 }
168 }
169
170 /**
171 * Store pointers to any connected child blocks.
172 * @param {!Blockly.Block} containerBlock Root block in mutator.
173 * @this Blockly.Block
174 */
175 public void saveConnections(Block containerBlock)
176 {
177 var itemBlock = (ListsCreateWithItemBlock)containerBlock.getInputTargetBlock("STACK");
178 var i = 0;
179 while (itemBlock != null) {
180 var input = this.getInput("ADD" + i);
181 itemBlock.valueConnection_ = (input != null) ? input.connection.targetConnection : null;
182 i++;
183 itemBlock = (itemBlock.nextConnection != null) ?
184 (ListsCreateWithItemBlock)itemBlock.nextConnection.targetBlock() : null;
185 }
186 }
187
188 /**
189 * Modify this block to have the correct number of inputs.
190 * @private
191 * @this Blockly.Block
192 */
193 private void updateShape_()
194 {
195 if (this.itemCount_ != 0 && this.getInput("EMPTY") != null) {
196 this.removeInput("EMPTY");
197 }
198 else if (this.itemCount_ == 0 && this.getInput("EMPTY") == null) {
199 this.appendDummyInput("EMPTY")
200 .appendField(Msg.LISTS_CREATE_EMPTY_TITLE);
201 }
202 // Add new inputs.
203 int i;
204 for (i = 0; i < this.itemCount_; i++) {
205 if (this.getInput("ADD" + i) == null) {
206 var input = this.appendValueInput("ADD" + i);
207 if (i == 0) {
208 input.appendField(Msg.LISTS_CREATE_WITH_INPUT_WITH);
209 }
210 }
211 }
212 // Remove deleted inputs.
213 while (this.getInput("ADD" + i) != null) {
214 this.removeInput("ADD" + i);
215 i++;
216 }
217 }
218 }
219
220 public class ListsCreateWithContainerBlock : Block
221 {
222 public const string type_name = "lists_create_with_container";
223
224 public ListsCreateWithContainerBlock()
225 : base(type_name)
226 {
227 }
228
229 /**
230 * Mutator block for list container.
231 * @this Blockly.Block
232 */
233 public void init()
234 {
235 this.setColour(Lists.HUE);
236 this.appendDummyInput()
237 .appendField(Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD);
238 this.appendStatementInput("STACK");
239 this.setTooltip(Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP);
240 this.contextMenu = false;
241 }
242 }
243
244 [IgnoreCast]
245 public class ListsCreateWithItemBlock : Block
246 {
247 public const string type_name = "lists_create_with_item";
248 public Connection valueConnection_;
249
250 public ListsCreateWithItemBlock()
251 : base(type_name)
252 {
253 }
254
255 /**
256 * Mutator bolck for adding items.
257 * @this Blockly.Block
258 */
259 public void init()
260 {
261 this.setColour(Lists.HUE);
262 this.appendDummyInput()
263 .appendField(Msg.LISTS_CREATE_WITH_ITEM_TITLE);
264 this.setPreviousStatement(true);
265 this.setNextStatement(true);
266 this.setTooltip(Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);
267 this.contextMenu = false;
268 }
269 }
270
271 public class ListsRepeatBlock : Block
272 {
273 public const string type_name = "lists_repeat";
274
275 public ListsRepeatBlock()
276 : base(type_name)
277 {
278 }
279
280 /**
281 * Block for creating a list with one element repeated.
282 * @this Blockly.Block
283 */
284 public void init()
285 {
286 this.jsonInit(new {
287 message0 = Msg.LISTS_REPEAT_TITLE,
288 args0 = new object[] {
289 new {
290 type = "input_value",
291 name = "ITEM"
292 },
293 new {
294 type = "input_value",
295 name = "NUM",
296 check = "Number"
297 }
298 },
299 output = "Array",
300 colour = Lists.HUE,
301 tooltip = Msg.LISTS_REPEAT_TOOLTIP,
302 helpUrl = Msg.LISTS_REPEAT_HELPURL
303 });
304 }
305 }
306
307 public class ListsLengthBlock : Block
308 {
309 public const string type_name = "lists_length";
310
311 public ListsLengthBlock()
312 : base(type_name)
313 {
314 }
315
316 /**
317 * Block for list length.
318 * @this Blockly.Block
319 */
320 public void init()
321 {
322 this.jsonInit(new {
323 message0 = Msg.LISTS_LENGTH_TITLE,
324 args0 = new object[] {
325 new {
326 type = "input_value",
327 name = "VALUE",
328 check = new [] { "String", "Array" }
329 }
330 },
331 output = "Number",
332 colour = Lists.HUE,
333 tooltip = Msg.LISTS_LENGTH_TOOLTIP,
334 helpUrl = Msg.LISTS_LENGTH_HELPURL
335 });
336 }
337 }
338
339 public class ListsIsEmptyBlock : Block
340 {
341 public const string type_name = "lists_isEmpty";
342
343 public ListsIsEmptyBlock()
344 : base(type_name)
345 {
346 }
347
348 /**
349 * Block for is the list empty?
350 * @this Blockly.Block
351 */
352 public void init()
353 {
354 this.jsonInit(new {
355 message0 = Msg.LISTS_ISEMPTY_TITLE,
356 args0 = new object[] {
357 new {
358 type = "input_value",
359 name = "VALUE",
360 check = new [] { "String", "Array" }
361 }
362 },
363 output = "Boolean",
364 colour = Lists.HUE,
365 tooltip = Msg.LISTS_ISEMPTY_TOOLTIP,
366 helpUrl = Msg.LISTS_ISEMPTY_HELPURL
367 });
368 }
369 }
370
371 public class ListsIndexOfBlock : Block
372 {
373 public const string type_name = "lists_indexOf";
374
375 public ListsIndexOfBlock()
376 : base(type_name)
377 {
378 }
379
380 /**
381 * Block for finding an item in the list.
382 * @this Blockly.Block
383 */
384 public void init()
385 {
386 var OPERATORS = new [] {
387 new [] {Msg.LISTS_INDEX_OF_FIRST, "FIRST"},
388 new [] {Msg.LISTS_INDEX_OF_LAST, "LAST"}
389 };
390 this.setHelpUrl(Msg.LISTS_INDEX_OF_HELPURL);
391 this.setColour(Lists.HUE);
392 this.setOutput(true, "Number");
393 this.appendValueInput("VALUE")
394 .setCheck("Array")
395 .appendField(Msg.LISTS_INDEX_OF_INPUT_IN_LIST);
396 this.appendValueInput("FIND")
397 .appendField(new FieldDropdown(OPERATORS), "END");
398 this.setInputsInline(true);
399 // Assign "this" to a variable for use in the tooltip closure below.
400 var thisBlock = this;
401 this.setTooltip(new Func<string>(() => {
402 return Msg.LISTS_INDEX_OF_TOOLTIP.Replace("%1",
403 this.workspace.options.oneBasedIndex ? "0" : "-1");
404 }));
405 }
406 }
407
408 public class ListsGetIndexBlock : Block
409 {
410 public const string type_name = "lists_getIndex";
411 string[][] WHERE_OPTIONS;
412
413 public ListsGetIndexBlock()
414 : base(type_name)
415 {
416 }
417
418 /**
419 * Block for getting element at index.
420 * @this Blockly.Block
421 */
422 public void init()
423 {
424 var MODE = new[] {
425 new [] {Msg.LISTS_GET_INDEX_GET, "GET"},
426 new [] {Msg.LISTS_GET_INDEX_GET_REMOVE, "GET_REMOVE"},
427 new [] {Msg.LISTS_GET_INDEX_REMOVE, "REMOVE"}
428 };
429 this.WHERE_OPTIONS = new string[][] {
430 new [] {Msg.LISTS_GET_INDEX_FROM_START, "FROM_START"},
431 new [] {Msg.LISTS_GET_INDEX_FROM_END, "FROM_END"},
432 new [] {Msg.LISTS_GET_INDEX_FIRST, "FIRST"},
433 new [] {Msg.LISTS_GET_INDEX_LAST, "LAST"},
434 new [] {Msg.LISTS_GET_INDEX_RANDOM, "RANDOM"}
435 };
436 this.setHelpUrl(Msg.LISTS_GET_INDEX_HELPURL);
437 this.setColour(Lists.HUE);
438 var modeMenu = new FieldDropdown(MODE, (value) => {
439 var isStatement = (value == "REMOVE");
440 this.updateStatement_(isStatement);
441 return Script.Undefined;
442 });
443 this.appendValueInput("VALUE")
444 .setCheck("Array")
445 .appendField(Msg.LISTS_GET_INDEX_INPUT_IN_LIST);
446 this.appendDummyInput()
447 .appendField(modeMenu, "MODE")
448 .appendField("", "SPACE");
449 this.appendDummyInput("AT");
450 if (!String.IsNullOrEmpty(Msg.LISTS_GET_INDEX_TAIL)) {
451 this.appendDummyInput("TAIL")
452 .appendField(Msg.LISTS_GET_INDEX_TAIL);
453 }
454 this.setInputsInline(true);
455 this.setOutput(true);
456 this.updateAt_(true);
457 // Assign "this" to a variable for use in the tooltip closure below.
458 var thisBlock = this;
459 this.setTooltip(new Func<string>(() => {
460 var mode = thisBlock.getFieldValue("MODE");
461 var where = thisBlock.getFieldValue("WHERE");
462 var tooltip = "";
463 switch (mode + " " + where) {
464 case "GET FROM_START":
465 case "GET FROM_END":
466 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM;
467 break;
468 case "GET FIRST":
469 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST;
470 break;
471 case "GET LAST":
472 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST;
473 break;
474 case "GET RANDOM":
475 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM;
476 break;
477 case "GET_REMOVE FROM_START":
478 case "GET_REMOVE FROM_END":
479 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM;
480 break;
481 case "GET_REMOVE FIRST":
482 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST;
483 break;
484 case "GET_REMOVE LAST":
485 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST;
486 break;
487 case "GET_REMOVE RANDOM":
488 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM;
489 break;
490 case "REMOVE FROM_START":
491 case "REMOVE FROM_END":
492 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM;
493 break;
494 case "REMOVE FIRST":
495 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST;
496 break;
497 case "REMOVE LAST":
498 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST;
499 break;
500 case "REMOVE RANDOM":
501 tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM;
502 break;
503 }
504 if (where == "FROM_START" || where == "FROM_END") {
505 var msg = (where == "FROM_START") ?
506 Msg.LISTS_INDEX_FROM_START_TOOLTIP :
507 Msg.LISTS_INDEX_FROM_END_TOOLTIP;
508 tooltip += " " + msg.Replace("%1",
509 thisBlock.workspace.options.oneBasedIndex ? "#1" : "#0");
510 }
511 return tooltip;
512 }));
513 }
514
515 /**
516 * Create XML to represent whether the block is a statement or a value.
517 * Also represent whether there is an "AT" input.
518 * @return {Element} XML storage element.
519 * @this Blockly.Block
520 */
521 public Element mutationToDom()
522 {
523 var container = Document.CreateElement("mutation");
524 var isStatement = this.outputConnection == null;
525 container.SetAttribute("statement", isStatement.ToString());
526 var isAt = this.getInput("AT").type == Blockly.INPUT_VALUE;
527 container.SetAttribute("at", isAt.ToString());
528 return container;
529 }
530
531 /**
532 * Parse XML to restore the "AT" input.
533 * @param {!Element} xmlElement XML storage element.
534 * @this Blockly.Block
535 */
536 public void domToMutation(Element xmlElement)
537 {
538 // Note: Until January 2013 this block did not have mutations,
539 // so "statement" defaults to false and "at" defaults to true.
540 var isStatement = (xmlElement.GetAttribute("statement") == "true");
541 this.updateStatement_(isStatement);
542 var isAt = (xmlElement.GetAttribute("at") != "false");
543 this.updateAt_(isAt);
544 }
545
546 /**
547 * Switch between a value block and a statement block.
548 * @param {boolean} newStatement True if the block should be a statement.
549 * False if the block should be a value.
550 * @private
551 * @this Blockly.Block
552 */
553 public void updateStatement_(bool newStatement)
554 {
555 var oldStatement = this.outputConnection == null;
556 if (newStatement != oldStatement) {
557 this.unplug(true);
558 if (newStatement) {
559 this.setOutput(false);
560 this.setPreviousStatement(true);
561 this.setNextStatement(true);
562 }
563 else {
564 this.setPreviousStatement(false);
565 this.setNextStatement(false);
566 this.setOutput(true);
567 }
568 }
569 }
570
571 /**
572 * Create or delete an input for the numeric index.
573 * @param {boolean} isAt True if the input should exist.
574 * @private
575 * @this Blockly.Block
576 */
577 public void updateAt_(bool isAt)
578 {
579 // Destroy old "AT" and "ORDINAL" inputs.
580 this.removeInput("AT");
581 this.removeInput("ORDINAL", true);
582 // Create either a value "AT" input or a dummy input.
583 if (isAt) {
584 this.appendValueInput("AT").setCheck("Number");
585 if (!String.IsNullOrEmpty(Msg.ORDINAL_NUMBER_SUFFIX)) {
586 this.appendDummyInput("ORDINAL")
587 .appendField(Msg.ORDINAL_NUMBER_SUFFIX);
588 }
589 }
590 else {
591 this.appendDummyInput("AT");
592 }
593 var menu = new FieldDropdown(this.WHERE_OPTIONS, (value) => {
594 var newAt = (value == "FROM_START") || (value == "FROM_END");
595 // The "isAt" variable is available due to this function being a closure.
596 if (newAt != isAt) {
597 this.updateAt_(newAt);
598 // This menu has been destroyed and replaced. Update the replacement.
599 this.setFieldValue(value, "WHERE");
600 return null;
601 }
602 return Script.Undefined;
603 });
604 this.getInput("AT").appendField(menu, "WHERE");
605 if (!String.IsNullOrEmpty(Msg.LISTS_GET_INDEX_TAIL)) {
606 this.moveInputBefore("TAIL", null);
607 }
608 }
609 }
610
611 public class ListsSetIndexBlock : Block
612 {
613 public const string type_name = "lists_setIndex";
614 string[][] WHERE_OPTIONS;
615
616 public ListsSetIndexBlock()
617 : base(type_name)
618 {
619 }
620
621 /**
622 * Block for setting the element at index.
623 * @this Blockly.Block
624 */
625 public void init()
626 {
627 var MODE = new[] {
628 new [] {Msg.LISTS_SET_INDEX_SET, "SET"},
629 new [] {Msg.LISTS_SET_INDEX_INSERT, "INSERT"}
630 };
631 this.WHERE_OPTIONS = new string[][]{
632 new [] {Msg.LISTS_GET_INDEX_FROM_START, "FROM_START"},
633 new [] {Msg.LISTS_GET_INDEX_FROM_END, "FROM_END"},
634 new [] {Msg.LISTS_GET_INDEX_FIRST, "FIRST"},
635 new [] {Msg.LISTS_GET_INDEX_LAST, "LAST"},
636 new [] {Msg.LISTS_GET_INDEX_RANDOM, "RANDOM"}
637 };
638 this.setHelpUrl(Msg.LISTS_SET_INDEX_HELPURL);
639 this.setColour(Lists.HUE);
640 this.appendValueInput("LIST")
641 .setCheck("Array")
642 .appendField(Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
643 this.appendDummyInput()
644 .appendField(new FieldDropdown(MODE), "MODE")
645 .appendField("", "SPACE");
646 this.appendDummyInput("AT");
647 this.appendValueInput("TO")
648 .appendField(Msg.LISTS_SET_INDEX_INPUT_TO);
649 this.setInputsInline(true);
650 this.setPreviousStatement(true);
651 this.setNextStatement(true);
652 this.setTooltip(Msg.LISTS_SET_INDEX_TOOLTIP);
653 this.updateAt_(true);
654 // Assign "this" to a variable for use in the tooltip closure below.
655 var thisBlock = this;
656 this.setTooltip(new Func<string>(() => {
657 var mode = thisBlock.getFieldValue("MODE");
658 var where = thisBlock.getFieldValue("WHERE");
659 var tooltip = "";
660 switch (mode + " " + where) {
661 case "SET FROM_START":
662 case "SET FROM_END":
663 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM;
664 break;
665 case "SET FIRST":
666 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST;
667 break;
668 case "SET LAST":
669 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST;
670 break;
671 case "SET RANDOM":
672 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM;
673 break;
674 case "INSERT FROM_START":
675 case "INSERT FROM_END":
676 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM;
677 break;
678 case "INSERT FIRST":
679 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST;
680 break;
681 case "INSERT LAST":
682 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST;
683 break;
684 case "INSERT RANDOM":
685 tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM;
686 break;
687 }
688 if (where == "FROM_START" || where == "FROM_END") {
689 tooltip += " " + Msg.LISTS_INDEX_FROM_START_TOOLTIP
690 .Replace("%1",
691 thisBlock.workspace.options.oneBasedIndex ? "#1" : "#0");
692 }
693 return tooltip;
694 }));
695 }
696
697 /**
698 * Create XML to represent whether there is an "AT" input.
699 * @return {Element} XML storage element.
700 * @this Blockly.Block
701 */
702 public Element mutationToDom()
703 {
704 var container = Document.CreateElement("mutation");
705 var isAt = this.getInput("AT").type == Blockly.INPUT_VALUE;
706 container.SetAttribute("at", isAt.ToString());
707 return container;
708 }
709
710 /**
711 * Parse XML to restore the "AT" input.
712 * @param {!Element} xmlElement XML storage element.
713 * @this Blockly.Block
714 */
715 public void domToMutation(Element xmlElement)
716 {
717 // Note: Until January 2013 this block did not have mutations,
718 // so "at" defaults to true.
719 var isAt = (xmlElement.GetAttribute("at") != "false");
720 this.updateAt_(isAt);
721 }
722
723 /**
724 * Create or delete an input for the numeric index.
725 * @param {boolean} isAt True if the input should exist.
726 * @private
727 * @this Blockly.Block
728 */
729 public void updateAt_(bool isAt)
730 {
731 // Destroy old "AT" and "ORDINAL" input.
732 this.removeInput("AT");
733 this.removeInput("ORDINAL", true);
734 // Create either a value "AT" input or a dummy input.
735 if (isAt) {
736 this.appendValueInput("AT").setCheck("Number");
737 if (!String.IsNullOrEmpty(Msg.ORDINAL_NUMBER_SUFFIX)) {
738 this.appendDummyInput("ORDINAL")
739 .appendField(Msg.ORDINAL_NUMBER_SUFFIX);
740 }
741 }
742 else {
743 this.appendDummyInput("AT");
744 }
745 var menu = new FieldDropdown(this.WHERE_OPTIONS, (value) => {
746 var newAt = (value == "FROM_START") || (value == "FROM_END");
747 // The "isAt" variable is available due to this function being a closure.
748 if (newAt != isAt) {
749 this.updateAt_(newAt);
750 // This menu has been destroyed and replaced. Update the replacement.
751 this.setFieldValue(value, "WHERE");
752 return null;
753 }
754 return Script.Undefined;
755 });
756 this.moveInputBefore("AT", "TO");
757 if (this.getInput("ORDINAL") != null) {
758 this.moveInputBefore("ORDINAL", "TO");
759 }
760
761 this.getInput("AT").appendField(menu, "WHERE");
762 }
763 }
764
765 public class ListsGetSublistBlock : Block
766 {
767 public const string type_name = "lists_getSublist";
768
769 public string[][][] WHERE_OPTIONS;
770
771 public ListsGetSublistBlock()
772 : base(type_name)
773 {
774 }
775
776 /**
777 * Block for getting sublist.
778 * @this Blockly.Block
779 */
780 public void init()
781 {
782 WHERE_OPTIONS = new[] {
783 new[] {
784 new [] {Msg.LISTS_GET_SUBLIST_START_FROM_START, "FROM_START"},
785 new [] {Msg.LISTS_GET_SUBLIST_START_FROM_END, "FROM_END"},
786 new [] {Msg.LISTS_GET_SUBLIST_START_FIRST, "FIRST"}
787 },
788 new[] {
789 new [] {Msg.LISTS_GET_SUBLIST_END_FROM_START, "FROM_START"},
790 new [] {Msg.LISTS_GET_SUBLIST_END_FROM_END, "FROM_END"},
791 new [] {Msg.LISTS_GET_SUBLIST_END_LAST, "LAST"}
792 }
793 };
794 this.setHelpUrl(Msg.LISTS_GET_SUBLIST_HELPURL);
795 this.setColour(Lists.HUE);
796 this.appendValueInput("LIST")
797 .setCheck("Array")
798 .appendField(Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST);
799 this.appendDummyInput("AT1");
800 this.appendDummyInput("AT2");
801 if (!String.IsNullOrEmpty(Msg.LISTS_GET_SUBLIST_TAIL)) {
802 this.appendDummyInput("TAIL")
803 .appendField(Msg.LISTS_GET_SUBLIST_TAIL);
804 }
805 this.setInputsInline(true);
806 this.setOutput(true, "Array");
807 this.updateAt_(1, true);
808 this.updateAt_(2, true);
809 this.setTooltip(Msg.LISTS_GET_SUBLIST_TOOLTIP);
810 }
811
812 /**
813 * Create XML to represent whether there are "AT" inputs.
814 * @return {Element} XML storage element.
815 * @this Blockly.Block
816 */
817 public Element mutationToDom()
818 {
819 var container = Document.CreateElement("mutation");
820 var isAt1 = this.getInput("AT1").type == Blockly.INPUT_VALUE;
821 container.SetAttribute("at1", isAt1.ToString());
822 var isAt2 = this.getInput("AT2").type == Blockly.INPUT_VALUE;
823 container.SetAttribute("at2", isAt2.ToString());
824 return container;
825 }
826
827 /**
828 * Parse XML to restore the "AT" inputs.
829 * @param {!Element} xmlElement XML storage element.
830 * @this Blockly.Block
831 */
832 public void domToMutation(Element xmlElement)
833 {
834 var isAt1 = (xmlElement.GetAttribute("at1") == "true");
835 var isAt2 = (xmlElement.GetAttribute("at2") == "true");
836 this.updateAt_(1, isAt1);
837 this.updateAt_(2, isAt2);
838 }
839
840 /**
841 * Create or delete an input for a numeric index.
842 * This block has two such inputs, independant of each other.
843 * @param {number} n Specify first or second input (1 or 2).
844 * @param {boolean} isAt True if the input should exist.
845 * @private
846 * @this Blockly.Block
847 */
848 public void updateAt_(int n, bool isAt)
849 {
850 // Create or delete an input for the numeric index.
851 // Destroy old "AT" and "ORDINAL" inputs.
852 this.removeInput("AT" + n);
853 this.removeInput("ORDINAL" + n, true);
854 // Create either a value "AT" input or a dummy input.
855 if (isAt) {
856 this.appendValueInput("AT" + n).setCheck("Number");
857 if (!String.IsNullOrEmpty(Msg.ORDINAL_NUMBER_SUFFIX)) {
858 this.appendDummyInput("ORDINAL" + n)
859 .appendField(Msg.ORDINAL_NUMBER_SUFFIX);
860 }
861 }
862 else {
863 this.appendDummyInput("AT" + n);
864 }
865 var menu = new FieldDropdown(WHERE_OPTIONS[n - 1], (value) => {
866 var newAt = (value == "FROM_START") || (value == "FROM_END");
867 // The "isAt" variable is available due to this function being a
868 // closure.
869 if (newAt != isAt) {
870 this.updateAt_(n, newAt);
871 // This menu has been destroyed and replaced.
872 // Update the replacement.
873 this.setFieldValue(value, "WHERE" + n);
874 return null;
875 }
876 return Script.Undefined;
877 });
878 this.getInput("AT" + n)
879 .appendField(menu, "WHERE" + n);
880 if (n == 1) {
881 this.moveInputBefore("AT1", "AT2");
882 if (this.getInput("ORDINAL1") != null) {
883 this.moveInputBefore("ORDINAL1", "AT2");
884 }
885 }
886 if (!String.IsNullOrEmpty(Msg.LISTS_GET_SUBLIST_TAIL)) {
887 this.moveInputBefore("TAIL", null);
888 }
889 }
890 }
891
892 public class ListsSortBlock : Block
893 {
894 public const string type_name = "lists_sort";
895
896 public ListsSortBlock()
897 : base(type_name)
898 {
899 }
900
901 /**
902 * Block for sorting a list.
903 * @this Blockly.Block
904 */
905 public void init()
906 {
907 this.jsonInit(new {
908 message0 = Msg.LISTS_SORT_TITLE,
909 args0 = new object[] {
910 new {
911 type = "field_dropdown",
912 name = "TYPE",
913 options = new [] {
914 new [] {Msg.LISTS_SORT_TYPE_NUMERIC, "NUMERIC"},
915 new [] {Msg.LISTS_SORT_TYPE_TEXT, "TEXT"},
916 new [] {Msg.LISTS_SORT_TYPE_IGNORECASE, "IGNORE_CASE"}
917 }
918 },
919 new {
920 type = "field_dropdown",
921 name = "DIRECTION",
922 options = new [] {
923 new [] {Msg.LISTS_SORT_ORDER_ASCENDING, "1"},
924 new [] {Msg.LISTS_SORT_ORDER_DESCENDING, "-1"}
925 }
926 },
927 new {
928 type = "input_value",
929 name = "LIST",
930 check = "Array"
931 }
932 },
933 output = "Array",
934 colour = Lists.HUE,
935 tooltip = Msg.LISTS_SORT_TOOLTIP,
936 helpUrl = Msg.LISTS_SORT_HELPURL
937 });
938 }
939 }
940
941 public class ListsSplitBlock : Block
942 {
943 public const string type_name = "lists_split";
944
945 public ListsSplitBlock()
946 : base(type_name)
947 {
948 }
949
950 /**
951 * Block for splitting text into a list, or joining a list into text.
952 * @this Blockly.Block
953 */
954 public void init()
955 {
956 // Assign "this" to a variable for use in the closures below.
957 var thisBlock = this;
958 var dropdown = new FieldDropdown(new[] {
959 new [] {Msg.LISTS_SPLIT_LIST_FROM_TEXT, "SPLIT"},
960 new [] {Msg.LISTS_SPLIT_TEXT_FROM_LIST, "JOIN"}
961 },
962 (newMode) => {
963 thisBlock.updateType_(newMode);
964 return Script.Undefined;
965 });
966 this.setHelpUrl(Msg.LISTS_SPLIT_HELPURL);
967 this.setColour(Lists.HUE);
968 this.appendValueInput("INPUT")
969 .setCheck("String")
970 .appendField(dropdown, "MODE");
971 this.appendValueInput("DELIM")
972 .setCheck("String")
973 .appendField(Msg.LISTS_SPLIT_WITH_DELIMITER);
974 this.setInputsInline(true);
975 this.setOutput(true, "Array");
976 this.setTooltip(new Func<string>(() => {
977 var mode = thisBlock.getFieldValue("MODE");
978 if (mode == "SPLIT") {
979 return Msg.LISTS_SPLIT_TOOLTIP_SPLIT;
980 }
981 else if (mode == "JOIN") {
982 return Msg.LISTS_SPLIT_TOOLTIP_JOIN;
983 }
984 throw new Exception("Unknown mode: " + mode);
985 }));
986 }
987
988 /**
989 * Modify this block to have the correct input and output types.
990 * @param {string} newMode Either "SPLIT" or "JOIN".
991 * @private
992 * @this Blockly.Block
993 */
994 public void updateType_(string newMode)
995 {
996 if (newMode == "SPLIT") {
997 this.outputConnection.setCheck("Array");
998 this.getInput("INPUT").setCheck("String");
999 }
1000 else {
1001 this.outputConnection.setCheck("String");
1002 this.getInput("INPUT").setCheck("Array");
1003 }
1004 }
1005
1006 /**
1007 * Create XML to represent the input and output types.
1008 * @return {!Element} XML storage element.
1009 * @this Blockly.Block
1010 */
1011 public Element mutationToDom()
1012 {
1013 var container = Document.CreateElement("mutation");
1014 container.SetAttribute("mode", this.getFieldValue("MODE"));
1015 return container;
1016 }
1017
1018 /**
1019 * Parse XML to restore the input and output types.
1020 * @param {!Element} xmlElement XML storage element.
1021 * @this Blockly.Block
1022 */
1023 public void domToMutation(Element xmlElement)
1024 {
1025 this.updateType_(xmlElement.GetAttribute("mode"));
1026 }
1027 }
1028}
Note: See TracBrowser for help on using the repository browser.