block_exporter_tools.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /**
  2. * @license
  3. * Blockly Demos: Block Factory
  4. *
  5. * Copyright 2016 Google Inc.
  6. * https://developers.google.com/blockly/
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. /**
  21. * @fileoverview Javascript for the BlockExporter Tools class, which generates
  22. * block definitions and generator stubs for given block types. Also generates
  23. * toolbox XML for the exporter's workspace. Depends on the FactoryUtils for
  24. * its code generation functions.
  25. *
  26. * @author quachtina96 (Tina Quach)
  27. */
  28. 'use strict';
  29. goog.provide('BlockExporterTools');
  30. goog.require('FactoryUtils');
  31. goog.require('BlockOption');
  32. goog.require('goog.dom');
  33. goog.require('goog.dom.xml');
  34. /**
  35. * Block Exporter Tools Class
  36. * @constructor
  37. */
  38. BlockExporterTools = function() {
  39. // Create container for hidden workspace.
  40. this.container = goog.dom.createDom('div', {
  41. 'id': 'blockExporterTools_hiddenWorkspace'
  42. }, ''); // Empty quotes for empty div.
  43. // Hide hidden workspace.
  44. this.container.style.display = 'none';
  45. document.body.appendChild(this.container);
  46. /**
  47. * Hidden workspace for the Block Exporter that holds pieces that make
  48. * up the block
  49. * @type {Blockly.Workspace}
  50. */
  51. this.hiddenWorkspace = Blockly.inject(this.container.id,
  52. {collapse: false,
  53. media: '../../media/'});
  54. };
  55. /**
  56. * Get Blockly Block object from XML that encodes the blocks used to design
  57. * the block.
  58. * @param {!Element} xml XML element that encodes the blocks used to design
  59. * the block. For example, the block XMLs saved in block library.
  60. * @return {!Blockly.Block} Root block (factory_base block) which contains
  61. * all information needed to generate block definition or null.
  62. * @private
  63. */
  64. BlockExporterTools.prototype.getRootBlockFromXml_ = function(xml) {
  65. // Render XML in hidden workspace.
  66. this.hiddenWorkspace.clear();
  67. Blockly.Xml.domToWorkspace(xml, this.hiddenWorkspace);
  68. // Get root block.
  69. var rootBlock = this.hiddenWorkspace.getTopBlocks()[0] || null;
  70. return rootBlock;
  71. };
  72. /**
  73. * Return the given language code of each block type in an array.
  74. * @param {!Object} blockXmlMap Map of block type to XML.
  75. * @param {string} definitionFormat 'JSON' or 'JavaScript'
  76. * @return {string} The concatenation of each block's language code in the
  77. * desired format.
  78. */
  79. BlockExporterTools.prototype.getBlockDefinitions =
  80. function(blockXmlMap, definitionFormat) {
  81. var blockCode = [];
  82. for (var blockType in blockXmlMap) {
  83. var xml = blockXmlMap[blockType];
  84. if (xml) {
  85. // Render and get block from hidden workspace.
  86. var rootBlock = this.getRootBlockFromXml_(xml);
  87. if (rootBlock) {
  88. // Generate the block's definition.
  89. var code = FactoryUtils.getBlockDefinition(blockType, rootBlock,
  90. definitionFormat, this.hiddenWorkspace);
  91. // Add block's definition to the definitions to return.
  92. } else {
  93. // Append warning comment and write to console.
  94. var code = '// No block definition generated for ' + blockType +
  95. '. Could not find root block in XML stored for this block.';
  96. console.log('No block definition generated for ' + blockType +
  97. '. Could not find root block in XML stored for this block.');
  98. }
  99. } else {
  100. // Append warning comment and write to console.
  101. var code = '// No block definition generated for ' + blockType +
  102. '. Block was not found in Block Library Storage.';
  103. console.log('No block definition generated for ' + blockType +
  104. '. Block was not found in Block Library Storage.');
  105. }
  106. blockCode.push(code);
  107. }
  108. // Surround json with [] and comma separate items.
  109. if (definitionFormat == "JSON") {
  110. return "[" + blockCode.join(",\n") + "]";
  111. }
  112. return blockCode.join("\n\n");
  113. };
  114. /**
  115. * Return the generator code of each block type in an array in a given language.
  116. * @param {!Object} blockXmlMap Map of block type to XML.
  117. * @param {string} generatorLanguage E.g. 'JavaScript', 'Python', 'PHP', 'Lua',
  118. * 'Dart'
  119. * @return {string} The concatenation of each block's generator code in the
  120. * desired format.
  121. */
  122. BlockExporterTools.prototype.getGeneratorCode =
  123. function(blockXmlMap, generatorLanguage) {
  124. var multiblockCode = [];
  125. // Define the custom blocks in order to be able to create instances of
  126. // them in the exporter workspace.
  127. this.addBlockDefinitions(blockXmlMap);
  128. for (var blockType in blockXmlMap) {
  129. var xml = blockXmlMap[blockType];
  130. if (xml) {
  131. // Render the preview block in the hidden workspace.
  132. var tempBlock =
  133. FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
  134. // Get generator stub for the given block and add to generator code.
  135. var blockGenCode =
  136. FactoryUtils.getGeneratorStub(tempBlock, generatorLanguage);
  137. } else {
  138. // Append warning comment and write to console.
  139. var blockGenCode = '// No generator stub generated for ' + blockType +
  140. '. Block was not found in Block Library Storage.';
  141. console.log('No block generator stub generated for ' + blockType +
  142. '. Block was not found in Block Library Storage.');
  143. }
  144. multiblockCode.push(blockGenCode);
  145. }
  146. return multiblockCode.join("\n\n");
  147. };
  148. /**
  149. * Evaluates block definition code of each block in given object mapping
  150. * block type to XML. Called in order to be able to create instances of the
  151. * blocks in the exporter workspace.
  152. * @param {!Object} blockXmlMap Map of block type to XML.
  153. */
  154. BlockExporterTools.prototype.addBlockDefinitions = function(blockXmlMap) {
  155. var blockDefs = this.getBlockDefinitions(blockXmlMap, 'JavaScript');
  156. eval(blockDefs);
  157. };
  158. /**
  159. * Pulls information about all blocks in the block library to generate XML
  160. * for the selector workpace's toolbox.
  161. * @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
  162. * @return {!Element} XML representation of the toolbox.
  163. */
  164. BlockExporterTools.prototype.generateToolboxFromLibrary
  165. = function(blockLibStorage) {
  166. // Create DOM for XML.
  167. var xmlDom = goog.dom.createDom('xml', {
  168. 'id' : 'blockExporterTools_toolbox',
  169. 'style' : 'display:none'
  170. });
  171. var allBlockTypes = blockLibStorage.getBlockTypes();
  172. // Object mapping block type to XML.
  173. var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes);
  174. // Define the custom blocks in order to be able to create instances of
  175. // them in the exporter workspace.
  176. this.addBlockDefinitions(blockXmlMap);
  177. for (var blockType in blockXmlMap) {
  178. // Get block.
  179. var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
  180. var category = FactoryUtils.generateCategoryXml([block], blockType);
  181. xmlDom.appendChild(category);
  182. }
  183. // If there are no blocks in library and the map is empty, append dummy
  184. // category.
  185. if (Object.keys(blockXmlMap).length == 0) {
  186. var category = goog.dom.createDom('category');
  187. category.setAttribute('name','Next Saved Block');
  188. xmlDom.appendChild(category);
  189. }
  190. return xmlDom;
  191. };
  192. /**
  193. * Generate XML for the workspace factory's category from imported block
  194. * definitions.
  195. * @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
  196. * @return {!Element} XML representation of a category.
  197. */
  198. BlockExporterTools.prototype.generateCategoryFromBlockLib =
  199. function(blockLibStorage) {
  200. var allBlockTypes = blockLibStorage.getBlockTypes();
  201. // Object mapping block type to XML.
  202. var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes);
  203. // Define the custom blocks in order to be able to create instances of
  204. // them in the exporter workspace.
  205. this.addBlockDefinitions(blockXmlMap);
  206. // Get array of defined blocks.
  207. var blocks = [];
  208. for (var blockType in blockXmlMap) {
  209. var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
  210. blocks.push(block);
  211. }
  212. return FactoryUtils.generateCategoryXml(blocks,'Block Library');
  213. };
  214. /**
  215. * Generate selector dom from block library storage. For each block in the
  216. * library, it has a block option, which consists of a checkbox, a label,
  217. * and a fixed size preview workspace.
  218. * @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
  219. * @param {string} blockSelectorId ID of the div element that will contain
  220. * the block options.
  221. * @return {!Object} Map of block type to Block Option object.
  222. */
  223. BlockExporterTools.prototype.createBlockSelectorFromLib =
  224. function(blockLibStorage, blockSelectorId) {
  225. // Object mapping each stored block type to XML.
  226. var allBlockTypes = blockLibStorage.getBlockTypes();
  227. var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes);
  228. // Define the custom blocks in order to be able to create instances of
  229. // them in the exporter workspace.
  230. this.addBlockDefinitions(blockXmlMap);
  231. var blockSelector = document.getElementById(blockSelectorId);
  232. // Clear the block selector.
  233. var child;
  234. while ((child = blockSelector.firstChild)) {
  235. blockSelector.removeChild(child);
  236. }
  237. // Append each block option's dom to the selector.
  238. var blockOptions = Object.create(null);
  239. for (var blockType in blockXmlMap) {
  240. // Get preview block's XML.
  241. var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
  242. var previewBlockXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);
  243. // Create block option, inject block into preview workspace, and append
  244. // option to block selector.
  245. var blockOpt = new BlockOption(blockSelector, blockType, previewBlockXml);
  246. blockOpt.createDom();
  247. blockSelector.appendChild(blockOpt.dom);
  248. blockOpt.showPreviewBlock();
  249. blockOptions[blockType] = blockOpt;
  250. }
  251. return blockOptions;
  252. };