/// <summary> /// Fetch the type(s) defined in the given input. /// </summary> /// <param name="block"> Block with input.</param> /// <param name="name">Name of the input.</param> /// <returns>List of types.</returns> private static JsArray <string> getTypesFrom_(Blockly.Block block, string name) { var typeBlock = block.getInputTargetBlock(name); JsArray <string> types; if (typeBlock == null || typeBlock.disabled) { types = new JsArray <string>(); } else if (typeBlock.type == "type_other") { types = new JsArray <string> { FactoryUtils.escapeString(typeBlock.getFieldValue("TYPE")) }; } else if (typeBlock.type == "type_group") { types = new JsArray <string>(); for (var n = 0; n < ((TypeGroup)typeBlock).typeCount_; n++) { types = types.Concat(FactoryUtils.getTypesFrom_(typeBlock, "TYPE" + n)); } // Remove duplicates. var hash = new Dictionary <string, object>(); for (var n = types.Length - 1; n >= 0; n--) { if (hash.ContainsKey(types[n])) { types.Splice(n, 1); } hash[types[n]] = true; } } else { var fi = typeBlock.GetType().GetField("valueType", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); types = new JsArray <string> { FactoryUtils.escapeString((string)fi.GetValue(null)) }; } return(types); }
/// <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")); }
/// <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, " ")); }