/**
* @license
* Visual Blocks Editor
*
* Copyright 2012 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview List blocks for Blockly.
* @author fraser@google.com (Neil Fraser)
*/
using System;
using System.Linq;
using System.Text;
using Bridge;
using Bridge.Html5;
using Bridge.jQuery2;
namespace WebMrbc
{
public class Lists
{
/**
* Common HSV hue for all blocks in this category.
*/
public static int HUE = 260;
}
public class ListsCreateEmptyBlock : Block
{
public const string type_name = "lists_create_empty";
public ListsCreateEmptyBlock()
: base(type_name)
{
}
/**
* Block for creating an empty list.
* The "list_create_with" block is preferred as it is more flexible.
*
*
*
* @this Blockly.Block
*/
public void init()
{
this.jsonInit(new {
message0 = Msg.LISTS_CREATE_EMPTY_TITLE,
output = "Array",
colour = Lists.HUE,
tooltip = Msg.LISTS_CREATE_EMPTY_TOOLTIP,
helpUrl = Msg.LISTS_CREATE_EMPTY_HELPURL
});
}
}
public class ListsCreateWithBlock : Block
{
public const string type_name = "lists_create_with";
internal int itemCount_;
public ListsCreateWithBlock()
: base(type_name)
{
}
/**
* Block for creating a list with any number of elements of any type.
* @this Blockly.Block
*/
public void init()
{
this.setHelpUrl(Msg.LISTS_CREATE_WITH_HELPURL);
this.setColour(Lists.HUE);
this.itemCount_ = 3;
this.updateShape_();
this.setOutput(true, "Array");
this.setMutator(new Mutator(new[] { ListsCreateWithItemBlock.type_name }));
this.setTooltip(Msg.LISTS_CREATE_WITH_TOOLTIP);
}
/**
* Create XML to represent list inputs.
* @return {!Element} XML storage element.
* @this Blockly.Block
*/
public Element mutationToDom()
{
var container = Document.CreateElement("mutation");
container.SetAttribute("items", this.itemCount_.ToString());
return container;
}
/**
* Parse XML to restore the list inputs.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
public void domToMutation(Element xmlElement)
{
var times = xmlElement.GetAttribute("items");
this.itemCount_ = times == null ? 0 : Script.ParseInt(times, 10);
this.updateShape_();
}
/**
* Populate the mutator's dialog with this block's components.
* @param {!Workspace} workspace Mutator's workspace.
* @return {!Blockly.Block} Root block in mutator.
* @this Blockly.Block
*/
public Block decompose(Workspace workspace)
{
var containerBlock = workspace.newBlock(ListsCreateWithContainerBlock.type_name);
containerBlock.initSvg();
var connection = containerBlock.getInput("STACK").connection;
for (var i = 0; i < this.itemCount_; i++) {
var itemBlock = workspace.newBlock(ListsCreateWithItemBlock.type_name);
itemBlock.initSvg();
connection.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
return containerBlock;
}
/**
* Reconfigure this block based on the mutator dialog's components.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this Blockly.Block
*/
public void compose(Block containerBlock)
{
var itemBlock = (ListsCreateWithItemBlock)containerBlock.getInputTargetBlock("STACK");
// Count number of inputs.
var connections = new Connection[0];
while (itemBlock != null) {
connections.Push(itemBlock.valueConnection_);
itemBlock = (itemBlock.nextConnection != null) ?
(ListsCreateWithItemBlock)itemBlock.nextConnection.targetBlock() : null;
}
// Disconnect any children that don"t belong.
for (var i = 0; i < this.itemCount_; i++) {
var connection = this.getInput("ADD" + i).connection.targetConnection;
if (connection != null && Array.IndexOf(connections, connection) == -1) {
connection.disconnect();
}
}
this.itemCount_ = connections.Length;
this.updateShape_();
// Reconnect any child blocks.
for (var i = 0; i < this.itemCount_; i++) {
Mutator.reconnect(connections[i], this, "ADD" + i);
}
}
/**
* Store pointers to any connected child blocks.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this Blockly.Block
*/
public void saveConnections(Block containerBlock)
{
var itemBlock = (ListsCreateWithItemBlock)containerBlock.getInputTargetBlock("STACK");
var i = 0;
while (itemBlock != null) {
var input = this.getInput("ADD" + i);
itemBlock.valueConnection_ = (input != null) ? input.connection.targetConnection : null;
i++;
itemBlock = (itemBlock.nextConnection != null) ?
(ListsCreateWithItemBlock)itemBlock.nextConnection.targetBlock() : null;
}
}
/**
* Modify this block to have the correct number of inputs.
* @private
* @this Blockly.Block
*/
private void updateShape_()
{
if (this.itemCount_ != 0 && this.getInput("EMPTY") != null) {
this.removeInput("EMPTY");
}
else if (this.itemCount_ == 0 && this.getInput("EMPTY") == null) {
this.appendDummyInput("EMPTY")
.appendField(Msg.LISTS_CREATE_EMPTY_TITLE);
}
// Add new inputs.
int i;
for (i = 0; i < this.itemCount_; i++) {
if (this.getInput("ADD" + i) == null) {
var input = this.appendValueInput("ADD" + i);
if (i == 0) {
input.appendField(Msg.LISTS_CREATE_WITH_INPUT_WITH);
}
}
}
// Remove deleted inputs.
while (this.getInput("ADD" + i) != null) {
this.removeInput("ADD" + i);
i++;
}
}
}
public class ListsCreateWithContainerBlock : Block
{
public const string type_name = "lists_create_with_container";
public ListsCreateWithContainerBlock()
: base(type_name)
{
}
/**
* Mutator block for list container.
* @this Blockly.Block
*/
public void init()
{
this.setColour(Lists.HUE);
this.appendDummyInput()
.appendField(Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD);
this.appendStatementInput("STACK");
this.setTooltip(Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP);
this.contextMenu = false;
}
}
[IgnoreCast]
public class ListsCreateWithItemBlock : Block
{
public const string type_name = "lists_create_with_item";
public Connection valueConnection_;
public ListsCreateWithItemBlock()
: base(type_name)
{
}
/**
* Mutator bolck for adding items.
* @this Blockly.Block
*/
public void init()
{
this.setColour(Lists.HUE);
this.appendDummyInput()
.appendField(Msg.LISTS_CREATE_WITH_ITEM_TITLE);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);
this.contextMenu = false;
}
}
public class ListsRepeatBlock : Block
{
public const string type_name = "lists_repeat";
public ListsRepeatBlock()
: base(type_name)
{
}
/**
* Block for creating a list with one element repeated.
* @this Blockly.Block
*/
public void init()
{
this.jsonInit(new {
message0 = Msg.LISTS_REPEAT_TITLE,
args0 = new object[] {
new {
type = "input_value",
name = "ITEM"
},
new {
type = "input_value",
name = "NUM",
check = "Number"
}
},
output = "Array",
colour = Lists.HUE,
tooltip = Msg.LISTS_REPEAT_TOOLTIP,
helpUrl = Msg.LISTS_REPEAT_HELPURL
});
}
}
public class ListsLengthBlock : Block
{
public const string type_name = "lists_length";
public ListsLengthBlock()
: base(type_name)
{
}
/**
* Block for list length.
* @this Blockly.Block
*/
public void init()
{
this.jsonInit(new {
message0 = Msg.LISTS_LENGTH_TITLE,
args0 = new object[] {
new {
type = "input_value",
name = "VALUE",
check = new [] { "String", "Array" }
}
},
output = "Number",
colour = Lists.HUE,
tooltip = Msg.LISTS_LENGTH_TOOLTIP,
helpUrl = Msg.LISTS_LENGTH_HELPURL
});
}
}
public class ListsIsEmptyBlock : Block
{
public const string type_name = "lists_isEmpty";
public ListsIsEmptyBlock()
: base(type_name)
{
}
/**
* Block for is the list empty?
* @this Blockly.Block
*/
public void init()
{
this.jsonInit(new {
message0 = Msg.LISTS_ISEMPTY_TITLE,
args0 = new object[] {
new {
type = "input_value",
name = "VALUE",
check = new [] { "String", "Array" }
}
},
output = "Boolean",
colour = Lists.HUE,
tooltip = Msg.LISTS_ISEMPTY_TOOLTIP,
helpUrl = Msg.LISTS_ISEMPTY_HELPURL
});
}
}
public class ListsIndexOfBlock : Block
{
public const string type_name = "lists_indexOf";
public ListsIndexOfBlock()
: base(type_name)
{
}
/**
* Block for finding an item in the list.
* @this Blockly.Block
*/
public void init()
{
var OPERATORS = new [] {
new [] {Msg.LISTS_INDEX_OF_FIRST, "FIRST"},
new [] {Msg.LISTS_INDEX_OF_LAST, "LAST"}
};
this.setHelpUrl(Msg.LISTS_INDEX_OF_HELPURL);
this.setColour(Lists.HUE);
this.setOutput(true, "Number");
this.appendValueInput("VALUE")
.setCheck("Array")
.appendField(Msg.LISTS_INDEX_OF_INPUT_IN_LIST);
this.appendValueInput("FIND")
.appendField(new FieldDropdown(OPERATORS), "END");
this.setInputsInline(true);
// Assign "this" to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(new Func(() => {
return Msg.LISTS_INDEX_OF_TOOLTIP.Replace("%1",
this.workspace.options.oneBasedIndex ? "0" : "-1");
}));
}
}
public class ListsGetIndexBlock : Block
{
public const string type_name = "lists_getIndex";
string[][] WHERE_OPTIONS;
public ListsGetIndexBlock()
: base(type_name)
{
}
/**
* Block for getting element at index.
* @this Blockly.Block
*/
public void init()
{
var MODE = new[] {
new [] {Msg.LISTS_GET_INDEX_GET, "GET"},
new [] {Msg.LISTS_GET_INDEX_GET_REMOVE, "GET_REMOVE"},
new [] {Msg.LISTS_GET_INDEX_REMOVE, "REMOVE"}
};
this.WHERE_OPTIONS = new string[][] {
new [] {Msg.LISTS_GET_INDEX_FROM_START, "FROM_START"},
new [] {Msg.LISTS_GET_INDEX_FROM_END, "FROM_END"},
new [] {Msg.LISTS_GET_INDEX_FIRST, "FIRST"},
new [] {Msg.LISTS_GET_INDEX_LAST, "LAST"},
new [] {Msg.LISTS_GET_INDEX_RANDOM, "RANDOM"}
};
this.setHelpUrl(Msg.LISTS_GET_INDEX_HELPURL);
this.setColour(Lists.HUE);
var modeMenu = new FieldDropdown(MODE, (value) => {
var isStatement = (value == "REMOVE");
this.updateStatement_(isStatement);
return Script.Undefined;
});
this.appendValueInput("VALUE")
.setCheck("Array")
.appendField(Msg.LISTS_GET_INDEX_INPUT_IN_LIST);
this.appendDummyInput()
.appendField(modeMenu, "MODE")
.appendField("", "SPACE");
this.appendDummyInput("AT");
if (!String.IsNullOrEmpty(Msg.LISTS_GET_INDEX_TAIL)) {
this.appendDummyInput("TAIL")
.appendField(Msg.LISTS_GET_INDEX_TAIL);
}
this.setInputsInline(true);
this.setOutput(true);
this.updateAt_(true);
// Assign "this" to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(new Func(() => {
var mode = thisBlock.getFieldValue("MODE");
var where = thisBlock.getFieldValue("WHERE");
var tooltip = "";
switch (mode + " " + where) {
case "GET FROM_START":
case "GET FROM_END":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM;
break;
case "GET FIRST":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST;
break;
case "GET LAST":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST;
break;
case "GET RANDOM":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM;
break;
case "GET_REMOVE FROM_START":
case "GET_REMOVE FROM_END":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM;
break;
case "GET_REMOVE FIRST":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST;
break;
case "GET_REMOVE LAST":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST;
break;
case "GET_REMOVE RANDOM":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM;
break;
case "REMOVE FROM_START":
case "REMOVE FROM_END":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM;
break;
case "REMOVE FIRST":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST;
break;
case "REMOVE LAST":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST;
break;
case "REMOVE RANDOM":
tooltip = Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM;
break;
}
if (where == "FROM_START" || where == "FROM_END") {
var msg = (where == "FROM_START") ?
Msg.LISTS_INDEX_FROM_START_TOOLTIP :
Msg.LISTS_INDEX_FROM_END_TOOLTIP;
tooltip += " " + msg.Replace("%1",
thisBlock.workspace.options.oneBasedIndex ? "#1" : "#0");
}
return tooltip;
}));
}
/**
* Create XML to represent whether the block is a statement or a value.
* Also represent whether there is an "AT" input.
* @return {Element} XML storage element.
* @this Blockly.Block
*/
public Element mutationToDom()
{
var container = Document.CreateElement("mutation");
var isStatement = this.outputConnection == null;
container.SetAttribute("statement", isStatement.ToString());
var isAt = this.getInput("AT").type == Blockly.INPUT_VALUE;
container.SetAttribute("at", isAt.ToString());
return container;
}
/**
* Parse XML to restore the "AT" input.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
public void domToMutation(Element xmlElement)
{
// Note: Until January 2013 this block did not have mutations,
// so "statement" defaults to false and "at" defaults to true.
var isStatement = (xmlElement.GetAttribute("statement") == "true");
this.updateStatement_(isStatement);
var isAt = (xmlElement.GetAttribute("at") != "false");
this.updateAt_(isAt);
}
/**
* Switch between a value block and a statement block.
* @param {boolean} newStatement True if the block should be a statement.
* False if the block should be a value.
* @private
* @this Blockly.Block
*/
public void updateStatement_(bool newStatement)
{
var oldStatement = this.outputConnection == null;
if (newStatement != oldStatement) {
this.unplug(true);
if (newStatement) {
this.setOutput(false);
this.setPreviousStatement(true);
this.setNextStatement(true);
}
else {
this.setPreviousStatement(false);
this.setNextStatement(false);
this.setOutput(true);
}
}
}
/**
* Create or delete an input for the numeric index.
* @param {boolean} isAt True if the input should exist.
* @private
* @this Blockly.Block
*/
public void updateAt_(bool isAt)
{
// Destroy old "AT" and "ORDINAL" inputs.
this.removeInput("AT");
this.removeInput("ORDINAL", true);
// Create either a value "AT" input or a dummy input.
if (isAt) {
this.appendValueInput("AT").setCheck("Number");
if (!String.IsNullOrEmpty(Msg.ORDINAL_NUMBER_SUFFIX)) {
this.appendDummyInput("ORDINAL")
.appendField(Msg.ORDINAL_NUMBER_SUFFIX);
}
}
else {
this.appendDummyInput("AT");
}
var menu = new FieldDropdown(this.WHERE_OPTIONS, (value) => {
var newAt = (value == "FROM_START") || (value == "FROM_END");
// The "isAt" variable is available due to this function being a closure.
if (newAt != isAt) {
this.updateAt_(newAt);
// This menu has been destroyed and replaced. Update the replacement.
this.setFieldValue(value, "WHERE");
return null;
}
return Script.Undefined;
});
this.getInput("AT").appendField(menu, "WHERE");
if (!String.IsNullOrEmpty(Msg.LISTS_GET_INDEX_TAIL)) {
this.moveInputBefore("TAIL", null);
}
}
}
public class ListsSetIndexBlock : Block
{
public const string type_name = "lists_setIndex";
string[][] WHERE_OPTIONS;
public ListsSetIndexBlock()
: base(type_name)
{
}
/**
* Block for setting the element at index.
* @this Blockly.Block
*/
public void init()
{
var MODE = new[] {
new [] {Msg.LISTS_SET_INDEX_SET, "SET"},
new [] {Msg.LISTS_SET_INDEX_INSERT, "INSERT"}
};
this.WHERE_OPTIONS = new string[][]{
new [] {Msg.LISTS_GET_INDEX_FROM_START, "FROM_START"},
new [] {Msg.LISTS_GET_INDEX_FROM_END, "FROM_END"},
new [] {Msg.LISTS_GET_INDEX_FIRST, "FIRST"},
new [] {Msg.LISTS_GET_INDEX_LAST, "LAST"},
new [] {Msg.LISTS_GET_INDEX_RANDOM, "RANDOM"}
};
this.setHelpUrl(Msg.LISTS_SET_INDEX_HELPURL);
this.setColour(Lists.HUE);
this.appendValueInput("LIST")
.setCheck("Array")
.appendField(Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
this.appendDummyInput()
.appendField(new FieldDropdown(MODE), "MODE")
.appendField("", "SPACE");
this.appendDummyInput("AT");
this.appendValueInput("TO")
.appendField(Msg.LISTS_SET_INDEX_INPUT_TO);
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Msg.LISTS_SET_INDEX_TOOLTIP);
this.updateAt_(true);
// Assign "this" to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(new Func(() => {
var mode = thisBlock.getFieldValue("MODE");
var where = thisBlock.getFieldValue("WHERE");
var tooltip = "";
switch (mode + " " + where) {
case "SET FROM_START":
case "SET FROM_END":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM;
break;
case "SET FIRST":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST;
break;
case "SET LAST":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST;
break;
case "SET RANDOM":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM;
break;
case "INSERT FROM_START":
case "INSERT FROM_END":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM;
break;
case "INSERT FIRST":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST;
break;
case "INSERT LAST":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST;
break;
case "INSERT RANDOM":
tooltip = Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM;
break;
}
if (where == "FROM_START" || where == "FROM_END") {
tooltip += " " + Msg.LISTS_INDEX_FROM_START_TOOLTIP
.Replace("%1",
thisBlock.workspace.options.oneBasedIndex ? "#1" : "#0");
}
return tooltip;
}));
}
/**
* Create XML to represent whether there is an "AT" input.
* @return {Element} XML storage element.
* @this Blockly.Block
*/
public Element mutationToDom()
{
var container = Document.CreateElement("mutation");
var isAt = this.getInput("AT").type == Blockly.INPUT_VALUE;
container.SetAttribute("at", isAt.ToString());
return container;
}
/**
* Parse XML to restore the "AT" input.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
public void domToMutation(Element xmlElement)
{
// Note: Until January 2013 this block did not have mutations,
// so "at" defaults to true.
var isAt = (xmlElement.GetAttribute("at") != "false");
this.updateAt_(isAt);
}
/**
* Create or delete an input for the numeric index.
* @param {boolean} isAt True if the input should exist.
* @private
* @this Blockly.Block
*/
public void updateAt_(bool isAt)
{
// Destroy old "AT" and "ORDINAL" input.
this.removeInput("AT");
this.removeInput("ORDINAL", true);
// Create either a value "AT" input or a dummy input.
if (isAt) {
this.appendValueInput("AT").setCheck("Number");
if (!String.IsNullOrEmpty(Msg.ORDINAL_NUMBER_SUFFIX)) {
this.appendDummyInput("ORDINAL")
.appendField(Msg.ORDINAL_NUMBER_SUFFIX);
}
}
else {
this.appendDummyInput("AT");
}
var menu = new FieldDropdown(this.WHERE_OPTIONS, (value) => {
var newAt = (value == "FROM_START") || (value == "FROM_END");
// The "isAt" variable is available due to this function being a closure.
if (newAt != isAt) {
this.updateAt_(newAt);
// This menu has been destroyed and replaced. Update the replacement.
this.setFieldValue(value, "WHERE");
return null;
}
return Script.Undefined;
});
this.moveInputBefore("AT", "TO");
if (this.getInput("ORDINAL") != null) {
this.moveInputBefore("ORDINAL", "TO");
}
this.getInput("AT").appendField(menu, "WHERE");
}
}
public class ListsGetSublistBlock : Block
{
public const string type_name = "lists_getSublist";
public string[][][] WHERE_OPTIONS;
public ListsGetSublistBlock()
: base(type_name)
{
}
/**
* Block for getting sublist.
* @this Blockly.Block
*/
public void init()
{
WHERE_OPTIONS = new[] {
new[] {
new [] {Msg.LISTS_GET_SUBLIST_START_FROM_START, "FROM_START"},
new [] {Msg.LISTS_GET_SUBLIST_START_FROM_END, "FROM_END"},
new [] {Msg.LISTS_GET_SUBLIST_START_FIRST, "FIRST"}
},
new[] {
new [] {Msg.LISTS_GET_SUBLIST_END_FROM_START, "FROM_START"},
new [] {Msg.LISTS_GET_SUBLIST_END_FROM_END, "FROM_END"},
new [] {Msg.LISTS_GET_SUBLIST_END_LAST, "LAST"}
}
};
this.setHelpUrl(Msg.LISTS_GET_SUBLIST_HELPURL);
this.setColour(Lists.HUE);
this.appendValueInput("LIST")
.setCheck("Array")
.appendField(Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST);
this.appendDummyInput("AT1");
this.appendDummyInput("AT2");
if (!String.IsNullOrEmpty(Msg.LISTS_GET_SUBLIST_TAIL)) {
this.appendDummyInput("TAIL")
.appendField(Msg.LISTS_GET_SUBLIST_TAIL);
}
this.setInputsInline(true);
this.setOutput(true, "Array");
this.updateAt_(1, true);
this.updateAt_(2, true);
this.setTooltip(Msg.LISTS_GET_SUBLIST_TOOLTIP);
}
/**
* Create XML to represent whether there are "AT" inputs.
* @return {Element} XML storage element.
* @this Blockly.Block
*/
public Element mutationToDom()
{
var container = Document.CreateElement("mutation");
var isAt1 = this.getInput("AT1").type == Blockly.INPUT_VALUE;
container.SetAttribute("at1", isAt1.ToString());
var isAt2 = this.getInput("AT2").type == Blockly.INPUT_VALUE;
container.SetAttribute("at2", isAt2.ToString());
return container;
}
/**
* Parse XML to restore the "AT" inputs.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
public void domToMutation(Element xmlElement)
{
var isAt1 = (xmlElement.GetAttribute("at1") == "true");
var isAt2 = (xmlElement.GetAttribute("at2") == "true");
this.updateAt_(1, isAt1);
this.updateAt_(2, isAt2);
}
/**
* Create or delete an input for a numeric index.
* This block has two such inputs, independant of each other.
* @param {number} n Specify first or second input (1 or 2).
* @param {boolean} isAt True if the input should exist.
* @private
* @this Blockly.Block
*/
public void updateAt_(int n, bool isAt)
{
// Create or delete an input for the numeric index.
// Destroy old "AT" and "ORDINAL" inputs.
this.removeInput("AT" + n);
this.removeInput("ORDINAL" + n, true);
// Create either a value "AT" input or a dummy input.
if (isAt) {
this.appendValueInput("AT" + n).setCheck("Number");
if (!String.IsNullOrEmpty(Msg.ORDINAL_NUMBER_SUFFIX)) {
this.appendDummyInput("ORDINAL" + n)
.appendField(Msg.ORDINAL_NUMBER_SUFFIX);
}
}
else {
this.appendDummyInput("AT" + n);
}
var menu = new FieldDropdown(WHERE_OPTIONS[n - 1], (value) => {
var newAt = (value == "FROM_START") || (value == "FROM_END");
// The "isAt" variable is available due to this function being a
// closure.
if (newAt != isAt) {
this.updateAt_(n, newAt);
// This menu has been destroyed and replaced.
// Update the replacement.
this.setFieldValue(value, "WHERE" + n);
return null;
}
return Script.Undefined;
});
this.getInput("AT" + n)
.appendField(menu, "WHERE" + n);
if (n == 1) {
this.moveInputBefore("AT1", "AT2");
if (this.getInput("ORDINAL1") != null) {
this.moveInputBefore("ORDINAL1", "AT2");
}
}
if (!String.IsNullOrEmpty(Msg.LISTS_GET_SUBLIST_TAIL)) {
this.moveInputBefore("TAIL", null);
}
}
}
public class ListsSortBlock : Block
{
public const string type_name = "lists_sort";
public ListsSortBlock()
: base(type_name)
{
}
/**
* Block for sorting a list.
* @this Blockly.Block
*/
public void init()
{
this.jsonInit(new {
message0 = Msg.LISTS_SORT_TITLE,
args0 = new object[] {
new {
type = "field_dropdown",
name = "TYPE",
options = new [] {
new [] {Msg.LISTS_SORT_TYPE_NUMERIC, "NUMERIC"},
new [] {Msg.LISTS_SORT_TYPE_TEXT, "TEXT"},
new [] {Msg.LISTS_SORT_TYPE_IGNORECASE, "IGNORE_CASE"}
}
},
new {
type = "field_dropdown",
name = "DIRECTION",
options = new [] {
new [] {Msg.LISTS_SORT_ORDER_ASCENDING, "1"},
new [] {Msg.LISTS_SORT_ORDER_DESCENDING, "-1"}
}
},
new {
type = "input_value",
name = "LIST",
check = "Array"
}
},
output = "Array",
colour = Lists.HUE,
tooltip = Msg.LISTS_SORT_TOOLTIP,
helpUrl = Msg.LISTS_SORT_HELPURL
});
}
}
public class ListsSplitBlock : Block
{
public const string type_name = "lists_split";
public ListsSplitBlock()
: base(type_name)
{
}
/**
* Block for splitting text into a list, or joining a list into text.
* @this Blockly.Block
*/
public void init()
{
// Assign "this" to a variable for use in the closures below.
var thisBlock = this;
var dropdown = new FieldDropdown(new[] {
new [] {Msg.LISTS_SPLIT_LIST_FROM_TEXT, "SPLIT"},
new [] {Msg.LISTS_SPLIT_TEXT_FROM_LIST, "JOIN"}
},
(newMode) => {
thisBlock.updateType_(newMode);
return Script.Undefined;
});
this.setHelpUrl(Msg.LISTS_SPLIT_HELPURL);
this.setColour(Lists.HUE);
this.appendValueInput("INPUT")
.setCheck("String")
.appendField(dropdown, "MODE");
this.appendValueInput("DELIM")
.setCheck("String")
.appendField(Msg.LISTS_SPLIT_WITH_DELIMITER);
this.setInputsInline(true);
this.setOutput(true, "Array");
this.setTooltip(new Func(() => {
var mode = thisBlock.getFieldValue("MODE");
if (mode == "SPLIT") {
return Msg.LISTS_SPLIT_TOOLTIP_SPLIT;
}
else if (mode == "JOIN") {
return Msg.LISTS_SPLIT_TOOLTIP_JOIN;
}
throw new Exception("Unknown mode: " + mode);
}));
}
/**
* Modify this block to have the correct input and output types.
* @param {string} newMode Either "SPLIT" or "JOIN".
* @private
* @this Blockly.Block
*/
public void updateType_(string newMode)
{
if (newMode == "SPLIT") {
this.outputConnection.setCheck("Array");
this.getInput("INPUT").setCheck("String");
}
else {
this.outputConnection.setCheck("String");
this.getInput("INPUT").setCheck("Array");
}
}
/**
* Create XML to represent the input and output types.
* @return {!Element} XML storage element.
* @this Blockly.Block
*/
public Element mutationToDom()
{
var container = Document.CreateElement("mutation");
container.SetAttribute("mode", this.getFieldValue("MODE"));
return container;
}
/**
* Parse XML to restore the input and output types.
* @param {!Element} xmlElement XML storage element.
* @this Blockly.Block
*/
public void domToMutation(Element xmlElement)
{
this.updateType_(xmlElement.GetAttribute("mode"));
}
}
}