chao 6 달 전
부모
커밋
bb9775a4c7

+ 124 - 21
src/blockly/blocklyXml.js

@@ -257,31 +257,26 @@ export default {
           <value name="text_abc"><shadow type="text"><field name="TEXT">abc</field></shadow></value>
           <value name="append_text"><shadow type="text"><field name="TEXT">def</field></shadow></value>
         </block>
-      </category>
- </xml>`
-}
-
-{/* 
         <block type="text_is_number">
-        <value name="TEXT">
-         <shadow type="text">
-        <field name="TEXT"></field>
-        </shadow>
-        </value>
+          <value name="TEXT">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
         </block>
         <block type="text_length">
-        <value name="VALUE">
-        <shadow type="text">
-         <field name="TEXT"></field>
-        </shadow>
-        </value>
+          <value name="VALUE">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
         </block>
         <block type="text_isEmpty">
-        <value name="VALUE">
-        <shadow type="text">
-         <field name="TEXT"></field>
-        </shadow>
-        </value>
+          <value name="VALUE">
+            <shadow type="text">
+              <field name="TEXT"></field>
+            </shadow>
+          </value>
         </block>
         <block type="text_indexOf">
         <value name="VALUE">
@@ -341,4 +336,112 @@ export default {
         </block>
         <block type="CocoRobo_code_annotation">
         <value name="data"><shadow type="text"><field name="TEXT"></field></shadow></value>
-        </block> */}
+        </block>
+      </category>
+      <category id="catLists" name="Lists" colour="#40bfe4">
+        <block type="lists_create_with"><mutation items="0"></mutation></block>
+        <block type="lists_create_with"></block>
+        <block type="text_list"><field name="TEXT">0, 0, 0</field></block>
+        <block type="CocoRobo_return_list">
+          <value name="list_name"><block type="variables_get"><field name="VAR">my_list</field></block></value>
+          <value name="list_items"><shadow type="text_list"><field name="TEXT">0, 0, 0</field></shadow></value>
+        </block>
+        <block type="list_order_item">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+          <value name="list_order_item"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
+        </block>
+        <block type="lists_append">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+        </block>
+        <block type="list_item_exist">
+         <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+         <value name="list_item"><shadow type="text"><field name="TEXT">cocorobo</field></shadow></value>
+        </block>
+        <block type="lists_extend">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+          <value name="extend_list"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+        </block>
+        <block type="lists_clear">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+        </block>
+        <block type="lists_repeat">
+          <value name="NUM">
+            <shadow type="math_number">
+              <field name="NUM">5</field>
+            </shadow>
+          </value>
+        </block>
+        <block type="lists_length"></block>
+        <block type="lists_isEmpty"></block>
+        <block type="list_first_index">
+          <value name="elem"><shadow type="text"><field name="TEXT">cocorobo</field></shadow></value>
+          <value name="my_list"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+        </block>
+        <block type="set_list_order_item">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+          <value name="list_order_item"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
+          <value name="set_value"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
+        </block>
+        <block type="insert_list_order_item">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+          <value name="list_order_item"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
+          <value name="set_value"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
+        </block>
+        <block type="parts_of_list">
+          <value name="list_name"><shadow type="text_list"><field name="TEXT"></field></shadow></value>
+          <value name="start_item"><shadow type="math_number"><field name="NUM">0</field></shadow></value>
+          <value name="end_item"><shadow type="math_number"><field name="NUM">2</field></shadow></value>
+        </block>
+        <block type="lists_getIndex">
+          <mutation statement="true" at="true"></mutation>
+          <field name="MODE">REMOVE</field>
+          <field name="WHERE">FROM_START</field>
+          <value name="VALUE">
+            <shadow type="text_list"><field name="TEXT"></field></shadow>
+          </value>
+        </block>
+        <block type="lists_getIndex">
+          <mutation statement="false" at="true"></mutation>
+          <field name="MODE">GET_REMOVE</field>
+          <field name="WHERE">FROM_START</field>
+          <value name="VALUE">
+            <shadow type="text_list"><field name="TEXT"></field></shadow>
+          </value>
+        </block>
+        <block type="lists_split">
+          <value name="DELIM"><shadow type="text"><field name="TEXT">,</field></shadow></value>
+        </block>
+        <block type="lists_sort"></block>
+        </category>
+        <category id="catDictionary" name="Dictionary" colour="32">
+          <block type="text_dict"><field name="TEXT">"Age":8</field></block>
+          <block type="dict_create_with">
+            <value name="dict_name"><block type="variables_get"><field name="VAR">my_dict</field></block></value>
+            <value name="dict_items"><shadow type="text_dict"><field name="TEXT">"Age":8</field></shadow></value>
+          </block>
+          <block type="add_dict_key_value">
+            <value name="dict_name"><shadow type="text_dict"><field name="TEXT"></field></shadow></value>
+            <value name="dict_items"><shadow type="text"><field name="TEXT">Age</field></shadow></value>
+            <value name="add_value"><shadow type="math_number"><field name="NUM">10</field></shadow></value>
+          </block>
+          <block type="dict_key_value">
+            <value name="dict_name"><shadow type="text_dict"><field name="TEXT"></field></shadow></value>
+            <value name="dict_items"><shadow type="text"><field name="TEXT">Age</field></shadow></value>
+          </block>
+          <block type="dict_length">
+            <value name="dict_name"><shadow type="text_dict"><field name="TEXT"></field></shadow></value>
+          </block>
+          <block type="dict_key_exist">
+            <value name="dict_name"><shadow type="text_dict"><field name="TEXT"></field></shadow></value>
+            <value name="dict_items"><shadow type="text"><field name="TEXT">Age</field></shadow></value>
+          </block>
+          <block type="dict_key_or_val_list">
+            <value name="dict_name"><shadow type="text_dict"><field name="TEXT"></field></shadow></value>
+          </block>
+        </category>
+ </xml>`
+}
+
+{/* <block type="dict_create_with_items_insert"></block> */}
+        
+        

+ 278 - 0
src/blockly/blocks/dictionary.js

@@ -0,0 +1,278 @@
+import Blockly from 'blockly';
+import { pythonGenerator } from "blockly/python";
+import CategoryColors from './define_color'
+
+Blockly.Python = pythonGenerator
+
+Blockly.dictionary = {
+    HUE: CategoryColors['Dictionary'],
+};
+
+// Blockly.FieldDropdown.prototype.setText = function (a) {
+//     this.sourceBlock_ && this.arrow_ && (this.arrow_.style.fill = this.sourceBlock_.getColour());
+//     null !== a && a !== this.text_ && (this.text_ = a,
+//         this.updateTextNode_(),
+//         this.textElement_ && (this.sourceBlock_.RTL ? this.textElement_.insertBefore(this.arrow_, this.textElement_.firstChild) : this.textElement_.appendChild(this.arrow_)),
+//         this.sourceBlock_ && this.sourceBlock_.rendered && (this.sourceBlock_.render(),
+//             this.sourceBlock_.bumpNeighbours_()))
+// };
+// Blockly.FieldDropdown.prototype.setValue = function (a) {
+//     if (null !== a && a !== this.value_) {
+//         this.sourceBlock_ && Blockly.Events.isEnabled() && Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_, "field", this.name, this.value_, a));
+//         this.value_ = a;
+//         for (var b = this.getOptions_(), c = 0; c < b.length; c++)
+//             if (b[c][1] == a) {
+//                 this.setText(b[c][0]);
+//                 return
+//             }
+//         this.setText(a)
+//     }
+// };
+// Blockly.FieldDropdown.prototype.getOptions_ = function () {
+//     return Blockly.isFunction(this.menuGenerator_) ? this.menuGenerator_.call(this) : this.menuGenerator_
+// };
+// Blockly.isFunction = function (a) {
+//     return "function" == Blockly.typeOf(a)
+// };
+Blockly.typeOf = function (a) {
+    var b = typeof a;
+    if ("object" == b)
+        if (a) {
+            if (a instanceof Array)
+                return "array";
+            if (a instanceof Object)
+                return b;
+            var c = Object.prototype.toString.call(a);
+            if ("[object Window]" == c)
+                return "object";
+            if ("[object Array]" == c || "number" == typeof a.length && "undefined" != typeof a.splice && "undefined" != typeof a.propertyIsEnumerable && !a.propertyIsEnumerable("splice"))
+                return "array";
+            if ("[object Function]" == c || "undefined" != typeof a.call && "undefined" != typeof a.propertyIsEnumerable && !a.propertyIsEnumerable("call"))
+                return "function"
+        } else
+            return "null";
+    else if ("function" == b && "undefined" == typeof a.call)
+        return "object";
+    return b
+}
+Blockly.Blocks.dict_create_with_items_insert = {
+    init: function () {
+        this.setColour(CategoryColors.Dictionary);
+        this.appendDummyInput("")
+            .appendField(new Blockly.FieldLabel(Blockly.Msg.DICT_CREATE_WITH_INPUT_WITH), "TIP");
+        this.itemCount_ = 3;
+        // this.updateShape_();
+        this.setOutput(!0);
+        this.setMutator(new Blockly.icons.MutatorIcon(["dict_create_with_item"], this));
+        this.setTooltip(Blockly.Msg.DICT_CREATE_WITH_INPUT_WITH)
+    },
+    mutationToDom: function () {
+        var a = document.createElement("mutation");
+        a.setAttribute("items", this.itemCount_);
+        return a
+    },
+    domToMutation: function (a) {
+        this.itemCount_ = parseInt(a.getAttribute("items"), 10);
+        this.updateShape_()
+    },
+    decompose: function (a) {
+        var b = a.newBlock("dict_create_with_container");
+        b.initSvg();
+        for (var c = b.getInput("STACK").connection, e = 0; e < this.itemCount_; e++) {
+            var d = a.newBlock("dict_create_with_item");
+            d.initSvg();
+            c.connect(d.previousConnection);
+            c = d.nextConnection
+        }
+        return b
+    },
+    compose: function (a) {
+        a = a.getInputTargetBlock("STACK");
+        for (var b = [], c = 0; a;)
+            b[c] = a.valueConnection_,
+                a = a.nextConnection && a.nextConnection.targetBlock(),
+                c++;
+        this.itemCount_ = c;
+        this.updateShape_();
+        for (c = 0; c < this.itemCount_; c++)
+            b[c] && this.getInput("ADD" + c).connection.connect(b[c])
+    },
+    saveConnections: function (a) {
+        a = a.getInputTargetBlock("STACK");
+        for (var b = 0; a;) {
+            var c = this.getInput("ADD" + b);
+            a.valueConnection_ = c && c.connection.targetConnection;
+            b++;
+            a = a.nextConnection && a.nextConnection.targetBlock()
+        }
+    },
+    updateShape_: function () {
+        this.getInput("EMPTY") && this.removeInput("EMPTY");
+        for (var a = [], b = 0; this.getInput("ADD" + b); b++)
+            a.push(this.getFieldValue("KEY" + b)),
+                this.removeInput("ADD" + b);
+        if (0 == this.itemCount_)
+            this.getField("TIP").setText(Blockly.Msg.DICT_CREATE_EMPTY_TITLE);
+        else
+            for (this.getField("TIP").setText(Blockly.Msg.DICT_CREATE_WITH_INPUT_WITH),
+                b = 0; b < this.itemCount_; b++)
+                this.appendValueInput("ADD" + b).setCheck(null).setAlign(Blockly.ALIGN_RIGHT).appendField(new Blockly.FieldTextInput(a.length > b ? a[b] : 0 == b ? "key_0" : "key_" + b), "KEY" + b).appendField(":")
+    },
+    getVars: function () {
+        return [this.getFieldValue("VAR")]
+    },
+    renameVar: function (a, b) {
+        Blockly.Names.equals(a, this.getFieldValue("VAR")) && this.setTitleValue(b, "VAR")
+    }
+};
+Blockly.Blocks.dict_create_with_item = {
+    init: function () {
+        this.setColour(CategoryColors.Dictionary);
+        this.appendDummyInput().appendField(Blockly.Msg.VARIABLES_DEFAULT_NAME);
+        this.setPreviousStatement(!0);
+        this.setNextStatement(!0);
+        this.setTooltip(Blockly.Msg.DICT_CREATE_WITH_ITEM_TOOLTIP);
+        this.contextMenu = !1
+    }
+};
+Blockly.Blocks.dict_create_with_container = {
+    init: function () {
+        this.setColour(CategoryColors.Dictionary);
+        this.appendDummyInput().appendField(Blockly.Msg.DICT_CREATE_WITH_CONTAINER_TITLE_ADD);
+        this.appendStatementInput("STACK");
+        this.setTooltip(Blockly.Msg.DICT_CREATE_WITH_CONTAINER_TOOLTIP);
+        this.contextMenu = !1
+    }
+};
+Blockly.Blocks.dict_create_with = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            nextStatement: null,
+            previousStatement: null,
+            colour: CategoryColors.Dictionary,
+            helpUrl: Blockly.Msg.DICT_CREATE_WITH_HELPURL,
+            tooltip: Blockly.Msg.DICT_CREATE_WITH_TOOLTIP,
+            message0: Blockly.Msg.DICT_CREATE_WITH_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "dict_name"
+            }, {
+                type: "input_value",
+                name: "dict_items"
+            }]
+        })
+    }
+};
+Blockly.Blocks.dict_key_value = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Dictionary,
+            args0: [{
+                type: "input_value",
+                name: "dict_name"
+            }, {
+                type: "input_value",
+                name: "dict_items"
+            }],
+            output: ["String", "Number", "Boolean", "Array"],
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.DICT_KEY_VALUE_HELPURL,
+            tooltip: Blockly.Msg.DICT_KEY_VALUE_TOOLTIP,
+            message0: Blockly.Msg.DICT_KEY_VALUE_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.add_dict_key_value = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Dictionary,
+            args0: [{
+                type: "input_value",
+                name: "dict_name"
+            }, {
+                type: "input_value",
+                name: "dict_items"
+            }, {
+                type: "input_value",
+                name: "add_value"
+            }],
+            inputsInline: !0,
+            nextStatement: null,
+            previousStatement: null,
+            helpUrl: Blockly.Msg.ADD_DICT_KEY_VALUE_HELPURL,
+            tooltip: Blockly.Msg.ADD_DICT_KEY_VALUE_TOOLTIP,
+            message0: Blockly.Msg.ADD_DICT_KEY_VALUE_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.dict_length = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Dictionary,
+            args0: [{
+                type: "input_value",
+                name: "dict_name"
+            }],
+            output: "Number",
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.DICT_LENGTH_HELPURL,
+            tooltip: Blockly.Msg.DICT_LENGTH_TOOLTIP,
+            message0: Blockly.Msg.DICT_LENGTH_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.dict_key_exist = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Dictionary,
+            args0: [{
+                type: "input_value",
+                name: "dict_name"
+            }, {
+                type: "input_value",
+                name: "dict_items"
+            }],
+            output: "Boolean",
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.DICT_KEY_EXIST_HELPURL,
+            tooltip: Blockly.Msg.DICT_KEY_EXIST_TOOLTIP,
+            message0: Blockly.Msg.DICT_KEY_EXIST_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.dict_key_or_val_list = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Dictionary,
+            args0: [{
+                type: "input_value",
+                name: "dict_name"
+            }, {
+                options: [
+                    [Blockly.Msg.DICT_KEYS, "keys"],
+                    [Blockly.Msg.DICT_VALUES, "values"]
+                ],
+                type: "field_dropdown",
+                name: "key_or_val"
+            }],
+            output: "Array",
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.DICT_KEY_OR_VAL_LIST_HELPURL,
+            // tooltip: Blockly.Msg.DICT_KEY_OR_VAL_LIST_TOOLTIP,
+            message0: Blockly.Msg.DICT_KEY_OR_VAL_LIST_MESSAGE0
+        });
+        var thisBlock = this;
+        this.setTooltip(function () {
+            var mode = thisBlock.getFieldValue('key_or_val');
+            var TOOLTIPS = {
+                'keys': Blockly.Msg.DICT_KEY_OR_VAL_LIST_TOOLTIP.replace('%2', Blockly.Msg.DICT_KEYS),
+                'values': Blockly.Msg.DICT_KEY_OR_VAL_LIST_TOOLTIP.replace('%2', Blockly.Msg.DICT_VALUES)
+            };
+            return TOOLTIPS[mode];
+        });
+    }
+};
+
+
+export default Blockly;

+ 5 - 1
src/blockly/blocks/index.js

@@ -3,7 +3,11 @@ import logic from './logic';
 import math from './math';
 import variable from './variables';
 import text from './text';
+import list from './list';
+import dictionary from './dictionary';
 
-const Blockly = {...basic,...logic,...math,...variable,...text};
+const Blockly = { ...basic, ...logic, ...math, ...variable, ...text, ...list
+    , ...dictionary
+ };
 
 export default Blockly;

+ 1171 - 0
src/blockly/blocks/list.js

@@ -0,0 +1,1171 @@
+import Blockly from 'blockly';
+import { pythonGenerator } from "blockly/python";
+import CategoryColors from './define_color'
+
+Blockly.Python = pythonGenerator
+
+Blockly.lists = {
+    HUE: CategoryColors['List'],
+};
+
+Blockly.Blocks['lists_create_with'] = {
+    /**
+     * Block for creating a list with any number of elements of any type.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.setHelpUrl(Blockly.Msg.LISTS_CREATE_WITH_HELPURL);
+        this.setColour(CategoryColors['List']);
+        this.itemCount_ = 3;
+        this.updateShape_();
+        this.setOutput(true, 'Array');
+        this.setMutator(new Blockly.icons.MutatorIcon(['lists_create_with_item'], this));
+        this.setInputsInline(true);
+        this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP);
+    },
+    /**
+     * Create XML to represent list inputs.
+     * @return {!Element} XML storage element.
+     * @this Blockly.Block
+     */
+    mutationToDom: function () {
+        var container = document.createElement('mutation');
+        container.setAttribute('items', this.itemCount_);
+        /*if (this.itemCount_ > 3) {
+            container.setAttribute("inline", "false");
+        } */
+        return container;
+    },
+    /**
+     * Parse XML to restore the list inputs.
+     * @param {!Element} xmlElement XML storage element.
+     * @this Blockly.Block
+     */
+    domToMutation: function (xmlElement) {
+        this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
+        this.updateShape_();
+    },
+    /**
+     * Populate the mutator's dialog with this block's components.
+     * @param {!Blockly.Workspace} workspace Mutator's workspace.
+     * @return {!Blockly.Block} Root block in mutator.
+     * @this Blockly.Block
+     */
+    decompose: function (workspace) {
+        var containerBlock = workspace.newBlock('lists_create_with_container');
+        containerBlock.initSvg();
+        var connection = containerBlock.getInput('STACK').connection;
+        for (var i = 0; i < this.itemCount_; i++) {
+            var itemBlock = workspace.newBlock('lists_create_with_item');
+            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
+     */
+    compose: function (containerBlock) {
+        var itemBlock = containerBlock.getInputTargetBlock('STACK');
+        // Count number of inputs.
+        var connections = [];
+        while (itemBlock) {
+            connections.push(itemBlock.valueConnection_);
+            itemBlock = itemBlock.nextConnection &&
+                itemBlock.nextConnection.targetBlock();
+        }
+        // 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 && connections.indexOf(connection) == -1) {
+                connection.disconnect();
+            }
+        }
+        this.itemCount_ = connections.length;
+        this.updateShape_();
+        // Reconnect any child blocks.
+        for (var i = 0; i < this.itemCount_; i++) {
+            Blockly.icons.MutatorIcon.reconnect(connections[i], this, 'ADD' + i);
+        }
+    },
+    /**
+     * Store pointers to any connected child blocks.
+     * @param {!Blockly.Block} containerBlock Root block in mutator.
+     * @this Blockly.Block
+     */
+    saveConnections: function (containerBlock) {
+        var itemBlock = containerBlock.getInputTargetBlock('STACK');
+        var i = 0;
+        while (itemBlock) {
+            var input = this.getInput('ADD' + i);
+            itemBlock.valueConnection_ = input && input.connection.targetConnection;
+            i++;
+            itemBlock = itemBlock.nextConnection &&
+                itemBlock.nextConnection.targetBlock();
+        }
+    },
+    /**
+     * Modify this block to have the correct number of inputs.
+     * @private
+     * @this Blockly.Block
+     */
+    updateShape_: function () {
+        if (this.itemCount_ && this.getInput('EMPTY')) {
+            this.removeInput('EMPTY');
+        } else if (!this.itemCount_ && !this.getInput('EMPTY')) {
+            this.appendDummyInput('EMPTY')
+                .appendField(Blockly.Msg.LISTS_CREATE_EMPTY_TITLE);
+        }
+        // Add new inputs.
+        for (var i = 0; i < this.itemCount_; i++) {
+            if (!this.getInput('ADD' + i)) {
+                var input = this.appendValueInput('ADD' + i);
+                if (i == 0) {
+                    input.appendField(Blockly.Msg.LISTS_CREATE_WITH_INPUT_WITH);
+                }
+            }
+        }
+        if (this.itemCount_ > 3) {
+            this.setInputsInline(false);
+        } else {
+            this.setInputsInline(true);
+        }
+        // Remove deleted inputs.
+        while (this.getInput('ADD' + i)) {
+            this.removeInput('ADD' + i);
+            i++;
+        }
+    }
+};
+
+Blockly.Blocks['lists_create_with_container'] = {
+    /**
+     * Mutator block for list container.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.setColour(Blockly.lists.HUE);
+        this.appendDummyInput()
+            .appendField(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD);
+        this.appendStatementInput('STACK');
+        this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP);
+        this.contextMenu = false;
+    }
+};
+
+Blockly.Blocks['lists_create_with_item'] = {
+    /**
+     * Mutator bolck for adding items.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.setColour(Blockly.lists.HUE);
+        this.appendDummyInput()
+            .appendField(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TITLE);
+        this.setPreviousStatement(true);
+        this.setNextStatement(true);
+        this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP);
+        this.contextMenu = false;
+    }
+};
+
+Blockly.Blocks.text_list = {
+    init: function () {
+        this.setHelpUrl(Blockly.Msg.TEXT_TEXT_HELPURL);
+        this.setColour("#40bfe4");
+        this.setTooltip(Blockly.Msg.Text_List_TOOLTIP);
+        this.appendDummyInput().appendField(Blockly.Msg.TEXT_LIST_START).appendField(new Blockly.FieldTextInput(""), "TEXT").appendField(Blockly.Msg.TEXT_LIST_END);
+        this.setOutput(!0, "Array")
+    }
+};
+Blockly.Blocks.CocoRobo_return_list = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                type: "input_value",
+                name: "list_items"
+            }],
+            nextStatement: null,
+            previousStatement: null,
+            helpUrl: Blockly.Msg.CocoRobo_HELPURL,
+            tooltip: Blockly.Msg.CocoRobo_return_list_TOOLTIP,
+            message0: Blockly.Msg.CocoRobo_return_list_MESSAGE0,
+            inputsInline: !0
+        })
+    }
+};
+
+Blockly.Python.quote_empty = function (a) {
+    return a
+};
+Blockly.Blocks.list_order_item = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                options: [
+                    [Blockly.Msg.CocoRobo_ORDER, "["],
+                    [Blockly.Msg.CocoRobo_REVERSE_ORDER, "[-"]
+                ],
+                type: "field_dropdown",
+                name: "list_order"
+            }, {
+                check: "Number",
+                type: "input_value",
+                name: "list_order_item"
+            }],
+            output: ["Number", "String", "Boolean", "Array"],
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.LIST_ORDER_ITEM_HELPURL,
+            tooltip: Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP,
+            message0: Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.lists_append = {
+    init: function () {
+        this.jsonInit({
+            message0: Blockly.Msg.CocoRobo_lists_append_MESSAGE0,
+            args0: [{
+                check: "Array",
+                type: "input_value",
+                name: "list_name"
+            }, {
+                type: "input_value",
+                name: "last_item",
+                check: ["String", "Number"]
+            }],
+            nextStatement: null,
+            previousStatement: null,
+            inputsInline: !0,
+            colour: CategoryColors.List,
+            tooltip: Blockly.Msg.CocoRobo_lists_append_TOOLTIP,
+            helpUrl: Blockly.Msg.CocoRobo_lists_append_HELPURL
+        })
+    }
+};
+Blockly.Blocks.lists_extend = {
+    init: function () {
+        this.jsonInit({
+            message0: Blockly.Msg.CocoRobo_lists_extend_MESSAGE0,
+            args0: [{
+                check: "Array",
+                type: "input_value",
+                name: "list_name"
+            }, {
+                type: "input_value",
+                name: "extend_list",
+                check: "Array"
+            }],
+            nextStatement: null,
+            previousStatement: null,
+            inputsInline: !0,
+            colour: CategoryColors.List,
+            tooltip: Blockly.Msg.CocoRobo_lists_extend_TOOLTIP,
+            helpUrl: Blockly.Msg.CocoRobo_lists_extend_HELPURL
+        })
+    }
+};
+Blockly.Blocks.CocoRobo_return_list = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                type: "input_value",
+                name: "list_items"
+            }],
+            nextStatement: null,
+            previousStatement: null,
+            helpUrl: Blockly.Msg.CocoRobo_HELPURL,
+            tooltip: Blockly.Msg.CocoRobo_return_list_TOOLTIP,
+            message0: Blockly.Msg.CocoRobo_return_list_MESSAGE0,
+            inputsInline: !0
+        })
+    }
+};
+Blockly.Blocks.list_item_exist = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                type: "input_value",
+                name: "list_item"
+            }],
+            output: "Boolean",
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.LIST_ITEM_EXIST_HELPURL,
+            tooltip: Blockly.Msg.LIST_ITEM_EXIST_TOOLTIP,
+            message0: Blockly.Msg.LIST_ITEM_EXIST_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.set_list_order_item = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                options: [
+                    [Blockly.Msg.CocoRobo_ORDER, "["],
+                    [Blockly.Msg.CocoRobo_REVERSE_ORDER, "[-"]
+                ],
+                type: "field_dropdown",
+                name: "list_order"
+            }, {
+                check: "Number",
+                type: "input_value",
+                name: "list_order_item"
+            }, {
+                type: "input_value",
+                name: "set_value"
+            }],
+            inputsInline: !0,
+            nextStatement: null,
+            previousStatement: null,
+            helpUrl: Blockly.Msg.SET_LIST_ORDER_ITEM_HELPURL,
+            tooltip: Blockly.Msg.SET_LIST_ORDER_ITEM_TOOLTIP,
+            message0: Blockly.Msg.SET_LIST_ORDER_ITEM_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.insert_list_order_item = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                options: [
+                    [Blockly.Msg.CocoRobo_ORDER, "("],
+                    [Blockly.Msg.CocoRobo_REVERSE_ORDER, "(-"]
+                ],
+                type: "field_dropdown",
+                name: "list_order"
+            }, {
+                check: "Number",
+                type: "input_value",
+                name: "list_order_item"
+            }, {
+                type: "input_value",
+                name: "set_value"
+            }],
+            inputsInline: !0,
+            nextStatement: null,
+            previousStatement: null,
+            helpUrl: Blockly.Msg.INSERT_LIST_ORDER_ITEM_HELPURL,
+            tooltip: Blockly.Msg.INSERT_LIST_ORDER_ITEM_TOOLTIP,
+            message0: Blockly.Msg.INSERT_LIST_ORDER_ITEM_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.list_order_item = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                options: [
+                    [Blockly.Msg.CocoRobo_ORDER, "["],
+                    [Blockly.Msg.CocoRobo_REVERSE_ORDER, "[-"]
+                ],
+                type: "field_dropdown",
+                name: "list_order"
+            }, {
+                check: "Number",
+                type: "input_value",
+                name: "list_order_item"
+            }],
+            output: ["Number", "String", "Boolean", "Array"],
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.LIST_ORDER_ITEM_HELPURL,
+            tooltip: Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP,
+            message0: Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.parts_of_list = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.List,
+            args0: [{
+                type: "input_value",
+                name: "list_name"
+            }, {
+                options: [
+                    [Blockly.Msg.CocoRobo_ORDER, "["],
+                    [Blockly.Msg.CocoRobo_REVERSE_ORDER, "[-"]
+                ],
+                type: "field_dropdown",
+                name: "list_start_order"
+            }, {
+                check: "Number",
+                type: "input_value",
+                name: "start_item"
+            }, {
+                options: [
+                    [Blockly.Msg.CocoRobo_ORDER, ":"],
+                    [Blockly.Msg.CocoRobo_REVERSE_ORDER, ":-"]
+                ],
+                type: "field_dropdown",
+                name: "list_end_order"
+            }, {
+                check: "Number",
+                type: "input_value",
+                name: "end_item"
+            }],
+            output: null,
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.PARTS_OF_LIST_HELPURL,
+            tooltip: Blockly.Msg.PARTS_OF_LIST_TOOLTIP,
+            message0: Blockly.Msg.PARTS_OF_LIST_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.list_to_tuple = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Tuple,
+            args0: [{
+                check: "Array",
+                type: "input_value",
+                name: "list_name"
+            }],
+            output: null,
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.LIST_TO_TUPLE_HELPURL,
+            tooltip: Blockly.Msg.LIST_TO_TUPLE_TOOLTIP,
+            message0: Blockly.Msg.LIST_TO_TUPLE_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.lists_clear = {
+    init: function () {
+        this.jsonInit({
+            message0: Blockly.Msg.CocoRobo_lists_clear_MESSAGE0,
+            args0: [{
+                check: "Array",
+                type: "input_value",
+                name: "list_name"
+            }],
+            nextStatement: null,
+            previousStatement: null,
+            inputsInline: !0,
+            colour: CategoryColors.List,
+            tooltip: Blockly.Msg.CocoRobo_lists_clear_TOOLTIP,
+            helpUrl: Blockly.Msg.CocoRobo_lists_clear_HELPURL
+        })
+    }
+};
+Blockly.Blocks['lists_repeat'] = {
+  /**
+   * Block for creating a list with one element repeated.
+   * @this Blockly.Block
+   */
+  init: function() {
+    this.jsonInit({
+      "message0": Blockly.Msg.LISTS_REPEAT_TITLE,
+      "args0": [
+        {
+          "type": "input_value",
+          "name": "ITEM"
+        },
+        {
+          "type": "input_value",
+          "name": "NUM",
+          "check": "Number"
+        }
+      ],
+      "output": "Array",
+      "colour": CategoryColors.List,
+      "tooltip": Blockly.Msg.LISTS_REPEAT_TOOLTIP,
+      "helpUrl": Blockly.Msg.LISTS_REPEAT_HELPURL
+    });
+  }
+};
+
+Blockly.Blocks['lists_length'] = {
+  /**
+   * Block for list length.
+   * @this Blockly.Block
+   */
+  init: function() {
+    this.jsonInit({
+      "message0": Blockly.Msg.LISTS_LENGTH_TITLE,
+      "args0": [
+        {
+          "type": "input_value",
+          "name": "VALUE",
+          "check": ['String', 'Array']
+        }
+      ],
+      "output": 'Number',
+      "colour": CategoryColors.List,
+      "tooltip": Blockly.Msg.LISTS_LENGTH_TOOLTIP,
+      "helpUrl": Blockly.Msg.LISTS_LENGTH_HELPURL
+    });
+  }
+};
+
+Blockly.Blocks['lists_isEmpty'] = {
+  /**
+   * Block for is the list empty?
+   * @this Blockly.Block
+   */
+  init: function() {
+    this.jsonInit({
+      "message0": Blockly.Msg.LISTS_ISEMPTY_TITLE,
+      "args0": [
+        {
+          "type": "input_value",
+          "name": "VALUE",
+          "check": ['String', 'Array']
+        }
+      ],
+      "output": 'Boolean',
+      "colour": CategoryColors.List,
+      "tooltip": Blockly.Msg.LISTS_ISEMPTY_TOOLTIP,
+      "helpUrl": Blockly.Msg.LISTS_ISEMPTY_HELPURL
+    });
+  }
+};
+
+Blockly.Blocks['lists_indexOf'] = {
+  /**
+   * Block for finding an item in the list.
+   * @this Blockly.Block
+   */
+  init: function() {
+    var OPERATORS =
+        [[Blockly.Msg.LISTS_INDEX_OF_FIRST, 'FIRST'],
+         [Blockly.Msg.LISTS_INDEX_OF_LAST, 'LAST']];
+    this.setHelpUrl(Blockly.Msg.LISTS_INDEX_OF_HELPURL);
+    this.setColour(CategoryColors.List);
+    this.setOutput(true, 'Number');
+    this.appendValueInput('VALUE')
+        .setCheck(['Array', 'String'])
+        .appendField(Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST);
+    this.appendValueInput('FIND')
+        .appendField(new Blockly.FieldDropdown(OPERATORS), 'END');
+    this.setInputsInline(true);
+    // Assign 'this' to a variable for use in the tooltip closure below.
+    var thisBlock = this;
+    this.setTooltip(function() {
+      return Blockly.Msg.LISTS_INDEX_OF_TOOLTIP.replace('%1',
+          this.workspace.options.oneBasedIndex ? '0' : '-1');
+    });
+  }
+};
+
+Blockly.Blocks['lists_index'] = {
+    /**
+     * Block for getting element at index.
+     * @this Blockly.Block
+     */
+    init: function() {
+        this.setHelpUrl(Blockly.Msg.LISTS_GET_INDEX_HELPURL);
+        this.setColour(CategoryColors.List);
+        this.appendValueInput('ITEM')
+            .setCheck('Number')
+            .appendField("get");
+        this.appendValueInput('LIST')
+            .setCheck(['Array', 'String'])
+            .appendField("th item of");
+        this.setInputsInline(true);
+        this.setOutput(true);
+        // Assign 'this' to a variable for use in the tooltip closure below.
+        var thisBlock = this;
+        this.setTooltip("Get's the ith item from the list");
+    }
+}
+
+Blockly.Blocks['lists_getIndex'] = {
+  /**
+   * Block for getting element at index.
+   * @this Blockly.Block
+   */
+  init: function() {
+    var MODE =
+        [[Blockly.Msg.LISTS_GET_INDEX_GET, 'GET'],
+         [Blockly.Msg.LISTS_GET_INDEX_GET_REMOVE, 'GET_REMOVE'],
+         [Blockly.Msg.LISTS_GET_INDEX_REMOVE, 'REMOVE']];
+    this.WHERE_OPTIONS =
+        [[Blockly.Msg.LISTS_GET_INDEX_FROM_START, 'FROM_START'],
+         [Blockly.Msg.LISTS_GET_INDEX_FROM_END, 'FROM_END'],
+         [Blockly.Msg.LISTS_GET_INDEX_FIRST, 'FIRST'],
+         [Blockly.Msg.LISTS_GET_INDEX_LAST, 'LAST'],
+         [Blockly.Msg.LISTS_GET_INDEX_RANDOM, 'RANDOM']
+         ];
+    this.setHelpUrl(Blockly.Msg.LISTS_GET_INDEX_HELPURL);
+    this.setColour(CategoryColors.List);
+    var modeMenu = new Blockly.FieldDropdown(MODE, function(value) {
+      var isStatement = (value == 'REMOVE');
+      this.sourceBlock_.updateStatement_(isStatement);
+    });
+    this.appendValueInput('VALUE')
+        .setCheck('Array')
+        .appendField(Blockly.Msg.LISTS_GET_INDEX_INPUT_IN_LIST);
+    this.appendDummyInput()
+        .appendField(modeMenu, 'MODE')
+        .appendField('', 'SPACE');
+    this.appendDummyInput('AT');
+    if (Blockly.Msg.LISTS_GET_INDEX_TAIL) {
+      this.appendDummyInput('TAIL')
+          .appendField(Blockly.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(function() {
+      var mode = thisBlock.getFieldValue('MODE');
+      var where = thisBlock.getFieldValue('WHERE');
+      var tooltip = '';
+      switch (mode + ' ' + where) {
+        case 'GET FROM_START':
+        case 'GET FROM_END':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM;
+          break;
+        case 'GET FIRST':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST;
+          break;
+        case 'GET LAST':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST;
+          break;
+        case 'GET RANDOM':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM;
+          break;
+        case 'GET_REMOVE FROM_START':
+        case 'GET_REMOVE FROM_END':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM;
+          break;
+        case 'GET_REMOVE FIRST':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST;
+          break;
+        case 'GET_REMOVE LAST':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST;
+          break;
+        case 'GET_REMOVE RANDOM':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM;
+          break;
+        case 'REMOVE FROM_START':
+        case 'REMOVE FROM_END':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM;
+          break;
+        case 'REMOVE FIRST':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST;
+          break;
+        case 'REMOVE LAST':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST;
+          break;
+        case 'REMOVE RANDOM':
+          tooltip = Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM;
+          break;
+      }
+      if (where == 'FROM_START' || where == 'FROM_END') {
+        var msg = (where == 'FROM_START') ?
+            Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP :
+            Blockly.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
+   */
+  mutationToDom: function() {
+    var container = document.createElement('mutation');
+    var isStatement = !this.outputConnection;
+    container.setAttribute('statement', isStatement);
+    var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
+    container.setAttribute('at', isAt);
+    return container;
+  },
+  /**
+   * Parse XML to restore the 'AT' input.
+   * @param {!Element} xmlElement XML storage element.
+   * @this Blockly.Block
+   */
+  domToMutation: function(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
+   */
+  updateStatement_: function(newStatement) {
+    var oldStatement = !this.outputConnection;
+    if (newStatement != oldStatement) {
+      this.unplug(true, 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
+   */
+  updateAt_: function(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 (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
+        this.appendDummyInput('ORDINAL')
+            .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
+      }
+    } else {
+      this.appendDummyInput('AT');
+    }
+    var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
+      var newAt = (value == 'FROM_START') || (value == 'FROM_END');
+      // The 'isAt' variable is available due to this function being a closure.
+      if (newAt != isAt) {
+        var block = this.sourceBlock_;
+        block.updateAt_(newAt);
+        // This menu has been destroyed and replaced.  Update the replacement.
+        block.setFieldValue(value, 'WHERE');
+        return null;
+      }
+      return undefined;
+    });
+    this.getInput('AT').appendField(menu, 'WHERE');
+    if (Blockly.Msg.LISTS_GET_INDEX_TAIL) {
+      this.moveInputBefore('TAIL', null);
+    }
+  }
+};
+
+Blockly.Blocks['lists_setIndex'] = {
+  /**
+   * Block for setting the element at index.
+   * @this Blockly.Block
+   */
+  init: function() {
+    var MODE =
+        [[Blockly.Msg.LISTS_SET_INDEX_SET, 'SET'],
+         [Blockly.Msg.LISTS_SET_INDEX_INSERT, 'INSERT']];
+    this.WHERE_OPTIONS =
+        [[Blockly.Msg.LISTS_GET_INDEX_FROM_START, 'FROM_START'],
+         [Blockly.Msg.LISTS_GET_INDEX_FROM_END, 'FROM_END'],
+         [Blockly.Msg.LISTS_GET_INDEX_FIRST, 'FIRST'],
+         [Blockly.Msg.LISTS_GET_INDEX_LAST, 'LAST'],
+         [Blockly.Msg.LISTS_GET_INDEX_RANDOM, 'RANDOM']];
+    this.setHelpUrl(Blockly.Msg.LISTS_SET_INDEX_HELPURL);
+    this.setColour(CategoryColors.List);
+    this.appendValueInput('LIST')
+        .setCheck('Array')
+        .appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
+    this.appendDummyInput()
+        .appendField(new Blockly.FieldDropdown(MODE), 'MODE')
+        .appendField('', 'SPACE');
+    this.appendDummyInput('AT');
+    this.appendValueInput('TO')
+        .appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_TO);
+    this.setInputsInline(true);
+    this.setPreviousStatement(true);
+    this.setNextStatement(true);
+    this.setTooltip(Blockly.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(function() {
+      var mode = thisBlock.getFieldValue('MODE');
+      var where = thisBlock.getFieldValue('WHERE');
+      var tooltip = '';
+      switch (mode + ' ' + where) {
+        case 'SET FROM_START':
+        case 'SET FROM_END':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM;
+          break;
+        case 'SET FIRST':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST;
+          break;
+        case 'SET LAST':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST;
+          break;
+        case 'SET RANDOM':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM;
+          break;
+        case 'INSERT FROM_START':
+        case 'INSERT FROM_END':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM;
+          break;
+        case 'INSERT FIRST':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST;
+          break;
+        case 'INSERT LAST':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST;
+          break;
+        case 'INSERT RANDOM':
+          tooltip = Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM;
+          break;
+      }
+      if (where == 'FROM_START' || where == 'FROM_END') {
+        tooltip += '  ' + Blockly.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
+   */
+  mutationToDom: function() {
+    var container = document.createElement('mutation');
+    var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
+    container.setAttribute('at', isAt);
+    return container;
+  },
+  /**
+   * Parse XML to restore the 'AT' input.
+   * @param {!Element} xmlElement XML storage element.
+   * @this Blockly.Block
+   */
+  domToMutation: function(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
+   */
+  updateAt_: function(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 (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
+        this.appendDummyInput('ORDINAL')
+            .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
+      }
+    } else {
+      this.appendDummyInput('AT');
+    }
+    var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
+      var newAt = (value == 'FROM_START') || (value == 'FROM_END');
+      // The 'isAt' variable is available due to this function being a closure.
+      if (newAt != isAt) {
+        var block = this.sourceBlock_;
+        block.updateAt_(newAt);
+        // This menu has been destroyed and replaced.  Update the replacement.
+        block.setFieldValue(value, 'WHERE');
+        return null;
+      }
+      return undefined;
+    });
+    this.moveInputBefore('AT', 'TO');
+    if (this.getInput('ORDINAL')) {
+      this.moveInputBefore('ORDINAL', 'TO');
+    }
+
+    this.getInput('AT').appendField(menu, 'WHERE');
+  }
+};
+
+Blockly.Blocks['lists_getSublist'] = {
+  /**
+   * Block for getting sublist.
+   * @this Blockly.Block
+   */
+  init: function() {
+    this['WHERE_OPTIONS_1'] =
+        [[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START, 'FROM_START'],
+         [Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END, 'FROM_END'],
+         [Blockly.Msg.LISTS_GET_SUBLIST_START_FIRST, 'FIRST']];
+    this['WHERE_OPTIONS_2'] =
+        [[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_START, 'FROM_START'],
+         [Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END, 'FROM_END'],
+         [Blockly.Msg.LISTS_GET_SUBLIST_END_LAST, 'LAST']];
+    this.setHelpUrl(Blockly.Msg.LISTS_GET_SUBLIST_HELPURL);
+    this.setColour(CategoryColors.List);
+    this.appendValueInput('LIST')
+        .setCheck(['Array', "String"])
+        .appendField(Blockly.Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST);
+    this.appendDummyInput('AT1');
+    this.appendDummyInput('AT2');
+    if (Blockly.Msg.LISTS_GET_SUBLIST_TAIL) {
+      this.appendDummyInput('TAIL')
+          .appendField(Blockly.Msg.LISTS_GET_SUBLIST_TAIL);
+    }
+    this.setInputsInline(true);
+    this.setOutput(true, 'Array');
+    this.updateAt_(1, true);
+    this.updateAt_(2, true);
+    this.setTooltip(Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP);
+  },
+  /**
+   * Create XML to represent whether there are 'AT' inputs.
+   * @return {Element} XML storage element.
+   * @this Blockly.Block
+   */
+  mutationToDom: function() {
+    var container = document.createElement('mutation');
+    var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE;
+    container.setAttribute('at1', isAt1);
+    var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE;
+    container.setAttribute('at2', isAt2);
+    return container;
+  },
+  /**
+   * Parse XML to restore the 'AT' inputs.
+   * @param {!Element} xmlElement XML storage element.
+   * @this Blockly.Block
+   */
+  domToMutation: function(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
+   */
+  updateAt_: function(n, 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 (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
+        this.appendDummyInput('ORDINAL' + n)
+            .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
+      }
+    } else {
+      this.appendDummyInput('AT' + n);
+    }
+    var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n],
+        function(value) {
+          var newAt = (value == 'FROM_START') || (value == 'FROM_END');
+          // The 'isAt' variable is available due to this function being a
+          // closure.
+          if (newAt != isAt) {
+            var block = this.sourceBlock_;
+            block.updateAt_(n, newAt);
+            // This menu has been destroyed and replaced.
+            // Update the replacement.
+            block.setFieldValue(value, 'WHERE' + n);
+            return null;
+          }
+          return undefined;
+        });
+    this.getInput('AT' + n)
+        .appendField(menu, 'WHERE' + n);
+    if (n == 1) {
+      this.moveInputBefore('AT1', 'AT2');
+      if (this.getInput('ORDINAL1')) {
+        this.moveInputBefore('ORDINAL1', 'AT2');
+      }
+    }
+    if (Blockly.Msg.LISTS_GET_SUBLIST_TAIL) {
+      this.moveInputBefore('TAIL', null);
+    }
+  }
+};
+
+Blockly.Blocks['lists_sort'] = {
+  /**
+   * Block for sorting a list.
+   * @this Blockly.Block
+   */
+  init: function() {
+    this.jsonInit({
+      "message0": Blockly.Msg.LISTS_SORT_TITLE,
+      "args0": [
+        {
+          "type": "field_dropdown",
+          "name": "TYPE",
+          "options": [
+            [Blockly.Msg.LISTS_SORT_TYPE_NUMERIC, "NUMERIC"],
+            [Blockly.Msg.LISTS_SORT_TYPE_TEXT, "TEXT"],
+            [Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE, "IGNORE_CASE"]
+          ]
+        },
+        {
+          "type": "field_dropdown",
+          "name": "DIRECTION",
+          "options": [
+            [Blockly.Msg.LISTS_SORT_ORDER_ASCENDING, "1"],
+            [Blockly.Msg.LISTS_SORT_ORDER_DESCENDING, "-1"]
+          ]
+        },
+        {
+          "type": "input_value",
+          "name": "LIST",
+          "check": "Array"
+        }
+      ],
+      "output": "Array",
+      "colour": CategoryColors.List,
+      "tooltip": Blockly.Msg.LISTS_SORT_TOOLTIP,
+      "helpUrl": Blockly.Msg.LISTS_SORT_HELPURL
+    });
+  }
+};
+
+Blockly.Blocks['lists_split'] = {
+  /**
+   * Block for splitting text into a list, or joining a list into text.
+   * @this Blockly.Block
+   */
+  init: function() {
+    // Assign 'this' to a variable for use in the closures below.
+    var thisBlock = this;
+    var dropdown = new Blockly.FieldDropdown(
+        [[Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT, 'SPLIT'],
+         [Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST, 'JOIN']],
+        function(newMode) {
+          thisBlock.updateType_(newMode);
+        });
+    this.setHelpUrl(Blockly.Msg.LISTS_SPLIT_HELPURL);
+    this.setColour(CategoryColors.List);
+    this.appendValueInput('INPUT')
+        .setCheck('String')
+        .appendField(dropdown, 'MODE');
+    this.appendValueInput('DELIM')
+        .setCheck('String')
+        .appendField(Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER);
+    this.setInputsInline(true);
+    this.setOutput(true, 'Array');
+    this.setTooltip(function() {
+      var mode = thisBlock.getFieldValue('MODE');
+      if (mode == 'SPLIT') {
+        return Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT;
+      } else if (mode == 'JOIN') {
+        return Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN;
+      }
+      throw '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
+   */
+  updateType_: function(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
+   */
+  mutationToDom: function() {
+    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
+   */
+  domToMutation: function(xmlElement) {
+    this.updateType_(xmlElement.getAttribute('mode'));
+  }
+};
+Blockly.Blocks.list_first_index = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: "Number",
+            colour: CategoryColors.List,
+            helpUrl: Blockly.Msg.LIST_FIRST_INDEX_HELPURL,
+            // tooltip: Blockly.Msg.LIST_FIRST_INDEX_TOOLTIP,
+            message0: Blockly.Msg.LIST_FIRST_INDEX_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "elem"
+            }, {
+                check: "Array",
+                type: "input_value",
+                name: "my_list"
+            }, {
+                options: [
+                    [Blockly.Msg.FIRST, "first"],
+                    [Blockly.Msg.LAST, "last"]
+                ],
+                type: "field_dropdown",
+                name: "last_or_first"
+            }]
+        });
+        var thisBlock = this;
+        this.setTooltip(function () {
+            var mode = thisBlock.getFieldValue('last_or_first');
+            var TOOLTIPS = {
+                'first': Blockly.Msg.LIST_FIRST_INDEX_TOOLTIP.replace('%3', Blockly.Msg.FIRST),
+                'last': Blockly.Msg.LIST_FIRST_INDEX_TOOLTIP.replace('%3', Blockly.Msg.LAST)
+            };
+            return TOOLTIPS[mode];
+        });
+    }
+};
+export default Blockly;

+ 527 - 3
src/blockly/blocks/text.js

@@ -124,7 +124,7 @@ Blockly.Blocks['text_join'] = {
         this.updateShape_();
         // Reconnect any child blocks.
         for (var i = 0; i < this.itemCount_; i++) {
-            Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
+            Blockly.icons.MutatorIcon.reconnect(connections[i], this, 'ADD' + i);
         }
     },
     /**
@@ -271,7 +271,7 @@ Blockly.Blocks.tuple_create_with_items_insert = {
         this.itemCount_ = a.length;
         this.updateShape_();
         for (b = 0; b < this.itemCount_; b++)
-            Blockly.Mutator.reconnect(a[b], this, "ADD" + b)
+            Blockly.icons.MutatorIcon.reconnect(a[b], this, "ADD" + b)
     },
     saveConnections: function (a) {
         a = a.getInputTargetBlock("STACK");
@@ -338,7 +338,7 @@ Blockly.Blocks.CocoRobo_text_ESC = {
         this.jsonInit({
             inputsInline: !0,
             output: null,
-            colour: Blockly.Blocks.texts.HUE,
+            colour: Blockly.texts.HUE,
             helpUrl: Blockly.Msg.CocoRobo_TEXT_ESC_HELPURL,
             tooltip: Blockly.Msg.CocoRobo_TEXT_ESC_TOOLTIP,
             message0: Blockly.Msg.CocoRobo_TEXT_ESC_MESSAGE0,
@@ -359,4 +359,528 @@ Blockly.Blocks.CocoRobo_text_ESC = {
         })
     }
 };
+Blockly.Blocks.text_append_text = {
+    init: function () {
+        this.jsonInit({
+            colour: CategoryColors.Text,
+            args0: [{
+                type: "input_value",
+                name: "text_abc"
+            }, {
+                type: "input_value",
+                name: "append_text"
+            }],
+            output: "String",
+            inputsInline: !0,
+            helpUrl: Blockly.Msg.TEXT_APPEND_TEXT_HELPURL,
+            tooltip: Blockly.Msg.TEXT_APPEND_TEXT_TOOLTIP,
+            message0: Blockly.Msg.TEXT_APPEND_TEXT_MESSAGE0
+        })
+    }
+};
+Blockly.Blocks.text_is_number = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: "Boolean",
+            colour: CategoryColors.Text,
+            helpUrl: Blockly.Msg.TEXT_IS_NUMBER_HELPURL,
+            // tooltip: Blockly.Msg.TEXT_IS_NUMBER_TOOLTIP,
+            message0: Blockly.Msg.TEXT_IS_NUMBER_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "TEXT",
+                check: "String"
+            }, {
+                options: [
+                    [Blockly.Msg.TEXT_IS_DIGIT, ".isdigit()"],
+                    [Blockly.Msg.TEXT_IS_ALPHA, ".isalpha()"]
+                ],
+                type: "field_dropdown",
+                name: "TYPE"
+            }]
+        });
+        var thisBlock = this;
+        this.setTooltip(function () {
+            var mode = thisBlock.getFieldValue('TYPE');
+            var TOOLTIPS = {
+                '.isdigit()': Blockly.Msg.TEXT_IS_NUMBER_TOOLTIP.replace('%1', Blockly.Msg.TEXT_IS_DIGIT),
+                '.isalpha()': Blockly.Msg.TEXT_IS_NUMBER_TOOLTIP.replace('%1', Blockly.Msg.TEXT_IS_ALPHA)
+            };
+            return TOOLTIPS[mode];
+        });
+    }
+};
+Blockly.Blocks['text_length'] = {
+    /**
+     * Block for string length.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.jsonInit({
+            "message0": Blockly.Msg.TEXT_LENGTH_TITLE,
+            "args0": [
+                {
+                    "type": "input_value",
+                    "name": "VALUE",
+                    "check": ['String', 'Array']
+                }
+            ],
+            "output": 'Number',
+            "colour": CategoryColors.Text,
+            "tooltip": Blockly.Msg.TEXT_LENGTH_TOOLTIP,
+            "helpUrl": Blockly.Msg.TEXT_LENGTH_HELPURL
+        });
+    }
+};
+
+Blockly.Blocks['text_isEmpty'] = {
+    /**
+     * Block for is the string null?
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.jsonInit({
+            "message0": Blockly.Msg.TEXT_ISEMPTY_TITLE,
+            "args0": [
+                {
+                    "type": "input_value",
+                    "name": "VALUE",
+                    "check": ['String', 'Array']
+                }
+            ],
+            "output": 'Boolean',
+            "colour": CategoryColors.Text,
+            "tooltip": Blockly.Msg.TEXT_ISEMPTY_TOOLTIP,
+            "helpUrl": Blockly.Msg.TEXT_ISEMPTY_HELPURL
+        });
+    }
+};
+
+Blockly.Blocks['text_indexOf'] = {
+    /**
+     * Block for finding a substring in the text.
+     * @this Blockly.Block
+     */
+    init: function () {
+        var OPERATORS =
+            [[Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST, 'FIRST'],
+            [Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST, 'LAST']];
+        this.setHelpUrl(Blockly.Msg.TEXT_INDEXOF_HELPURL);
+        this.setColour(CategoryColors.Text);
+        this.setOutput(true, 'Number');
+        this.appendValueInput('VALUE')
+            .setCheck('String')
+            .appendField(Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT);
+        this.appendValueInput('FIND')
+            .setCheck('String')
+            .appendField(new Blockly.FieldDropdown(OPERATORS), 'END');
+        if (Blockly.Msg.TEXT_INDEXOF_TAIL) {
+            this.appendDummyInput().appendField(Blockly.Msg.TEXT_INDEXOF_TAIL);
+        }
+        this.setInputsInline(true);
+        // Assign 'this' to a variable for use in the tooltip closure below.
+        var thisBlock = this;
+        this.setTooltip(function () {
+            return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace('%1',
+                thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
+        });
+    }
+};
+
+Blockly.Blocks['text_charAt'] = {
+    /**
+     * Block for getting a character from the string.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.WHERE_OPTIONS =
+            [[Blockly.Msg.TEXT_CHARAT_FROM_START, 'FROM_START'],
+            [Blockly.Msg.TEXT_CHARAT_FROM_END, 'FROM_END'],
+            [Blockly.Msg.TEXT_CHARAT_FIRST, 'FIRST'],
+            [Blockly.Msg.TEXT_CHARAT_LAST, 'LAST'],
+            [Blockly.Msg.TEXT_CHARAT_RANDOM, 'RANDOM']];
+        this.setHelpUrl(Blockly.Msg.TEXT_CHARAT_HELPURL);
+        this.setColour(CategoryColors.Text);
+        this.setOutput(true, 'String');
+        this.appendValueInput('VALUE')
+            .setCheck('String')
+            .appendField(Blockly.Msg.TEXT_CHARAT_INPUT_INTEXT);
+        this.appendDummyInput('AT');
+        this.setInputsInline(true);
+        this.updateAt_(true);
+        // Assign 'this' to a variable for use in the tooltip closure below.
+        var thisBlock = this;
+        this.setTooltip(function () {
+            var where = thisBlock.getFieldValue('WHERE');
+            var tooltip = Blockly.Msg.TEXT_CHARAT_TOOLTIP;
+            if (where == 'FROM_START' || where == 'FROM_END') {
+                var msg = (where == 'FROM_START') ?
+                    Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP :
+                    Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP;
+                tooltip += '  ' + msg.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
+     */
+    mutationToDom: function () {
+        var container = document.createElement('mutation');
+        var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
+        container.setAttribute('at', isAt);
+        return container;
+    },
+    /**
+     * Parse XML to restore the 'AT' input.
+     * @param {!Element} xmlElement XML storage element.
+     * @this Blockly.Block
+     */
+    domToMutation: function (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
+     */
+    updateAt_: function (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 (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
+                this.appendDummyInput('ORDINAL')
+                    .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
+            }
+        } else {
+            this.appendDummyInput('AT');
+        }
+        if (Blockly.Msg.TEXT_CHARAT_TAIL) {
+            this.removeInput('TAIL', true);
+            this.appendDummyInput('TAIL')
+                .appendField(Blockly.Msg.TEXT_CHARAT_TAIL);
+        }
+        var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function (value) {
+            var newAt = (value == 'FROM_START') || (value == 'FROM_END');
+            // The 'isAt' variable is available due to this function being a closure.
+            if (newAt != isAt) {
+                var block = this.sourceBlock_;
+                block.updateAt_(newAt);
+                // This menu has been destroyed and replaced.  Update the replacement.
+                block.setFieldValue(value, 'WHERE');
+                return null;
+            }
+            return undefined;
+        });
+        this.getInput('AT').appendField(menu, 'WHERE');
+    }
+};
+
+Blockly.Blocks['text_getSubstring'] = {
+    /**
+     * Block for getting substring.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this['WHERE_OPTIONS_1'] =
+            [[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START, 'FROM_START'],
+            [Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END, 'FROM_END'],
+            [Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST, 'FIRST']];
+        this['WHERE_OPTIONS_2'] =
+            [[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START, 'FROM_START'],
+            [Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END, 'FROM_END'],
+            [Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST, 'LAST']];
+        this.setHelpUrl(Blockly.Msg.TEXT_GET_SUBSTRING_HELPURL);
+        this.setColour(CategoryColors.Text);
+        this.appendValueInput('STRING')
+            .setCheck('String')
+            .appendField(Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT);
+        this.appendDummyInput('AT1');
+        this.appendDummyInput('AT2');
+        if (Blockly.Msg.TEXT_GET_SUBSTRING_TAIL) {
+            this.appendDummyInput('TAIL')
+                .appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL);
+        }
+        this.setInputsInline(true);
+        this.setOutput(true, 'String');
+        this.updateAt_(1, true);
+        this.updateAt_(2, true);
+        this.setTooltip(Blockly.Msg.TEXT_GET_SUBSTRING_TOOLTIP);
+    },
+    /**
+     * Create XML to represent whether there are 'AT' inputs.
+     * @return {!Element} XML storage element.
+     * @this Blockly.Block
+     */
+    mutationToDom: function () {
+        var container = document.createElement('mutation');
+        var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE;
+        container.setAttribute('at1', isAt1);
+        var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE;
+        container.setAttribute('at2', isAt2);
+        return container;
+    },
+    /**
+     * Parse XML to restore the 'AT' inputs.
+     * @param {!Element} xmlElement XML storage element.
+     * @this Blockly.Block
+     */
+    domToMutation: function (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
+     */
+    updateAt_: function (n, 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 (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
+                this.appendDummyInput('ORDINAL' + n)
+                    .appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
+            }
+        } else {
+            this.appendDummyInput('AT' + n);
+        }
+        // Move tail, if present, to end of block.
+        if (n == 2 && Blockly.Msg.TEXT_GET_SUBSTRING_TAIL) {
+            this.removeInput('TAIL', true);
+            this.appendDummyInput('TAIL')
+                .appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL);
+        }
+        var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n],
+            function (value) {
+                var newAt = (value == 'FROM_START') || (value == 'FROM_END');
+                // The 'isAt' variable is available due to this function being a
+                // closure.
+                if (newAt != isAt) {
+                    var block = this.sourceBlock_;
+                    block.updateAt_(n, newAt);
+                    // This menu has been destroyed and replaced.
+                    // Update the replacement.
+                    block.setFieldValue(value, 'WHERE' + n);
+                    return null;
+                }
+                return undefined;
+            });
+
+        this.getInput('AT' + n)
+            .appendField(menu, 'WHERE' + n);
+        if (n == 1) {
+            this.moveInputBefore('AT1', 'AT2');
+        }
+    }
+};
+
+Blockly.Blocks['text_changeCase'] = {
+    /**
+     * Block for changing capitalization.
+     * @this Blockly.Block
+     */
+    init: function () {
+        var OPERATORS =
+            [[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_UPPERCASE, 'UPPERCASE'],
+            [Blockly.Msg.TEXT_CHANGECASE_OPERATOR_LOWERCASE, 'LOWERCASE'],
+            [Blockly.Msg.TEXT_CHANGECASE_OPERATOR_TITLECASE, 'TITLECASE']];
+        this.setHelpUrl(Blockly.Msg.TEXT_CHANGECASE_HELPURL);
+        this.setColour(CategoryColors.Text);
+        this.appendValueInput('TEXT')
+            .setCheck('String')
+            .appendField(new Blockly.FieldDropdown(OPERATORS), 'CASE');
+        this.setOutput(true, 'String');
+        this.setTooltip(Blockly.Msg.TEXT_CHANGECASE_TOOLTIP);
+    }
+};
+
+Blockly.Blocks['text_trim'] = {
+    /**
+     * Block for trimming spaces.
+     * @this Blockly.Block
+     */
+    init: function () {
+        var OPERATORS =
+            [[Blockly.Msg.TEXT_TRIM_OPERATOR_BOTH, 'BOTH'],
+            [Blockly.Msg.TEXT_TRIM_OPERATOR_LEFT, 'LEFT'],
+            [Blockly.Msg.TEXT_TRIM_OPERATOR_RIGHT, 'RIGHT']];
+        this.setHelpUrl(Blockly.Msg.TEXT_TRIM_HELPURL);
+        this.setColour(CategoryColors.Text);
+        this.appendValueInput('TEXT')
+            .setCheck('String')
+            .appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
+        this.setOutput(true, 'String');
+        this.setTooltip(Blockly.Msg.TEXT_TRIM_TOOLTIP);
+    }
+};
+
+Blockly.Blocks['text_print'] = {
+    /**
+     * Block for print statement.
+     * @this Blockly.Block
+     */
+    init: function () {
+        this.jsonInit({
+            "message0": Blockly.Msg.TEXT_PRINT_TITLE,
+            "args0": [
+                {
+                    "type": "input_value",
+                    "name": "TEXT"
+                }
+            ],
+            "previousStatement": null,
+            "nextStatement": null,
+            "colour": CategoryColors.Text,
+            "tooltip": Blockly.Msg.TEXT_PRINT_TOOLTIP,
+            "helpUrl": Blockly.Msg.TEXT_PRINT_HELPURL
+        });
+    }
+};
+Blockly.Blocks.text_to_byte = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: null,
+            colour: "#33cc99" /*CategoryColors.Text*/,
+            helpUrl: Blockly.Msg.TEXT_TO_BYTE_HELPURL,
+            tooltip: Blockly.Msg.TEXT_TO_BYTE_TOOLTIP,
+            message0: Blockly.Msg.TEXT_TO_BYTE_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "TEXT",
+                check: "String"
+            }]
+        })
+    }
+};
+Blockly.Blocks.other_to_byte = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: null,
+            colour: CategoryColors.Text,
+            helpUrl: Blockly.Msg.OTHER_TO_BYTE_HELPURL,
+            tooltip: Blockly.Msg.OTHER_TO_BYTE_TOOLTIP,
+            message0: Blockly.Msg.OTHER_TO_BYTE_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "input_var"
+            }]
+        })
+    }
+};
+Blockly.Blocks.bit_inversion = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: "Number",
+            colour: CategoryColors.Math,
+            helpUrl: Blockly.Msg.BIT_INVERSION_HELPURL,
+            tooltip: Blockly.Msg.BIT_INVERSION_TOOLTIP,
+            message0: Blockly.Msg.BIT_INVERSION_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "data"
+            }]
+        })
+    }
+};
+Blockly.Blocks.CocoRobo_bytes_decode = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: "String",
+            colour: CategoryColors.Text,
+            helpUrl: Blockly.Msg.CocoRobo_BYTES_DECODE_HELPURL,
+            tooltip: Blockly.Msg.CocoRobo_BYTES_DECODE_TOOLTIP,
+            message0: Blockly.Msg.CocoRobo_BYTES_DECODE_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "bytes_decode"
+            }]
+        })
+    }
+};
+Blockly.Blocks.CocoRobo_ujson_dumps = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: "String",
+            colour: CategoryColors.Text,
+            helpUrl: Blockly.Msg.CocoRobo_UJSON_DUMPS_HELPURL,
+            tooltip: Blockly.Msg.CocoRobo_UJSON_DUMPS_TOOLTIP,
+            message0: Blockly.Msg.CocoRobo_UJSON_DUMPS_MESSAGE0,
+            args0: [{
+                type: "input_value",
+                name: "data"
+            }]
+        })
+    }
+};
+Blockly.Blocks.CocoRobo_ujson_loads = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            output: null,
+            colour: CategoryColors.Text,
+            helpUrl: Blockly.Msg.CocoRobo_UJSON_LOADS_HELPURL,
+            tooltip: Blockly.Msg.CocoRobo_UJSON_LOADS_TOOLTIP,
+            message0: Blockly.Msg.CocoRobo_UJSON_LOADS_MESSAGE0,
+            args0: [{
+                check: "String",
+                type: "input_value",
+                name: "data"
+            }]
+        })
+    }
+};
+Blockly.Blocks.CocoRobo_code_annotation = {
+    init: function () {
+        this.jsonInit({
+            inputsInline: !0,
+            nextStatement: null,
+            previousStatement: null,
+            // output: null,
+            colour: CategoryColors.Text,
+            helpUrl: Blockly.Msg.CocoRobo_code_annotation_HELPURL,
+            tooltip: Blockly.Msg.CocoRobo_code_annotation_TOOLTIP,
+            message0: Blockly.Msg.CocoRobo_code_annotation_MESSAGE0,
+            args0: [{
+                check: "String",
+                type: "input_value",
+                name: "data"
+            }]
+        })
+    }
+};
+Blockly.Blocks.text_dict = {
+    init: function () {
+        this.setHelpUrl(Blockly.Msg.TEXT_TEXT_HELPURL);
+        this.setColour(32);
+        this.appendDummyInput().appendField(Blockly.Msg.TEXT_DICT_START).appendField(new Blockly.FieldTextInput(""), "TEXT").appendField(Blockly.Msg.TEXT_DICT_END);
+        this.setOutput(!0, "Array");
+        this.setTooltip(Blockly.Msg.Text_Dict_TOOLTIP)
+    }
+};
 export default Blockly;

+ 80 - 1
src/blockly/msg/en.js

@@ -8,7 +8,8 @@ Blockly.Msg["catLoops"] = "Loops";
 Blockly.Msg["catMath"] = "Math";
 Blockly.Msg["catText"] = "Text";
 Blockly.Msg["catVariables"] = "Variables";
-
+Blockly.Msg["catLists"] = "Lists";
+Blockly.Msg["catDictionary"] = "Dictionary";
 
 //
 Blockly.Msg["ADD_COMMENT"] = "Add Comment";
@@ -523,8 +524,86 @@ Blockly.Msg.text_additional_delimiter_deli = "by delimiter:";
 Blockly.Msg.text_additional_delimiter_end = "Split and generate a list";
 Blockly.Msg.TUPLE_CREATE_WITH_ITEMS_INSERT_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
 Blockly.Msg.TUPLE_CREATE_WITH_ITEMS_INSERT_TOOLTIP = 'The sequence count starts from 0, and the reverse count starts from 1.';
+Blockly.Msg.CocoRobo_TEXT_ESC_HELPURL = '';
+Blockly.Msg.CocoRobo_TEXT_ESC_MESSAGE0 = 'Text ESC %1';
+Blockly.Msg.CocoRobo_TEXT_ESC_TOOLTIP = 'Text ESC';
+Blockly.Msg.TEXT_APPEND_TEXT_HELPURL = '';
+Blockly.Msg.TEXT_APPEND_TEXT_TOOLTIP = 'Append text after the specified text and return the result';
+Blockly.Msg.TEXT_APPEND_TEXT_MESSAGE0 = '%1 append text %2';
+Blockly.Msg.TEXT_IS_NUMBER_HELPURL = '';
+Blockly.Msg.TEXT_IS_NUMBER_TOOLTIP = 'Judge the text %1 and return True or False';
+Blockly.Msg.TEXT_IS_NUMBER_MESSAGE0 = '%1 %2';
 
 
+// 列表
+Blockly.Msg.CocoRobo_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_return_list_MESSAGE0 = 'Define list %1 = %2';
+Blockly.Msg.CocoRobo_return_list_TOOLTIP = 'Define a list';
+Blockly.Msg.CocoRobo_ORDER = '#';
+Blockly.Msg.CocoRobo_REVERSE_ORDER = 'Countdown #';
+Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0 = 'List %1 %2 %3 item';
+Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP = 'The sequence count starts from 0, and the reverse count starts from 1.';
+Blockly.Msg.LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_lists_append_MESSAGE0 = 'Add item %2 at the end of list %1';
+Blockly.Msg.CocoRobo_lists_append_TOOLTIP = 'Add item to the end of the list';
+Blockly.Msg.CocoRobo_lists_append_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ITEM_EXIST_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ITEM_EXIST_TOOLTIP = 'The list contains a specific string';
+Blockly.Msg.LIST_ITEM_EXIST_MESSAGE0 = 'List %1 include %2';
+Blockly.Msg.SET_LIST_ORDER_ITEM_MESSAGE0 = 'Set list %1 %2 %3 item as %4';
+Blockly.Msg.SET_LIST_ORDER_ITEM_TOOLTIP = 'The sequence count starts from 0, and the reverse count starts from 1.';
+Blockly.Msg.SET_LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_MESSAGE0 = 'Insert list %1 %2 %3 item as %4';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_TOOLTIP = 'The sequence count starts from 0, and the reverse count starts from 1.';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0 = 'List %1 %2 %3 item';
+Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP = 'The sequence count starts from 0, and the reverse count starts from 1.';
+Blockly.Msg.LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.PARTS_OF_LIST_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.PARTS_OF_LIST_TOOLTIP = 'Incremental order fetching \n Example: \n reciprocal number 5 to reciprocal number 2; \n 0th to 5th';
+Blockly.Msg.PARTS_OF_LIST_MESSAGE0 = 'Return list %1 takes %2 %3 items to %4 %5 items';
+Blockly.Msg.LIST_TO_TUPLE_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_TO_TUPLE_TOOLTIP = 'List conversion to tuple';
+Blockly.Msg.LIST_TO_TUPLE_MESSAGE0 = 'List %1 conversion to tuple';
+Blockly.Msg.CocoRobo_lists_clear_MESSAGE0 = 'Clear list %1';
+Blockly.Msg.CocoRobo_lists_clear_TOOLTIP = 'Clear the list';
+Blockly.Msg.CocoRobo_lists_clear_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_lists_extend_MESSAGE0 = 'List %1 append list %2';
+Blockly.Msg.CocoRobo_lists_extend_TOOLTIP = 'Append list to list';
+Blockly.Msg.CocoRobo_lists_extend_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_FIRST_INDEX_HELPURL = '';
+Blockly.Msg.LIST_FIRST_INDEX_MESSAGE0 = 'Location of item%1 in list%2 %3';
+Blockly.Msg.LIST_FIRST_INDEX_TOOLTIP = 'Returns the position where the specified string appears in the list %3';
+Blockly.Msg.FIRST = 'First';
+Blockly.Msg.LAST = 'Last';
+
+// 字典
+Blockly.Msg.DICT_CREATE_WITH_ITEM_TOOLTIP = "Add an item to the dictionary.";
+Blockly.Msg.DICT_KEY_VALUE_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_VALUE_TOOLTIP = 'Returns the value of the specified key of the dictionary';
+Blockly.Msg.DICT_KEY_VALUE_MESSAGE0 = 'Value of the key %2 in dictionary %1';
+
+Blockly.Msg.ADD_DICT_KEY_VALUE_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.ADD_DICT_KEY_VALUE_TOOLTIP = 'Update the key if the key already exists';
+Blockly.Msg.ADD_DICT_KEY_VALUE_MESSAGE0 = 'Add key %2 & value %3 to dictionary %1';
+
+Blockly.Msg.DICT_LENGTH_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_LENGTH_TOOLTIP = 'Returns the length of the dictionary';
+Blockly.Msg.DICT_LENGTH_MESSAGE0 = 'Length of dictionary %1';
+
+Blockly.Msg.DICT_KEY_EXIST_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_EXIST_TOOLTIP = 'Determine whether the dictionary contains the specified key, and return True or False';
+Blockly.Msg.DICT_KEY_EXIST_MESSAGE0 = 'Dictionary %1 include key %2';
+
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_TOOLTIP = 'Return list of %2 in dictionary';
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_MESSAGE0 = 'List of %2 in dictionary %1';
+Blockly.Msg.DICT_KEYS = 'KEY';
+Blockly.Msg.DICT_VALUES = 'VALUE';
+Blockly.Msg.DICT_CREATE_WITH_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_CREATE_WITH_TOOLTIP = 'Define a dictionary';
+Blockly.Msg.DICT_CREATE_WITH_MESSAGE0 = 'Define dictionary %1 = %2';
+
 
 
 

+ 88 - 0
src/blockly/msg/zh-hans.js

@@ -8,6 +8,9 @@ Blockly.Msg["catLoops"] = "循环";
 Blockly.Msg["catMath"] = "数学";
 Blockly.Msg["catText"] = "文本";
 Blockly.Msg["catVariables"] = "变量";
+Blockly.Msg["catLists"] = "列表";
+Blockly.Msg["catDictionary"] = "字典";
+
 
 
 // 
@@ -1006,6 +1009,91 @@ Blockly.Msg.text_additional_delimiter_deli = "使用分隔符:";
 Blockly.Msg.text_additional_delimiter_end = "进行拆分并生成列表";
 Blockly.Msg.TUPLE_CREATE_WITH_ITEMS_INSERT_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
 Blockly.Msg.TUPLE_CREATE_WITH_ITEMS_INSERT_TOOLTIP = '正序计数从0开始,倒序计数从1开始';
+Blockly.Msg.CocoRobo_TEXT_ESC_HELPURL = '';
+Blockly.Msg.CocoRobo_TEXT_ESC_MESSAGE0 = '文本转义字符 %1';
+Blockly.Msg.CocoRobo_TEXT_ESC_TOOLTIP = '文本转义字符';
+Blockly.Msg.TEXT_APPEND_TEXT_HELPURL = '';
+Blockly.Msg.TEXT_APPEND_TEXT_TOOLTIP = '在指定文本后追加文本,并返回结果';
+Blockly.Msg.TEXT_APPEND_TEXT_MESSAGE0 = '%1 追加文本 %2';
+Blockly.Msg.TEXT_APPEND_TEXT_HELPURL = '';
+Blockly.Msg.TEXT_APPEND_TEXT_TOOLTIP = '在指定文本後追加文本,並返回結果';
+Blockly.Msg.TEXT_APPEND_TEXT_MESSAGE0 = '%1 追加文本 %2';
+Blockly.Msg.TEXT_IS_NUMBER_HELPURL = '';
+Blockly.Msg.TEXT_IS_NUMBER_TOOLTIP = '判断文本%1,并返回True或False';
+Blockly.Msg.TEXT_IS_NUMBER_MESSAGE0 = '%1 %2';
+
+
+// 列表
+Blockly.Msg.CocoRobo_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_return_list_MESSAGE0 ='定义列表 %1 = %2';
+Blockly.Msg.CocoRobo_return_list_TOOLTIP = '定义一个列表';
+Blockly.Msg.CocoRobo_ORDER = '第';
+Blockly.Msg.CocoRobo_REVERSE_ORDER  = '倒数第';
+Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0 = '列表%1 %2 %3 项';
+Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP = '正序计数从0开始,倒序计数从1开始';
+Blockly.Msg.LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_lists_append_MESSAGE0 = '列表%1 末尾添加项%2';
+Blockly.Msg.CocoRobo_lists_append_TOOLTIP = '列表末尾添加项';
+Blockly.Msg.CocoRobo_lists_append_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ITEM_EXIST_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ITEM_EXIST_TOOLTIP= '列表包含特定的字符串';
+Blockly.Msg.LIST_ITEM_EXIST_MESSAGE0= '列表%1 包含%2';
+Blockly.Msg.SET_LIST_ORDER_ITEM_MESSAGE0 = '设列表%1 %2 %3 项为%4';
+Blockly.Msg.SET_LIST_ORDER_ITEM_TOOLTIP = '正序计数从0开始,倒序计数从1开始';
+Blockly.Msg.SET_LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_MESSAGE0 = '插入列表%1 %2 %3 项为%4';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_TOOLTIP = '正序计数从0开始,倒序计数从1开始';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0 = '列表%1 %2 %3 项';
+Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP = '正序计数从0开始,倒序计数从1开始';
+Blockly.Msg.LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.PARTS_OF_LIST_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.PARTS_OF_LIST_TOOLTIP = '递增顺序取项\n举例:\n 倒数第5到倒数第2;\n 第0到第5';
+Blockly.Msg.PARTS_OF_LIST_MESSAGE0 = '返回列表%1 取%2 %3 项到 %4 %5 项';
+Blockly.Msg.LIST_TO_TUPLE_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_TO_TUPLE_TOOLTIP = '列表转元组';
+Blockly.Msg.LIST_TO_TUPLE_MESSAGE0 = '列表%1 转元组';
+Blockly.Msg.CocoRobo_lists_clear_MESSAGE0 = '列表%1 清空';
+Blockly.Msg.CocoRobo_lists_clear_TOOLTIP = '清空列表';
+Blockly.Msg.CocoRobo_lists_clear_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_lists_extend_MESSAGE0 = '列表%1 追加列表%2';
+Blockly.Msg.CocoRobo_lists_extend_TOOLTIP = '在列表中追加列表';
+Blockly.Msg.CocoRobo_lists_extend_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_FIRST_INDEX_HELPURL = '';
+Blockly.Msg.LIST_FIRST_INDEX_MESSAGE0 = '项 %1 在列表 %2 %3 出现的位置';
+Blockly.Msg.LIST_FIRST_INDEX_TOOLTIP = '返回指定字符串在列表中%3出现的位置';
+Blockly.Msg.FIRST = '第一次';
+Blockly.Msg.LAST = '最后一次';
+
+// 字典
+Blockly.Msg.DICT_CREATE_WITH_ITEM_TOOLTIP = "将一个项添加到字典中。";
+Blockly.Msg.DICT_CREATE_WITH_ITEMS_INSERT_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_CREATE_WITH_ITEMS_INSERT_TOOLTIP = '初始化字典';
+
+Blockly.Msg.DICT_KEY_VALUE_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_VALUE_TOOLTIP = '返回字典指定键的值';
+Blockly.Msg.DICT_KEY_VALUE_MESSAGE0 = '字典%1 键%2 的值';
+
+Blockly.Msg.ADD_DICT_KEY_VALUE_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.ADD_DICT_KEY_VALUE_TOOLTIP = '已有该键则更新该键';
+Blockly.Msg.ADD_DICT_KEY_VALUE_MESSAGE0 = '字典%1 添加键%2 值%3';
+
+Blockly.Msg.DICT_LENGTH_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_LENGTH_TOOLTIP= '返回字典的长度';
+Blockly.Msg.DICT_LENGTH_MESSAGE0= '字典%1 长度';
+
+Blockly.Msg.DICT_KEY_EXIST_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_EXIST_TOOLTIP= '判断字典中是否包含指定键,并返回True或False';
+Blockly.Msg.DICT_KEY_EXIST_MESSAGE0= '字典%1 包含键%2';
+
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_TOOLTIP= '返回字典%2的列表';
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_MESSAGE0= '字典%1 %2的列表';
+Blockly.Msg.DICT_KEYS= '键';
+Blockly.Msg.DICT_VALUES= '值';
+Blockly.Msg.DICT_CREATE_WITH_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_CREATE_WITH_TOOLTIP = '定义一个字典';
+Blockly.Msg.DICT_CREATE_WITH_MESSAGE0 = '定义字典 %1 = %2';
 
 
 export default Blockly.Msg

+ 79 - 0
src/blockly/msg/zh-hant.js

@@ -8,6 +8,9 @@ Blockly.Msg["catLoops"] = "循環";
 Blockly.Msg["catMath"] = "數學";
 Blockly.Msg["catText"] = "文本";
 Blockly.Msg["catVariables"] = "變量";
+Blockly.Msg["catLists"] = "陣列";
+Blockly.Msg["catDictionary"] = "字典";
+
 
 
 // 
@@ -984,6 +987,82 @@ Blockly.Msg.text_additional_delimiter_deli = "使用分隔符:";
 Blockly.Msg.text_additional_delimiter_end = "進行拆分並生成陣列";
 Blockly.Msg.TUPLE_CREATE_WITH_ITEMS_INSERT_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
 Blockly.Msg.TUPLE_CREATE_WITH_ITEMS_INSERT_TOOLTIP = '正序計數從0開始,倒序計數從1開始';
+Blockly.Msg.CocoRobo_TEXT_ESC_HELPURL = '';
+Blockly.Msg.CocoRobo_TEXT_ESC_MESSAGE0 = '文本轉義字元 %1 ';
+Blockly.Msg.CocoRobo_TEXT_ESC_TOOLTIP = '文本轉義字元';
+Blockly.Msg.TEXT_IS_NUMBER_HELPURL = '';
+Blockly.Msg.TEXT_IS_NUMBER_TOOLTIP = '判斷文本%1,並返回True或False';
+Blockly.Msg.TEXT_IS_NUMBER_MESSAGE0 = '%1 %2';
+
+// 列表
+Blockly.Msg.CocoRobo_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_return_list_MESSAGE0 ='定義陣列 %1 = %2';
+Blockly.Msg.CocoRobo_return_list_TOOLTIP = '定義一個陣列';
+Blockly.Msg.CocoRobo_ORDER = '第';
+Blockly.Msg.CocoRobo_REVERSE_ORDER  = '倒數第';
+Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0 = '陣列%1 %2 %3 項';
+Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP = '正序計數從0開始,倒序計數從1開始';
+Blockly.Msg.LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_lists_append_MESSAGE0 = '陣列%1 末尾添加項%2';
+Blockly.Msg.CocoRobo_lists_append_TOOLTIP = '陣列末尾添加項';
+Blockly.Msg.CocoRobo_lists_append_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ITEM_EXIST_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ITEM_EXIST_TOOLTIP= '陣列包含特定的字串';
+Blockly.Msg.LIST_ITEM_EXIST_MESSAGE0= '陣列%1 包含%2';
+Blockly.Msg.SET_LIST_ORDER_ITEM_MESSAGE0 = '設陣列%1 %2 %3 項為%4';
+Blockly.Msg.SET_LIST_ORDER_ITEM_TOOLTIP = '正序計數從0開始,倒序計數從1開始';
+Blockly.Msg.SET_LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_MESSAGE0 = '插入陣列%1 %2 %3 項為%4';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_TOOLTIP = '正序計數從0開始,倒序計數從1開始';
+Blockly.Msg.INSERT_LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_ORDER_ITEM_MESSAGE0 = '陣列%1 %2 %3 項';
+Blockly.Msg.LIST_ORDER_ITEM_TOOLTIP = '正序計數從0開始,倒序計數從1開始';
+Blockly.Msg.LIST_ORDER_ITEM_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.PARTS_OF_LIST_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.PARTS_OF_LIST_TOOLTIP = '遞增順序取項\n舉例:\n 倒數第5到倒數第2;\n 第0到第5';
+Blockly.Msg.PARTS_OF_LIST_MESSAGE0 = '返回陣列%1 取%2 %3 項到 %4 %5 項';
+Blockly.Msg.LIST_TO_TUPLE_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_TO_TUPLE_TOOLTIP = '陣列轉元組';
+Blockly.Msg.LIST_TO_TUPLE_MESSAGE0 = '陣列%1 轉元組';
+Blockly.Msg.CocoRobo_lists_clear_MESSAGE0 = '陣列%1 清空';
+Blockly.Msg.CocoRobo_lists_clear_TOOLTIP = '清空陣列';
+Blockly.Msg.CocoRobo_lists_clear_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.CocoRobo_lists_extend_MESSAGE0 = '陣列%1 追加陣列%2';
+Blockly.Msg.CocoRobo_lists_extend_TOOLTIP = '在陣列中追加陣列';
+Blockly.Msg.CocoRobo_lists_extend_HELPURL = 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.LIST_FIRST_INDEX_HELPURL = '';
+Blockly.Msg.LIST_FIRST_INDEX_MESSAGE0 = '項 %1 在陣列 %2 %3 出現的位置';
+Blockly.Msg.LIST_FIRST_INDEX_TOOLTIP = '返回指定字串在陣列中%3出現的位置';
+Blockly.Msg.FIRST = '首次';
+Blockly.Msg.LAST = '最後一次';
+
+// 字典
+Blockly.Msg.DICT_CREATE_WITH_ITEM_TOOLTIP = "將一個項添加到字典中。";
+Blockly.Msg.DICT_KEY_VALUE_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_VALUE_TOOLTIP = '返回字典指定鍵的值';
+Blockly.Msg.DICT_KEY_VALUE_MESSAGE0 = '字典%1 鍵%2 的值';
+
+Blockly.Msg.ADD_DICT_KEY_VALUE_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.ADD_DICT_KEY_VALUE_TOOLTIP = '已有該鍵則更新該鍵';
+Blockly.Msg.ADD_DICT_KEY_VALUE_MESSAGE0 = '字典%1 添加鍵%2 值%3';
+
+Blockly.Msg.DICT_LENGTH_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_LENGTH_TOOLTIP= '返回字典的長度';
+Blockly.Msg.DICT_LENGTH_MESSAGE0= '字典%1 長度';
+
+Blockly.Msg.DICT_KEY_EXIST_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_EXIST_TOOLTIP= '判斷字典中是否包含指定鍵,並返回True或False';
+Blockly.Msg.DICT_KEY_EXIST_MESSAGE0= '字典%1 包含鍵%2';
+
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_HELPURL= 'https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_TOOLTIP= '返回字典%2的陣列';
+Blockly.Msg.DICT_KEY_OR_VAL_LIST_MESSAGE0= '字典%1 %2的陣列';
+Blockly.Msg.DICT_KEYS= '鍵';
+Blockly.Msg.DICT_VALUES= '值';
+Blockly.Msg.DICT_CREATE_WITH_HELPURL ='https://CocoRobox.readthedocs.io/zh_CN/latest/CocoRoboX.html';
+Blockly.Msg.DICT_CREATE_WITH_TOOLTIP = '定義一個字典';
+Blockly.Msg.DICT_CREATE_WITH_MESSAGE0 = '定義字典 %1 = %2';
+
 
 
 export default Blockly.Msg;

+ 57 - 0
src/blockly/pythonCode/dictionary.js

@@ -0,0 +1,57 @@
+import { pythonGenerator } from "blockly/python";
+
+const Blockly = {
+    Python: pythonGenerator || { Msg: Object.create(null) }
+};
+Blockly.Python.text_dict = function (a) {
+    return ["{" + Blockly.Python.quote_empty(a.getFieldValue("TEXT")) + "}", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.dict_create_with = function(a) {
+    var b = Blockly.Python.valueToCode(a, "dict_name", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "dict_items", Blockly.Python.ORDER_ATOMIC);
+    return b + " \x3d " + a + "\n"
+};
+Blockly.Python.dict_key_value = function(a) {
+    var b = Blockly.Python.valueToCode(a, "dict_name", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "dict_items", Blockly.Python.ORDER_ATOMIC);
+    return [b + ".get(" + a + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.add_dict_key_value = function(a) {
+    var b = Blockly.Python.valueToCode(a, "dict_name", Blockly.Python.ORDER_ATOMIC),
+        c = Blockly.Python.valueToCode(a, "dict_items", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "add_value", Blockly.Python.ORDER_ATOMIC);
+    return b + "[" + c + "] \x3d " + a + "\n"
+};
+Blockly.Python.dict_length = function(a) {
+    return ["len(" + Blockly.Python.valueToCode(a, "dict_name", Blockly.Python.ORDER_ATOMIC) + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.dict_key_exist = function(a) {
+    var b = Blockly.Python.valueToCode(a, "dict_name", Blockly.Python.ORDER_ATOMIC);
+    return [Blockly.Python.valueToCode(a, "dict_items", Blockly.Python.ORDER_ATOMIC) + " in " + b, Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.dict_key_or_val_list = function(a) {
+    var b = Blockly.Python.valueToCode(a, "dict_name", Blockly.Python.ORDER_ATOMIC);
+    a = a.getFieldValue("key_or_val");
+    return ["list(" + b + "." + a + "())", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.dict_create_with_items_insert = function() {
+    for (var a = Array(this.itemCount_), b = 0; b < this.itemCount_; b++) {
+        var c = this.getFieldValue("KEY" + b);
+        a[b] = '"' + c + '":' + (Blockly.Python.valueToCode(this, "ADD" + b, Blockly.Python.ORDER_NONE) || "0")
+    }
+    a = "{" + a.join(", ") + "}";
+    return [a, Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.pinyin_dict_create_with_items_insert = function() {
+    for (var a = Array(this.itemCount_), b = 0; b < this.itemCount_; b++) {
+        var c = this.getFieldValue("KEY" + b);
+        a[b] = '"' + c + '":' + (Blockly.Python.valueToCode(this, "ADD" + b, Blockly.Python.ORDER_NONE) || "0")
+    }
+    a = "{" + a.join(", ") + "}";
+    return [a, Blockly.Python.ORDER_ATOMIC]
+};
+
+
+
+
+export default Blockly;

+ 4 - 1
src/blockly/pythonCode/index.js

@@ -1,9 +1,12 @@
 import basicCode from './basicCode'
 import logic from './logic'
 import text from './text'
+import list from './list'
+import dictionary from './dictionary'
 
 const python = {
-    ...basicCode,...logic,...text
+    ...basicCode,...logic,...text,...list
+    ,...dictionary
 }
 
 export default python

+ 279 - 0
src/blockly/pythonCode/list.js

@@ -0,0 +1,279 @@
+import { pythonGenerator } from "blockly/python";
+
+const Blockly = {
+    Python: pythonGenerator || { Msg: Object.create(null) }
+};
+Blockly.Python.lists_create_with = function (a) {
+    for (var b = Array(a.itemCount_), c = 0; c < a.itemCount_; c++)
+        b[c] = Blockly.Python.valueToCode(a, "ADD" + c, Blockly.Python.ORDER_NONE) || "None";
+    return ["[" + b.join(", ") + "]", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.text_list = function (a) {
+    return ["[" + Blockly.Python.quote_empty(a.getFieldValue("TEXT")) + "]", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.lists_create_with_object = function (a) {
+    for (var b = Array(a.itemCount_), c = 0; c < a.itemCount_; c++)
+        b[c] = Blockly.Python.valueToCode(a, "ADD" + c, Blockly.Python.ORDER_NONE) || "None";
+    return ["[" + b.join(", ") + "]", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.CocoRobo_return_list = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "list_items", Blockly.Python.ORDER_ATOMIC);
+    return b + " \x3d " + a + "\n"
+};
+Blockly.Python.lists_append = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "last_item", Blockly.Python.ORDER_ATOMIC);
+    return b + ".append(" + a + ")\n"
+};
+Blockly.Python.lists_extend = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "extend_list", Blockly.Python.ORDER_ATOMIC);
+    return b + ".extend(" + a + ")\n"
+};
+Blockly.Python.lists_clear = function (a) {
+    return Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC) + ".clear()\n"
+};
+Blockly.Python.list_order_item = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC),
+        c = a.getFieldValue("list_order");
+    a = Blockly.Python.valueToCode(a, "list_order_item", Blockly.Python.ORDER_ATOMIC);
+    return [b + c + a + "]", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.lists_clear = function (a) {
+    return Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC) + ".clear()\n"
+};
+Blockly.Python.parts_of_list = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC),
+        c = a.getFieldValue("list_start_order"),
+        d = a.getFieldValue("list_end_order"),
+        e = Blockly.Python.valueToCode(a, "start_item", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "end_item", Blockly.Python.ORDER_ATOMIC);
+    switch (d) {
+        case ":":
+            var f = parseInt(a) + 1;
+            break;
+        case ":-":
+            f = a - 1
+    }
+    return [b + c + e + d + f + "]", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.list_to_tuple = function (a) {
+    return ["tuple(" + Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC) + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.list_order_item = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC),
+        c = a.getFieldValue("list_order");
+    a = Blockly.Python.valueToCode(a, "list_order_item", Blockly.Python.ORDER_ATOMIC);
+    return [b + c + a + "]", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.set_list_order_item = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC),
+        c = a.getFieldValue("list_order"),
+        d = Blockly.Python.valueToCode(a, "list_order_item", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "set_value", Blockly.Python.ORDER_ATOMIC);
+    return b + c + d + "] \x3d " + a + "\n"
+};
+Blockly.Python.insert_list_order_item = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC),
+        c = a.getFieldValue("list_order"),
+        d = Blockly.Python.valueToCode(a, "list_order_item", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "set_value", Blockly.Python.ORDER_ATOMIC);
+    return b + ".insert" + c + d + ", " + a + ")\n"
+};
+Blockly.Python.list_item_exist = function (a) {
+    var b = Blockly.Python.valueToCode(a, "list_name", Blockly.Python.ORDER_ATOMIC);
+    return [Blockly.Python.valueToCode(a, "list_item", Blockly.Python.ORDER_ATOMIC) + " in " + b, Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.lists_repeat = function (a) {
+    var b = Blockly.Python.valueToCode(a, "ITEM", Blockly.Python.ORDER_NONE) || "None";
+    a = Blockly.Python.valueToCode(a, "NUM", Blockly.Python.ORDER_MULTIPLICATIVE) || "0";
+    return ["[" + b + "] * " + a, Blockly.Python.ORDER_MULTIPLICATIVE]
+};
+Blockly.Python.lists_length = function (a) {
+    return ["len(" + (Blockly.Python.valueToCode(a, "VALUE", Blockly.Python.ORDER_NONE) || "[]") + ")", Blockly.Python.ORDER_FUNCTION_CALL]
+};
+Blockly.Python.lists_isEmpty = function (a) {
+    return ["not len(" + (Blockly.Python.valueToCode(a, "VALUE", Blockly.Python.ORDER_NONE) || "[]") + ")", Blockly.Python.ORDER_LOGICAL_NOT]
+};
+Blockly.Python.lists_indexOf = function (a) {
+    var b = Blockly.Python.valueToCode(a, "FIND", Blockly.Python.ORDER_NONE) || "[]"
+        , c = Blockly.Python.valueToCode(a, "VALUE", Blockly.Python.ORDER_NONE) || "''";
+    if (a.workspace.options.oneBasedIndex)
+        var d = " 0"
+            , e = " + 1"
+            , f = "";
+    else
+        d = " -1",
+            e = "",
+            f = " - 1";
+    if ("FIRST" == a.getFieldValue("END"))
+        return a = Blockly.Python.provideFunction_("first_index", ["def " + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + "(my_list, elem):", "  try: index = my_list.index(elem)" + e, "  except: index =" + d, "  return index"]),
+            [a + "(" + c + ", " + b + ")", Blockly.Python.ORDER_FUNCTION_CALL];
+    a = Blockly.Python.provideFunction_("last_index", ["def " + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + "(my_list, elem):", "  try: index = len(my_list) - my_list[::-1].index(elem)" + f, "  except: index =" + d, "  return index"]);
+    return [a + "(" + c + ", " + b + ")", Blockly.Python.ORDER_FUNCTION_CALL]
+};
+Blockly.Python.lists_getIndex = function (a) {
+    var b = a.getFieldValue("MODE") || "GET"
+        , c = a.getFieldValue("WHERE") || "FROM_START"
+        , d = Blockly.Python.valueToCode(a, "VALUE", "RANDOM" == c ? Blockly.Python.ORDER_NONE : Blockly.Python.ORDER_MEMBER) || "[]";
+    switch (c) {
+        case "FIRST":
+            if ("GET" == b)
+                return [d + "[0]", Blockly.Python.ORDER_MEMBER];
+            if ("GET_REMOVE" == b)
+                return [d + ".pop(0)", Blockly.Python.ORDER_FUNCTION_CALL];
+            if ("REMOVE" == b)
+                return d + ".pop(0)\n";
+            break;
+        case "LAST":
+            if ("GET" == b)
+                return [d + "[-1]", Blockly.Python.ORDER_MEMBER];
+            if ("GET_REMOVE" == b)
+                return [d + ".pop()", Blockly.Python.ORDER_FUNCTION_CALL];
+            if ("REMOVE" == b)
+                return d + ".pop()\n";
+            break;
+        case "FROM_START":
+            a = Blockly.Python.valueToCode(a, "AT", Blockly.Python.ORDER_ATOMIC);
+            if ("GET" == b)
+                return [d + "[" + a + "]", Blockly.Python.ORDER_MEMBER];
+            if ("GET_REMOVE" == b)
+                return [d + ".pop(" + a + ")", Blockly.Python.ORDER_FUNCTION_CALL];
+            if ("REMOVE" == b)
+                return d + ".pop(" + a + ")\n";
+            break;
+        case "FROM_END":
+            a = Blockly.Python.getAdjustedInt(a, "AT", 1, !0);
+            if ("GET" == b)
+                return [d + "[" + a + "]", Blockly.Python.ORDER_MEMBER];
+            if ("GET_REMOVE" == b)
+                return [d + ".pop(" + a + ")", Blockly.Python.ORDER_FUNCTION_CALL];
+            if ("REMOVE" == b)
+                return d + ".pop(" + a + ")\n";
+            break;
+        case "RANDOM":
+            Blockly.Python.definitions_.import_random = "import random";
+            if ("GET" == b)
+                return ["random.choice(" + d + ")", Blockly.Python.ORDER_FUNCTION_CALL];
+            d = Blockly.Python.provideFunction_("lists_remove_random_item", ["def " + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + "(myList):", "  x = int(random.random() * len(myList))", "  return myList.pop(x)"]) + "(" + d + ")";
+            if ("GET_REMOVE" == b)
+                return [d, Blockly.Python.ORDER_FUNCTION_CALL];
+            if ("REMOVE" == b)
+                return d + "\n"
+    }
+    throw "Unhandled combination (lists_getIndex).";
+};
+Blockly.Python.lists_setIndex = function (a) {
+    var b = Blockly.Python.valueToCode(a, "LIST", Blockly.Python.ORDER_MEMBER) || "[]"
+        , c = a.getFieldValue("MODE") || "GET"
+        , d = a.getFieldValue("WHERE") || "FROM_START"
+        , e = Blockly.Python.valueToCode(a, "TO", Blockly.Python.ORDER_NONE) || "None";
+    switch (d) {
+        case "FIRST":
+            if ("SET" == c)
+                return b + "[0] = " + e + "\n";
+            if ("INSERT" == c)
+                return b + ".insert(0, " + e + ")\n";
+            break;
+        case "LAST":
+            if ("SET" == c)
+                return b + "[-1] = " + e + "\n";
+            if ("INSERT" == c)
+                return b + ".append(" + e + ")\n";
+            break;
+        case "FROM_START":
+            a = Blockly.Python.getAdjustedInt(a, "AT");
+            if ("SET" == c)
+                return b + "[" + a + "] = " + e + "\n";
+            if ("INSERT" == c)
+                return b + ".insert(" + a + ", " + e + ")\n";
+            break;
+        case "FROM_END":
+            a = Blockly.Python.getAdjustedInt(a, "AT", 1, !0);
+            if ("SET" == c)
+                return b + "[" + a + "] = " + e + "\n";
+            if ("INSERT" == c)
+                return b + ".insert(" + a + ", " + e + ")\n";
+            break;
+        case "RANDOM":
+            Blockly.Python.definitions_.import_random = "import random";
+            b.match(/^\w+$/) ? a = "" : (a = Blockly.Python.variableDB_.getDistinctName("tmp_list", Blockly.Variables.NAME_TYPE),
+                d = a + " = " + b + "\n",
+                b = a,
+                a = d);
+            d = Blockly.Python.variableDB_.getDistinctName("tmp_x", Blockly.Variables.NAME_TYPE);
+            a += d + " = int(random.random() * len(" + b + "))\n";
+            if ("SET" == c)
+                return a + (b + "[" + d + "] = " + e + "\n");
+            if ("INSERT" == c)
+                return a + (b + ".insert(" + d + ", " + e + ")\n")
+    }
+    throw "Unhandled combination (lists_setIndex).";
+};
+Blockly.Python.lists_getSublist = function (a) {
+    var b = Blockly.Python.valueToCode(a, "LIST", Blockly.Python.ORDER_MEMBER) || "[]"
+        , c = a.getFieldValue("WHERE1")
+        , d = a.getFieldValue("WHERE2");
+    switch (c) {
+        case "FROM_START":
+            c = Blockly.Python.getAdjustedInt(a, "AT1");
+            "0" == c && (c = "");
+            break;
+        case "FROM_END":
+            c = Blockly.Python.getAdjustedInt(a, "AT1", 1, !0);
+            break;
+        case "FIRST":
+            c = "";
+            break;
+        default:
+            throw "Unhandled option (lists_getSublist)";
+    }
+    switch (d) {
+        case "FROM_START":
+            a = Blockly.Python.getAdjustedInt(a, "AT2", 1);
+            break;
+        case "FROM_END":
+            a = Blockly.Python.getAdjustedInt(a, "AT2", 0, !0);
+            Blockly.isNumber(String(a)) ? "0" == a && (a = "") : (Blockly.Python.definitions_.import_sys = "import sys",
+                a += " or sys.maxsize");
+            break;
+        case "LAST":
+            a = "";
+            break;
+        default:
+            throw "Unhandled option (lists_getSublist)";
+    }
+    return [b + "[" + c + " : " + a + "]", Blockly.Python.ORDER_MEMBER]
+};
+Blockly.Python.lists_sort = function (a) {
+    var b = Blockly.Python.valueToCode(a, "LIST", Blockly.Python.ORDER_NONE) || "[]"
+        , c = a.getFieldValue("TYPE");
+    a = "1" === a.getFieldValue("DIRECTION") ? "False" : "True";
+    return [Blockly.Python.provideFunction_("lists_sort", ["def " + Blockly.Python.FUNCTION_NAME_PLACEHOLDER_ + "(my_list, type, reverse):", "  def try_float(s):", "    try:", "      return float(s)", "    except:", "      return 0", "  key_funcs = {", '    "NUMERIC": try_float,', '    "TEXT": str,', '    "IGNORE_CASE": lambda s: str(s).lower()', "  }", "  key_func = key_funcs[type]", "  list_cpy = list(my_list)", "  return sorted(list_cpy, key=key_func, reverse=reverse)"]) + "(" + b + ', "' + c + '", ' + a + ")", Blockly.Python.ORDER_FUNCTION_CALL]
+};
+Blockly.Python.lists_split = function (a) {
+    var b = a.getFieldValue("MODE");
+    if ("SPLIT" == b)
+        b = Blockly.Python.valueToCode(a, "INPUT", Blockly.Python.ORDER_MEMBER) || "''",
+            a = Blockly.Python.valueToCode(a, "DELIM", Blockly.Python.ORDER_NONE),
+            a = b + ".split(" + a + ")";
+    else if ("JOIN" == b)
+        b = Blockly.Python.valueToCode(a, "INPUT", Blockly.Python.ORDER_NONE) || "[]",
+            a = Blockly.Python.valueToCode(a, "DELIM", Blockly.Python.ORDER_MEMBER) || "''",
+            a = a + ".join(" + b + ")";
+    else
+        throw "Unknown mode: " + b;
+    return [a, Blockly.Python.ORDER_FUNCTION_CALL]
+};
+Blockly.Python.list_first_index = function(a) {
+    var b = Blockly.Python.valueToCode(a, "elem", Blockly.Python.ORDER_ATOMIC),
+        c = Blockly.Python.valueToCode(a, "my_list", Blockly.Python.ORDER_ATOMIC);
+    a = a.getFieldValue("last_or_first");
+    Blockly.Python.codeFunctions_.list_first_index = "def first_index(my_list, elem):\n    try: index \x3d my_list.index(elem)\n    except: index \x3d 0\n    return index";
+    Blockly.Python.codeFunctions_.list_last_index = "def last_index(my_list, elem):\n    try: index \x3d len(my_list) - my_list[::-1].index(elem) - 1\n    except: index \x3d 0\n    return index";
+    return [a + "_index(" + c + ", " + b + ")", Blockly.Python.ORDER_ATOMIC]
+};
+
+
+export default Blockly;

+ 39 - 3
src/blockly/pythonCode/text.js

@@ -34,11 +34,10 @@ Blockly.Python['text_split_string_by_delimiter'] = function (block) {
 };
 Blockly.Python.text_format = function (a) {
     var b = Blockly.Python.valueToCode(a, "FORMAT", Blockly.Python.ORDER_ATOMIC);
-    console.log(Blockly.Python.valueToCode);
     let c = Blockly.Python.valueToCode(a, "CONTENT", Blockly.Python.ORDER_ATOMIC);
     return [b + " % " + c, Blockly.Python.ORDER_ATOMIC]
 };
-Blockly.Python.tuple_create_with_items_insert = function(a) {
+Blockly.Python.tuple_create_with_items_insert = function (a) {
     for (var b = Array(a.itemCount_), c = 0; c < a.itemCount_; c++)
         b[c] = Blockly.Python.valueToCode(a, "ADD" + c, Blockly.Python.ORDER_NONE) || "None";
     return [1 == a.itemCount_ ? "(" + b[0] + ",)" : "(" + b.join(", ") + ")", Blockly.Python.ORDER_ATOMIC]
@@ -49,9 +48,46 @@ Blockly.Python.text_format2 = function (a) {
     a = Blockly.Python.valueToCode(a, "CONTENT", Blockly.Python.ORDER_ATOMIC);
     return [b + ".format" + a, Blockly.Python.ORDER_ATOMIC]
 };
-Blockly.Python.CocoRobo_text_ESC = function(a) {
+Blockly.Python.CocoRobo_text_ESC = function (a) {
     // Blockly.Python.definitions_.import_usocket = "import usocket";
     return ['"' + a.getFieldValue("mode") + '"', Blockly.Python.ORDER_ATOMIC]
 };
+Blockly.Python.text_append_text = function (a) {
+    var b = Blockly.Python.valueToCode(a, "text_abc", Blockly.Python.ORDER_ATOMIC);
+    a = Blockly.Python.valueToCode(a, "append_text", Blockly.Python.ORDER_ATOMIC);
+    return ["str(" + b + ") + str(" + a + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.text_is_number = function (a) {
+    var b = Blockly.Python.valueToCode(a, "TEXT", Blockly.Python.ORDER_ATOMIC);
+    a = a.getFieldValue("TYPE");
+    return [b + a, Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.text_to_byte = function (a) {
+    return ["bytes(" + Blockly.Python.valueToCode(a, "TEXT", Blockly.Python.ORDER_ATOMIC) + ", 'utf-8')", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.other_to_byte = function (a) {
+    return ["bytes(" + Blockly.Python.valueToCode(a, "input_var", Blockly.Python.ORDER_ATOMIC) + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.bit_inversion = function (a) {
+    return ["~" + Blockly.Python.valueToCode(a, "data", Blockly.Python.ORDER_ATOMIC), Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.CocoRobo_bytes_decode = function (a) {
+    return [Blockly.Python.valueToCode(a, "bytes_decode", Blockly.Python.ORDER_ATOMIC) + ".decode('UTF-8','ignore')", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.CocoRobo_ujson_dumps = function (a) {
+    Blockly.Python.definitions_.import_ujson = "import json";
+    return ["json.dumps(" + Blockly.Python.valueToCode(a, "data", Blockly.Python.ORDER_ATOMIC) + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.CocoRobo_ujson_loads = function (a) {
+    Blockly.Python.definitions_.import_ujson = "import json";
+    return ["json.loads(" + Blockly.Python.valueToCode(a, "data", Blockly.Python.ORDER_ATOMIC) + ")", Blockly.Python.ORDER_ATOMIC]
+};
+Blockly.Python.CocoRobo_code_annotation = function (a) {
+    var code = Blockly.Python.valueToCode(a, "data", Blockly.Python.ORDER_ATOMIC);
+    return "# " + code.replace("'", '').replace("'", '') + "\n";
+};
+Blockly.Python.text_dict = function (a) {
+    return ["{" + Blockly.Python.quote_empty(a.getFieldValue("TEXT")) + "}", Blockly.Python.ORDER_ATOMIC]
+};
 
 export default Blockly;