Help! I've spend countless hours building a dynamic version of blockly (blocks are created based on response data from an http request to get API commands). The XML is dynamically appended to the DOM with JavaScript and the block fields are populated based on API command parameters. Everything works swimmingly EXCEPT, when you go to dispose of a group of blocks that have been dragged into the workspace, blockly barfs. The reason seems to be that, when iterating through the blocks and nulling their workspaces blockly.prototype.dispose, blockly checks to make sure that the topBlock of the workspace of each block is the same as the block itself, but it finds that the topBlock is = [ ]. I don't have any idea why this is happening and it is a dealbreaker.
function getCommandNames(result) { if (result !== "") { let postCommands = [], getCommands = []; let theDataObject = JSON.parse(result); $.each(theDataObject.Post, function (i, com) { if ($.inArray(com, postCommands) === -1) { postCommands.push(com) } }); $.each(theDataObject.Get, function (i, com) { if ($.inArray(com, getCommands) === -1) { getCommands.push(com) } }); let allCommandsOnRobot = postCommands.concat(getCommands); let postCommandInfo = [], getCommandInfo = [], commands = []; for (let index of allCommandsOnRobot) { !notYetImplemented.includes(index) ? commands.push(index) : null; } getArguments(commands, postCommandInfo, getCommandInfo); } } function getArguments(commands, postCommandInfo, getCommandInfo) { if (commands.length) { let endpoint = "info/help?command=" + commands[0]; sendGetRequestToRobot(endpoint, ip, function (result) { let theDataObject = JSON.parse(result); if (theDataObject.Post) { postCommandInfo.push(theDataObject.Post[0]) }; if (theDataObject.Get) { getCommandInfo.push(theDataObject.Get[0]) }; commands.shift(); getArguments(commands, postCommandInfo, getCommandInfo); }); } else { constructCategories(postCommandInfo, getCommandInfo); } }; //#generate categories (aka tabs) from robot function constructCategories(postCommandInfo, getCommandInfo) { var commandGroupOfEachPostCommand = postCommandInfo.map(x => x.ApiCommand.ApiCommandGroup); var commandGroupOfEachGetCommand = getCommandInfo.map(x => x.ApiCommand.ApiCommandGroup) const postCommandGroups = {}; const getCommandGroups = {}; for (const key of commandGroupOfEachPostCommand) { !postCommandGroups[key] ? postCommandGroups[key] = new Array : null; !getCommandGroups[key] ? getCommandGroups[key] = new Array : null; }; var categoryArray = Object.keys(postCommandGroups); for (var i = 0; i < categoryArray.length; i++) { for (var j = 0; j < postCommandInfo.length; j++) { postCommandInfo[j].ApiCommand.ApiCommandGroup === categoryArray[i] ? postCommandGroups[categoryArray[i]].push(postCommandInfo[j]) : null }; for (var n = 0; n < getCommandInfo.length; n++) { getCommandInfo[n].ApiCommand.ApiCommandGroup === categoryArray[i] ? getCommandGroups[categoryArray[i]].push(getCommandInfo[n]) : null }; }; constructCommandObjects(getCommandGroups, postCommandGroups, categoryArray); } function constructCommandObjects(getCommandGroups, postCommandGroups, categories) { let postCommandObjects = []; getCommandObjects = [] for (var k = 0; k < categories.length; k++) { let completePostCategory = []; let completeGetCategory = []; let postGroup = postCommandGroups[categories[k]] let getGroup = getCommandGroups[categories[k]] for (var x = 0; x < postGroup.length; x++) { let command = { "Name": postGroup[x].ApiCommand.Name, "Endpoint": postGroup[x].Endpoint, "Arguments": postGroup[x].ApiCommand.Arguments, } completePostCategory.push(command); } for (var z = 0; z < getGroup.length; z++) { let command = { "Name": getGroup[z].ApiCommand.Name, "Endpoint": getGroup[z].Endpoint, "Arguments": getGroup[z].ApiCommand.Arguments, } completeGetCategory.push(command); } if (categories[k] === "Display") { completePostCategory.push({ "Name": "BrowseToImageFile", "Arguments": null }) } if (categories[k] === "Speakers") { completePostCategory.push({ "Name": "BrowseToAudioFile", "Arguments": null }) } postCommandObjects.push(completePostCategory); getCommandObjects.push(completeGetCategory); } updateMyBlocks(categories, postCommandObjects, getCommandObjects); } function updateMyBlocks(categories, postCommandObjects, getCommandObjects) { var colours = ["#745CA6", "#FBBD0B", "#D11149", "#4285F4", "#745CA6", "#E6C229", "#F17105", "#1A8FE3", "#6B09EE"]; var blocklyColours = [260, 46, 344, 218, 260, 48, 28, 206, 266]; //start appending category tags to #toolbox for (var y = 0; y < categories.length; y++) { let getBlocks = getCommandObjects[y]; let postBlocks = postCommandObjects[y]; var MyBlocks = document.createElement("category"); My.setAttribute("name", categories[y]); My.setAttribute("colour", colours[y]); for (var i = 0; i < getBlocks.length; i++) { addBlock(getBlocks[i], MyBlocks, blocklyColours[y], "GET"); } for (var m = 0; m < postBlocks.length; m++) { addBlock(postBlocks[m], MyBlocks, blocklyColours[y], "POST"); } toolbox.appendChild(MyBlocks); } workspace.updateToolbox(toolbox); } //#Add blocks and tabs to UI function addBlock(block, MyBlocks, colour, httpVerb) { var blockName = block.Name.replace("Api.", ""); var endpoint = block.Endpoint; var legacyCommands = Object.keys(hardCodedCommands); var commandArguments = block.Arguments; var argsAsStrings = commandArguments ? Object.keys(commandArguments) : ""; var parameters = Array.from(argsAsStrings, x => commandArguments[x]); var xmlFields = []; var myBlock = document.createElement("block"); myBlock.setAttribute("type", blockName); myBlock.setAttribute("disabled", false); My.appendChild(myBlock); if (!arrayContains(legacyCommands, blockName)) { Blockly.Blocks[blockName] = { init: function () { this.setColour(colour); var dummy = this.appendDummyInput(); dummy.appendField(blockName); for (var k = 0; k < argsAsStrings.length; k++) { var argDetails = parameters[k]; dummy.appendField(argDetails.Name); let fieldValue = "FIELD_" + blockName + "_" + argsAsStrings[k]; let typeInfo = argDetails.GetValueType; let type = typeInfo.substring(7, typeInfo.indexOf(",")); let fieldTypes = blocklyTypes(argDetails.Value, -100, 100); let fieldType = fieldTypes[type]; dummy.appendField(fieldType, fieldValue); addFieldsToBlock(myBlock, xmlFields, fieldValue); } this.setPreviousStatement(true, null); this.setNextStatement(true, null); this.setOutput(true); this.setTooltip(blockName); } } Blockly.JavaScript[blockName] = function (block) { const payload = {}; var httpRequest; for (const arg of argsAsStrings) { var input = parseInt(block.getFieldValue("FIELD_" + blockName + "_" + arg)); payload[arg] = input; }; var code; console.log(payload); if (httpVerb === "GET") { code = "sendGetRequestToRobot(\"" + endpoint + "\",\"" + ip + "\");"; } else { code = "sendPostRequestToRobot(\"" + endpoint + "\",\"" + ip + "\"," + JSON.stringify(payload) + ");"; } return code; }; } else { legacyBlocks(block, My, blockName, myBlock, xmlFields, argsAsStrings, colour, endpoint); }
The XML is dynamically appended to the DOM with JavaScript and the block fields are populated based on API command parameters
--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
var toolbox = document.getElementById('toolbox')
var workspace = Blockly.inject(BlocklyDiv, options).
var BlocklyDiv = document.getElementById('blocklyDiv')
var options = {toolbox: toolbox, collapse: true, etc}
--
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
console.log('#1', options);var toolbox = "I'm a toolbox"console.log('#2', options);
var options = {toolbox: toolbox, collapse: true}
console.log('#3', options);
#1 undefined#2 undefined#3 {toolbox: "I'm a toolbox", collapse: true}
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.
I get "Uncaught Block not present in workspace's list of top-most blocks." Do you know anything about what causes that error?
when I go to delete more than one block at a time...
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+unsubscribe@googlegroups.com.
I get "Uncaught Block not present in workspace's list of top-most blocks." Do you know anything about what causes that error?
when I go to delete more than one block at a time...
<div id="blocklyArea" class="container-fluid" style="height:95%; position: relative;"> <div id="blocklyDiv" class="col-xs-12" style="height:100%;"> </div> </div> <xml id="toolbox" style="display:none;"><category name="Blockly Blocks" colour="#4285F4"> <block type="controls_if"></block> <block type="logic_compare"> <field name="OP">EQ</field> </block>
</category></xml> <script type="text/javascript" src="Content/Blockly/js/blockly_compressed.js"></script> <script type="text/javascript" src="Scripts/javascript_compressed.js"></script> <script type="text/javascript" src="Content/Blockly/js/blocks_compressed.js"></script> <script type="text/javascript" src="Content/Misty/js/misty_helper_functions.js"></script> <script type="text/javascript" src="Content/Misty/js/misty_ajax.js"></script> <script type="text/javascript" src="Content/Misty/js/misty_blocks.js"></script> <script type="text/javascript" src="Content/Blockly/js/msg/en.js"></script> <script type="text/javascript" src="Content/Blockly/js/msg/messages.js"></script> <script type="text/javascript" src="Content/Blockly/js/acorn_interpreter.js"></script> <script type="text/javascript" src="Content/Misty/js/misty_index_setup.js"></script>
$(document).ready(function () { $("#connect-to-robot").on('click'
, addressChanged); $("#abort-script").on("click", abortFunction); $("#show-javascript").on('click', showJavaScript); $("#export-session").on('click', exportBlocklySession); $("#run-script").on('click', runJavaScript); $("#browse-files").on('click', openFilePicker);
var onresize = function (e) { // Compute the absolute coordinates and dimensions of blocklyArea. var element = blocklyArea; var x = 0; var y = 0; do { x += element.offsetLeft; y += element.offsetTop; element = element.offsetParent; } while (element); // Position blocklyDiv over blocklyArea. blocklyDiv.style.left = x + 'px'; blocklyDiv.style.top = y + 'px'; blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; }; window.addEventListener('resize', onresize, false); onresize(); Blockly.svgResize(workspace);
$(document).ready(function () { $("#connect-to-robot").on('click'
, addressChanged); $("#abort-script").on("click", abortFunction); $("#show-javascript").on('click', showJavaScript); $("#export-session").on('click', exportBlocklySession); $("#run-script").on('click', runJavaScript); $("#browse-files").on('click', openFilePicker);
var onresize = function (e) { // Compute the absolute coordinates and dimensions of blocklyArea. var element = blocklyArea; var x = 0; var y = 0; do { x += element.offsetLeft; y += element.offsetTop; element = element.offsetParent; } while (element); // Position blocklyDiv over blocklyArea. blocklyDiv.style.left = x + 'px'; blocklyDiv.style.top = y + 'px'; blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; }; window.addEventListener('resize', onresize, false); onresize(); Blockly.svgResize(workspace);
$(document).ready(function () { $("#connect-to-robot").on('click'
, addressChanged); $("#abort-script").on("click", abortFunction); $("#show-javascript").on('click', showJavaScript); $("#export-session").on('click', exportBlocklySession); $("#run-script").on('click', runJavaScript); $("#browse-files").on('click', openFilePicker);
var onresize = function (e) { // Compute the absolute coordinates and dimensions of blocklyArea. var element = blocklyArea; var x = 0; var y = 0; do { x += element.offsetLeft; y += element.offsetTop; element = element.offsetParent; } while (element); // Position blocklyDiv over blocklyArea. blocklyDiv.style.left = x + 'px'; blocklyDiv.style.top = y + 'px'; blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; }; window.addEventListener('resize', onresize, false); onresize(); Blockly.svgResize(workspace);
function addressChanged(e) { //connect to robot, get capabilities e.preventDefault(); e.stopPropagation(); listOfEnabledBlocks = []; capabilities = []; audioAssetsCallMade = false; var input = $("#ip-address").val(); try { updateBlockValidity(input); } catch (err) { console.log("Error updating block validity:" + err); } }; function updateBlockValidity(input) { ip = validateIPAddress(input);
if (ip !== "") { $(connectingAnimation).prependTo('#blocklyDiv'
); sendGetRequestToRobot("info/help", ip, getCommandNames); } } function validateIPAddress(input) { // right now we only except ip addresses. var ipNumbers = input.split('.'); var ipNums = new Array(4); var ip = ""; if (ipNumbers.length !== 4) { console.error("IP Address needs to be in the format of ###.###.###.### where ### is a number between 0-255."); return ""; } for (let i = 0; i < 4; i++) { ipNums[i] = parseInt(ipNumbers[i]); if (ipNums[i] < 0 || ipNums[i] > 255) { console.error("IP Address needs to be in the format of ###.###.###.### where ### is a number between 0-255."); return ""; } } ip = ipNums.join('.'); return ip; };
function getCommandNames(result) { if (result !== ""
) { let allCommandsOnRobot = []; let theDataObject = JSON.parse(result); $.each(theDataObject, function (i, com) { if ($.inArray(com, allCommandsOnRobot) === -1) { allCommandsOnRobot.push(com) } }); let CommandInfo = [], commands = [];
for (let index of allCommandsOnRobot) { !notYetImplemented.includes(index) ? commands.push(index) : null
;
}
getArguments(commands, CommandInfo);
}
}
function getArguments(commands, CommandInfo) {
if (commands.length) { let endpoint = "info/help?command=" + commands[0]; sendGetRequestToRobot(endpoint, ip, function (result) { let theDataObject =
JSON.parse(result); commands.shift(); getArguments(commands, CommandInfo); }); } else { $("#connecting-animation").remove(); $("#connect-to-robot").html("Connected").removeClass("disconnected").addClass("connected"); monitorConnectionStatus = setInterval(function () { sendGetRequestToRobot("info/device", ip) }, 2000); enableButtons(); constructCategories(CommandInfo); } };
//#generate categories (aka tabs) from robot
function constructCategories(CommandInfo) { var commandGroupOfEachCommand = CommandInfo.map(x => x.ApiCommand.ApiCommandGroup) const CommandGroups = {}; for (const key of commandGroupOfEachCommand) { !CommandGroups[key] ? CommandGroups[key] = new Array : null; }; var categoryArray = Object.keys(postCommandGroups); for (var i = 0; i < categoryArray.length; i++) { for (var j = 0; j < CommandInfo.length; j++) { CommandInfo[j].ApiCommand.ApiCommandGroup === categoryArray[i] ? CommandGroups[categoryArray[i]].push(CommandInfo[j]) : null }; }; constructCommandObjects(CommandGroups, categoryArray); } function constructCommandObjects(CommandGroups, categories) { let CommandObjects = [];
for (var k = 0; k < categories.length; k++
) { let completeCategory = []; let Group = CommandGroups[categories[k]] for (var x = 0; x < Group.length; x++) {
let command = { "Name": postGroup[x].ApiCommand.Name, "Endpoint": postGroup[x].Endpoint, "Arguments"
: postGroup[x].ApiCommand.Arguments, } completeCategory.push(command); } if (categories[k] === "Stuff") { completeCategory.push({ "Name": "abc", "Arguments": null }) } if (categories[k] === "Other Stuff") { completeCategory.push({ "Name": "xyz", "Arguments": null }) } CommandObjects.push(completeCategory); } updateMistyBlocks(categories, CommandObjects); } function updateMistyBlocks(categories, CommandObjects) {
var colours = ["#745CA6", "#FBBD0B", "#D11149", "#4285F4", "#745CA6", "#E6C229", "#F17105", "#1A8FE3", "#6B09EE"]; var blocklyColours = [260, 46, 344, 218, 260, 48, 28, 206, 266
]; while (toolbox.childNodes[3]) { toolbox.removeChild(toolbox.childNodes[3]); }
//start appending category tags to #toolbox for (var y = 0; y < categories.length; y++
) {
Blocks = CommandObjects[y];
var Misty = document.createElement("category"); Misty.setAttribute("name", categories[y]); Misty.setAttribute("colour"
, colours[y]); for (var i = 0; i < Blocks.length; i++) { addBlock(Blocks[i], Misty, blocklyColours[y]); toolbox.appendChild(Misty); } workspace.updateToolbox(toolbox); }
//#Add blocks and tabs to UI
function addBlock(block, Misty, colour) { var blockName = block.Name;
var endpoint = block.Endpoint; var legacyCommands = Object.keys(hardCodedCommands); var commandArguments = block.Arguments; var argsAsStrings = commandArguments ? Object.keys(commandArguments) : ""; var parameters = Array.from(argsAsStrings, x => commandArguments[x]); var xmlFields =
[];
var mistyBlock = document.createElement("block"); mistyBlock.setAttribute("type", blockName); mistyBlock.setAttribute("disabled", false
); Misty.appendChild(mistyBlock); if (!arrayContains(legacyCommands, blockName)) { Blockly.Blocks[blockName] = { init: function () {
this.setColour(colour); var dummy = this
.appendDummyInput(); dummy.appendField(blockName);
for (var k = 0; k < argsAsStrings.length; k++) { var argDetails =
parameters[k]; dummy.appendField(argDetails.Name);
let fieldValue = "FIELD_" + blockName + "_" +
argsAsStrings[k];
let typeInfo = argDetails.GetValueType; let type = typeInfo.substring(7, typeInfo.indexOf(",")); let fieldTypes = blocklyTypes(argDetails.Value, -100, 100); let fieldType =
fieldTypes[type]; dummy.appendField(fieldType, fieldValue); addFieldsToBlock(mistyBlock, xmlFields, fieldValue); }
this.setPreviousStatement(true, null); this.setNextStatement(true, null); this.setOutput(true); this.setTooltip(blockName); } } Blockly.JavaScript[blockName] = function (block) { const payload = {}; var httpRequest; for (const arg of argsAsStrings) { var input = parseInt(block.getFieldValue("FIELD_" + blockName + "_" + arg)); payload[arg] = input; }; var code; console.log(payload); if (httpVerb === "GET") { code = "sendGetRequestToRobot(\"" + endpoint + "\",\"" + ip + "\");"; } else { code = "sendPostRequestToRobot(\"" + endpoint + "\",\"" + ip + "\"," + JSON.stringify(payload) + ");"; } return
code;
};
} else {
legacyBlocks(block, Misty, blockName, mistyBlock, xmlFields, argsAsStrings, colour, endpoint);
}
};
$(document).ready(function () { $("#connect-to-robot").on('click'
, addressChanged); $("#abort-script").on("click", abortFunction); $("#show-javascript").on('click', showJavaScript); $("#export-session").on('click', exportBlocklySession); $("#run-script").on('click', runJavaScript); $("#browse-files").on('click', openFilePicker);
var toolbox = document.getElementById('toolbox'); var blocklyArea = document.getElementById('blocklyArea'); var blocklyDiv = document.getElementById('blocklyDiv'); });
Andrew,