summaryrefslogtreecommitdiff
path: root/blockly/demos/blocklyfactory/factory.js
diff options
context:
space:
mode:
Diffstat (limited to 'blockly/demos/blocklyfactory/factory.js')
-rw-r--r--blockly/demos/blocklyfactory/factory.js265
1 files changed, 265 insertions, 0 deletions
diff --git a/blockly/demos/blocklyfactory/factory.js b/blockly/demos/blocklyfactory/factory.js
new file mode 100644
index 0000000..837d04b
--- /dev/null
+++ b/blockly/demos/blocklyfactory/factory.js
@@ -0,0 +1,265 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * 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 Blockly's Block Factory application through
+ * which users can build blocks using a visual interface and dynamically
+ * generate a preview block and starter code for the block (block definition and
+ * generator stub. Uses the Block Factory namespace. Depends on the FactoryUtils
+ * for its code generation functions.
+ *
+ * @author fraser@google.com (Neil Fraser), quachtina96 (Tina Quach)
+ */
+'use strict';
+
+/**
+ * Namespace for Block Factory.
+ */
+goog.provide('BlockFactory');
+
+goog.require('FactoryUtils');
+goog.require('StandardCategories');
+
+
+/**
+ * Workspace for user to build block.
+ * @type {Blockly.Workspace}
+ */
+BlockFactory.mainWorkspace = null;
+
+/**
+ * Workspace for preview of block.
+ * @type {Blockly.Workspace}
+ */
+BlockFactory.previewWorkspace = null;
+
+/**
+ * Name of block if not named.
+ */
+BlockFactory.UNNAMED = 'unnamed';
+
+/**
+ * Existing direction ('ltr' vs 'rtl') of preview.
+ */
+BlockFactory.oldDir = null;
+
+/*
+ * The starting xml for the Block Factory main workspace. Contains the
+ * unmovable, undeletable factory_base block.
+ */
+BlockFactory.STARTER_BLOCK_XML_TEXT = '<xml><block type="factory_base" ' +
+ 'deletable="false" movable="false"></block></xml>';
+
+/**
+ * Change the language code format.
+ */
+BlockFactory.formatChange = function() {
+ var mask = document.getElementById('blocklyMask');
+ var languagePre = document.getElementById('languagePre');
+ var languageTA = document.getElementById('languageTA');
+ if (document.getElementById('format').value == 'Manual') {
+ Blockly.hideChaff();
+ mask.style.display = 'block';
+ languagePre.style.display = 'none';
+ languageTA.style.display = 'block';
+ var code = languagePre.textContent.trim();
+ languageTA.value = code;
+ languageTA.focus();
+ BlockFactory.updatePreview();
+ } else {
+ mask.style.display = 'none';
+ languageTA.style.display = 'none';
+ languagePre.style.display = 'block';
+ BlockFactory.updateLanguage();
+ }
+ BlockFactory.disableEnableLink();
+};
+
+/**
+ * Update the language code based on constructs made in Blockly.
+ */
+BlockFactory.updateLanguage = function() {
+ var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
+ if (!rootBlock) {
+ return;
+ }
+ var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
+ if (!blockType) {
+ blockType = BlockFactory.UNNAMED;
+ }
+ var format = document.getElementById('format').value;
+ var code = FactoryUtils.getBlockDefinition(blockType, rootBlock, format,
+ BlockFactory.mainWorkspace);
+ FactoryUtils.injectCode(code, 'languagePre');
+ BlockFactory.updatePreview();
+};
+
+/**
+ * Update the generator code.
+ * @param {!Blockly.Block} block Rendered block in preview workspace.
+ */
+BlockFactory.updateGenerator = function(block) {
+ var language = document.getElementById('language').value;
+ var generatorStub = FactoryUtils.getGeneratorStub(block, language);
+ FactoryUtils.injectCode(generatorStub, 'generatorPre');
+};
+
+/**
+ * Update the preview display.
+ */
+BlockFactory.updatePreview = function() {
+ // Toggle between LTR/RTL if needed (also used in first display).
+ var newDir = document.getElementById('direction').value;
+ if (BlockFactory.oldDir != newDir) {
+ if (BlockFactory.previewWorkspace) {
+ BlockFactory.previewWorkspace.dispose();
+ }
+ var rtl = newDir == 'rtl';
+ BlockFactory.previewWorkspace = Blockly.inject('preview',
+ {rtl: rtl,
+ media: '../../media/',
+ scrollbars: true});
+ BlockFactory.oldDir = newDir;
+ }
+ BlockFactory.previewWorkspace.clear();
+
+ // Fetch the code and determine its format (JSON or JavaScript).
+ var format = document.getElementById('format').value;
+ if (format == 'Manual') {
+ var code = document.getElementById('languageTA').value;
+ // If the code is JSON, it will parse, otherwise treat as JS.
+ try {
+ JSON.parse(code);
+ format = 'JSON';
+ } catch (e) {
+ format = 'JavaScript';
+ }
+ } else {
+ var code = document.getElementById('languagePre').textContent;
+ }
+ if (!code.trim()) {
+ // Nothing to render. Happens while cloud storage is loading.
+ return;
+ }
+
+ // Backup Blockly.Blocks object so that main workspace and preview don't
+ // collide if user creates a 'factory_base' block, for instance.
+ var backupBlocks = Blockly.Blocks;
+ try {
+ // Make a shallow copy.
+ Blockly.Blocks = Object.create(null);
+ for (var prop in backupBlocks) {
+ Blockly.Blocks[prop] = backupBlocks[prop];
+ }
+
+ if (format == 'JSON') {
+ var json = JSON.parse(code);
+ Blockly.Blocks[json.type || BlockFactory.UNNAMED] = {
+ init: function() {
+ this.jsonInit(json);
+ }
+ };
+ } else if (format == 'JavaScript') {
+ eval(code);
+ } else {
+ throw 'Unknown format: ' + format;
+ }
+
+ // Look for a block on Blockly.Blocks that does not match the backup.
+ var blockType = null;
+ for (var type in Blockly.Blocks) {
+ if (typeof Blockly.Blocks[type].init == 'function' &&
+ Blockly.Blocks[type] != backupBlocks[type]) {
+ blockType = type;
+ break;
+ }
+ }
+ if (!blockType) {
+ return;
+ }
+
+ // Create the preview block.
+ var previewBlock = BlockFactory.previewWorkspace.newBlock(blockType);
+ previewBlock.initSvg();
+ previewBlock.render();
+ previewBlock.setMovable(false);
+ previewBlock.setDeletable(false);
+ previewBlock.moveBy(15, 10);
+ BlockFactory.previewWorkspace.clearUndo();
+ BlockFactory.updateGenerator(previewBlock);
+
+ // Warn user only if their block type is already exists in Blockly's
+ // standard library.
+ var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
+ if (StandardCategories.coreBlockTypes.indexOf(blockType) != -1) {
+ rootBlock.setWarningText('A core Blockly block already exists ' +
+ 'under this name.');
+
+ } else if (blockType == 'block_type') {
+ // Warn user to let them know they can't save a block under the default
+ // name 'block_type'
+ rootBlock.setWarningText('You cannot save a block with the default ' +
+ 'name, "block_type"');
+
+ } else {
+ rootBlock.setWarningText(null);
+ }
+
+ } finally {
+ Blockly.Blocks = backupBlocks;
+ }
+};
+
+/**
+ * Disable link and save buttons if the format is 'Manual', enable otherwise.
+ */
+BlockFactory.disableEnableLink = function() {
+ var linkButton = document.getElementById('linkButton');
+ var saveBlockButton = document.getElementById('localSaveButton');
+ var saveToLibButton = document.getElementById('saveToBlockLibraryButton');
+ var disabled = document.getElementById('format').value == 'Manual';
+ linkButton.disabled = disabled;
+ saveBlockButton.disabled = disabled;
+ saveToLibButton.disabled = disabled;
+};
+
+/**
+ * Render starter block (factory_base).
+ */
+BlockFactory.showStarterBlock = function() {
+ BlockFactory.mainWorkspace.clear();
+ var xml = Blockly.Xml.textToDom(BlockFactory.STARTER_BLOCK_XML_TEXT);
+ Blockly.Xml.domToWorkspace(xml, BlockFactory.mainWorkspace);
+};
+
+/**
+ * Returns whether or not the current block open is the starter block.
+ */
+BlockFactory.isStarterBlock = function() {
+ var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
+ // The starter block does not have blocks nested into the factory_base block.
+ return !(rootBlock.getChildren().length > 0 ||
+ // The starter block's name is the default, 'block_type'.
+ rootBlock.getFieldValue('NAME').trim().toLowerCase() != 'block_type' ||
+ // The starter block has no connections.
+ rootBlock.getFieldValue('CONNECTIONS') != 'NONE' ||
+ // The starter block has automatic inputs.
+ rootBlock.getFieldValue('INLINE') != 'AUTO');
+};