mirror of
https://github.com/google/blockly.git
synced 2026-01-10 02:17:09 +01:00
Blockly Factory: Generate Category Xml (#552)
* generate category xml from block library and from imported block defs * simplified algorithms for parsing block definition and cleaned up style * refactored getCategoryFromBlockDefs, breaking it up and moving it to FactoryUtils * refactored getCategoryXml, fixed bug in updatingToolBox of exporter * removed unneeded function, added quick check for empty library * nit comments
This commit is contained in:
@@ -177,13 +177,27 @@ BlockExporterController.prototype.updateToolbox = function(opt_toolboxXml) {
|
||||
// Use given xml or xml generated from updated block library.
|
||||
var updatedToolbox = opt_toolboxXml ||
|
||||
this.tools.generateToolboxFromLibrary(this.blockLibStorage);
|
||||
|
||||
// Update the view's toolbox.
|
||||
this.view.setToolbox(updatedToolbox);
|
||||
|
||||
// Render the toolbox in the selector workspace.
|
||||
this.view.renderToolbox(updatedToolbox);
|
||||
|
||||
// Do not try to disable any selected blocks deleted from the block library.
|
||||
// Instead, deselect them.
|
||||
var selectedBlocks = this.view.getSelectedBlocks();
|
||||
var updatedSelectedBlocks = [];
|
||||
for (var i = 0, selectedBlock; selectedBlock = selectedBlocks[i]; i++) {
|
||||
if (this.blockLibStorage[selectedBlock.type]) {
|
||||
updatedSelectedBlocks.push(selectedBlock);
|
||||
} else {
|
||||
this.view.removeBlock(selectedBlock);
|
||||
}
|
||||
}
|
||||
// Disable any selected blocks.
|
||||
var selectedBlocks = this.getSelectedBlockTypes_();
|
||||
for (var i = 0, blockType; blockType = selectedBlocks[i]; i++) {
|
||||
var selectedBlockTypes = this.getSelectedBlockTypes_();
|
||||
for (var i = 0, blockType; blockType = selectedBlockTypes[i]; i++) {
|
||||
this.setBlockEnabled(blockType, false);
|
||||
}
|
||||
};
|
||||
@@ -262,8 +276,11 @@ BlockExporterController.prototype.onDeselectBlockForExport_ = function(event) {
|
||||
// Get type of block created.
|
||||
var deletedBlockXml = event.oldXml;
|
||||
var blockType = deletedBlockXml.getAttribute('type');
|
||||
// Enable the deselected block.
|
||||
this.setBlockEnabled(blockType, true);
|
||||
// Do not try to enable any blocks deleted from the block library.
|
||||
if (this.blockLibStorage[blockType]) {
|
||||
// Enable the deselected block.
|
||||
this.setBlockEnabled(blockType, true);
|
||||
}
|
||||
// Show currently selected blocks in helper text.
|
||||
this.view.listSelectedBlocks(this.getSelectedBlockTypes_());
|
||||
}
|
||||
@@ -300,3 +317,12 @@ BlockExporterController.prototype.addAllBlocksToWorkspace = function() {
|
||||
// Clean up workspace.
|
||||
this.view.cleanUpSelectorWorkspace();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the category xml containing all blocks in the block library.
|
||||
*
|
||||
* @return {Element} Xml for a category to be used in toolbox.
|
||||
*/
|
||||
BlockExporterController.prototype.getBlockLibCategory = function() {
|
||||
return this.tools.generateCategoryFromBlockLib(this.blockLibStorage);
|
||||
};
|
||||
|
||||
@@ -188,25 +188,45 @@ BlockExporterTools.prototype.generateToolboxFromLibrary
|
||||
this.addBlockDefinitions(blockXmlMap);
|
||||
|
||||
for (var blockType in blockXmlMap) {
|
||||
// Create category DOM element.
|
||||
var categoryElement = goog.dom.createDom('category');
|
||||
categoryElement.setAttribute('name',blockType);
|
||||
|
||||
// Get block.
|
||||
var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
|
||||
|
||||
// Get preview block XML.
|
||||
var blockChild = Blockly.Xml.blockToDom(block);
|
||||
blockChild.removeAttribute('id');
|
||||
|
||||
// Add block to category and category to XML.
|
||||
categoryElement.appendChild(blockChild);
|
||||
xmlDom.appendChild(categoryElement);
|
||||
var category = FactoryUtils.generateCategoryXml([block], blockType);
|
||||
xmlDom.appendChild(category);
|
||||
}
|
||||
|
||||
// If there are no blocks in library, append dummy category.
|
||||
var categoryElement = goog.dom.createDom('category');
|
||||
categoryElement.setAttribute('name','Next Saved Block');
|
||||
xmlDom.appendChild(categoryElement);
|
||||
// If there are no blocks in library and the map is empty, append dummy
|
||||
// category.
|
||||
if (Object.keys(blockXmlMap).length == 0) {
|
||||
var category = goog.dom.createDom('category');
|
||||
category.setAttribute('name','Next Saved Block');
|
||||
xmlDom.appendChild(category);
|
||||
}
|
||||
return xmlDom;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate xml for the workspace factory's category from imported block
|
||||
* definitions.
|
||||
*
|
||||
* @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object.
|
||||
* @return {!Element} Xml representation of a category.
|
||||
*/
|
||||
BlockExporterTools.prototype.generateCategoryFromBlockLib =
|
||||
function(blockLibStorage) {
|
||||
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);
|
||||
|
||||
// Get array of defined blocks.
|
||||
var blocks = [];
|
||||
for (var blockType in blockXmlMap) {
|
||||
var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
|
||||
blocks.push(block);
|
||||
}
|
||||
|
||||
return FactoryUtils.generateCategoryXml(blocks,'Block Library');
|
||||
};
|
||||
|
||||
@@ -119,6 +119,16 @@ BlockExporterView.prototype.addBlock = function(blockType) {
|
||||
newBlock.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a block from the selector workspace.
|
||||
*
|
||||
* @param {!Blockly.Block} block - Type of block to add to selector workspce.
|
||||
*/
|
||||
BlockExporterView.prototype.removeBlock = function(block) {
|
||||
block.dispose();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears selector workspace.
|
||||
*/
|
||||
|
||||
@@ -730,3 +730,145 @@ FactoryUtils.getDefinedBlock = function(blockType, workspace) {
|
||||
workspace.clear();
|
||||
return workspace.newBlock(blockType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a block definition get the type of the block it defines.
|
||||
*
|
||||
* @param {!string} blockDef - A single block definition.
|
||||
* @return {string} Type of block defined by the given definition.
|
||||
*/
|
||||
FactoryUtils.getBlockTypeFromJsDef = function(blockDef) {
|
||||
var indexOfStartBracket = blockDef.indexOf('[\'');
|
||||
var indexOfEndBracket = blockDef.indexOf('\']');
|
||||
if (indexOfStartBracket != -1 && indexOfEndBracket != -1) {
|
||||
return blockDef.substring(indexOfStartBracket + 2, indexOfEndBracket);
|
||||
} else {
|
||||
throw new Error ('Could not parse block type out of JavaScript block ' +
|
||||
'definition. Brackets normally enclosing block type not found.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a category containing blocks of the specified block types.
|
||||
*
|
||||
* @param {!Array.<Blockly.Block>} blocks - Blocks to include in the category.
|
||||
* @param {string} categoryName - Name to use for the generated category.
|
||||
* @return {Element} - Category xml containing the given block types.
|
||||
*/
|
||||
FactoryUtils.generateCategoryXml = function(blocks, categoryName) {
|
||||
// Create category DOM element.
|
||||
var categoryElement = goog.dom.createDom('category');
|
||||
categoryElement.setAttribute('name', categoryName);
|
||||
|
||||
// For each block, add block element to category.
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
|
||||
// Get preview block XML.
|
||||
var blockXml = Blockly.Xml.blockToDom(block);
|
||||
blockXml.removeAttribute('id');
|
||||
|
||||
// Add block to category and category to XML.
|
||||
categoryElement.appendChild(blockXml);
|
||||
}
|
||||
return categoryElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a string containing JavaScript block definition(s) to create an array
|
||||
* in which each element is a single block definition.
|
||||
*
|
||||
* @param {!string} blockDefsString - JavaScript block definition(s).
|
||||
* @return {!Array.<string>} - Array of block definitions.
|
||||
*/
|
||||
FactoryUtils.parseJsBlockDefs = function(blockDefsString) {
|
||||
var blockDefArray = [];
|
||||
var defStart = blockDefsString.indexOf('Blockly.Blocks');
|
||||
|
||||
while (blockDefsString.indexOf('Blockly.Blocks', defStart) != -1) {
|
||||
var nextStart = blockDefsString.indexOf('Blockly.Blocks', defStart + 1);
|
||||
if (nextStart == -1) {
|
||||
// This is the last block definition.
|
||||
nextStart = blockDefsString.length;
|
||||
}
|
||||
var blockDef = blockDefsString.substring(defStart, nextStart);
|
||||
blockDefArray.push(blockDef);
|
||||
defStart = nextStart;
|
||||
}
|
||||
return blockDefArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a string containing JSON block definition(s) to create an array
|
||||
* in which each element is a single block definition. Expected input is
|
||||
* one or more block definitions in the form of concatenated, stringified
|
||||
* JSON objects.
|
||||
*
|
||||
* @param {!string} blockDefsString - String containing JSON block
|
||||
* definition(s).
|
||||
* @return {!Array.<string>} - Array of block definitions.
|
||||
*/
|
||||
FactoryUtils.parseJsonBlockDefs = function(blockDefsString) {
|
||||
var blockDefArray = [];
|
||||
var unbalancedBracketCount = 0;
|
||||
var defStart = 0;
|
||||
// Iterate through the blockDefs string. Keep track of whether brackets
|
||||
// are balanced.
|
||||
for (var i = 0; i < blockDefsString.length; i++) {
|
||||
var currentChar = blockDefsString[i];
|
||||
if (currentChar == '{') {
|
||||
unbalancedBracketCount++;
|
||||
}
|
||||
else if (currentChar == '}') {
|
||||
unbalancedBracketCount--;
|
||||
if (unbalancedBracketCount == 0 && i > 0) {
|
||||
// The brackets are balanced. We've got a complete block defintion.
|
||||
var blockDef = blockDefsString.substring(defStart, i + 1);
|
||||
blockDefArray.push(blockDef);
|
||||
defStart = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return blockDefArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* Define blocks from imported block definitions.
|
||||
*
|
||||
* @param {!string} blockDefsString - Block definition(s).
|
||||
* @param {!string} format - Block definition format ('JSON' or 'JavaScript').
|
||||
* @return {!Element} Array of block types defined.
|
||||
*/
|
||||
FactoryUtils.defineAndGetBlockTypes = function(blockDefsString, format) {
|
||||
var blockTypes = [];
|
||||
|
||||
// Define blocks and get block types.
|
||||
if (format == 'JSON') {
|
||||
var blockDefArray = FactoryUtils.parseJsonBlockDefs(blockDefsString);
|
||||
|
||||
// Populate array of blocktypes and define each block.
|
||||
for (var i = 0, blockDef; blockDef = blockDefArray[i]; i++) {
|
||||
var json = JSON.parse(blockDef);
|
||||
blockTypes.push(json.type);
|
||||
|
||||
// Define the block.
|
||||
Blockly.Blocks[json.type] = {
|
||||
init: function() {
|
||||
this.jsonInit(json);
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if (format == 'JavaScript') {
|
||||
var blockDefArray = FactoryUtils.parseJsBlockDefs(blockDefsString);
|
||||
|
||||
// Populate array of block types.
|
||||
for (var i = 0, blockDef; blockDef = blockDefArray[i]; i++) {
|
||||
var blockType = FactoryUtils.getBlockTypeFromJsDef(blockDef);
|
||||
blockTypes.push(blockType);
|
||||
}
|
||||
|
||||
// Define all blocks.
|
||||
eval(blockDefsString);
|
||||
}
|
||||
|
||||
return blockTypes;
|
||||
};
|
||||
Reference in New Issue
Block a user