Example #1
0
        /// <summary>
        /// Define blocks from imported block definitions.
        /// </summary>
        /// <param  name="blockDefsString"></param> Block definition(s).
        /// <param name="format"> Block definition format ("JSON" or "JavaScript").</param>
        /// <returns>Array of block types defined.</returns>
        public static JsArray <string> defineAndGetBlockTypes(string blockDefsString, string format)
        {
            var blockTypes = new JsArray <string>();

            // Define blocks and get block types.
            if (format == "JSON")
            {
                var blockDefArray = FactoryUtils.parseJsonBlockDefinitions(blockDefsString);

                // Populate array of blocktypes and define each block.
                for (var i = 0; i < blockDefArray.Length; i++)
                {
                    var blockDef = blockDefArray[i];
                    var json     = (Dictionary <string, object>)JSON.Parse(blockDef);
                    blockTypes.Push((string)json["type"]);

                    // Define the block.
                    Blockly.Core.Blocks.Add((string)json["type"], blockDef);
                }
            }
            else if (format == "JavaScript")
            {
                var blockDefArray = FactoryUtils.parseJsBlockDefinitions(blockDefsString);

                // Populate array of block types.
                for (var i = 0; i < blockDefArray.Length; i++)
                {
                    var blockDef  = blockDefArray[i];
                    var blockType = FactoryUtils.getBlockTypeFromJsDefinition(blockDef);
                    blockTypes.Push(blockType);
                }

                // Define all blocks.
                Script.Eval(blockDefsString);
            }

            return(blockTypes);
        }
        /// <summary>
        /// Generate selector dom from block library storage. For each block in the
        /// library, it has a block option, which consists of a checkbox, a label,
        /// and a fixed size preview workspace.
        /// </summary>
        /// <param name="blockLibStorage"> Block Library Storage object.</param>
        /// <param name="blockSelectorId"> ID of the div element that will contain
        /// the block options.</param>
        /// <returns>Map of block type to Block Option object.</returns>
        public Dictionary <string, BlockOption> createBlockSelectorFromLib(BlockLibraryStorage blockLibStorage, string blockSelectorId)
        {
            // Object mapping each stored block type to XML.
            var allBlockTypes = blockLibStorage.getBlockTypes();
            var blockXmlMap   = blockLibStorage.getBlockXmlMap(allBlockTypes);

            // Define the custom blocks in order to be able to create instances of
            // them in the exporter workspace.
            this.addBlockDefinitions(blockXmlMap);

            var blockSelector = Document.GetElementById(blockSelectorId);
            // Clear the block selector.
            Node child;

            while ((child = blockSelector.FirstChild) != null)
            {
                blockSelector.RemoveChild(child);
            }

            // Append each block option's dom to the selector.
            var blockOptions = new Dictionary <string, BlockOption>();

            foreach (var blockType in blockXmlMap.Keys)
            {
                // Get preview block's XML.
                var block           = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
                var previewBlockXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);

                // Create block option, inject block into preview workspace, and append
                // option to block selector.
                var blockOpt = new BlockOption(blockSelector, blockType, previewBlockXml);
                blockOpt.createDom();
                blockSelector.AppendChild(blockOpt.dom);
                blockOpt.showPreviewBlock();
                blockOptions[blockType] = blockOpt;
            }
            return(blockOptions);
        }
        /// <summary>
        /// Saves current block to local storage and updates dropdown.
        /// </summary>
        public void saveToBlockLibrary()
        {
            var blockType = this.getCurrentBlockType();

            // If user has not changed the name of the starter block.
            if (blockType == "block_type")
            {
                // Do not save block if it has the default type, "block_type".
                Window.Alert("You cannot save a block under the name \"block_type\". Try changing " +
                             "the name before saving. Then, click on the \"Block Library\" button " +
                             "to view your saved blocks.");
                return;
            }

            // Create block XML.
            var xmlElement = goog.dom.createDom("xml");
            var block      = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);

            xmlElement.AppendChild(Blockly.Xml.blockToDomWithXY(block));

            // Do not add option again if block type is already in library.
            if (!this.has(blockType))
            {
                this.view.addOption(blockType, true);
            }

            // Save block.
            this.storage.addBlock(blockType, xmlElement);
            this.storage.saveToLocalStorage();

            // Show saved block without other stray blocks sitting in Block Factory's
            // main workspace.
            this.openBlock(blockType);

            // Add select handler to the new option.
            this.addOptionSelectHandler(blockType);
        }
        /// <summary>
        /// Pulls information about all blocks in the block library to generate XML
        /// for the selector workpace's toolbox.
        /// </summary>
        /// <param name="blockLibStorage"> Block Library Storage object.</param>
        /// <returns>XML representation of the toolbox.</returns>
        Element generateToolboxFromLibrary(BlockLibraryStorage blockLibStorage)
        {
            // Create DOM for XML.
            var xmlDom = goog.dom.createDom("xml", new Dictionary <string, string> {
                { "id", "blockExporterTools_toolbox" },
                { "style", "display:none" }
            });

            var allBlockTypes = blockLibStorage.getBlockTypes();
            // Object mapping block type to XML.
            var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes);

            // Define the custom blocks in order to be able to create instances of
            // them in the exporter workspace.
            this.addBlockDefinitions(blockXmlMap);

            foreach (var blockType in blockXmlMap.Keys)
            {
                // Get block.
                var block    = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
                var category = FactoryUtils.generateCategoryXml(new JsArray <Blockly.Block> {
                    block
                }, blockType);
                xmlDom.AppendChild(category);
            }

            // If there are no blocks in library and the map is empty, append dummy
            // category.
            if (blockXmlMap.Count == 0)
            {
                var category = goog.dom.createDom("category");
                category.SetAttribute("name", "Next Saved Block");
                xmlDom.AppendChild(category);
            }
            return(xmlDom);
        }
Example #5
0
        /// <summary>
        /// Update the preview display.
        /// </summary>
        public static void updatePreview()
        {
            // Toggle between LTR/RTL if needed (also used in first display).
            var newDir = ((HTMLSelectElement)Document.GetElementById("direction")).Value;

            if (BlockFactory.oldDir != newDir)
            {
                if (BlockFactory.previewWorkspace != null)
                {
                    BlockFactory.previewWorkspace.dispose();
                }
                var rtl = newDir == "rtl";
                BlockFactory.previewWorkspace = Blockly.Core.inject("preview", new Dictionary <string, object> {
                    { "rtl", rtl },
                    { "media", "../../media/" },
                    { "scrollbars", true }
                });
                BlockFactory.oldDir = newDir;
            }
            BlockFactory.previewWorkspace.clear();

            // Fetch the code and determine its format (JSON or JavaScript).
            var    format = ((HTMLSelectElement)Document.GetElementById("format")).Value;
            string code;

            if (format == "Manual")
            {
                code = ((HTMLTextAreaElement)Document.GetElementById("languageTA")).Value;
                // If the code is JSON, it will parse, otherwise treat as JS.
                try {
                    JSON.Parse(code);
                    format = "JSON";
                }
                catch (Exception) {
                    format = "JavaScript";
                }
            }
            else
            {
                code = Document.GetElementById("languagePre").TextContent;
            }
            if (String.IsNullOrEmpty(code.Trim()))
            {
                // Nothing to render.  Happens while cloud storage is loading.
                return;
            }

            // Backup Blockly.Core.Blocks object so that main workspace and preview don't
            // collide if user creates a "factory_base" block, for instance.
            var backupBlocks = Blockly.Core.Blocks;

            try {
                // Make a shallow copy.
                Blockly.Core.Blocks = new Blockly.Blocks();
                foreach (var prop in backupBlocks.Keys)
                {
                    Blockly.Core.Blocks[prop] = backupBlocks[prop];
                }

                if (format == "JSON")
                {
                    var json = (Dictionary <string, object>)JSON.Parse(code);
                    Blockly.Core.Blocks.Add(json.ContainsKey("type") ? (string)json["type"] : BlockFactory.UNNAMED, code);
                }
                else if (format == "JavaScript")
                {
                    Script.Eval(code);
                }
                else
                {
                    throw new Exception("Unknown format: " + format);
                }

                // Look for a block on Blockly.Core.Blocks that does not match the backup.
                string blockType = null;
                foreach (var type in Blockly.Core.Blocks.Keys)
                {
                    if (!backupBlocks.ContainsKey(type) || Blockly.Core.Blocks[type] != backupBlocks[type])
                    {
                        blockType = type;
                        break;
                    }
                }
                if (String.IsNullOrEmpty(blockType))
                {
                    return;
                }

                // Create the preview block.
                var previewBlock = (Blockly.BlockSvg)BlockFactory.previewWorkspace.newBlock(blockType);
                previewBlock.initSvg();
                previewBlock.render();
                previewBlock.setMovable(false);
                previewBlock.setDeletable(false);
                previewBlock.moveBy(15, 10);
                BlockFactory.previewWorkspace.clearUndo();
                BlockFactory.updateGenerator(previewBlock);

                // Warn user only if their block type is already exists in Blockly's
                // standard library.
                var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
                if (Array.IndexOf(StandardCategories.coreBlockTypes, blockType) != -1)
                {
                    rootBlock.setWarningText("A core Blockly block already exists " +
                                             "under this name.");
                }
                else if (blockType == "block_type")
                {
                    // Warn user to let them know they can't save a block under the default
                    // name "block_type"
                    rootBlock.setWarningText("You cannot save a block with the default " +
                                             "name, \"block_type\"");
                }
                else
                {
                    rootBlock.setWarningText(null);
                }
            }
            finally {
                Blockly.Core.Blocks = backupBlocks;
            }
        }
Example #6
0
        /// <summary>
        /// Called on each tab click. Hides and shows specific content based on which tab
        /// (Block Factory, Workspace Factory, or Exporter) is selected.
        /// </summary>
        public void onTab()
        {
            // Get tab div elements.
            var blockFactoryTab     = this.tabMap[AppController.BLOCK_FACTORY];
            var exporterTab         = this.tabMap[AppController.EXPORTER];
            var workspaceFactoryTab = this.tabMap[AppController.WORKSPACE_FACTORY];

            // Warn user if they have unsaved changes when leaving Block Factory.
            if (this.lastSelectedTab == AppController.BLOCK_FACTORY &&
                this.selectedTab != AppController.BLOCK_FACTORY)
            {
                var hasUnsavedChanges =
                    !FactoryUtils.savedBlockChanges(this.blockLibraryController);
                if (hasUnsavedChanges &&
                    !Window.Confirm("You have unsaved changes in Block Factory."))
                {
                    // If the user doesn't want to switch tabs with unsaved changes,
                    // stay on Block Factory Tab.
                    this.setSelected_(AppController.BLOCK_FACTORY);
                    this.lastSelectedTab = AppController.BLOCK_FACTORY;
                    return;
                }
            }

            // Only enable key events in workspace factory if workspace factory tab is
            // selected.
            this.workspaceFactoryController.keyEventsEnabled =
                this.selectedTab == AppController.WORKSPACE_FACTORY;

            // Turn selected tab on and other tabs off.
            this.styleTabs_();

            if (this.selectedTab == AppController.EXPORTER)
            {
                // Hide other tabs.
                FactoryUtils.hide("workspaceFactoryContent");
                FactoryUtils.hide("blockFactoryContent");
                // Show exporter tab.
                FactoryUtils.show("blockLibraryExporter");

                // Need accurate state in order to know which blocks are used in workspace
                // factory.
                this.workspaceFactoryController.saveStateFromWorkspace();

                // Update exporter's list of the types of blocks used in workspace factory.
                var usedBlockTypes = this.workspaceFactoryController.getAllUsedBlockTypes();
                this.exporter.setUsedBlockTypes(usedBlockTypes);

                // Update exporter's block selector to reflect current block library.
                this.exporter.updateSelector();

                // Update the exporter's preview to reflect any changes made to the blocks.
                this.exporter.updatePreview();
            }
            else if (this.selectedTab == AppController.BLOCK_FACTORY)
            {
                // Hide other tabs.
                FactoryUtils.hide("blockLibraryExporter");
                FactoryUtils.hide("workspaceFactoryContent");
                // Show Block Factory.
                FactoryUtils.show("blockFactoryContent");
            }
            else if (this.selectedTab == AppController.WORKSPACE_FACTORY)
            {
                // Hide other tabs.
                FactoryUtils.hide("blockLibraryExporter");
                FactoryUtils.hide("blockFactoryContent");
                // Show workspace factory container.
                FactoryUtils.show("workspaceFactoryContent");
                // Update block library category.
                var categoryXml = this.exporter.getBlockLibraryCategory();
                var blockTypes  = this.blockLibraryController.getStoredBlockTypes();
                this.workspaceFactoryController.setBlockLibCategory(categoryXml,
                                                                    blockTypes);
            }

            // Resize to render workspaces" toolboxes correctly for all tabs.
            Window.DispatchEvent(new Event("resize"));
        }
Example #7
0
        /// <summary>
        /// Returns field strings and any config.
        /// </summary>
        /// <param  name="block">Input block.</param>
        /// <returns>Field strings.</returns>
        private static JsArray <string> getFieldsJs_(Blockly.Block block)
        {
            var fields = new JsArray <string>();

            while (block != null)
            {
                if (!block.disabled && !block.getInheritedDisabled())
                {
                    switch (block.type)
                    {
                    case "field_static":
                        // Result: "hello"
                        fields.Push(FactoryUtils.escapeString(block.getFieldValue("TEXT")));
                        break;

                    case "field_input":
                        // Result: new Blockly.FieldTextInput("Hello"), "GREET"
                        fields.Push("new Blockly.FieldTextInput(" +
                                    FactoryUtils.escapeString(block.getFieldValue("TEXT")) + "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_number":
                        // Result: new Blockly.FieldNumber(10, 0, 100, 1), "NUMBER"
                        var args = new JsArray <double> {
                            Script.ParseFloat(block.getFieldValue("VALUE")),
                            Script.ParseFloat(block.getFieldValue("MIN")),
                            Script.ParseFloat(block.getFieldValue("MAX")),
                            Script.ParseFloat(block.getFieldValue("PRECISION"))
                        };
                        // Remove any trailing arguments that aren't needed.
                        if (args[3] == 0)
                        {
                            args.Pop();
                            if (args[2] == Double.PositiveInfinity)
                            {
                                args.Pop();
                                if (args[1] == Double.NegativeInfinity)
                                {
                                    args.Pop();
                                }
                            }
                        }
                        fields.Push("new Blockly.FieldNumber(" + args.Join(", ") + "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_angle":
                        // Result: new Blockly.FieldAngle(90), "ANGLE"
                        fields.Push("new Blockly.FieldAngle(" +
                                    Script.ParseFloat(block.getFieldValue("ANGLE")) + "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_checkbox":
                        // Result: new Blockly.FieldCheckbox("TRUE"), "CHECK"
                        fields.Push("new Blockly.FieldCheckbox(" +
                                    FactoryUtils.escapeString(block.getFieldValue("CHECKED")) +
                                    "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_colour":
                        // Result: new Blockly.FieldColour("#ff0000"), "COLOUR"
                        fields.Push("new Blockly.FieldColour(" +
                                    FactoryUtils.escapeString(block.getFieldValue("COLOUR")) +
                                    "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_date":
                        // Result: new Blockly.FieldDate("2015-02-04"), "DATE"
                        fields.Push("new Blockly.FieldDate(" +
                                    FactoryUtils.escapeString(block.getFieldValue("DATE")) + "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_variable":
                        // Result: new Blockly.FieldVariable("item"), "VAR"
                        var varname
                            = FactoryUtils.escapeString(block.getFieldValue("TEXT") ?? null);
                        fields.Push("new Blockly.FieldVariable(" + varname + "), " +
                                    FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        break;

                    case "field_dropdown":
                        // Result:
                        // new Blockly.FieldDropdown([["yes", "1"], ["no", "0"]]), "TOGGLE"
                        var options = new JsArray <string>();
                        for (var i = 0; i < ((FieldDropdown)block).optionCount_; i++)
                        {
                            options[i] = "[" +
                                         FactoryUtils.escapeString(block.getFieldValue("USER" + i)) +
                                         ", " +
                                         FactoryUtils.escapeString(block.getFieldValue("CPU" + i)) + "]";
                        }
                        if (options.Length > 0)
                        {
                            fields.Push("new Blockly.FieldDropdown([" +
                                        options.Join(", ") + "]), " +
                                        FactoryUtils.escapeString(block.getFieldValue("FIELDNAME")));
                        }
                        break;

                    case "field_image":
                        // Result: new Blockly.FieldImage("http://...", 80, 60)
                        var src    = FactoryUtils.escapeString(block.getFieldValue("SRC"));
                        var width  = Convert.ToDouble(block.getFieldValue("WIDTH"));
                        var height = Convert.ToDouble(block.getFieldValue("HEIGHT"));
                        var alt    = FactoryUtils.escapeString(block.getFieldValue("ALT"));
                        fields.Push("new Blockly.FieldImage(" +
                                    src + ", " + width + ", " + height + ", " + alt + ")");
                        break;
                    }
                }
                block = block.nextConnection == null ? null : block.nextConnection.targetBlock();
            }
            return(fields);
        }
Example #8
0
        /// <summary>
        /// Update the language code as JavaScript.
        /// </summary>
        /// <param name="blockType"> Name of block.</param>
        /// <param name="rootBlock"> Factory_base block.</param>
        /// <param name="workspace"></param> Where the root block lives.
        /// <returns>Generated language code.</returns>
        private static string formatJavaScript_(string blockType, Blockly.Block rootBlock, Blockly.Workspace workspace)
        {
            var code = new JsArray <string>();

            code.Push("Blockly.Core.Blocks[\"" + blockType + "\"] = {");
            code.Push("  init: () => {");
            // Generate inputs.
            var TYPES = new Dictionary <string, string>()
            {
                { "input_value", "appendValueInput" },
                { "input_statement", "appendStatementInput" },
                { "input_dummy", "appendDummyInput" }
            };
            var contentsBlock = rootBlock.getInputTargetBlock("INPUTS");

            while (contentsBlock != null)
            {
                if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled())
                {
                    var name = "";
                    // Dummy inputs don't have names.  Other inputs do.
                    if (contentsBlock.type != "input_dummy")
                    {
                        name =
                            FactoryUtils.escapeString(contentsBlock.getFieldValue("INPUTNAME"));
                    }
                    code.Push("    this." + TYPES[contentsBlock.type] + "(" + name + ")");
                    var check = FactoryUtils.getOptTypesFrom(contentsBlock, "TYPE");
                    if (!String.IsNullOrEmpty(check))
                    {
                        code.Push("        .setCheck(" + check + ")");
                    }
                    var align = contentsBlock.getFieldValue("ALIGN");
                    if (align != "LEFT")
                    {
                        code.Push("        .setAlign(Blockly.ALIGN_" + align + ")");
                    }
                    var fields = FactoryUtils.getFieldsJs_(
                        contentsBlock.getInputTargetBlock("FIELDS"));
                    for (var i = 0; i < fields.Length; i++)
                    {
                        code.Push("        .appendField(" + fields[i] + ")");
                    }
                    // Add semicolon to last line to finish the statement.
                    code[code.Length - 1] += ";";
                }
                contentsBlock = contentsBlock.nextConnection == null ? null :
                                contentsBlock.nextConnection.targetBlock();
            }
            // Generate inline/external switch.
            if (rootBlock.getFieldValue("INLINE") == "EXT")
            {
                code.Push("    this.setInputsInline(false);");
            }
            else if (rootBlock.getFieldValue("INLINE") == "INT")
            {
                code.Push("    this.setInputsInline(true);");
            }
            // Generate output, or next/previous connections.
            switch (rootBlock.getFieldValue("CONNECTIONS"))
            {
            case "LEFT":
                code.Push(FactoryUtils.connectionLineJs_("setOutput", "OUTPUTTYPE", workspace));
                break;

            case "BOTH":
                code.Push(
                    FactoryUtils.connectionLineJs_("setPreviousStatement", "TOPTYPE", workspace));
                code.Push(
                    FactoryUtils.connectionLineJs_("setNextStatement", "BOTTOMTYPE", workspace));
                break;

            case "TOP":
                code.Push(
                    FactoryUtils.connectionLineJs_("setPreviousStatement", "TOPTYPE", workspace));
                break;

            case "BOTTOM":
                code.Push(
                    FactoryUtils.connectionLineJs_("setNextStatement", "BOTTOMTYPE", workspace));
                break;
            }
            // Generate colour.
            var colourBlock = rootBlock.getInputTargetBlock("COLOUR");

            if (colourBlock != null && !colourBlock.disabled)
            {
                var hue = Script.ParseFloat(colourBlock.getFieldValue("HUE"));
                if (!Double.IsNaN(hue))
                {
                    code.Push("    this.setColour(" + hue + ");");
                }
            }
            code.Push("    this.setTooltip(\"\");");
            code.Push("    this.setHelpUrl(\"http://www.example.com/\");");
            code.Push("  }");
            code.Push("};");
            return(code.Join("\n"));
        }
Example #9
0
        /// <summary>
        /// Update the language code as JSON.
        /// </summary>
        /// <param name="blockType"> Name of block.</param>
        /// <param  name="rootBlock">Factory_base block.</param>
        /// <returns>Generanted language code.</returns>
        private static string formatJson_(string blockType, Blockly.Block rootBlock)
        {
            var JS = new Dictionary <string, object>();

            // Type is not used by Blockly, but may be used by a loader.
            JS["type"] = blockType;
            // Generate inputs.
            var message       = new JsArray <string>();
            var args          = new JsArray <object>();
            var contentsBlock = rootBlock.getInputTargetBlock("INPUTS");

            Blockly.Block lastInput = null;
            while (contentsBlock != null)
            {
                if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled())
                {
                    var fields = FactoryUtils.getFieldsJson_(
                        contentsBlock.getInputTargetBlock("FIELDS"));
                    for (var i = 0; i < fields.Length; i++)
                    {
                        if (fields[i] is string str)
                        {
                            message.Push(str.Replace(new Regex("%", RegexOptions.Multiline), "%%"));
                        }
                        else
                        {
                            args.Push(fields[i]);
                            message.Push("%" + args.Length);
                        }
                    }

                    var input = new Dictionary <string, object>();
                    input.Add("type", contentsBlock.type);
                    // Dummy inputs don't have names.  Other inputs do.
                    if (contentsBlock.type != "input_dummy")
                    {
                        input["name"] = contentsBlock.getFieldValue("INPUTNAME");
                    }
                    var check = JSON.Parse(
                        FactoryUtils.getOptTypesFrom(contentsBlock, "TYPE") ?? "null");
                    if (check != null)
                    {
                        input["check"] = check;
                    }
                    var align = contentsBlock.getFieldValue("ALIGN");
                    if (align != "LEFT")
                    {
                        input["align"] = align;
                    }
                    args.Push(input);
                    message.Push("%" + args.Length);
                    lastInput = contentsBlock;
                }
                contentsBlock = contentsBlock.nextConnection == null ? null :
                                contentsBlock.nextConnection.targetBlock();
            }
            // Remove last input if dummy and not empty.
            if (lastInput != null && lastInput.type == "input_dummy")
            {
                var fields = lastInput.getInputTargetBlock("FIELDS");
                if (fields != null && FactoryUtils.getFieldsJson_(fields).Join("").Trim() != "")
                {
                    var align = lastInput.getFieldValue("ALIGN");
                    if (align != "LEFT")
                    {
                        JS["lastDummyAlign0"] = align;
                    }
                    args.Pop();
                    message.Pop();
                }
            }
            JS["message0"] = message.Join(" ");
            if (args.Length > 0)
            {
                JS["args0"] = args;
            }
            // Generate inline/external switch.
            if (rootBlock.getFieldValue("INLINE") == "EXT")
            {
                JS["inputsInline"] = false;
            }
            else if (rootBlock.getFieldValue("INLINE") == "INT")
            {
                JS["inputsInline"] = true;
            }
            // Generate output, or next/previous connections.
            switch (rootBlock.getFieldValue("CONNECTIONS"))
            {
            case "LEFT":
                JS["output"] =
                    JSON.Parse(
                        FactoryUtils.getOptTypesFrom(rootBlock, "OUTPUTTYPE") ?? "null");
                break;

            case "BOTH":
                JS["previousStatement"] =
                    JSON.Parse(
                        FactoryUtils.getOptTypesFrom(rootBlock, "TOPTYPE") ?? "null");
                JS["nextStatement"] =
                    JSON.Parse(
                        FactoryUtils.getOptTypesFrom(rootBlock, "BOTTOMTYPE") ?? "null");
                break;

            case "TOP":
                JS["previousStatement"] =
                    JSON.Parse(
                        FactoryUtils.getOptTypesFrom(rootBlock, "TOPTYPE") ?? "null");
                break;

            case "BOTTOM":
                JS["nextStatement"] =
                    JSON.Parse(
                        FactoryUtils.getOptTypesFrom(rootBlock, "BOTTOMTYPE") ?? "null");
                break;
            }
            // Generate colour.
            var colourBlock = rootBlock.getInputTargetBlock("COLOUR");

            if (colourBlock != null && !colourBlock.disabled)
            {
                var hue = Script.ParseFloat(colourBlock.getFieldValue("HUE"));
                JS["colour"] = hue;
            }
            JS["tooltip"] = "";
            JS["helpUrl"] = "http://www.example.com/";
            return(JSON.Stringify(JS, null, "  "));
        }
Example #10
0
        /// <summary>
        /// Add event listeners for workspace factory.
        /// </summary>
        /// <param name="controller"> The controller for the workspace
        /// factory tab.</param>
        private static void addWorkspaceFactoryEventListeners_(WorkspaceFactoryController controller)
        {
            // Use up and down arrow keys to move categories.
            Window.AddEventListener("keydown", new Action <Event>((e) => {
                // Don't let arrow keys have any effect if not in Workspace Factory
                // editing the toolbox.
                if (!(controller.keyEventsEnabled && controller.selectedMode
                      == WorkspaceFactoryController.MODE_TOOLBOX))
                {
                    return;
                }

                if (e.KeyCode == 38)
                {
                    // Arrow up.
                    controller.moveElement(-1);
                }
                else if (e.KeyCode == 40)
                {
                    // Arrow down.
                    controller.moveElement(1);
                }
            }));

            // Determines if a block breaks shadow block placement rules.
            // Breaks rules if (1) a shadow block no longer has a valid
            // parent, or (2) a normal block is inside of a shadow block.
            var isInvalidBlockPlacement = new Func <Blockly.Block, bool>((block) => {
                return((controller.isUserGenShadowBlock(block.id) &&
                        block.getSurroundParent() == null) ||
                       (!controller.isUserGenShadowBlock(block.id) && block.getSurroundParent() != null &&
                        controller.isUserGenShadowBlock(block.getSurroundParent().id)));
            });

            // Add change listeners for toolbox workspace in workspace factory.
            controller.toolboxWorkspace.addChangeListener((e) => {
                // Listen for Blockly move and delete events to update preview.
                // Not listening for Blockly create events because causes the user to drop
                // blocks when dragging them into workspace. Could cause problems if ever
                // load blocks into workspace directly without calling updatePreview.
                if (e.type == Blockly.Events.MOVE || e.type == Blockly.Events.DELETE ||
                    e.type == Blockly.Events.CHANGE)
                {
                    controller.saveStateFromWorkspace();
                    controller.updatePreview();
                }

                // Listen for Blockly UI events to correctly enable the "Edit Block" button.
                // Only enable "Edit Block" when a block is selected and it has a
                // surrounding parent, meaning it is nested in another block (blocks that
                // are not nested in parents cannot be shadow blocks).
                if (e.type == Blockly.Events.MOVE || (e.type == Blockly.Events.UI &&
                                                      ((Blockly.Events.Ui)e).element == "selected"))
                {
                    var selected = Blockly.Core.selected;

                    // Show shadow button if a block is selected. Show "Add Shadow" if
                    // a block is not a shadow block, show "Remove Shadow" if it is a
                    // shadow block.
                    if (selected != null)
                    {
                        var isShadow = controller.isUserGenShadowBlock(selected.id);
                        WorkspaceFactoryInit.displayAddShadow_(!isShadow);
                        WorkspaceFactoryInit.displayRemoveShadow_(isShadow);
                    }
                    else
                    {
                        WorkspaceFactoryInit.displayAddShadow_(false);
                        WorkspaceFactoryInit.displayRemoveShadow_(false);
                    }

                    if (selected != null && selected.getSurroundParent() != null &&
                        !controller.isUserGenShadowBlock(selected.getSurroundParent().id))
                    {
                        // Selected block is a valid shadow block or could be a valid shadow
                        // block.

                        // Enable block editing and remove warnings if the block is not a
                        // variable user-generated shadow block.
                        ((HTMLButtonElement)Document.GetElementById("button_addShadow")).Disabled    = false;
                        ((HTMLButtonElement)Document.GetElementById("button_removeShadow")).Disabled = false;

                        if (!FactoryUtils.hasVariableField(selected) &&
                            controller.isDefinedBlock(selected))
                        {
                            selected.setWarningText(null);
                        }
                    }
                    else
                    {
                        // Selected block cannot be a valid shadow block.

                        if (selected != null && isInvalidBlockPlacement(selected))
                        {
                            // Selected block breaks shadow block rules.
                            // Invalid shadow block if (1) a shadow block no longer has a valid
                            // parent, or (2) a normal block is inside of a shadow block.

                            if (!controller.isUserGenShadowBlock(selected.id))
                            {
                                // Warn if a non-shadow block is nested inside a shadow block.
                                selected.setWarningText("Only shadow blocks can be nested inside\n"
                                                        + "other shadow blocks.");
                            }
                            else if (!FactoryUtils.hasVariableField(selected))
                            {
                                // Warn if a shadow block is invalid only if not replacing
                                // warning for variables.
                                selected.setWarningText("Shadow blocks must be nested inside other"
                                                        + " blocks.");
                            }

                            // Give editing options so that the user can make an invalid shadow
                            // block a normal block.
                            ((HTMLButtonElement)Document.GetElementById("button_removeShadow")).Disabled = false;
                            ((HTMLButtonElement)Document.GetElementById("button_addShadow")).Disabled    = true;
                        }
                        else
                        {
                            // Selected block does not break any shadow block rules, but cannot
                            // be a shadow block.

                            // Remove possible "invalid shadow block placement" warning.
                            if (selected != null && controller.isDefinedBlock(selected) &&
                                (!FactoryUtils.hasVariableField(selected) ||
                                 !controller.isUserGenShadowBlock(selected.id)))
                            {
                                selected.setWarningText(null);
                            }

                            // No block selected that is a shadow block or could be a valid shadow
                            // block. Disable block editing.
                            ((HTMLButtonElement)Document.GetElementById("button_addShadow")).Disabled    = true;
                            ((HTMLButtonElement)Document.GetElementById("button_removeShadow")).Disabled = true;
                        }
                    }
                }

                // Convert actual shadow blocks added from the toolbox to user-generated
                // shadow blocks.
                if (e.type == Blockly.Events.CREATE)
                {
                    controller.convertShadowBlocks();

                    // Let the user create a Variables or Functions category if they use
                    // blocks from either category.

                    // Get all children of a block and add them to childList.
                    Action <Blockly.Block, JsArray <Blockly.Block> > getAllChildren = null;
                    getAllChildren = new Action <Blockly.Block, JsArray <Blockly.Block> >((block, childList) => {
                        childList.Push(block);
                        var children = block.getChildren();
                        for (var i = 0; i < children.Length; i++)
                        {
                            var child = children[i];
                            getAllChildren(child, childList);
                        }
                    });

                    var newBaseBlock = controller.toolboxWorkspace.getBlockById(e.blockId);
                    var allNewBlocks = new JsArray <Blockly.Block>();
                    getAllChildren(newBaseBlock, allNewBlocks);
                    var variableCreated  = false;
                    var procedureCreated = false;

                    // Check if the newly created block or any of its children are variable
                    // or procedure blocks.
                    for (var i = 0; i < allNewBlocks.Length; i++)
                    {
                        var block = allNewBlocks[i];
                        if (FactoryUtils.hasVariableField(block))
                        {
                            variableCreated = true;
                        }
                        else if (FactoryUtils.isProcedureBlock(block))
                        {
                            procedureCreated = true;
                        }
                    }

                    // If any of the newly created blocks are variable or procedure blocks,
                    // prompt the user to create the corresponding standard category.
                    if (variableCreated && !controller.hasVariablesCategory())
                    {
                        if (Window.Confirm("Your new block has a variables field. To use this block "
                                           + "fully, you will need a Variables category. Do you want to add "
                                           + "a Variables category to your custom toolbox?"))
                        {
                            controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
                            controller.loadCategoryByName("variables");
                        }
                    }

                    if (procedureCreated && !controller.hasProceduresCategory())
                    {
                        if (Window.Confirm("Your new block is a function block. To use this block "
                                           + "fully, you will need a Functions category. Do you want to add "
                                           + "a Functions category to your custom toolbox?"))
                        {
                            controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
                            controller.loadCategoryByName("functions");
                        }
                    }
                }
            });
        }