Files
blockly/demos/blockfactory/block_exporter_tools.js
2017-10-24 14:06:29 -07:00

278 lines
9.9 KiB
JavaScript

/**
* @license
* Blockly Demos: Block Factory
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Javascript for the BlockExporter Tools class, which generates
* block definitions and generator stubs for given block types. Also generates
* toolbox XML for the exporter's workspace. Depends on the FactoryUtils for
* its code generation functions.
*
* @author quachtina96 (Tina Quach)
*/
'use strict';
goog.provide('BlockExporterTools');
goog.require('FactoryUtils');
goog.require('BlockOption');
goog.require('goog.dom');
goog.require('goog.dom.xml');
/**
* Block Exporter Tools Class
* @constructor
*/
BlockExporterTools = function() {
// Create container for hidden workspace.
this.container = goog.dom.createDom('div', {
'id': 'blockExporterTools_hiddenWorkspace'
}, ''); // Empty quotes for empty div.
// Hide hidden workspace.
this.container.style.display = 'none';
document.body.appendChild(this.container);
/**
* Hidden workspace for the Block Exporter that holds pieces that make
* up the block
* @type {Blockly.Workspace}
*/
this.hiddenWorkspace = Blockly.inject(this.container.id,
{collapse: false,
media: '../../media/'});
};
/**
* Get Blockly Block object from XML that encodes the blocks used to design
* the block.
* @param {!Element} xml XML element that encodes the blocks used to design
* the block. For example, the block XMLs saved in block library.
* @return {!Blockly.Block} Root block (factory_base block) which contains
* all information needed to generate block definition or null.
* @private
*/
BlockExporterTools.prototype.getRootBlockFromXml_ = function(xml) {
// Render XML in hidden workspace.
this.hiddenWorkspace.clear();
Blockly.Xml.domToWorkspace(xml, this.hiddenWorkspace);
// Get root block.
var rootBlock = this.hiddenWorkspace.getTopBlocks()[0] || null;
return rootBlock;
};
/**
* Return the given language code of each block type in an array.
* @param {!Object} blockXmlMap Map of block type to XML.
* @param {string} definitionFormat 'JSON' or 'JavaScript'
* @return {string} The concatenation of each block's language code in the
* desired format.
*/
BlockExporterTools.prototype.getBlockDefinitions =
function(blockXmlMap, definitionFormat) {
var blockCode = [];
for (var blockType in blockXmlMap) {
var xml = blockXmlMap[blockType];
if (xml) {
// Render and get block from hidden workspace.
var rootBlock = this.getRootBlockFromXml_(xml);
if (rootBlock) {
// Generate the block's definition.
var code = FactoryUtils.getBlockDefinition(blockType, rootBlock,
definitionFormat, this.hiddenWorkspace);
// Add block's definition to the definitions to return.
} else {
// Append warning comment and write to console.
var code = '// No block definition generated for ' + blockType +
'. Could not find root block in XML stored for this block.';
console.log('No block definition generated for ' + blockType +
'. Could not find root block in XML stored for this block.');
}
} else {
// Append warning comment and write to console.
var code = '// No block definition generated for ' + blockType +
'. Block was not found in Block Library Storage.';
console.log('No block definition generated for ' + blockType +
'. Block was not found in Block Library Storage.');
}
blockCode.push(code);
}
// Surround json with [] and comma separate items.
if (definitionFormat == "JSON") {
return "[" + blockCode.join(",\n") + "]";
}
return blockCode.join("\n\n");
};
/**
* Return the generator code of each block type in an array in a given language.
* @param {!Object} blockXmlMap Map of block type to XML.
* @param {string} generatorLanguage E.g. 'JavaScript', 'Python', 'PHP', 'Lua',
* 'Dart'
* @return {string} The concatenation of each block's generator code in the
* desired format.
*/
BlockExporterTools.prototype.getGeneratorCode =
function(blockXmlMap, generatorLanguage) {
var multiblockCode = [];
// Define the custom blocks in order to be able to create instances of
// them in the exporter workspace.
this.addBlockDefinitions(blockXmlMap);
for (var blockType in blockXmlMap) {
var xml = blockXmlMap[blockType];
if (xml) {
// Render the preview block in the hidden workspace.
var tempBlock =
FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
// Get generator stub for the given block and add to generator code.
var blockGenCode =
FactoryUtils.getGeneratorStub(tempBlock, generatorLanguage);
} else {
// Append warning comment and write to console.
var blockGenCode = '// No generator stub generated for ' + blockType +
'. Block was not found in Block Library Storage.';
console.log('No block generator stub generated for ' + blockType +
'. Block was not found in Block Library Storage.');
}
multiblockCode.push(blockGenCode);
}
return multiblockCode.join("\n\n");
};
/**
* Evaluates block definition code of each block in given object mapping
* block type to XML. Called in order to be able to create instances of the
* blocks in the exporter workspace.
* @param {!Object} blockXmlMap Map of block type to XML.
*/
BlockExporterTools.prototype.addBlockDefinitions = function(blockXmlMap) {
var blockDefs = this.getBlockDefinitions(blockXmlMap, 'JavaScript');
eval(blockDefs);
};
/**
* Pulls information about all blocks in the block library to generate XML
* for the selector workpace's toolbox.
* @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
* @return {!Element} XML representation of the toolbox.
*/
BlockExporterTools.prototype.generateToolboxFromLibrary
= function(blockLibStorage) {
// Create DOM for XML.
var xmlDom = goog.dom.createDom('xml', {
'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);
for (var blockType in blockXmlMap) {
// Get block.
var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
var category = FactoryUtils.generateCategoryXml([block], blockType);
xmlDom.appendChild(category);
}
// 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');
};
/**
* 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.
* @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
* @param {string} blockSelectorId ID of the div element that will contain
* the block options.
* @return {!Object} Map of block type to Block Option object.
*/
BlockExporterTools.prototype.createBlockSelectorFromLib =
function(blockLibStorage, 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.
var child;
while ((child = blockSelector.firstChild)) {
blockSelector.removeChild(child);
}
// Append each block option's dom to the selector.
var blockOptions = Object.create(null);
for (var blockType in blockXmlMap) {
// 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;
};