- This is a demo of a version of Blockly designed for screen readers,
- optimized for NVDA on Firefox. It allows users to create programs in a
- workspace by manipulating groups of blocks.
-
-
To explore a group of blocks, use the arrow keys.
-
To navigate between groups, use Tab or Shift-Tab.
-
To add new blocks, use the buttons in the menu on the right.
-
To delete or add links to existing blocks, press Enter while you're on that block.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 10
-
-
-
-
-
-
-
- 1
-
-
-
-
- 10
-
-
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
- 1
-
-
-
-
-
-
- 9
-
-
-
-
-
-
- 45
-
-
-
-
-
-
-
- 0
-
-
-
-
-
-
- 1
-
-
-
-
-
-
- 3.1
-
-
-
-
-
-
-
- 64
-
-
-
-
- 10
-
-
-
-
-
-
- 50
-
-
-
-
- 1
-
-
-
-
- 100
-
-
-
-
-
-
- 1
-
-
-
-
- 100
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- abc
-
-
-
-
-
-
-
-
-
-
-
-
-
- text
-
-
-
-
- abc
-
-
-
-
-
-
- text
-
-
-
-
-
-
- text
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
-
-
-
-
-
-
- 5
-
-
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- ,
-
-
-
-
-
-
-
-
diff --git a/trunk/web/blockly/demos/blockfactory/app_controller.js b/trunk/web/blockly/demos/blockfactory/app_controller.js
deleted file mode 100644
index ac69780b264..00000000000
--- a/trunk/web/blockly/demos/blockfactory/app_controller.js
+++ /dev/null
@@ -1,735 +0,0 @@
-/**
- * @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 The AppController Class brings together the Block
- * Factory, Block Library, and Block Exporter functionality into a single web
- * app.
- *
- * @author quachtina96 (Tina Quach)
- */
-goog.provide('AppController');
-
-goog.require('BlockFactory');
-goog.require('FactoryUtils');
-goog.require('BlockLibraryController');
-goog.require('BlockExporterController');
-goog.require('goog.dom.classlist');
-goog.require('goog.ui.PopupColorPicker');
-goog.require('goog.ui.ColorPicker');
-
-
-/**
- * Controller for the Blockly Factory
- * @constructor
- */
-AppController = function() {
- // Initialize Block Library
- this.blockLibraryName = 'blockLibrary';
- this.blockLibraryController =
- new BlockLibraryController(this.blockLibraryName);
- this.blockLibraryController.populateBlockLibrary();
-
- // Construct Workspace Factory Controller.
- this.workspaceFactoryController = new WorkspaceFactoryController
- ('workspacefactory_toolbox', 'toolbox_blocks', 'preview_blocks');
-
- // Initialize Block Exporter
- this.exporter =
- new BlockExporterController(this.blockLibraryController.storage);
-
- // Map of tab type to the div element for the tab.
- this.tabMap = Object.create(null);
- this.tabMap[AppController.BLOCK_FACTORY] =
- document.getElementById('blockFactory_tab');
- this.tabMap[AppController.WORKSPACE_FACTORY] =
- document.getElementById('workspaceFactory_tab');
- this.tabMap[AppController.EXPORTER] =
- document.getElementById('blocklibraryExporter_tab');
-
- // Last selected tab.
- this.lastSelectedTab = null;
- // Selected tab.
- this.selectedTab = AppController.BLOCK_FACTORY;
-};
-
-// Constant values representing the three tabs in the controller.
-AppController.BLOCK_FACTORY = 'BLOCK_FACTORY';
-AppController.WORKSPACE_FACTORY = 'WORKSPACE_FACTORY';
-AppController.EXPORTER = 'EXPORTER';
-
-/**
- * Tied to the 'Import Block Library' button. Imports block library from file to
- * Block Factory. Expects user to upload a single file of JSON mapping each
- * block type to its XML text representation.
- */
-AppController.prototype.importBlockLibraryFromFile = function() {
- var self = this;
- var files = document.getElementById('files');
- // If the file list is empty, the user likely canceled in the dialog.
- if (files.files.length > 0) {
- // The input tag doesn't have the "multiple" attribute
- // so the user can only choose 1 file.
- var file = files.files[0];
- var fileReader = new FileReader();
-
- // Create a map of block type to XML text from the file when it has been
- // read.
- fileReader.addEventListener('load', function(event) {
- var fileContents = event.target.result;
- // Create empty object to hold the read block library information.
- var blockXmlTextMap = Object.create(null);
- try {
- // Parse the file to get map of block type to XML text.
- blockXmlTextMap = self.formatBlockLibraryForImport_(fileContents);
- } catch (e) {
- var message = 'Could not load your block library file.\n'
- window.alert(message + '\nFile Name: ' + file.name);
- return;
- }
-
- // Create a new block library storage object with inputted block library.
- var blockLibStorage = new BlockLibraryStorage(
- self.blockLibraryName, blockXmlTextMap);
-
- // Update block library controller with the new block library
- // storage.
- self.blockLibraryController.setBlockLibraryStorage(blockLibStorage);
- // Update the block library dropdown.
- self.blockLibraryController.populateBlockLibrary();
- // Update the exporter's block library storage.
- self.exporter.setBlockLibraryStorage(blockLibStorage);
- });
- // Read the file.
- fileReader.readAsText(file);
- }
-};
-
-/**
- * Tied to the 'Export Block Library' button. Exports block library to file that
- * contains JSON mapping each block type to its XML text representation.
- */
-AppController.prototype.exportBlockLibraryToFile = function() {
- // Get map of block type to XML.
- var blockLib = this.blockLibraryController.getBlockLibrary();
- // Concatenate the XMLs, each separated by a blank line.
- var blockLibText = this.formatBlockLibraryForExport_(blockLib);
- // Get file name.
- var filename = prompt('Enter the file name under which to save your block ' +
- 'library.', 'library.xml');
- // Download file if all necessary parameters are provided.
- if (filename) {
- FactoryUtils.createAndDownloadFile(blockLibText, filename, 'xml');
- } else {
- alert('Could not export Block Library without file name under which to ' +
- 'save library.');
- }
-};
-
-/**
- * Converts an object mapping block type to XML to text file for output.
- * @param {!Object} blockXmlMap Object mapping block type to XML.
- * @return {string} XML text containing the block XMLs.
- * @private
- */
-AppController.prototype.formatBlockLibraryForExport_ = function(blockXmlMap) {
- // Create DOM for XML.
- var xmlDom = goog.dom.createDom('xml', {
- 'xmlns':"http://www.w3.org/1999/xhtml"
- });
-
- // Append each block node to XML DOM.
- for (var blockType in blockXmlMap) {
- var blockXmlDom = Blockly.Xml.textToDom(blockXmlMap[blockType]);
- var blockNode = blockXmlDom.firstElementChild;
- xmlDom.appendChild(blockNode);
- }
-
- // Return the XML text.
- return Blockly.Xml.domToText(xmlDom);
-};
-
-/**
- * Converts imported block library to an object mapping block type to block XML.
- * @param {string} xmlText String representation of an XML with each block as
- * a child node.
- * @return {!Object} Object mapping block type to XML text.
- * @private
- */
-AppController.prototype.formatBlockLibraryForImport_ = function(xmlText) {
- var xmlDom = Blockly.Xml.textToDom(xmlText);
-
- // Get array of XMLs. Use an asterisk (*) instead of a tag name for the XPath
- // selector, to match all elements at that level and get all factory_base
- // blocks.
- var blockNodes = goog.dom.xml.selectNodes(xmlDom, '*');
-
- // Create empty map. The line below creates a truly empy object. It doesn't
- // have built-in attributes/functions such as length or toString.
- var blockXmlTextMap = Object.create(null);
-
- // Populate map.
- for (var i = 0, blockNode; blockNode = blockNodes[i]; i++) {
-
- // Add outer XML tag to the block for proper injection in to the
- // main workspace.
- // Create DOM for XML.
- var xmlDom = goog.dom.createDom('xml', {
- 'xmlns':"http://www.w3.org/1999/xhtml"
- });
- xmlDom.appendChild(blockNode);
-
- xmlText = Blockly.Xml.domToText(xmlDom);
- // All block types should be lowercase.
- var blockType = this.getBlockTypeFromXml_(xmlText).toLowerCase();
- // Some names are invalid so fix them up.
- blockType = FactoryUtils.cleanBlockType(blockType);
-
- blockXmlTextMap[blockType] = xmlText;
- }
-
- return blockXmlTextMap;
-};
-
-/**
- * Extracts out block type from XML text, the kind that is saved in block
- * library storage.
- * @param {string} xmlText A block's XML text.
- * @return {string} The block type that corresponds to the provided XML text.
- * @private
- */
-AppController.prototype.getBlockTypeFromXml_ = function(xmlText) {
- var xmlDom = Blockly.Xml.textToDom(xmlText);
- // Find factory base block.
- var factoryBaseBlockXml = xmlDom.getElementsByTagName('block')[0];
- // Get field elements from factory base.
- var fields = factoryBaseBlockXml.getElementsByTagName('field');
- for (var i = 0; i < fields.length; i++) {
- // The field whose name is 'NAME' holds the block type as its value.
- if (fields[i].getAttribute('name') == 'NAME') {
- return fields[i].childNodes[0].nodeValue;
- }
- }
-};
-
-/**
- * Add click handlers to each tab to allow switching between the Block Factory,
- * Workspace Factory, and Block Exporter tab.
- * @param {!Object} tabMap Map of tab name to div element that is the tab.
- */
-AppController.prototype.addTabHandlers = function(tabMap) {
- var self = this;
- for (var tabName in tabMap) {
- var tab = tabMap[tabName];
- // Use an additional closure to correctly assign the tab callback.
- tab.addEventListener('click', self.makeTabClickHandler_(tabName));
- }
-};
-
-/**
- * Set the selected tab.
- * @param {string} tabName AppController.BLOCK_FACTORY,
- * AppController.WORKSPACE_FACTORY, or AppController.EXPORTER
- * @private
- */
-AppController.prototype.setSelected_ = function(tabName) {
- this.lastSelectedTab = this.selectedTab;
- this.selectedTab = tabName;
-};
-
-/**
- * Creates the tab click handler specific to the tab specified.
- * @param {string} tabName AppController.BLOCK_FACTORY,
- * AppController.WORKSPACE_FACTORY, or AppController.EXPORTER
- * @return {!Function} The tab click handler.
- * @private
- */
-AppController.prototype.makeTabClickHandler_ = function(tabName) {
- var self = this;
- return function() {
- self.setSelected_(tabName);
- self.onTab();
- };
-};
-
-/**
- * Called on each tab click. Hides and shows specific content based on which tab
- * (Block Factory, Workspace Factory, or Exporter) is selected.
- */
-AppController.prototype.onTab = function() {
- // Get tab div elements.
- var blockFactoryTab = this.tabMap[AppController.BLOCK_FACTORY];
- var exporterTab = this.tabMap[AppController.EXPORTER];
- var workspaceFactoryTab = this.tabMap[AppController.WORKSPACE_FACTORY];
-
- // Warn user if they have unsaved changes when leaving Block Factory.
- if (this.lastSelectedTab == AppController.BLOCK_FACTORY &&
- this.selectedTab != AppController.BLOCK_FACTORY) {
-
- var hasUnsavedChanges =
- !FactoryUtils.savedBlockChanges(this.blockLibraryController);
- if (hasUnsavedChanges &&
- !confirm('You have unsaved changes in Block Factory.')) {
- // If the user doesn't want to switch tabs with unsaved changes,
- // stay on Block Factory Tab.
- this.setSelected_(AppController.BLOCK_FACTORY);
- this.lastSelectedTab = AppController.BLOCK_FACTORY;
- return;
- }
- }
-
- // Only enable key events in workspace factory if workspace factory tab is
- // selected.
- this.workspaceFactoryController.keyEventsEnabled =
- this.selectedTab == AppController.WORKSPACE_FACTORY;
-
- // Turn selected tab on and other tabs off.
- this.styleTabs_();
-
- if (this.selectedTab == AppController.EXPORTER) {
- // Hide other tabs.
- FactoryUtils.hide('workspaceFactoryContent');
- FactoryUtils.hide('blockFactoryContent');
- // Show exporter tab.
- FactoryUtils.show('blockLibraryExporter');
-
- // Need accurate state in order to know which blocks are used in workspace
- // factory.
- this.workspaceFactoryController.saveStateFromWorkspace();
-
- // Update exporter's list of the types of blocks used in workspace factory.
- var usedBlockTypes = this.workspaceFactoryController.getAllUsedBlockTypes();
- this.exporter.setUsedBlockTypes(usedBlockTypes);
-
- // Update exporter's block selector to reflect current block library.
- this.exporter.updateSelector();
-
- // Update the exporter's preview to reflect any changes made to the blocks.
- this.exporter.updatePreview();
-
- } else if (this.selectedTab == AppController.BLOCK_FACTORY) {
- // Hide other tabs.
- FactoryUtils.hide('blockLibraryExporter');
- FactoryUtils.hide('workspaceFactoryContent');
- // Show Block Factory.
- FactoryUtils.show('blockFactoryContent');
-
- } else if (this.selectedTab == AppController.WORKSPACE_FACTORY) {
- // Hide other tabs.
- FactoryUtils.hide('blockLibraryExporter');
- FactoryUtils.hide('blockFactoryContent');
- // Show workspace factory container.
- FactoryUtils.show('workspaceFactoryContent');
- // Update block library category.
- var categoryXml = this.exporter.getBlockLibraryCategory();
- var blockTypes = this.blockLibraryController.getStoredBlockTypes();
- this.workspaceFactoryController.setBlockLibCategory(categoryXml,
- blockTypes);
- }
-
- // Resize to render workspaces' toolboxes correctly for all tabs.
- window.dispatchEvent(new Event('resize'));
-};
-
-/**
- * Called on each tab click. Styles the tabs to reflect which tab is selected.
- * @private
- */
-AppController.prototype.styleTabs_ = function() {
- for (var tabName in this.tabMap) {
- if (this.selectedTab == tabName) {
- goog.dom.classlist.addRemove(this.tabMap[tabName], 'taboff', 'tabon');
- } else {
- goog.dom.classlist.addRemove(this.tabMap[tabName], 'tabon', 'taboff');
- }
- }
-};
-
-/**
- * Assign button click handlers for the exporter.
- */
-AppController.prototype.assignExporterClickHandlers = function() {
- var self = this;
- document.getElementById('button_setBlocks').addEventListener('click',
- function() {
- self.openModal('dropdownDiv_setBlocks');
- });
-
- document.getElementById('dropdown_addAllUsed').addEventListener('click',
- function() {
- self.exporter.selectUsedBlocks();
- self.exporter.updatePreview();
- self.closeModal();
- });
-
- document.getElementById('dropdown_addAllFromLib').addEventListener('click',
- function() {
- self.exporter.selectAllBlocks();
- self.exporter.updatePreview();
- self.closeModal();
- });
-
- document.getElementById('clearSelectedButton').addEventListener('click',
- function() {
- self.exporter.clearSelectedBlocks();
- self.exporter.updatePreview();
- });
-
- // Export blocks when the user submits the export settings.
- document.getElementById('exporterSubmitButton').addEventListener('click',
- function() {
- self.exporter.export();
- });
-};
-
-/**
- * Assign change listeners for the exporter. These allow for the dynamic update
- * of the exporter preview.
- */
-AppController.prototype.assignExporterChangeListeners = function() {
- var self = this;
-
- var blockDefCheck = document.getElementById('blockDefCheck');
- var genStubCheck = document.getElementById('genStubCheck');
-
- // Select the block definitions and generator stubs on default.
- blockDefCheck.checked = true;
- genStubCheck.checked = true;
-
- // Checking the block definitions checkbox displays preview of code to export.
- document.getElementById('blockDefCheck').addEventListener('change',
- function(e) {
- self.ifCheckedEnable(blockDefCheck.checked,
- ['blockDefs', 'blockDefSettings']);
- });
-
- // Preview updates when user selects different block definition format.
- document.getElementById('exportFormat').addEventListener('change',
- function(e) {
- self.exporter.updatePreview();
- });
-
- // Checking the generator stub checkbox displays preview of code to export.
- document.getElementById('genStubCheck').addEventListener('change',
- function(e) {
- self.ifCheckedEnable(genStubCheck.checked,
- ['genStubs', 'genStubSettings']);
- });
-
- // Preview updates when user selects different generator stub language.
- document.getElementById('exportLanguage').addEventListener('change',
- function(e) {
- self.exporter.updatePreview();
- });
-};
-
-/**
- * If given checkbox is checked, enable the given elements. Otherwise, disable.
- * @param {boolean} enabled True if enabled, false otherwise.
- * @param {!Array.} idArray Array of element IDs to enable when
- * checkbox is checked.
- */
-AppController.prototype.ifCheckedEnable = function(enabled, idArray) {
- for (var i = 0, id; id = idArray[i]; i++) {
- var element = document.getElementById(id);
- if (enabled) {
- element.classList.remove('disabled');
- } else {
- element.classList.add('disabled');
- }
- var fields = element.querySelectorAll('input, textarea, select');
- for (var j = 0, field; field = fields[j]; j++) {
- field.disabled = !enabled;
- }
- }
-};
-
-/**
- * Assign button click handlers for the block library.
- */
-AppController.prototype.assignLibraryClickHandlers = function() {
- var self = this;
-
- // Button for saving block to library.
- document.getElementById('saveToBlockLibraryButton').addEventListener('click',
- function() {
- self.blockLibraryController.saveToBlockLibrary();
- });
-
- // Button for removing selected block from library.
- document.getElementById('removeBlockFromLibraryButton').addEventListener(
- 'click',
- function() {
- self.blockLibraryController.removeFromBlockLibrary();
- });
-
- // Button for clearing the block library.
- document.getElementById('clearBlockLibraryButton').addEventListener('click',
- function() {
- self.blockLibraryController.clearBlockLibrary();
- });
-
- // Hide and show the block library dropdown.
- document.getElementById('button_blockLib').addEventListener('click',
- function() {
- self.openModal('dropdownDiv_blockLib');
- });
-};
-
-/**
- * Assign button click handlers for the block factory.
- */
-AppController.prototype.assignBlockFactoryClickHandlers = function() {
- var self = this;
- // Assign button event handlers for Block Factory.
- document.getElementById('localSaveButton')
- .addEventListener('click', function() {
- self.exportBlockLibraryToFile();
- });
-
- document.getElementById('helpButton').addEventListener('click',
- function() {
- open('https://developers.google.com/blockly/custom-blocks/block-factory',
- 'BlockFactoryHelp');
- });
-
- document.getElementById('files').addEventListener('change',
- function() {
- // Warn user.
- var replace = confirm('This imported block library will ' +
- 'replace your current block library.');
- if (replace) {
- self.importBlockLibraryFromFile();
- // Clear this so that the change event still fires even if the
- // same file is chosen again. If the user re-imports a file, we
- // want to reload the workspace with its contents.
- this.value = null;
- }
- });
-
- document.getElementById('createNewBlockButton')
- .addEventListener('click', function() {
- // If there are unsaved changes warn user, check if they'd like to
- // proceed with unsaved changes, and act accordingly.
- var proceedWithUnsavedChanges =
- self.blockLibraryController.warnIfUnsavedChanges();
- if (!proceedWithUnsavedChanges) {
- return;
- }
-
- BlockFactory.showStarterBlock();
- self.blockLibraryController.setNoneSelected();
-
- // Close the Block Library Dropdown.
- self.closeModal();
- });
-};
-
-/**
- * Add event listeners for the block factory.
- */
-AppController.prototype.addBlockFactoryEventListeners = function() {
- // Update code on changes to block being edited.
- BlockFactory.mainWorkspace.addChangeListener(BlockFactory.updateLanguage);
-
- // Disable blocks not attached to the factory_base block.
- BlockFactory.mainWorkspace.addChangeListener(Blockly.Events.disableOrphans);
-
- // Update the buttons on the screen based on whether
- // changes have been saved.
- var self = this;
- BlockFactory.mainWorkspace.addChangeListener(function() {
- self.blockLibraryController.updateButtons(FactoryUtils.savedBlockChanges(
- self.blockLibraryController));
- });
-
- document.getElementById('direction')
- .addEventListener('change', BlockFactory.updatePreview);
- document.getElementById('languageTA')
- .addEventListener('change', BlockFactory.updatePreview);
- document.getElementById('languageTA')
- .addEventListener('keyup', BlockFactory.updatePreview);
- document.getElementById('format')
- .addEventListener('change', BlockFactory.formatChange);
- document.getElementById('language')
- .addEventListener('change', BlockFactory.updatePreview);
-};
-
-/**
- * Handle Blockly Storage with App Engine.
- */
-AppController.prototype.initializeBlocklyStorage = function() {
- BlocklyStorage.HTTPREQUEST_ERROR =
- 'There was a problem with the request.\n';
- BlocklyStorage.LINK_ALERT =
- 'Share your blocks with this link:\n\n%1';
- BlocklyStorage.HASH_ERROR =
- 'Sorry, "%1" doesn\'t correspond with any saved Blockly file.';
- BlocklyStorage.XML_ERROR = 'Could not load your saved file.\n' +
- 'Perhaps it was created with a different version of Blockly?';
- var linkButton = document.getElementById('linkButton');
- linkButton.style.display = 'inline-block';
- linkButton.addEventListener('click',
- function() {
- BlocklyStorage.link(BlockFactory.mainWorkspace);});
- BlockFactory.disableEnableLink();
-};
-
-/**
- * Handle resizing of elements.
- */
-AppController.prototype.onresize = function(event) {
- if (this.selectedTab == AppController.BLOCK_FACTORY) {
- // Handle resizing of Block Factory elements.
- var expandList = [
- document.getElementById('blocklyPreviewContainer'),
- document.getElementById('blockly'),
- document.getElementById('blocklyMask'),
- document.getElementById('preview'),
- document.getElementById('languagePre'),
- document.getElementById('languageTA'),
- document.getElementById('generatorPre'),
- ];
- for (var i = 0, expand; expand = expandList[i]; i++) {
- expand.style.width = (expand.parentNode.offsetWidth - 2) + 'px';
- expand.style.height = (expand.parentNode.offsetHeight - 2) + 'px';
- }
- } else if (this.selectedTab == AppController.EXPORTER) {
- // Handle resize of Exporter block options.
- this.exporter.view.centerPreviewBlocks();
- }
-};
-
-/**
- * Handler for the window's 'beforeunload' event. When a user has unsaved
- * changes and refreshes or leaves the page, confirm that they want to do so
- * before actually refreshing.
- * @param {!Event} e beforeunload event.
- */
-AppController.prototype.confirmLeavePage = function(e) {
- if ((!BlockFactory.isStarterBlock() &&
- !FactoryUtils.savedBlockChanges(blocklyFactory.blockLibraryController)) ||
- blocklyFactory.workspaceFactoryController.hasUnsavedChanges()) {
-
- var confirmationMessage = 'You will lose any unsaved changes. ' +
- 'Are you sure you want to exit this page?';
- e.returnValue = confirmationMessage;
- return confirmationMessage;
- }
-};
-
-/**
- * Show a modal element, usually a dropdown list.
- * @param {string} id ID of element to show.
- */
-AppController.prototype.openModal = function(id) {
- Blockly.hideChaff();
- this.modalName_ = id;
- document.getElementById(id).style.display = 'block';
- document.getElementById('modalShadow').style.display = 'block';
-};
-
-/**
- * Hide a previously shown modal element.
- */
-AppController.prototype.closeModal = function() {
- var id = this.modalName_;
- if (!id) {
- return;
- }
- document.getElementById(id).style.display = 'none';
- document.getElementById('modalShadow').style.display = 'none';
- this.modalName_ = null;
-};
-
-/**
- * Name of currently open modal.
- * @type {string?}
- * @private
- */
-AppController.prototype.modalName_ = null;
-
-/**
- * Initialize Blockly and layout. Called on page load.
- */
-AppController.prototype.init = function() {
- // Block Factory has a dependency on bits of Closure that core Blockly
- // doesn't have. When you run this from file:// without a copy of Closure,
- // it breaks it non-obvious ways. Warning about this for now until the
- // dependency is broken.
- // TODO: #668.
- if (!window.goog.dom.xml) {
- alert('Sorry: Closure dependency not found. We are working on removing ' +
- 'this dependency. In the meantime, you can use our hosted demo\n ' +
- 'https://blockly-demo.appspot.com/static/demos/blockfactory/index.html' +
- '\nor use these instructions to continue running locally:\n' +
- 'https://developers.google.com/blockly/guides/modify/web/closure');
- return;
- }
-
- var self = this;
- // Handle Blockly Storage with App Engine.
- if ('BlocklyStorage' in window) {
- this.initializeBlocklyStorage();
- }
-
- // Assign click handlers.
- this.assignExporterClickHandlers();
- this.assignLibraryClickHandlers();
- this.assignBlockFactoryClickHandlers();
- // Hide and show the block library dropdown.
- document.getElementById('modalShadow').addEventListener('click',
- function() {
- self.closeModal();
- });
-
- this.onresize();
- window.addEventListener('resize', function() {
- self.onresize();
- });
-
- // Inject Block Factory Main Workspace.
- var toolbox = document.getElementById('blockfactory_toolbox');
- BlockFactory.mainWorkspace = Blockly.inject('blockly',
- {collapse: false,
- toolbox: toolbox,
- media: '../../media/'});
-
- // Add tab handlers for switching between Block Factory and Block Exporter.
- this.addTabHandlers(this.tabMap);
-
- // Assign exporter change listeners.
- this.assignExporterChangeListeners();
-
- // Create the root block on Block Factory main workspace.
- if ('BlocklyStorage' in window && window.location.hash.length > 1) {
- BlocklyStorage.retrieveXml(window.location.hash.substring(1),
- BlockFactory.mainWorkspace);
- } else {
- BlockFactory.showStarterBlock();
- }
- BlockFactory.mainWorkspace.clearUndo();
-
- // Add Block Factory event listeners.
- this.addBlockFactoryEventListeners();
-
- // Workspace Factory init.
- WorkspaceFactoryInit.initWorkspaceFactory(this.workspaceFactoryController);
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_exporter_controller.js b/trunk/web/blockly/demos/blockfactory/block_exporter_controller.js
deleted file mode 100644
index b237f48a70f..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_exporter_controller.js
+++ /dev/null
@@ -1,323 +0,0 @@
-/**
- * @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 Block Exporter Controller class. Allows
- * users to export block definitions and generator stubs of their saved blocks
- * easily using a visual interface. Depends on Block Exporter View and Block
- * Exporter Tools classes. Interacts with Export Settings in the index.html.
- *
- * @author quachtina96 (Tina Quach)
- */
-
-'use strict';
-
-goog.provide('BlockExporterController');
-
-goog.require('FactoryUtils');
-goog.require('StandardCategories');
-goog.require('BlockExporterView');
-goog.require('BlockExporterTools');
-goog.require('goog.dom.xml');
-
-
-/**
- * BlockExporter Controller Class
- * @param {!BlockLibrary.Storage} blockLibStorage Block Library Storage.
- * @constructor
- */
-BlockExporterController = function(blockLibStorage) {
- // BlockLibrary.Storage object containing user's saved blocks.
- this.blockLibStorage = blockLibStorage;
- // Utils for generating code to export.
- this.tools = new BlockExporterTools();
- // The ID of the block selector, a div element that will be populated with the
- // block options.
- this.selectorID = 'blockSelector';
- // Map of block types stored in block library to their corresponding Block
- // Option objects.
- this.blockOptions = this.tools.createBlockSelectorFromLib(
- this.blockLibStorage, this.selectorID);
- // View provides the block selector and export settings UI.
- this.view = new BlockExporterView(this.blockOptions);
-};
-
-/**
- * Set the block library storage object from which exporter exports.
- * @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object
- * that stores the blocks.
- */
-BlockExporterController.prototype.setBlockLibraryStorage =
- function(blockLibStorage) {
- this.blockLibStorage = blockLibStorage;
-};
-
-/**
- * Get the block library storage object from which exporter exports.
- * @return {!BlockLibraryStorage} blockLibStorage Block Library Storage object
- * that stores the blocks.
- */
-BlockExporterController.prototype.getBlockLibraryStorage =
- function(blockLibStorage) {
- return this.blockLibStorage;
-};
-
-/**
- * Get selected blocks from block selector, pulls info from the Export
- * Settings form in Block Exporter, and downloads code accordingly.
- */
-BlockExporterController.prototype.export = function() {
- // Get selected blocks' information.
- var blockTypes = this.view.getSelectedBlockTypes();
- var blockXmlMap = this.blockLibStorage.getBlockXmlMap(blockTypes);
-
- // Pull block definition(s) settings from the Export Settings form.
- var wantBlockDef = document.getElementById('blockDefCheck').checked;
- var definitionFormat = document.getElementById('exportFormat').value;
- var blockDef_filename = document.getElementById('blockDef_filename').value;
-
- // Pull block generator stub(s) settings from the Export Settings form.
- var wantGenStub = document.getElementById('genStubCheck').checked;
- var language = document.getElementById('exportLanguage').value;
- var generatorStub_filename = document.getElementById(
- 'generatorStub_filename').value;
-
- if (wantBlockDef) {
- // User wants to export selected blocks' definitions.
- if (!blockDef_filename) {
- // User needs to enter filename.
- alert('Please enter a filename for your block definition(s) download.');
- } else {
- // Get block definition code in the selected format for the blocks.
- var blockDefs = this.tools.getBlockDefinitions(blockXmlMap,
- definitionFormat);
- // Download the file, using .js file ending for JSON or Javascript.
- FactoryUtils.createAndDownloadFile(
- blockDefs, blockDef_filename, 'javascript');
- }
- }
-
- if (wantGenStub) {
- // User wants to export selected blocks' generator stubs.
- if (!generatorStub_filename) {
- // User needs to enter filename.
- alert('Please enter a filename for your generator stub(s) download.');
- } else {
- // Get generator stub code in the selected language for the blocks.
- var genStubs = this.tools.getGeneratorCode(blockXmlMap,
- language);
- // Get the correct file extension.
- var fileType = (language == 'JavaScript') ? 'javascript' : 'plain';
- // Download the file.
- FactoryUtils.createAndDownloadFile(
- genStubs, generatorStub_filename, fileType);
- }
- }
-
-};
-
-/**
- * Update the Exporter's block selector with block options generated from blocks
- * stored in block library.
- */
-BlockExporterController.prototype.updateSelector = function() {
- // Get previously selected block types.
- var oldSelectedTypes = this.view.getSelectedBlockTypes();
-
- // Generate options from block library and assign to view.
- this.blockOptions = this.tools.createBlockSelectorFromLib(
- this.blockLibStorage, this.selectorID);
- this.addBlockOptionSelectHandlers();
- this.view.setBlockOptions(this.blockOptions);
-
- // Select all previously selected blocks.
- for (var i = 0, blockType; blockType = oldSelectedTypes[i]; i++) {
- if (this.blockOptions[blockType]) {
- this.view.select(blockType);
- }
- }
-
- this.view.listSelectedBlocks();
-};
-
-/**
- * Tied to the 'Clear Selected Blocks' button in the Block Exporter.
- * Deselects all blocks in the selector and updates text accordingly.
- */
-BlockExporterController.prototype.clearSelectedBlocks = function() {
- this.view.deselectAllBlocks();
- this.view.listSelectedBlocks();
-};
-
-/**
- * Tied to the 'All Stored' button in the Block Exporter 'Select' dropdown.
- * Selects all blocks stored in block library for export.
- */
-BlockExporterController.prototype.selectAllBlocks = function() {
- var allBlockTypes = this.blockLibStorage.getBlockTypes();
- for (var i = 0, blockType; blockType = allBlockTypes[i]; i++) {
- this.view.select(blockType);
- }
- this.view.listSelectedBlocks();
-};
-
-/**
- * Returns the category XML containing all blocks in the block library.
- * @return {Element} XML for a category to be used in toolbox.
- */
-BlockExporterController.prototype.getBlockLibraryCategory = function() {
- return this.tools.generateCategoryFromBlockLib(this.blockLibStorage);
-};
-
-/**
- * Add select handlers to each block option to update the view and the selected
- * blocks accordingly.
- */
-BlockExporterController.prototype.addBlockOptionSelectHandlers = function() {
- var self = this;
-
- // Click handler for a block option. Toggles whether or not it's selected and
- // updates helper text accordingly.
- var updateSelectedBlockTypes_ = function(blockOption) {
- // Toggle selected.
- blockOption.setSelected(!blockOption.isSelected());
-
- // Show currently selected blocks in helper text.
- self.view.listSelectedBlocks();
- };
-
- // Returns a block option select handler.
- var makeBlockOptionSelectHandler_ = function(blockOption) {
- return function() {
- updateSelectedBlockTypes_(blockOption);
- self.updatePreview();
- };
- };
-
- // Assign a click handler to each block option.
- for (var blockType in this.blockOptions) {
- var blockOption = this.blockOptions[blockType];
- // Use an additional closure to correctly assign the tab callback.
- blockOption.dom.addEventListener(
- 'click', makeBlockOptionSelectHandler_(blockOption));
- }
-};
-
-/**
- * Tied to the 'All Used' button in the Block Exporter's 'Select' button.
- * Selects all blocks stored in block library and used in workspace factory.
- */
-BlockExporterController.prototype.selectUsedBlocks = function() {
- // Deselect all blocks.
- this.view.deselectAllBlocks();
-
- // Get list of block types that are in block library and used in workspace
- // factory.
- var storedBlockTypes = this.blockLibStorage.getBlockTypes();
- var sharedBlockTypes = [];
- // Keep list of custom block types used but not in library.
- var unstoredCustomBlockTypes = [];
-
- for (var i = 0, blockType; blockType = this.usedBlockTypes[i]; i++) {
- if (storedBlockTypes.indexOf(blockType) != -1) {
- sharedBlockTypes.push(blockType);
- } else if (StandardCategories.coreBlockTypes.indexOf(blockType) == -1) {
- unstoredCustomBlockTypes.push(blockType);
- }
- }
-
- // Select each shared block type.
- for (var i = 0, blockType; blockType = sharedBlockTypes[i]; i++) {
- this.view.select(blockType);
- }
- this.view.listSelectedBlocks();
-
- if (unstoredCustomBlockTypes.length > 0){
- // Warn user to import block defifnitions and generator code for blocks
- // not in their Block Library nor Blockly's standard library.
- var blockTypesText = unstoredCustomBlockTypes.join(', ');
- var customWarning = 'Custom blocks used in workspace factory but not ' +
- 'stored in block library:\n ' + blockTypesText +
- '\n\nDon\'t forget to include block definitions and generator code ' +
- 'for these blocks.';
- alert(customWarning);
- }
-};
-
-/**
- * Set the array that holds the block types used in workspace factory.
- * @param {!Array.} usedBlockTypes Block types used in
- */
-BlockExporterController.prototype.setUsedBlockTypes =
- function(usedBlockTypes) {
- this.usedBlockTypes = usedBlockTypes;
-};
-
-/**
- * Updates preview code (block definitions and generator stubs) in the exporter
- * preview to reflect selected blocks.
- */
-BlockExporterController.prototype.updatePreview = function() {
- // Generate preview code for selected blocks.
- var blockDefs = this.getBlockDefinitionsOfSelected();
- var genStubs = this.getGeneratorStubsOfSelected();
-
- // Update the text areas containing the code.
- FactoryUtils.injectCode(blockDefs, 'blockDefs_textArea');
- FactoryUtils.injectCode(genStubs, 'genStubs_textArea');
-};
-
-/**
- * Returns a map of each selected block's type to its corresponding XML.
- * @return {!Object} A map of each selected block's type (a string) to its
- * corresponding XML element.
- */
-BlockExporterController.prototype.getSelectedBlockXmlMap = function() {
- var blockTypes = this.view.getSelectedBlockTypes();
- return this.blockLibStorage.getBlockXmlMap(blockTypes);
-};
-
-/**
- * Get block definition code in the selected format for selected blocks.
- * @return {string} The concatenation of each selected block's language code
- * in the format specified in export settings.
- */
-BlockExporterController.prototype.getBlockDefinitionsOfSelected = function() {
- // Get selected blocks' information.
- var blockXmlMap = this.getSelectedBlockXmlMap();
-
- // Get block definition code in the selected format for the blocks.
- var definitionFormat = document.getElementById('exportFormat').value;
- return this.tools.getBlockDefinitions(blockXmlMap, definitionFormat);
-};
-
-/**
- * Get generator stubs in the selected language for selected blocks.
- * @return {string} The concatenation of each selected block's generator stub
- * in the language specified in export settings.
- */
-BlockExporterController.prototype.getGeneratorStubsOfSelected = function() {
- // Get selected blocks' information.
- var blockXmlMap = this.getSelectedBlockXmlMap();
-
- // Get generator stub code in the selected language for the blocks.
- var language = document.getElementById('exportLanguage').value;
- return this.tools.getGeneratorCode(blockXmlMap, language);
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_exporter_tools.js b/trunk/web/blockly/demos/blockfactory/block_exporter_tools.js
deleted file mode 100644
index 4d6d9bec65a..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_exporter_tools.js
+++ /dev/null
@@ -1,277 +0,0 @@
-/**
- * @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;
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_exporter_view.js b/trunk/web/blockly/demos/blockfactory/block_exporter_view.js
deleted file mode 100644
index 198598c1418..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_exporter_view.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/**
- * @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 Block Exporter View class. Reads from and
- * manages a block selector through which users select blocks to export.
- *
- * @author quachtina96 (Tina Quach)
- */
-
-'use strict';
-
-goog.provide('BlockExporterView');
-
-goog.require('BlockExporterTools');
-goog.require('BlockOption');
-goog.require('goog.dom');
-
-
-/**
- * BlockExporter View Class
- * @param {!Object} blockOptions Map of block types to BlockOption objects.
- * @constructor
- */
-BlockExporterView = function(blockOptions) {
- // Map of block types to BlockOption objects to select from.
- this.blockOptions = blockOptions;
-};
-
-/**
- * Set the block options in the selector of this instance of
- * BlockExporterView.
- * @param {!Object} blockOptions Map of block types to BlockOption objects.
- */
-BlockExporterView.prototype.setBlockOptions = function(blockOptions) {
- this.blockOptions = blockOptions;
-};
-
-/**
- * Updates the helper text to show list of currently selected blocks.
- */
-BlockExporterView.prototype.listSelectedBlocks = function() {
-
- var selectedBlocksText = this.getSelectedBlockTypes().join(",\n ");
- document.getElementById('selectedBlocksText').textContent = selectedBlocksText;
-};
-
-/**
- * Selects a given block type in the selector.
- * @param {string} blockType Type of block to selector.
- */
-BlockExporterView.prototype.select = function(blockType) {
- this.blockOptions[blockType].setSelected(true);
-};
-
-/**
- * Deselects a block in the selector.
- * @param {!Blockly.Block} block Type of block to add to selector workspce.
- */
-BlockExporterView.prototype.deselect = function(blockType) {
- this.blockOptions[blockType].setSelected(false);
-};
-
-
-/**
- * Deselects all blocks.
- */
-BlockExporterView.prototype.deselectAllBlocks = function() {
- for (var blockType in this.blockOptions) {
- this.deselect(blockType);
- }
-};
-
-/**
- * Given an array of selected blocks, selects these blocks in the view, marking
- * the checkboxes accordingly.
- * @param {Array.} blockTypes Array of block types to select.
- */
-BlockExporterView.prototype.setSelectedBlockTypes = function(blockTypes) {
- for (var i = 0, blockType; blockType = blockTypes[i]; i++) {
- this.select(blockType);
- }
-};
-
-/**
- * Returns array of selected blocks.
- * @return {!Array.} Array of all selected block types.
- */
-BlockExporterView.prototype.getSelectedBlockTypes = function() {
- var selectedTypes = [];
- for (var blockType in this.blockOptions) {
- var blockOption = this.blockOptions[blockType];
- if (blockOption.isSelected()) {
- selectedTypes.push(blockType);
- }
- }
- return selectedTypes;
-};
-
-/**
- * Centers the preview block of each block option in the exporter selector.
- */
-BlockExporterView.prototype.centerPreviewBlocks = function() {
- for (var blockType in this.blockOptions) {
- this.blockOptions[blockType].centerBlock();
- }
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_library_controller.js b/trunk/web/blockly/demos/blockfactory/block_library_controller.js
deleted file mode 100644
index 1066ab127d7..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_library_controller.js
+++ /dev/null
@@ -1,321 +0,0 @@
-/**
- * @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 Contains the code for Block Library Controller, which
- * depends on Block Library Storage and Block Library UI. Provides the
- * interfaces for the user to
- * - save their blocks to the browser
- * - re-open and edit saved blocks
- * - delete blocks
- * - clear their block library
- * Depends on BlockFactory functions defined in factory.js.
- *
- * @author quachtina96 (Tina Quach)
- */
-'use strict';
-
-goog.provide('BlockLibraryController');
-
-goog.require('BlockLibraryStorage');
-goog.require('BlockLibraryView');
-goog.require('BlockFactory');
-
-
-/**
- * Block Library Controller Class
- * @param {string} blockLibraryName Desired name of Block Library, also used
- * to create the key for where it's stored in local storage.
- * @param {!BlockLibraryStorage} opt_blockLibraryStorage Optional storage
- * object that allows user to import a block library.
- * @constructor
- */
-BlockLibraryController = function(blockLibraryName, opt_blockLibraryStorage) {
- this.name = blockLibraryName;
- // Create a new, empty Block Library Storage object, or load existing one.
- this.storage = opt_blockLibraryStorage || new BlockLibraryStorage(this.name);
- // The BlockLibraryView object handles the proper updating and formatting of
- // the block library dropdown.
- this.view = new BlockLibraryView();
-};
-
-/**
- * Returns the block type of the block the user is building.
- * @return {string} The current block's type.
- * @private
- */
-BlockLibraryController.prototype.getCurrentBlockType = function() {
- var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
- var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
- // Replace invalid characters.
- return FactoryUtils.cleanBlockType(blockType);
-};
-
-/**
- * Removes current block from Block Library and updates the save and delete
- * buttons so that user may save block to library and but not delete.
- * @param {string} blockType Type of block.
- */
-BlockLibraryController.prototype.removeFromBlockLibrary = function() {
- var blockType = this.getCurrentBlockType();
- this.storage.removeBlock(blockType);
- this.storage.saveToLocalStorage();
- this.populateBlockLibrary();
- this.view.updateButtons(blockType, false, false);
-};
-
-/**
- * Updates the workspace to show the block user selected from library
- * @param {string} blockType Block to edit on block factory.
- */
-BlockLibraryController.prototype.openBlock = function(blockType) {
- if (blockType) {
- var xml = this.storage.getBlockXml(blockType);
- BlockFactory.mainWorkspace.clear();
- Blockly.Xml.domToWorkspace(xml, BlockFactory.mainWorkspace);
- BlockFactory.mainWorkspace.clearUndo();
- } else {
- BlockFactory.showStarterBlock();
- this.view.setSelectedBlockType(null);
- }
-};
-
-/**
- * Returns type of block selected from library.
- * @return {string} Type of block selected.
- */
-BlockLibraryController.prototype.getSelectedBlockType = function() {
- return this.view.getSelectedBlockType();
-};
-
-/**
- * Confirms with user before clearing the block library in local storage and
- * updating the dropdown and displaying the starter block (factory_base).
- */
-BlockLibraryController.prototype.clearBlockLibrary = function() {
- var check = confirm('Delete all blocks from library?');
- if (check) {
- // Clear Block Library Storage.
- this.storage.clear();
- this.storage.saveToLocalStorage();
- // Update dropdown.
- this.view.clearOptions();
- // Show default block.
- BlockFactory.showStarterBlock();
- // User may not save the starter block, but will get explicit instructions
- // upon clicking the red save button.
- this.view.updateButtons(null);
- }
-};
-
-/**
- * Saves current block to local storage and updates dropdown.
- */
-BlockLibraryController.prototype.saveToBlockLibrary = function() {
- var blockType = this.getCurrentBlockType();
- // If user has not changed the name of the starter block.
- if (blockType == 'block_type') {
- // Do not save block if it has the default type, 'block_type'.
- alert('You cannot save a block under the name "block_type". Try changing ' +
- 'the name before saving. Then, click on the "Block Library" button ' +
- 'to view your saved blocks.');
- return;
- }
-
- // Create block XML.
- var xmlElement = goog.dom.createDom('xml');
- var block = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
- xmlElement.appendChild(Blockly.Xml.blockToDomWithXY(block));
-
- // Do not add option again if block type is already in library.
- if (!this.has(blockType)) {
- this.view.addOption(blockType, true, true);
- }
-
- // Save block.
- this.storage.addBlock(blockType, xmlElement);
- this.storage.saveToLocalStorage();
-
- // Show saved block without other stray blocks sitting in Block Factory's
- // main workspace.
- this.openBlock(blockType);
-
- // Add select handler to the new option.
- this.addOptionSelectHandler(blockType);
-};
-
-/**
- * Checks to see if the given blockType is already in Block Library
- * @param {string} blockType Type of block.
- * @return {boolean} Boolean indicating whether or not block is in the library.
- */
-BlockLibraryController.prototype.has = function(blockType) {
- var blockLibrary = this.storage.blocks;
- return (blockType in blockLibrary && blockLibrary[blockType] != null);
-};
-
-/**
- * Populates the dropdown menu.
- */
-BlockLibraryController.prototype.populateBlockLibrary = function() {
- this.view.clearOptions();
- // Add an unselected option for each saved block.
- var blockLibrary = this.storage.blocks;
- for (var blockType in blockLibrary) {
- this.view.addOption(blockType, false);
- }
- this.addOptionSelectHandlers();
-};
-
-/**
- * Return block library mapping block type to XML.
- * @return {Object} Object mapping block type to XML text.
- */
-BlockLibraryController.prototype.getBlockLibrary = function() {
- return this.storage.getBlockXmlTextMap();
-};
-
-/**
- * Return stored XML of a given block type.
- * @param {string} blockType The type of block.
- * @return {!Element} XML element of a given block type or null.
- */
-BlockLibraryController.prototype.getBlockXml = function(blockType) {
- return this.storage.getBlockXml(blockType);
-};
-
-/**
- * Set the block library storage object from which exporter exports.
- * @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
- */
-BlockLibraryController.prototype.setBlockLibraryStorage
- = function(blockLibStorage) {
- this.storage = blockLibStorage;
-};
-
-/**
- * Get the block library storage object from which exporter exports.
- * @return {!BlockLibraryStorage} blockLibStorage Block Library Storage object
- * that stores the blocks.
- */
-BlockLibraryController.prototype.getBlockLibraryStorage = function() {
- return this.blockLibStorage;
-};
-
-/**
- * Get the block library storage object from which exporter exports.
- * @return {boolean} True if the Block Library is empty, false otherwise.
- */
-BlockLibraryController.prototype.hasEmptyBlockLibrary = function() {
- return this.storage.isEmpty();
-};
-
-/**
- * Get all block types stored in block library.
- * @return {!Array.} Array of block types.
- */
-BlockLibraryController.prototype.getStoredBlockTypes = function() {
- return this.storage.getBlockTypes();
-};
-
-/**
- * Sets the currently selected block option to none.
- */
-BlockLibraryController.prototype.setNoneSelected = function() {
- this.view.setSelectedBlockType(null);
-};
-
-/**
- * If there are unsaved changes to the block in open in Block Factory
- * and the block is not the starter block, check if user wants to proceed,
- * knowing that it will cause them to lose their changes.
- * @return {boolean} Whether or not to proceed.
- */
-BlockLibraryController.prototype.warnIfUnsavedChanges = function() {
- if (!FactoryUtils.savedBlockChanges(this)) {
- return confirm('You have unsaved changes. By proceeding without saving ' +
- ' your block first, you will lose these changes.');
- }
- return true;
-};
-
-/**
- * Add select handler for an option of a given block type. The handler will to
- * update the view and the selected block accordingly.
- * @param {string} blockType The type of block represented by the option is for.
- */
-BlockLibraryController.prototype.addOptionSelectHandler = function(blockType) {
- var self = this;
-
- // Click handler for a block option. Sets the block option as the selected
- // option and opens the block for edit in Block Factory.
- var setSelectedAndOpen_ = function(blockOption) {
- var blockType = blockOption.textContent;
- self.view.setSelectedBlockType(blockType);
- self.openBlock(blockType);
- // The block is saved in the block library and all changes have been saved
- // when the user opens a block from the block library dropdown.
- // Thus, the buttons show up as a disabled update button and an enabled
- // delete.
- self.view.updateButtons(blockType, true, true);
- blocklyFactory.closeModal();
- };
-
- // Returns a block option select handler.
- var makeOptionSelectHandler_ = function(blockOption) {
- return function() {
- // If there are unsaved changes warn user, check if they'd like to
- // proceed with unsaved changes, and act accordingly.
- var proceedWithUnsavedChanges = self.warnIfUnsavedChanges();
- if (!proceedWithUnsavedChanges) {
- return;
- }
- setSelectedAndOpen_(blockOption);
- };
- };
-
- // Assign a click handler to the block option.
- var blockOption = this.view.optionMap[blockType];
- // Use an additional closure to correctly assign the tab callback.
- blockOption.addEventListener(
- 'click', makeOptionSelectHandler_(blockOption));
-};
-
-/**
- * Add select handlers to each option to update the view and the selected
- * blocks accordingly.
- */
-BlockLibraryController.prototype.addOptionSelectHandlers = function() {
- // Assign a click handler to each block option.
- for (var blockType in this.view.optionMap) {
- this.addOptionSelectHandler(blockType);
- }
-};
-
-/**
- * Update the save and delete buttons based on the current block type of the
- * block the user is currently editing.
- * @param {boolean} Whether changes to the block have been saved.
- */
-BlockLibraryController.prototype.updateButtons = function(savedChanges) {
- var blockType = this.getCurrentBlockType();
- var isInLibrary = this.has(blockType);
- this.view.updateButtons(blockType, isInLibrary, savedChanges);
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_library_storage.js b/trunk/web/blockly/demos/blockfactory/block_library_storage.js
deleted file mode 100644
index 750717752f0..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_library_storage.js
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * @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 Block Library's Storage Class.
- * Depends on Block Library for its namespace.
- *
- * @author quachtina96 (Tina Quach)
- */
-
-'use strict';
-
-goog.provide('BlockLibraryStorage');
-
-
-/**
- * Represents a block library's storage.
- * @param {string} blockLibraryName Desired name of Block Library, also used
- * to create the key for where it's stored in local storage.
- * @param {Object} opt_blocks Object mapping block type to XML.
- * @constructor
- */
-BlockLibraryStorage = function(blockLibraryName, opt_blocks) {
- // Add prefix to this.name to avoid collisions in local storage.
- this.name = 'BlockLibraryStorage.' + blockLibraryName;
- if (!opt_blocks) {
- // Initialize this.blocks by loading from local storage.
- this.loadFromLocalStorage();
- if (this.blocks == null) {
- this.blocks = Object.create(null);
- // The line above is equivalent of {} except that this object is TRULY
- // empty. It doesn't have built-in attributes/functions such as length or
- // toString.
- this.saveToLocalStorage();
- }
- } else {
- this.blocks = opt_blocks;
- this.saveToLocalStorage();
- }
-};
-
-/**
- * Reads the named block library from local storage and saves it in this.blocks.
- */
-BlockLibraryStorage.prototype.loadFromLocalStorage = function() {
- // goog.global is synonymous to window, and allows for flexibility
- // between browsers.
- var object = goog.global.localStorage[this.name];
- this.blocks = object ? JSON.parse(object) : null;
-};
-
-/**
- * Writes the current block library (this.blocks) to local storage.
- */
-BlockLibraryStorage.prototype.saveToLocalStorage = function() {
- goog.global.localStorage[this.name] = JSON.stringify(this.blocks);
-};
-
-/**
- * Clears the current block library.
- */
-BlockLibraryStorage.prototype.clear = function() {
- this.blocks = Object.create(null);
- // The line above is equivalent of {} except that this object is TRULY
- // empty. It doesn't have built-in attributes/functions such as length or
- // toString.
-};
-
-/**
- * Saves block to block library.
- * @param {string} blockType Type of block.
- * @param {Element} blockXML The block's XML pulled from workspace.
- */
-BlockLibraryStorage.prototype.addBlock = function(blockType, blockXML) {
- var prettyXml = Blockly.Xml.domToPrettyText(blockXML);
- this.blocks[blockType] = prettyXml;
-};
-
-/**
- * Removes block from current block library (this.blocks).
- * @param {string} blockType Type of block.
- */
-BlockLibraryStorage.prototype.removeBlock = function(blockType) {
- delete this.blocks[blockType];
-};
-
-/**
- * Returns the XML of given block type stored in current block library
- * (this.blocks).
- * @param {string} blockType Type of block.
- * @return {Element} The XML that represents the block type or null.
- */
-BlockLibraryStorage.prototype.getBlockXml = function(blockType) {
- var xml = this.blocks[blockType] || null;
- if (xml) {
- var xml = Blockly.Xml.textToDom(xml);
- }
- return xml;
-};
-
-
-/**
- * Returns map of each block type to its corresponding XML stored in current
- * block library (this.blocks).
- * @param {!Array.} blockTypes Types of blocks.
- * @return {!Object} Map of block type to corresponding XML.
- */
-BlockLibraryStorage.prototype.getBlockXmlMap = function(blockTypes) {
- var blockXmlMap = {};
- for (var i = 0; i < blockTypes.length; i++) {
- var blockType = blockTypes[i];
- var xml = this.getBlockXml(blockType);
- blockXmlMap[blockType] = xml;
- }
- return blockXmlMap;
-};
-
-/**
- * Returns array of all block types stored in current block library.
- * @return {!Array.} Array of block types stored in library.
- */
-BlockLibraryStorage.prototype.getBlockTypes = function() {
- return Object.keys(this.blocks);
-};
-
-/**
- * Checks to see if block library is empty.
- * @return {boolean} True if empty, false otherwise.
- */
-BlockLibraryStorage.prototype.isEmpty = function() {
- for (var blockType in this.blocks) {
- return false;
- }
- return true;
-};
-
-/**
- * Returns array of all block types stored in current block library.
- * @return {!Array.} Map of block type to corresponding XML text.
- */
-BlockLibraryStorage.prototype.getBlockXmlTextMap = function() {
- return this.blocks;
-};
-
-/**
- * Returns boolean of whether or not a given blockType is stored in block
- * library.
- * @param {string} blockType Type of block.
- * @return {boolean} Whether or not blockType is stored in block library.
- */
-BlockLibraryStorage.prototype.has = function(blockType) {
- return !!this.blocks[blockType];
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_library_view.js b/trunk/web/blockly/demos/blockfactory/block_library_view.js
deleted file mode 100644
index 16181f290c6..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_library_view.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * @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 BlockLibraryView class. It manages the display
- * of the Block Library dropdown, save, and delete buttons.
- *
- * @author quachtina96 (Tina Quach)
- */
-
-'use strict';
-
-goog.provide('BlockLibraryView');
-
-goog.require('goog.dom');
-goog.require('goog.dom.classlist');
-
-
-/**
- * BlockLibraryView Class
- * @constructor
- */
-var BlockLibraryView = function() {
- // Div element to contain the block types to choose from.
- this.dropdown = document.getElementById('dropdownDiv_blockLib');
- // Map of block type to corresponding 'a' element that is the option in the
- // dropdown. Used to quickly and easily get a specific option.
- this.optionMap = Object.create(null);
- // Save and delete buttons.
- this.saveButton = document.getElementById('saveToBlockLibraryButton');
- this.deleteButton = document.getElementById('removeBlockFromLibraryButton');
- // Initially, user should not be able to delete a block. They must save a
- // block or select a stored block first.
- this.deleteButton.disabled = true;
-};
-
-/**
- * Creates a node of a given element type and appends to the node with given ID.
- * @param {string} blockType Type of block.
- * @param {boolean} selected Whether or not the option should be selected on
- * the dropdown.
- */
-BlockLibraryView.prototype.addOption = function(blockType, selected) {
- // Create option.
- var option = goog.dom.createDom('a', {
- 'id': 'dropdown_' + blockType,
- 'class': 'blockLibOpt'
- }, blockType);
-
- // Add option to dropdown.
- this.dropdown.appendChild(option);
- this.optionMap[blockType] = option;
-
- // Select the block.
- if (selected) {
- this.setSelectedBlockType(blockType);
- }
-};
-
-/**
- * Sets a given block type to selected and all other blocks to deselected.
- * If null, deselects all blocks.
- * @param {string} blockTypeToSelect Type of block to select or null.
- */
-BlockLibraryView.prototype.setSelectedBlockType = function(blockTypeToSelect) {
- // Select given block type and deselect all others. Will deselect all blocks
- // if null or invalid block type selected.
- for (var blockType in this.optionMap) {
- var option = this.optionMap[blockType];
- if (blockType == blockTypeToSelect) {
- this.selectOption_(option);
- } else {
- this.deselectOption_(option);
- }
- }
-};
-
-/**
- * Selects a given option.
- * @param {!Element} option HTML 'a' element in the dropdown that represents
- * a particular block type.
- * @private
- */
-BlockLibraryView.prototype.selectOption_ = function(option) {
- goog.dom.classlist.add(option, 'dropdown-content-selected');
-};
-
-/**
- * Deselects a given option.
- * @param {!Element} option HTML 'a' element in the dropdown that represents
- * a particular block type.
- * @private
- */
-BlockLibraryView.prototype.deselectOption_ = function(option) {
- goog.dom.classlist.remove(option, 'dropdown-content-selected');
-};
-
-/**
- * Updates the save and delete buttons to represent how the current block will
- * be saved by including the block type in the button text as well as indicating
- * whether the block is being saved or updated.
- * @param {string} blockType The type of block being edited.
- * @param {boolean} isInLibrary Whether the block type is in the library.
- * @param {boolean} savedChanges Whether changes to block have been saved.
- */
-BlockLibraryView.prototype.updateButtons =
- function(blockType, isInLibrary, savedChanges) {
- if (blockType) {
- // User is editing a block.
-
- if (!isInLibrary) {
- // Block type has not been saved to library yet. Disable the delete button
- // and allow user to save.
- this.saveButton.textContent = 'Save "' + blockType + '"';
- this.saveButton.disabled = false;
- this.deleteButton.disabled = true;
- } else {
- // Block type has already been saved. Disable the save button unless the
- // there are unsaved changes (checked below).
- this.saveButton.textContent = 'Update "' + blockType + '"';
- this.saveButton.disabled = true;
- this.deleteButton.disabled = false;
- }
- this.deleteButton.textContent = 'Delete "' + blockType + '"';
-
- // If changes to block have been made and are not saved, make button
- // green to encourage user to save the block.
- if (!savedChanges) {
- var buttonFormatClass = 'button_warn';
-
- // If block type is the default, 'block_type', make button red to alert
- // user.
- if (blockType == 'block_type') {
- buttonFormatClass = 'button_alert';
- }
- goog.dom.classlist.add(this.saveButton, buttonFormatClass);
- this.saveButton.disabled = false;
-
- } else {
- // No changes to save.
- var classesToRemove = ['button_alert', 'button_warn'];
- goog.dom.classlist.removeAll(this.saveButton, classesToRemove);
- this.saveButton.disabled = true;
- }
-
- }
-};
-
-/**
- * Removes option currently selected in dropdown from dropdown menu.
- */
-BlockLibraryView.prototype.removeSelectedOption = function() {
- var selectedOption = this.getSelectedOption();
- this.dropdown.removeNode(selectedOption);
-};
-
-/**
- * Returns block type of selected block.
- * @return {string} Type of block selected.
- */
-BlockLibraryView.prototype.getSelectedBlockType = function() {
- var selectedOption = this.getSelectedOption();
- var blockType = selectedOption.textContent;
- return blockType;
-};
-
-/**
- * Returns selected option.
- * @return {!Element} HTML 'a' element that is the option for a block type.
- */
-BlockLibraryView.prototype.getSelectedOption = function() {
- return this.dropdown.getElementsByClassName('dropdown-content-selected')[0];
-};
-
-/**
- * Removes all options from dropdown.
- */
-BlockLibraryView.prototype.clearOptions = function() {
- var blockOpts = this.dropdown.getElementsByClassName('blockLibOpt');
- var option;
- while ((option = blockOpts[0])) {
- option.parentNode.removeChild(option);
- }
-};
diff --git a/trunk/web/blockly/demos/blockfactory/block_option.js b/trunk/web/blockly/demos/blockfactory/block_option.js
deleted file mode 100644
index 8bc1a2fd411..00000000000
--- a/trunk/web/blockly/demos/blockfactory/block_option.js
+++ /dev/null
@@ -1,176 +0,0 @@
-/**
- * @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 BlockOption class, used to represent each of
- * the various blocks that you may select. Each block option has a checkbox,
- * a label, and a preview workspace through which to view the block.
- *
- * @author quachtina96 (Tina Quach)
- */
-'use strict';
-
-goog.provide('BlockOption');
-goog.require('goog.dom');
-
-
- /**
- * BlockOption Class
- * A block option includes checkbox, label, and div element that shows a preview
- * of the block.
- * @param {!Element} blockSelector Scrollable div that will contain the
- * block options for the selector.
- * @param {string} blockType Type of block for which to create an option.
- * @param {!Element} previewBlockXml XML element containing the preview block.
- * @constructor
- */
-var BlockOption = function(blockSelector, blockType, previewBlockXml) {
- // The div to contain the block option.
- this.blockSelector = blockSelector;
- // The type of block represented by the option.
- this.blockType = blockType;
- // The checkbox for the option. Set in createDom.
- this.checkbox = null;
- // The dom for the option. Set in createDom.
- this.dom = null;
- // Xml element containing the preview block.
- this.previewBlockXml = previewBlockXml;
- // Workspace containing preview of block. Set upon injection of workspace in
- // showPreviewBlock.
- this.previewWorkspace = null;
- // Whether or not block the option is selected.
- this.selected = false;
- // Using this.selected rather than this.checkbox.checked allows for proper
- // handling of click events on the block option; Without this, clicking
- // directly on the checkbox does not toggle selection.
-};
-
-/**
- * Creates the dom for a single block option. Includes checkbox, label, and div
- * in which to inject the preview block.
- * @return {!Element} Root node of the selector dom which consists of a
- * checkbox, a label, and a fixed size preview workspace per block.
- */
-BlockOption.prototype.createDom = function() {
- // Create the div for the block option.
- var blockOptContainer = goog.dom.createDom('div', {
- 'id': this.blockType,
- 'class': 'blockOption'
- }, ''); // Empty quotes for empty div.
-
- // Create and append div in which to inject the workspace for viewing the
- // block option.
- var blockOptionPreview = goog.dom.createDom('div', {
- 'id' : this.blockType + '_workspace',
- 'class': 'blockOption_preview'
- }, '');
- blockOptContainer.appendChild(blockOptionPreview);
-
- // Create and append container to hold checkbox and label.
- var checkLabelContainer = goog.dom.createDom('div', {
- 'class': 'blockOption_checkLabel'
- }, '');
- blockOptContainer.appendChild(checkLabelContainer);
-
- // Create and append container for checkbox.
- var checkContainer = goog.dom.createDom('div', {
- 'class': 'blockOption_check'
- }, '');
- checkLabelContainer.appendChild(checkContainer);
-
- // Create and append checkbox.
- this.checkbox = goog.dom.createDom('input', {
- 'type': 'checkbox',
- 'id': this.blockType + '_check'
- }, '');
- checkContainer.appendChild(this.checkbox);
-
- // Create and append container for block label.
- var labelContainer = goog.dom.createDom('div', {
- 'class': 'blockOption_label'
- }, '');
- checkLabelContainer.appendChild(labelContainer);
-
- // Create and append text node for the label.
- var labelText = goog.dom.createDom('p', {
- 'id': this.blockType + '_text'
- }, this.blockType);
- labelContainer.appendChild(labelText);
-
- this.dom = blockOptContainer;
- return this.dom;
-};
-
-/**
- * Injects a workspace containing the block into the block option's preview div.
- */
-BlockOption.prototype.showPreviewBlock = function() {
- // Get ID of preview workspace.
- var blockOptPreviewID = this.dom.id + '_workspace';
-
- // Inject preview block.
- var workspace = Blockly.inject(blockOptPreviewID, {readOnly:true});
- Blockly.Xml.domToWorkspace(this.previewBlockXml, workspace);
- this.previewWorkspace = workspace;
-
- // Center the preview block in the workspace.
- this.centerBlock();
-};
-
-/**
- * Centers the preview block in the workspace.
- */
-BlockOption.prototype.centerBlock = function() {
- // Get metrics.
- var block = this.previewWorkspace.getTopBlocks()[0];
- var blockMetrics = block.getHeightWidth();
- var blockCoordinates = block.getRelativeToSurfaceXY();
- var workspaceMetrics = this.previewWorkspace.getMetrics();
-
- // Calculate new coordinates.
- var x = workspaceMetrics.viewWidth/2 - blockMetrics['width']/2 -
- blockCoordinates.x;
- var y = workspaceMetrics.viewHeight/2 - blockMetrics['height']/2 -
- blockCoordinates.y;
-
- // Move block.
- block.moveBy(x, y);
-};
-
-/**
- * Selects or deselects the block option.
- * @param {!boolean} selected True if selecting option, false if deselecting
- * option.
- */
-BlockOption.prototype.setSelected = function(selected) {
- this.selected = selected;
- if (this.checkbox) {
- this.checkbox.checked = selected;
- }
-};
-
-/**
- * Returns boolean telling whether or not block is selected.
- * @return {!boolean} True if selecting option, false if deselecting
- * option.
- */
-BlockOption.prototype.isSelected = function() {
- return this.selected;
-};
diff --git a/trunk/web/blockly/demos/blockfactory/blocks.js b/trunk/web/blockly/demos/blockfactory/blocks.js
deleted file mode 100644
index 6dcd0061709..00000000000
--- a/trunk/web/blockly/demos/blockfactory/blocks.js
+++ /dev/null
@@ -1,905 +0,0 @@
-/**
- * Blockly Demos: Block Factory Blocks
- *
- * Copyright 2012 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 Blocks for Blockly's Block Factory application.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-Blockly.Blocks['factory_base'] = {
- // Base of new block.
- init: function() {
- this.setColour(120);
- this.appendDummyInput()
- .appendField('name')
- .appendField(new Blockly.FieldTextInput('block_type'), 'NAME');
- this.appendStatementInput('INPUTS')
- .setCheck('Input')
- .appendField('inputs');
- var dropdown = new Blockly.FieldDropdown([
- ['automatic inputs', 'AUTO'],
- ['external inputs', 'EXT'],
- ['inline inputs', 'INT']]);
- this.appendDummyInput()
- .appendField(dropdown, 'INLINE');
- dropdown = new Blockly.FieldDropdown([
- ['no connections', 'NONE'],
- ['← left output', 'LEFT'],
- ['↕ top+bottom connections', 'BOTH'],
- ['↑ top connection', 'TOP'],
- ['↓ bottom connection', 'BOTTOM']],
- function(option) {
- this.sourceBlock_.updateShape_(option);
- // Connect a shadow block to this new input.
- this.sourceBlock_.spawnOutputShadow_(option);
- });
- this.appendDummyInput()
- .appendField(dropdown, 'CONNECTIONS');
- this.appendValueInput('TOOLTIP')
- .setCheck('String')
- .appendField('tooltip');
- this.appendValueInput('HELPURL')
- .setCheck('String')
- .appendField('help url');
- this.appendValueInput('COLOUR')
- .setCheck('Colour')
- .appendField('colour');
- this.setTooltip('Build a custom block by plugging\n' +
- 'fields, inputs and other blocks here.');
- this.setHelpUrl(
- 'https://developers.google.com/blockly/guides/create-custom-blocks/block-factory');
- },
- mutationToDom: function() {
- var container = document.createElement('mutation');
- container.setAttribute('connections', this.getFieldValue('CONNECTIONS'));
- return container;
- },
- domToMutation: function(xmlElement) {
- var connections = xmlElement.getAttribute('connections');
- this.updateShape_(connections);
- },
- spawnOutputShadow_: function(option) {
- // Helper method for deciding which type of outputs this block needs
- // to attach shaddow blocks to.
- switch (option) {
- case 'LEFT':
- this.connectOutputShadow_('OUTPUTTYPE');
- break;
- case 'TOP':
- this.connectOutputShadow_('TOPTYPE');
- break;
- case 'BOTTOM':
- this.connectOutputShadow_('BOTTOMTYPE');
- break;
- case 'BOTH':
- this.connectOutputShadow_('TOPTYPE');
- this.connectOutputShadow_('BOTTOMTYPE');
- break;
- }
- },
- connectOutputShadow_: function(outputType) {
- // Helper method to create & connect shadow block.
- var type = this.workspace.newBlock('type_null');
- type.setShadow(true);
- type.outputConnection.connect(this.getInput(outputType).connection);
- type.initSvg();
- type.render();
- },
- updateShape_: function(option) {
- var outputExists = this.getInput('OUTPUTTYPE');
- var topExists = this.getInput('TOPTYPE');
- var bottomExists = this.getInput('BOTTOMTYPE');
- if (option == 'LEFT') {
- if (!outputExists) {
- this.addTypeInput_('OUTPUTTYPE', 'output type');
- }
- } else if (outputExists) {
- this.removeInput('OUTPUTTYPE');
- }
- if (option == 'TOP' || option == 'BOTH') {
- if (!topExists) {
- this.addTypeInput_('TOPTYPE', 'top type');
- }
- } else if (topExists) {
- this.removeInput('TOPTYPE');
- }
- if (option == 'BOTTOM' || option == 'BOTH') {
- if (!bottomExists) {
- this.addTypeInput_('BOTTOMTYPE', 'bottom type');
- }
- } else if (bottomExists) {
- this.removeInput('BOTTOMTYPE');
- }
- },
- addTypeInput_: function(name, label) {
- this.appendValueInput(name)
- .setCheck('Type')
- .appendField(label);
- this.moveInputBefore(name, 'COLOUR');
- }
-};
-
-var FIELD_MESSAGE = 'fields %1 %2';
-var FIELD_ARGS = [
- {
- "type": "field_dropdown",
- "name": "ALIGN",
- "options": [['left', 'LEFT'], ['right', 'RIGHT'], ['centre', 'CENTRE']],
- },
- {
- "type": "input_statement",
- "name": "FIELDS",
- "check": "Field"
- }
-];
-
-var TYPE_MESSAGE = 'type %1';
-var TYPE_ARGS = [
- {
- "type": "input_value",
- "name": "TYPE",
- "check": "Type",
- "align": "RIGHT"
- }
-];
-
-Blockly.Blocks['input_value'] = {
- // Value input.
- init: function() {
- this.jsonInit({
- "message0": "value input %1 %2",
- "args0": [
- {
- "type": "field_input",
- "name": "INPUTNAME",
- "text": "NAME"
- },
- {
- "type": "input_dummy"
- }
- ],
- "message1": FIELD_MESSAGE,
- "args1": FIELD_ARGS,
- "message2": TYPE_MESSAGE,
- "args2": TYPE_ARGS,
- "previousStatement": "Input",
- "nextStatement": "Input",
- "colour": 210,
- "tooltip": "A value socket for horizontal connections.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=71"
- });
- },
- onchange: function() {
- inputNameCheck(this);
- }
-};
-
-Blockly.Blocks['input_statement'] = {
- // Statement input.
- init: function() {
- this.jsonInit({
- "message0": "statement input %1 %2",
- "args0": [
- {
- "type": "field_input",
- "name": "INPUTNAME",
- "text": "NAME"
- },
- {
- "type": "input_dummy"
- },
- ],
- "message1": FIELD_MESSAGE,
- "args1": FIELD_ARGS,
- "message2": TYPE_MESSAGE,
- "args2": TYPE_ARGS,
- "previousStatement": "Input",
- "nextStatement": "Input",
- "colour": 210,
- "tooltip": "A statement socket for enclosed vertical stacks.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=246"
- });
- },
- onchange: function() {
- inputNameCheck(this);
- }
-};
-
-Blockly.Blocks['input_dummy'] = {
- // Dummy input.
- init: function() {
- this.jsonInit({
- "message0": "dummy input",
- "message1": FIELD_MESSAGE,
- "args1": FIELD_ARGS,
- "previousStatement": "Input",
- "nextStatement": "Input",
- "colour": 210,
- "tooltip": "For adding fields on a separate row with no " +
- "connections. Alignment options (left, right, centre) " +
- "apply only to multi-line fields.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293"
- });
- }
-};
-
-Blockly.Blocks['field_static'] = {
- // Text value.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('text')
- .appendField(new Blockly.FieldTextInput(''), 'TEXT');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Static text that serves as a label.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=88');
- }
-};
-
-Blockly.Blocks['field_input'] = {
- // Text input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('text input')
- .appendField(new Blockly.FieldTextInput('default'), 'TEXT')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('An input field for the user to enter text.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_number'] = {
- // Numeric input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('numeric input')
- .appendField(new Blockly.FieldNumber(0), 'VALUE')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.appendDummyInput()
- .appendField('min')
- .appendField(new Blockly.FieldNumber(-Infinity), 'MIN')
- .appendField('max')
- .appendField(new Blockly.FieldNumber(Infinity), 'MAX')
- .appendField('precision')
- .appendField(new Blockly.FieldNumber(0, 0), 'PRECISION');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('An input field for the user to enter a number.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_angle'] = {
- // Angle input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('angle input')
- .appendField(new Blockly.FieldAngle('90'), 'ANGLE')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('An input field for the user to enter an angle.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=372');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_dropdown'] = {
- // Dropdown menu.
- init: function() {
- this.appendDummyInput()
- .appendField('dropdown')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.optionList_ = ['text', 'text', 'text'];
- this.updateShape_();
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setMutator(new Blockly.Mutator(['field_dropdown_option_text',
- 'field_dropdown_option_image']));
- this.setColour(160);
- this.setTooltip('Dropdown menu with a list of options.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- },
- mutationToDom: function(workspace) {
- // Create XML to represent menu options.
- var container = document.createElement('mutation');
- container.setAttribute('options', JSON.stringify(this.optionList_));
- return container;
- },
- domToMutation: function(container) {
- // Parse XML to restore the menu options.
- var value = JSON.parse(container.getAttribute('options'));
- if (typeof value == 'number') {
- // Old format from before images were added. November 2016.
- this.optionList_ = [];
- for (var i = 0; i < value; i++) {
- this.optionList_.push('text');
- }
- } else {
- this.optionList_ = value;
- }
- this.updateShape_();
- },
- decompose: function(workspace) {
- // Populate the mutator's dialog with this block's components.
- var containerBlock = workspace.newBlock('field_dropdown_container');
- containerBlock.initSvg();
- var connection = containerBlock.getInput('STACK').connection;
- for (var i = 0; i < this.optionList_.length; i++) {
- var optionBlock = workspace.newBlock(
- 'field_dropdown_option_' + this.optionList_[i]);
- optionBlock.initSvg();
- connection.connect(optionBlock.previousConnection);
- connection = optionBlock.nextConnection;
- }
- return containerBlock;
- },
- compose: function(containerBlock) {
- // Reconfigure this block based on the mutator dialog's components.
- var optionBlock = containerBlock.getInputTargetBlock('STACK');
- // Count number of inputs.
- this.optionList_.length = 0;
- var data = [];
- while (optionBlock) {
- if (optionBlock.type == 'field_dropdown_option_text') {
- this.optionList_.push('text');
- } else if (optionBlock.type == 'field_dropdown_option_image') {
- this.optionList_.push('image');
- }
- data.push([optionBlock.userData_, optionBlock.cpuData_]);
- optionBlock = optionBlock.nextConnection &&
- optionBlock.nextConnection.targetBlock();
- }
- this.updateShape_();
- // Restore any data.
- for (var i = 0; i < this.optionList_.length; i++) {
- var userData = data[i][0];
- if (userData !== undefined) {
- if (typeof userData == 'string') {
- this.setFieldValue(userData || 'option', 'USER' + i);
- } else {
- this.setFieldValue(userData.src, 'SRC' + i);
- this.setFieldValue(userData.width, 'WIDTH' + i);
- this.setFieldValue(userData.height, 'HEIGHT' + i);
- this.setFieldValue(userData.alt, 'ALT' + i);
- }
- this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
- }
- }
- },
- saveConnections: function(containerBlock) {
- // Store all data for each option.
- var optionBlock = containerBlock.getInputTargetBlock('STACK');
- var i = 0;
- while (optionBlock) {
- optionBlock.userData_ = this.getUserData(i);
- optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
- i++;
- optionBlock = optionBlock.nextConnection &&
- optionBlock.nextConnection.targetBlock();
- }
- },
- updateShape_: function() {
- // Delete everything.
- var i = 0;
- while (this.getInput('OPTION' + i)) {
- this.removeInput('OPTION' + i);
- this.removeInput('OPTION_IMAGE' + i, true);
- i++;
- }
- // Rebuild block.
- var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
- for (var i = 0; i <= this.optionList_.length; i++) {
- var type = this.optionList_[i];
- if (type == 'text') {
- this.appendDummyInput('OPTION' + i)
- .appendField('•')
- .appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
- } else if (type == 'image') {
- this.appendDummyInput('OPTION' + i)
- .appendField('•')
- .appendField('image')
- .appendField(new Blockly.FieldTextInput(src), 'SRC' + i);
- this.appendDummyInput('OPTION_IMAGE' + i)
- .appendField(' ')
- .appendField('width')
- .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH' + i)
- .appendField('height')
- .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT' + i)
- .appendField('alt text')
- .appendField(new Blockly.FieldTextInput('*'), 'ALT' + i)
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
- }
- }
- },
- onchange: function() {
- if (this.workspace && this.optionList_.length < 1) {
- this.setWarningText('Drop down menu must\nhave at least one option.');
- } else {
- fieldNameCheck(this);
- }
- },
- getUserData: function(n) {
- if (this.optionList_[n] == 'text') {
- return this.getFieldValue('USER' + n);
- }
- if (this.optionList_[n] == 'image') {
- return {
- src: this.getFieldValue('SRC' + n),
- width: Number(this.getFieldValue('WIDTH' + n)),
- height: Number(this.getFieldValue('HEIGHT' + n)),
- alt: this.getFieldValue('ALT' + n)
- };
- }
- throw 'Unknown dropdown type';
- }
-};
-
-Blockly.Blocks['field_dropdown_container'] = {
- // Container.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('add options');
- this.appendStatementInput('STACK');
- this.setTooltip('Add, remove, or reorder options\n' +
- 'to reconfigure this dropdown menu.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- this.contextMenu = false;
- }
-};
-
-Blockly.Blocks['field_dropdown_option_text'] = {
- // Add text option.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('text option');
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setTooltip('Add a new text option to the dropdown menu.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- this.contextMenu = false;
- }
-};
-
-Blockly.Blocks['field_dropdown_option_image'] = {
- // Add image option.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('image option');
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setTooltip('Add a new image option to the dropdown menu.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- this.contextMenu = false;
- }
-};
-
-Blockly.Blocks['field_checkbox'] = {
- // Checkbox.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('checkbox')
- .appendField(new Blockly.FieldCheckbox('TRUE'), 'CHECKED')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Checkbox field.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=485');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_colour'] = {
- // Colour input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('colour')
- .appendField(new Blockly.FieldColour('#ff0000'), 'COLOUR')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Colour input field.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=495');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_date'] = {
- // Date input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('date')
- .appendField(new Blockly.FieldDate(), 'DATE')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Date input field.');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_variable'] = {
- // Dropdown for variables.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('variable')
- .appendField(new Blockly.FieldTextInput('item'), 'TEXT')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Dropdown menu for variable names.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=510');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_image'] = {
- // Image.
- init: function() {
- this.setColour(160);
- var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
- this.appendDummyInput()
- .appendField('image')
- .appendField(new Blockly.FieldTextInput(src), 'SRC');
- this.appendDummyInput()
- .appendField('width')
- .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH')
- .appendField('height')
- .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT')
- .appendField('alt text')
- .appendField(new Blockly.FieldTextInput('*'), 'ALT');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Static image (JPEG, PNG, GIF, SVG, BMP).\n' +
- 'Retains aspect ratio regardless of height and width.\n' +
- 'Alt text is for when collapsed.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=567');
- }
-};
-
-Blockly.Blocks['type_group'] = {
- // Group of types.
- init: function() {
- this.typeCount_ = 2;
- this.updateShape_();
- this.setOutput(true, 'Type');
- this.setMutator(new Blockly.Mutator(['type_group_item']));
- this.setColour(230);
- this.setTooltip('Allows more than one type to be accepted.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677');
- },
- mutationToDom: function(workspace) {
- // Create XML to represent a group of types.
- var container = document.createElement('mutation');
- container.setAttribute('types', this.typeCount_);
- return container;
- },
- domToMutation: function(container) {
- // Parse XML to restore the group of types.
- this.typeCount_ = parseInt(container.getAttribute('types'), 10);
- this.updateShape_();
- for (var i = 0; i < this.typeCount_; i++) {
- this.removeInput('TYPE' + i);
- }
- for (var i = 0; i < this.typeCount_; i++) {
- var input = this.appendValueInput('TYPE' + i)
- .setCheck('Type');
- if (i == 0) {
- input.appendField('any of');
- }
- }
- },
- decompose: function(workspace) {
- // Populate the mutator's dialog with this block's components.
- var containerBlock = workspace.newBlock('type_group_container');
- containerBlock.initSvg();
- var connection = containerBlock.getInput('STACK').connection;
- for (var i = 0; i < this.typeCount_; i++) {
- var typeBlock = workspace.newBlock('type_group_item');
- typeBlock.initSvg();
- connection.connect(typeBlock.previousConnection);
- connection = typeBlock.nextConnection;
- }
- return containerBlock;
- },
- compose: function(containerBlock) {
- // Reconfigure this block based on the mutator dialog's components.
- var typeBlock = containerBlock.getInputTargetBlock('STACK');
- // Count number of inputs.
- var connections = [];
- while (typeBlock) {
- connections.push(typeBlock.valueConnection_);
- typeBlock = typeBlock.nextConnection &&
- typeBlock.nextConnection.targetBlock();
- }
- // Disconnect any children that don't belong.
- for (var i = 0; i < this.typeCount_; i++) {
- var connection = this.getInput('TYPE' + i).connection.targetConnection;
- if (connection && connections.indexOf(connection) == -1) {
- connection.disconnect();
- }
- }
- this.typeCount_ = connections.length;
- this.updateShape_();
- // Reconnect any child blocks.
- for (var i = 0; i < this.typeCount_; i++) {
- Blockly.Mutator.reconnect(connections[i], this, 'TYPE' + i);
- }
- },
- saveConnections: function(containerBlock) {
- // Store a pointer to any connected child blocks.
- var typeBlock = containerBlock.getInputTargetBlock('STACK');
- var i = 0;
- while (typeBlock) {
- var input = this.getInput('TYPE' + i);
- typeBlock.valueConnection_ = input && input.connection.targetConnection;
- i++;
- typeBlock = typeBlock.nextConnection &&
- typeBlock.nextConnection.targetBlock();
- }
- },
- updateShape_: function() {
- // Modify this block to have the correct number of inputs.
- // Add new inputs.
- for (var i = 0; i < this.typeCount_; i++) {
- if (!this.getInput('TYPE' + i)) {
- var input = this.appendValueInput('TYPE' + i);
- if (i == 0) {
- input.appendField('any of');
- }
- }
- }
- // Remove deleted inputs.
- while (this.getInput('TYPE' + i)) {
- this.removeInput('TYPE' + i);
- i++;
- }
- }
-};
-
-Blockly.Blocks['type_group_container'] = {
- // Container.
- init: function() {
- this.jsonInit({
- "message0": "add types %1 %2",
- "args0": [
- {"type": "input_dummy"},
- {"type": "input_statement", "name": "STACK"}
- ],
- "colour": 230,
- "tooltip": "Add, or remove allowed type.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
- });
- }
-};
-
-Blockly.Blocks['type_group_item'] = {
- // Add type.
- init: function() {
- this.jsonInit({
- "message0": "type",
- "previousStatement": null,
- "nextStatement": null,
- "colour": 230,
- "tooltip": "Add a new allowed type.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
- });
- }
-};
-
-Blockly.Blocks['type_null'] = {
- // Null type.
- valueType: null,
- init: function() {
- this.jsonInit({
- "message0": "any",
- "output": "Type",
- "colour": 230,
- "tooltip": "Any type is allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_boolean'] = {
- // Boolean type.
- valueType: 'Boolean',
- init: function() {
- this.jsonInit({
- "message0": "Boolean",
- "output": "Type",
- "colour": 230,
- "tooltip": "Booleans (true/false) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_number'] = {
- // Number type.
- valueType: 'Number',
- init: function() {
- this.jsonInit({
- "message0": "Number",
- "output": "Type",
- "colour": 230,
- "tooltip": "Numbers (int/float) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_string'] = {
- // String type.
- valueType: 'String',
- init: function() {
- this.jsonInit({
- "message0": "String",
- "output": "Type",
- "colour": 230,
- "tooltip": "Strings (text) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_list'] = {
- // List type.
- valueType: 'Array',
- init: function() {
- this.jsonInit({
- "message0": "Array",
- "output": "Type",
- "colour": 230,
- "tooltip": "Arrays (lists) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_other'] = {
- // Other type.
- init: function() {
- this.jsonInit({
- "message0": "other %1",
- "args0": [{"type": "field_input", "name": "TYPE", "text": ""}],
- "output": "Type",
- "colour": 230,
- "tooltip": "Custom type to allow.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=702"
- });
- }
-};
-
-Blockly.Blocks['colour_hue'] = {
- // Set the colour of the block.
- init: function() {
- this.appendDummyInput()
- .appendField('hue:')
- .appendField(new Blockly.FieldAngle('0', this.validator), 'HUE');
- this.setOutput(true, 'Colour');
- this.setTooltip('Paint the block with this colour.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55');
- },
- validator: function(text) {
- // Update the current block's colour to match.
- var hue = parseInt(text, 10);
- if (!isNaN(hue)) {
- this.sourceBlock_.setColour(hue);
- }
- },
- mutationToDom: function(workspace) {
- var container = document.createElement('mutation');
- container.setAttribute('colour', this.getColour());
- return container;
- },
- domToMutation: function(container) {
- this.setColour(container.getAttribute('colour'));
- }
-};
-
-/**
- * Check to see if more than one field has this name.
- * Highly inefficient (On^2), but n is small.
- * @param {!Blockly.Block} referenceBlock Block to check.
- */
-function fieldNameCheck(referenceBlock) {
- if (!referenceBlock.workspace) {
- // Block has been deleted.
- return;
- }
- var name = referenceBlock.getFieldValue('FIELDNAME').toLowerCase();
- var count = 0;
- var blocks = referenceBlock.workspace.getAllBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- var otherName = block.getFieldValue('FIELDNAME');
- if (!block.disabled && !block.getInheritedDisabled() &&
- otherName && otherName.toLowerCase() == name) {
- count++;
- }
- }
- var msg = (count > 1) ?
- 'There are ' + count + ' field blocks\n with this name.' : null;
- referenceBlock.setWarningText(msg);
-}
-
-/**
- * Check to see if more than one input has this name.
- * Highly inefficient (On^2), but n is small.
- * @param {!Blockly.Block} referenceBlock Block to check.
- */
-function inputNameCheck(referenceBlock) {
- if (!referenceBlock.workspace) {
- // Block has been deleted.
- return;
- }
- var name = referenceBlock.getFieldValue('INPUTNAME').toLowerCase();
- var count = 0;
- var blocks = referenceBlock.workspace.getAllBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- var otherName = block.getFieldValue('INPUTNAME');
- if (!block.disabled && !block.getInheritedDisabled() &&
- otherName && otherName.toLowerCase() == name) {
- count++;
- }
- }
- var msg = (count > 1) ?
- 'There are ' + count + ' input blocks\n with this name.' : null;
- referenceBlock.setWarningText(msg);
-}
diff --git a/trunk/web/blockly/demos/blockfactory/factory.css b/trunk/web/blockly/demos/blockfactory/factory.css
deleted file mode 100644
index 6661c139c87..00000000000
--- a/trunk/web/blockly/demos/blockfactory/factory.css
+++ /dev/null
@@ -1,609 +0,0 @@
-/**
- * @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.
- */
-
-html, body {
- height: 100%;
- min-height: 375px;
-}
-
-body {
- background-color: #fff;
- font-family: sans-serif;
- margin: 0 5px;
- overflow: hidden;
-}
-
-h1 {
- font-weight: normal;
- font-size: 140%;
-}
-
-h3 {
- margin-top: 5px;
- margin-bottom: 0;
-}
-
-table {
- border: none;
- border-collapse: collapse;
- height: 100%;
- margin: 0;
- padding: 0;
- width: 100%;
-}
-
-td {
- vertical-align: top;
- padding: 0;
-}
-
-p {
- display: block;
- -webkit-margin-before: 0em;
- -webkit-margin-after: 0em;
- -webkit-margin-start: 0px;
- -webkit-margin-end: 0px;
- padding: 5px 0px;
-}
-
-#factoryHeader {
- display: table;
- height: 10%;
-}
-
-#blockly {
- position: absolute;
-}
-
-#blocklyMask {
- background-color: #000;
- cursor: not-allowed;
- display: none;
- position: fixed;
- opacity: 0.2;
- z-index: 9;
-}
-
-#preview {
- position: absolute;
-}
-
-pre,
-#languageTA {
- border: #ddd 1px solid;
- margin-top: 0;
- position: absolute;
- overflow: scroll;
-}
-
-#languageTA {
- display: none;
- font: 10pt monospace;
-}
-
-.downloadButton {
- padding: 5px;
-}
-
-.disabled {
- color: #888;
-}
-
-button:disabled, .buttonStyle:disabled {
- opacity: 0.6;
-}
-
-button>*, .buttonStyle>* {
- opacity: 1;
- vertical-align: text-bottom;
-}
-
-button, .buttonStyle {
- border-radius: 4px;
- border: 1px solid #ddd;
- background-color: #eee;
- color: #000;
- padding: 10px;
- margin: 10px 5px;
- font-size: small;
-}
-
-.buttonStyle:hover:not(:disabled), button:hover:not(:disabled) {
- box-shadow: 2px 2px 5px #888;
-}
-
-.buttonStyle:hover:not(:disabled)>*, button:hover:not(:disabled)>* {
- opacity: 1;
-}
-
-#linkButton {
- display: none;
-}
-
-#helpButton {
- float: right;
-}
-
-#blockFactoryContent {
- height: 85%;
- width: 100%;
- overflow: hidden;
-}
-
-#blockFactoryPreview {
- height: 100%;
- width: 100%;
-}
-
-#blockLibraryContainer {
- vertical-align: bottom;
-}
-
-#blockLibraryControls {
- text-align: right;
- vertical-align: middle;
-}
-
-#previewContainer {
- vertical-align: bottom;
-}
-
-#buttonContainer {
- text-align: right;
- vertical-align: middle;
-}
-
-#files {
- position: absolute;
- visibility: hidden;
-}
-
-.toolbox {
- display: none;
-}
-
-#blocklyWorkspaceContainer {
- width: 50%;
-}
-
-#workspaceFactoryContent {
- clear: both;
- display: none;
- height: 90%;
- overflow-x: hidden;
- overflow-y: scroll;
-}
-
-/* Exporter */
-
-#blockLibraryExporter {
- clear: both;
- display: none;
- height: 90%;
- overflow-x: hidden;
- overflow-y: scroll;
-}
-
-#exportSelector {
- display: inline-block;
- float: left;
- height: 70%;
- width: 30%;
-}
-
-#exportSettings {
- float: left;
- overflow: hidden;
- padding-left: 16px;
- width: 20%;
-}
-
-#selectedBlocksTextContainer {
- max-height: 200px;
- overflow-y: scroll;
- padding-bottom: 2em;
-}
-
-::-webkit-scrollbar {
- -webkit-appearance: none;
- width: 7px;
-}
-
-::-webkit-scrollbar-thumb {
- border-radius: 4px;
- background-color: #ccc;
- -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5);
-}
-
-.subsettings {
- margin: 0px 25px;
-}
-
-#exporterHiddenWorkspace {
- display: none;
-}
-
-#exportPreview {
- float: right;
- height: 90%;
- overflow: hidden;
- width: 45%;
-}
-
-.exportPreviewTextArea {
- display: block;
- float: right;
- height: 40%;
- width: 100%;
-}
-
-#genStubs_textArea, #blockDefs_textArea {
- display: block;
- height: 80%;
- margin-right: 20px;
- max-height: 300px;
- overflow: scroll;
- position: static;
-}
-
-#blockDefs_label, #genStubs_label {
- display: block;
-}
-
-#blockSelector {
- background-color: #eee;
- border: 1px solid lightgrey;
- width: 80%;
- height: 90%;
- overflow-y: scroll;
- position: relative;
-}
-
-/* Exporter Block Option */
-
-.blockOption {
- background-color: #eee;
- padding: 0px 20px;
- width: 95%;
-}
-
-.blockOption_check_label {
- position: relative;
-}
-
-.blockOption_check {
- float: left;
- display:inline;
- padding: 4px;
-}
-
-.blockOption_label {
- display:inline;
- max-width: inherit;
- word-wrap: break-word;
-}
-
-.blockOption_preview {
- height: 100px;
- padding-top: 10px;
- width: 90%;
-}
-
-/* Block Library */
-
-#dropdownDiv_blockLib {
- max-height: 65%;
- overflow-y: scroll;
-}
-
-#button_blockLib {
- border-color: darkgrey;
- font-size: large;
-}
-
-.button_alert {
- background-color: #fcc;
- border-color: #f99;
-}
-
-.button_warn {
- background-color: #aea;
- border-color: #5d5;
-}
-
-/* Tabs */
-
-.tab {
- float: left;
- padding: 5px 19px;
-}
-
-.tab:hover:not(.tabon){
- background-color: #e8e8e8;
-}
-
-.tab.tabon {
- background-color: #ccc;
-}
-
-.tab.taboff {
- cursor: pointer;
-}
-
-#tabContainer {
- background-color: #f8f8f8;
- border-top: 1px solid #ddd;
- border-bottom: 1px solid #ddd;
- display: table;
- width: 100%;
-}
-
-/* Workspace Factory */
-
-section {
- float: left;
-}
-
-aside {
- float: right;
-}
-
-#categoryTable>table {
- border: 1px solid #ccc;
- border-bottom: none;
- width: auto;
-}
-
-td.tabon {
- background-color: #ccc;
- border-bottom-color: #ccc;
- padding: 5px 19px;
-}
-
-td.taboff {
- cursor: pointer;
- padding: 5px 19px;
-}
-
-td.taboff:hover {
- background-color: #eee;
-}
-
-.large {
- font-size: large;
-}
-
-.inputfile {
- height: 0;
- opacity: 0;
- overflow: hidden;
- position: absolute;
- width: 0;
- z-index: -1;
-}
-#wfactoryHeader {
- height: 29%;
- padding: 0.5%;
-}
-
-#workspaceTabs {
- background-color: #f8f8f8;
- border: 1px solid #ccc;
- display: table;
- width: auto;
-}
-
-#toolbox_section {
- height: 85%;
- width: 60%;
-}
-
-#previewHelp {
- padding: 10px;
- width: 98%;
-}
-
-#toolbox_blocks {
- height: 100%;
- width: 100%;
-}
-
-#preview_blocks {
- height: 80%;
- padding: 10px;
- width: 100%;
-}
-
-#createDiv {
- height: 79%;
- padding: 0.5%;
- width: 60%;
-}
-
-#previewDiv {
- border: 10px solid #eee;
- height: 77%;
- margin-right: 0.5%;
- padding-bottom: 10px;
- width: 35%;
-}
-
-#previewBorder {
- border: 5px solid #ddd;
- height: 100%;
- padding-right: 20px;
-}
-
-.disabled {
- background-color: white;
- opacity: 0.5;
-}
-
-#toolbox_div {
- display: table;
- height: auto;
- margin-right: 5%;
- overflow: hidden;
- width: 35%;
-}
-
-#preload_div {
- display: table;
- height: 75%;
- margin-left: 2%;
- margin-right: 2%;
- max-height: 500px;
- overflow: hidden;
- overflow-y: scroll;
- width: 30%;
-}
-
-#shadowBlockDropdown {
- height: 15%;
-}
-
-#preloadHelp {
- display: table-row;
- height: 30%;
-}
-
-#workspace_options {
- display: table-row;
- margin-top: 2%;
-}
-
-#disable_div {
- background-color: white;
- height: 100%;
- left: 0;
- opacity: .5;
- position: absolute;
- top: 0;
- width: 100%;
- z-index: -1; /* Start behind workspace */
-}
-
-#grid_options, #zoom_options, #maxBlockNumber_option {
- padding-left: 15px;
-}
-
-#modalShadow {
- display: none;
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background: rgba(0, 0, 0, 0.05);
- z-index: 100;
-}
-
-/* Rules for Closure popup color picker */
-.goog-palette {
- outline: none;
- cursor: default;
-}
-
-.goog-palette-cell {
- height: 13px;
- width: 15px;
- margin: 0;
- border: 0;
- text-align: center;
- vertical-align: middle;
- border-right: 1px solid #000;
- font-size: 1px;
-}
-
-.goog-palette-colorswatch {
- border: 1px solid #000;
- height: 13px;
- position: relative;
- width: 15px;
-}
-
-.goog-palette-cell-hover .goog-palette-colorswatch {
- border: 1px solid #fff;
-}
-
-.goog-palette-cell-selected .goog-palette-colorswatch {
- border: 1px solid #000;
- color: #fff;
-}
-
-.goog-palette-table {
- border: 1px solid #000;
- border-collapse: collapse;
-}
-
-.goog-popupcolorpicker {
- position: absolute;
- z-index: 101; /* On top of the modal Shadow. */
-}
-
-/* The container
- needed to position the dropdown content */
-.dropdown {
- display: inline-block;
-}
-
-/* Dropdown Content (Hidden by Default) */
-.dropdown-content {
- background-color: #fff;
- box-shadow: 0px 8px 16px 0px rgba(0,0,0,.2);
- display: none;
- min-width: 170px;
- opacity: 1;
- position: absolute;
- z-index: 101; /* On top of the modal Shadow. */
-}
-
-/* Links inside the dropdown */
-.dropdown-content a, .dropdown-content label {
- color: black;
- display: block;
- font-size: small;
- padding: 12px 16px;
- text-decoration: none;
-}
-
-/* Change color of dropdown links on hover. */
-.dropdown-content a:hover, .dropdown-content label:hover {
- background-color: #EEE;
-}
-
-/* Change color of dropdown links on selected. */
-.dropdown-content-selected {
- background-color: #DDD;
-}
-
-/* Show the dropdown menu */
-.show {
- display: block;
-}
-
-.shadowBlock>.blocklyPath {
- fill-opacity: .5;
- stroke-opacity: .5;
-}
-
-.shadowBlock>.blocklyPathLight,
-.shadowBlock>.blocklyPathDark {
- display: none;
-}
diff --git a/trunk/web/blockly/demos/blockfactory/factory.js b/trunk/web/blockly/demos/blockfactory/factory.js
deleted file mode 100644
index 974cd8a7ec5..00000000000
--- a/trunk/web/blockly/demos/blockfactory/factory.js
+++ /dev/null
@@ -1,276 +0,0 @@
-/**
- * @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 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 = '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '230' +
- '';
-
-/**
- * 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');
-};
diff --git a/trunk/web/blockly/demos/blockfactory/factory_utils.js b/trunk/web/blockly/demos/blockfactory/factory_utils.js
deleted file mode 100644
index 5da02f9c47a..00000000000
--- a/trunk/web/blockly/demos/blockfactory/factory_utils.js
+++ /dev/null
@@ -1,999 +0,0 @@
-/**
- * @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 FactoryUtils is a namespace that holds block starter code
- * generation functions shared by the Block Factory, Workspace Factory, and
- * Exporter applications within Blockly Factory. Holds functions to generate
- * block definitions and generator stubs and to create and download files.
- *
- * @author fraser@google.com (Neil Fraser), quachtina96 (Tina Quach)
- */
- 'use strict';
-
-/**
- * Namespace for FactoryUtils.
- */
-goog.provide('FactoryUtils');
-
-
-/**
- * Get block definition code for the current block.
- * @param {string} blockType Type of block.
- * @param {!Blockly.Block} rootBlock RootBlock from main workspace in which
- * user uses Block Factory Blocks to create a custom block.
- * @param {string} format 'JSON' or 'JavaScript'.
- * @param {!Blockly.Workspace} workspace Where the root block lives.
- * @return {string} Block definition.
- */
-FactoryUtils.getBlockDefinition = function(blockType, rootBlock, format, workspace) {
- blockType = FactoryUtils.cleanBlockType(blockType);
- switch (format) {
- case 'JSON':
- var code = FactoryUtils.formatJson_(blockType, rootBlock);
- break;
- case 'JavaScript':
- var code = FactoryUtils.formatJavaScript_(blockType, rootBlock, workspace);
- break;
- }
- return code;
-};
-
-/**
- * Convert invalid block name to a valid one. Replaces whitespace
- * and prepend names that start with a digit with an '_'.
- * @param {string} blockType Type of block.
- * @return {string} Cleaned up block type.
- */
-FactoryUtils.cleanBlockType = function(blockType) {
- if (!blockType) {
- return '';
- }
- return blockType.replace(/\W/g, '_').replace(/^(\d)/, '_$1');
-};
-
-/**
- * Get the generator code for a given block.
- * @param {!Blockly.Block} block Rendered block in preview workspace.
- * @param {string} generatorLanguage 'JavaScript', 'Python', 'PHP', 'Lua',
- * 'Dart'.
- * @return {string} Generator code for multiple blocks.
- */
-FactoryUtils.getGeneratorStub = function(block, generatorLanguage) {
- function makeVar(root, name) {
- name = name.toLowerCase().replace(/\W/g, '_');
- return ' var ' + root + '_' + name;
- }
- // The makevar function lives in the original update generator.
- var language = generatorLanguage;
- var code = [];
- code.push("Blockly." + language + "['" + block.type +
- "'] = function(block) {");
-
- // Generate getters for any fields or inputs.
- for (var i = 0, input; input = block.inputList[i]; i++) {
- for (var j = 0, field; field = input.fieldRow[j]; j++) {
- var name = field.name;
- if (!name) {
- continue;
- }
- if (field instanceof Blockly.FieldVariable) {
- // Subclass of Blockly.FieldDropdown, must test first.
- code.push(makeVar('variable', name) +
- " = Blockly." + language +
- ".variableDB_.getName(block.getFieldValue('" + name +
- "'), Blockly.Variables.NAME_TYPE);");
- } else if (field instanceof Blockly.FieldAngle) {
- // Subclass of Blockly.FieldTextInput, must test first.
- code.push(makeVar('angle', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (Blockly.FieldDate && field instanceof Blockly.FieldDate) {
- // Blockly.FieldDate may not be compiled into Blockly.
- code.push(makeVar('date', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldColour) {
- code.push(makeVar('colour', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldCheckbox) {
- code.push(makeVar('checkbox', name) +
- " = block.getFieldValue('" + name + "') == 'TRUE';");
- } else if (field instanceof Blockly.FieldDropdown) {
- code.push(makeVar('dropdown', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldNumber) {
- code.push(makeVar('number', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldTextInput) {
- code.push(makeVar('text', name) +
- " = block.getFieldValue('" + name + "');");
- }
- }
- var name = input.name;
- if (name) {
- if (input.type == Blockly.INPUT_VALUE) {
- code.push(makeVar('value', name) +
- " = Blockly." + language + ".valueToCode(block, '" + name +
- "', Blockly." + language + ".ORDER_ATOMIC);");
- } else if (input.type == Blockly.NEXT_STATEMENT) {
- code.push(makeVar('statements', name) +
- " = Blockly." + language + ".statementToCode(block, '" +
- name + "');");
- }
- }
- }
- // Most languages end lines with a semicolon. Python does not.
- var lineEnd = {
- 'JavaScript': ';',
- 'Python': '',
- 'PHP': ';',
- 'Dart': ';'
- };
- code.push(" // TODO: Assemble " + language + " into code variable.");
- if (block.outputConnection) {
- code.push(" var code = '...';");
- code.push(" // TODO: Change ORDER_NONE to the correct strength.");
- code.push(" return [code, Blockly." + language + ".ORDER_NONE];");
- } else {
- code.push(" var code = '..." + (lineEnd[language] || '') + "\\n';");
- code.push(" return code;");
- }
- code.push("};");
-
- return code.join('\n');
-};
-
-/**
- * Update the language code as JSON.
- * @param {string} blockType Name of block.
- * @param {!Blockly.Block} rootBlock Factory_base block.
- * @return {string} Generanted language code.
- * @private
- */
-FactoryUtils.formatJson_ = function(blockType, rootBlock) {
- var JS = {};
- // Type is not used by Blockly, but may be used by a loader.
- JS.type = blockType;
- // Generate inputs.
- var message = [];
- var args = [];
- var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
- var lastInput = null;
- while (contentsBlock) {
- if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
- var fields = FactoryUtils.getFieldsJson_(
- contentsBlock.getInputTargetBlock('FIELDS'));
- for (var i = 0; i < fields.length; i++) {
- if (typeof fields[i] == 'string') {
- message.push(fields[i].replace(/%/g, '%%'));
- } else {
- args.push(fields[i]);
- message.push('%' + args.length);
- }
- }
-
- var input = {type: contentsBlock.type};
- // Dummy inputs don't have names. Other inputs do.
- if (contentsBlock.type != 'input_dummy') {
- input.name = contentsBlock.getFieldValue('INPUTNAME');
- }
- var check = JSON.parse(
- FactoryUtils.getOptTypesFrom(contentsBlock, 'TYPE') || 'null');
- if (check) {
- input.check = check;
- }
- var align = contentsBlock.getFieldValue('ALIGN');
- if (align != 'LEFT') {
- input.align = align;
- }
- args.push(input);
- message.push('%' + args.length);
- lastInput = contentsBlock;
- }
- contentsBlock = contentsBlock.nextConnection &&
- contentsBlock.nextConnection.targetBlock();
- }
- // Remove last input if dummy and not empty.
- if (lastInput && lastInput.type == 'input_dummy') {
- var fields = lastInput.getInputTargetBlock('FIELDS');
- if (fields && FactoryUtils.getFieldsJson_(fields).join('').trim() != '') {
- var align = lastInput.getFieldValue('ALIGN');
- if (align != 'LEFT') {
- JS.lastDummyAlign0 = align;
- }
- args.pop();
- message.pop();
- }
- }
- JS.message0 = message.join(' ');
- if (args.length) {
- JS.args0 = args;
- }
- // Generate inline/external switch.
- if (rootBlock.getFieldValue('INLINE') == 'EXT') {
- JS.inputsInline = false;
- } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
- JS.inputsInline = true;
- }
- // Generate output, or next/previous connections.
- switch (rootBlock.getFieldValue('CONNECTIONS')) {
- case 'LEFT':
- JS.output =
- JSON.parse(
- FactoryUtils.getOptTypesFrom(rootBlock, 'OUTPUTTYPE') || 'null');
- break;
- case 'BOTH':
- JS.previousStatement =
- JSON.parse(
- FactoryUtils.getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
- JS.nextStatement =
- JSON.parse(
- FactoryUtils.getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
- break;
- case 'TOP':
- JS.previousStatement =
- JSON.parse(
- FactoryUtils.getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
- break;
- case 'BOTTOM':
- JS.nextStatement =
- JSON.parse(
- FactoryUtils.getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
- break;
- }
- // Generate colour.
- var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
- if (colourBlock && !colourBlock.disabled) {
- var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
- JS.colour = hue;
- }
-
- JS.tooltip = FactoryUtils.getTooltipFromRootBlock_(rootBlock);
- JS.helpUrl = FactoryUtils.getHelpUrlFromRootBlock_(rootBlock);
-
- return JSON.stringify(JS, null, ' ');
-};
-
-/**
- * Update the language code as JavaScript.
- * @param {string} blockType Name of block.
- * @param {!Blockly.Block} rootBlock Factory_base block.
- * @param {!Blockly.Workspace} workspace Where the root block lives.
- * @return {string} Generated language code.
- * @private
- */
-FactoryUtils.formatJavaScript_ = function(blockType, rootBlock, workspace) {
- var code = [];
- code.push("Blockly.Blocks['" + blockType + "'] = {");
- code.push(" init: function() {");
- // Generate inputs.
- var TYPES = {'input_value': 'appendValueInput',
- 'input_statement': 'appendStatementInput',
- 'input_dummy': 'appendDummyInput'};
- var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
- while (contentsBlock) {
- if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
- var name = '';
- // Dummy inputs don't have names. Other inputs do.
- if (contentsBlock.type != 'input_dummy') {
- name =
- JSON.stringify(contentsBlock.getFieldValue('INPUTNAME'));
- }
- code.push(' this.' + TYPES[contentsBlock.type] + '(' + name + ')');
- var check = FactoryUtils.getOptTypesFrom(contentsBlock, 'TYPE');
- if (check) {
- code.push(' .setCheck(' + check + ')');
- }
- var align = contentsBlock.getFieldValue('ALIGN');
- if (align != 'LEFT') {
- code.push(' .setAlign(Blockly.ALIGN_' + align + ')');
- }
- var fields = FactoryUtils.getFieldsJs_(
- contentsBlock.getInputTargetBlock('FIELDS'));
- for (var i = 0; i < fields.length; i++) {
- code.push(' .appendField(' + fields[i] + ')');
- }
- // Add semicolon to last line to finish the statement.
- code[code.length - 1] += ';';
- }
- contentsBlock = contentsBlock.nextConnection &&
- contentsBlock.nextConnection.targetBlock();
- }
- // Generate inline/external switch.
- if (rootBlock.getFieldValue('INLINE') == 'EXT') {
- code.push(' this.setInputsInline(false);');
- } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
- code.push(' this.setInputsInline(true);');
- }
- // Generate output, or next/previous connections.
- switch (rootBlock.getFieldValue('CONNECTIONS')) {
- case 'LEFT':
- code.push(FactoryUtils.connectionLineJs_('setOutput', 'OUTPUTTYPE', workspace));
- break;
- case 'BOTH':
- code.push(
- FactoryUtils.connectionLineJs_('setPreviousStatement', 'TOPTYPE', workspace));
- code.push(
- FactoryUtils.connectionLineJs_('setNextStatement', 'BOTTOMTYPE', workspace));
- break;
- case 'TOP':
- code.push(
- FactoryUtils.connectionLineJs_('setPreviousStatement', 'TOPTYPE', workspace));
- break;
- case 'BOTTOM':
- code.push(
- FactoryUtils.connectionLineJs_('setNextStatement', 'BOTTOMTYPE', workspace));
- break;
- }
- // Generate colour.
- var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
- if (colourBlock && !colourBlock.disabled) {
- var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
- if (!isNaN(hue)) {
- code.push(' this.setColour(' + hue + ');');
- }
- }
-
- var tooltip = FactoryUtils.getTooltipFromRootBlock_(rootBlock);
- var helpUrl = FactoryUtils.getHelpUrlFromRootBlock_(rootBlock);
- code.push(" this.setTooltip('" + tooltip + "');");
- code.push(" this.setHelpUrl('" + helpUrl + "');");
- code.push(' }');
- code.push('};');
- return code.join('\n');
-};
-
-/**
- * Create JS code required to create a top, bottom, or value connection.
- * @param {string} functionName JavaScript function name.
- * @param {string} typeName Name of type input.
- * @param {!Blockly.Workspace} workspace Where the root block lives.
- * @return {string} Line of JavaScript code to create connection.
- * @private
- */
-FactoryUtils.connectionLineJs_ = function(functionName, typeName, workspace) {
- var type = FactoryUtils.getOptTypesFrom(
- FactoryUtils.getRootBlock(workspace), typeName);
- if (type) {
- type = ', ' + type;
- } else {
- type = '';
- }
- return ' this.' + functionName + '(true' + type + ');';
-};
-
-/**
- * Returns field strings and any config.
- * @param {!Blockly.Block} block Input block.
- * @return {!Array.} Field strings.
- * @private
- */
-FactoryUtils.getFieldsJs_ = function(block) {
- var fields = [];
- while (block) {
- if (!block.disabled && !block.getInheritedDisabled()) {
- switch (block.type) {
- case 'field_static':
- // Result: 'hello'
- fields.push(JSON.stringify(block.getFieldValue('TEXT')));
- break;
- case 'field_input':
- // Result: new Blockly.FieldTextInput('Hello'), 'GREET'
- fields.push('new Blockly.FieldTextInput(' +
- JSON.stringify(block.getFieldValue('TEXT')) + '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_number':
- // Result: new Blockly.FieldNumber(10, 0, 100, 1), 'NUMBER'
- var args = [
- Number(block.getFieldValue('VALUE')),
- Number(block.getFieldValue('MIN')),
- Number(block.getFieldValue('MAX')),
- Number(block.getFieldValue('PRECISION'))
- ];
- // Remove any trailing arguments that aren't needed.
- if (args[3] == 0) {
- args.pop();
- if (args[2] == Infinity) {
- args.pop();
- if (args[1] == -Infinity) {
- args.pop();
- }
- }
- }
- fields.push('new Blockly.FieldNumber(' + args.join(', ') + '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_angle':
- // Result: new Blockly.FieldAngle(90), 'ANGLE'
- fields.push('new Blockly.FieldAngle(' +
- parseFloat(block.getFieldValue('ANGLE')) + '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_checkbox':
- // Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
- fields.push('new Blockly.FieldCheckbox(' +
- JSON.stringify(block.getFieldValue('CHECKED')) +
- '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_colour':
- // Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
- fields.push('new Blockly.FieldColour(' +
- JSON.stringify(block.getFieldValue('COLOUR')) +
- '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_date':
- // Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
- fields.push('new Blockly.FieldDate(' +
- JSON.stringify(block.getFieldValue('DATE')) + '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_variable':
- // Result: new Blockly.FieldVariable('item'), 'VAR'
- var varname
- = JSON.stringify(block.getFieldValue('TEXT') || null);
- fields.push('new Blockly.FieldVariable(' + varname + '), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_dropdown':
- // Result:
- // new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
- var options = [];
- for (var i = 0; i < block.optionList_.length; i++) {
- options[i] = JSON.stringify([block.getUserData(i),
- block.getFieldValue('CPU' + i)]);
- }
- if (options.length) {
- fields.push('new Blockly.FieldDropdown([' +
- options.join(', ') + ']), ' +
- JSON.stringify(block.getFieldValue('FIELDNAME')));
- }
- break;
- case 'field_image':
- // Result: new Blockly.FieldImage('http://...', 80, 60, '*')
- var src = JSON.stringify(block.getFieldValue('SRC'));
- var width = Number(block.getFieldValue('WIDTH'));
- var height = Number(block.getFieldValue('HEIGHT'));
- var alt = JSON.stringify(block.getFieldValue('ALT'));
- fields.push('new Blockly.FieldImage(' +
- src + ', ' + width + ', ' + height + ', ' + alt + ')');
- break;
- }
- }
- block = block.nextConnection && block.nextConnection.targetBlock();
- }
- return fields;
-};
-
-/**
- * Returns field strings and any config.
- * @param {!Blockly.Block} block Input block.
- * @return {!Array.} Array of static text and field configs.
- * @private
- */
-FactoryUtils.getFieldsJson_ = function(block) {
- var fields = [];
- while (block) {
- if (!block.disabled && !block.getInheritedDisabled()) {
- switch (block.type) {
- case 'field_static':
- // Result: 'hello'
- fields.push(block.getFieldValue('TEXT'));
- break;
- case 'field_input':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- text: block.getFieldValue('TEXT')
- });
- break;
- case 'field_number':
- var obj = {
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- value: parseFloat(block.getFieldValue('VALUE'))
- };
- var min = parseFloat(block.getFieldValue('MIN'));
- if (min > -Infinity) {
- obj.min = min;
- }
- var max = parseFloat(block.getFieldValue('MAX'));
- if (max < Infinity) {
- obj.max = max;
- }
- var precision = parseFloat(block.getFieldValue('PRECISION'));
- if (precision) {
- obj.precision = precision;
- }
- fields.push(obj);
- break;
- case 'field_angle':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- angle: Number(block.getFieldValue('ANGLE'))
- });
- break;
- case 'field_checkbox':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- checked: block.getFieldValue('CHECKED') == 'TRUE'
- });
- break;
- case 'field_colour':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- colour: block.getFieldValue('COLOUR')
- });
- break;
- case 'field_date':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- date: block.getFieldValue('DATE')
- });
- break;
- case 'field_variable':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- variable: block.getFieldValue('TEXT') || null
- });
- break;
- case 'field_dropdown':
- var options = [];
- for (var i = 0; i < block.optionList_.length; i++) {
- options[i] = [block.getUserData(i),
- block.getFieldValue('CPU' + i)];
- }
- if (options.length) {
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- options: options
- });
- }
- break;
- case 'field_image':
- fields.push({
- type: block.type,
- src: block.getFieldValue('SRC'),
- width: Number(block.getFieldValue('WIDTH')),
- height: Number(block.getFieldValue('HEIGHT')),
- alt: block.getFieldValue('ALT')
- });
- break;
- }
- }
- block = block.nextConnection && block.nextConnection.targetBlock();
- }
- return fields;
-};
-
-/**
- * Fetch the type(s) defined in the given input.
- * Format as a string for appending to the generated code.
- * @param {!Blockly.Block} block Block with input.
- * @param {string} name Name of the input.
- * @return {?string} String defining the types.
- */
-FactoryUtils.getOptTypesFrom = function(block, name) {
- var types = FactoryUtils.getTypesFrom_(block, name);
- if (types.length == 0) {
- return undefined;
- } else if (types.indexOf('null') != -1) {
- return 'null';
- } else if (types.length == 1) {
- return types[0];
- } else {
- return '[' + types.join(', ') + ']';
- }
-};
-
-
-/**
- * Fetch the type(s) defined in the given input.
- * @param {!Blockly.Block} block Block with input.
- * @param {string} name Name of the input.
- * @return {!Array.} List of types.
- * @private
- */
-FactoryUtils.getTypesFrom_ = function(block, name) {
- var typeBlock = block.getInputTargetBlock(name);
- var types;
- if (!typeBlock || typeBlock.disabled) {
- types = [];
- } else if (typeBlock.type == 'type_other') {
- types = [JSON.stringify(typeBlock.getFieldValue('TYPE'))];
- } else if (typeBlock.type == 'type_group') {
- types = [];
- for (var n = 0; n < typeBlock.typeCount_; n++) {
- types = types.concat(FactoryUtils.getTypesFrom_(typeBlock, 'TYPE' + n));
- }
- // Remove duplicates.
- var hash = Object.create(null);
- for (var n = types.length - 1; n >= 0; n--) {
- if (hash[types[n]]) {
- types.splice(n, 1);
- }
- hash[types[n]] = true;
- }
- } else {
- types = [JSON.stringify(typeBlock.valueType)];
- }
- return types;
-};
-
-/**
- * Return the uneditable container block that everything else attaches to in
- * given workspace.
- * @param {!Blockly.Workspace} workspace Where the root block lives.
- * @return {Blockly.Block} Root block.
- */
-FactoryUtils.getRootBlock = function(workspace) {
- var blocks = workspace.getTopBlocks(false);
- for (var i = 0, block; block = blocks[i]; i++) {
- if (block.type == 'factory_base') {
- return block;
- }
- }
- return null;
-};
-
-// TODO(quachtina96): Move hide, show, makeInvisible, and makeVisible to a new
-// AppView namespace.
-
-/**
- * Hides element so that it's invisible and doesn't take up space.
- * @param {string} elementID ID of element to hide.
- */
-FactoryUtils.hide = function(elementID) {
- document.getElementById(elementID).style.display = 'none';
-};
-
-/**
- * Un-hides an element.
- * @param {string} elementID ID of element to hide.
- */
-FactoryUtils.show = function(elementID) {
- document.getElementById(elementID).style.display = 'block';
-};
-
-/**
- * Hides element so that it's invisible but still takes up space.
- * @param {string} elementID ID of element to hide.
- */
-FactoryUtils.makeInvisible = function(elementID) {
- document.getElementById(elementID).visibility = 'hidden';
-};
-
-/**
- * Makes element visible.
- * @param {string} elementID ID of element to hide.
- */
-FactoryUtils.makeVisible = function(elementID) {
- document.getElementById(elementID).visibility = 'visible';
-};
-
-/**
- * Create a file with the given attributes and download it.
- * @param {string} contents The contents of the file.
- * @param {string} filename The name of the file to save to.
- * @param {string} fileType The type of the file to save.
- */
-FactoryUtils.createAndDownloadFile = function(contents, filename, fileType) {
- var data = new Blob([contents], {type: 'text/' + fileType});
- var clickEvent = new MouseEvent("click", {
- "view": window,
- "bubbles": true,
- "cancelable": false
- });
-
- var a = document.createElement('a');
- a.href = window.URL.createObjectURL(data);
- a.download = filename;
- a.textContent = 'Download file!';
- a.dispatchEvent(clickEvent);
-};
-
-/**
- * Get Blockly Block by rendering pre-defined block in workspace.
- * @param {!Element} blockType Type of block that has already been defined.
- * @param {!Blockly.Workspace} workspace Workspace on which to render
- * the block.
- * @return {!Blockly.Block} The Blockly.Block of desired type.
- */
-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.getBlockTypeFromJsDefinition = 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.} 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.} Array of block definitions.
- */
-FactoryUtils.parseJsBlockDefinitions = 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.} Array of block definitions.
- */
-FactoryUtils.parseJsonBlockDefinitions = 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 {!Array.} Array of block types defined.
- */
-FactoryUtils.defineAndGetBlockTypes = function(blockDefsString, format) {
- var blockTypes = [];
-
- // Define blocks and get block types.
- if (format == 'JSON') {
- var blockDefArray = FactoryUtils.parseJsonBlockDefinitions(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.parseJsBlockDefinitions(blockDefsString);
-
- // Populate array of block types.
- for (var i = 0, blockDef; blockDef = blockDefArray[i]; i++) {
- var blockType = FactoryUtils.getBlockTypeFromJsDefinition(blockDef);
- blockTypes.push(blockType);
- }
-
- // Define all blocks.
- eval(blockDefsString);
- }
-
- return blockTypes;
-};
-
-/**
- * Inject code into a pre tag, with syntax highlighting.
- * Safe from HTML/script injection.
- * @param {string} code Lines of code.
- * @param {string} id ID of
element to inject into.
- */
-FactoryUtils.injectCode = function(code, id) {
- var pre = document.getElementById(id);
- pre.textContent = code;
- code = pre.textContent;
- code = prettyPrintOne(code, 'js');
- pre.innerHTML = code;
-};
-
-/**
- * Returns whether or not two blocks are the same based on their XML. Expects
- * XML with a single child node that is a factory_base block, the XML found on
- * Block Factory's main workspace.
- * @param {!Element} blockXml1 An XML element with a single child node that
- * is a factory_base block.
- * @param {!Element} blockXml2 An XML element with a single child node that
- * is a factory_base block.
- * @return {boolean} Whether or not two blocks are the same based on their XML.
- */
-FactoryUtils.sameBlockXml = function(blockXml1, blockXml2) {
- // Each XML element should contain a single child element with a 'block' tag
- if (blockXml1.tagName.toLowerCase() != 'xml' ||
- blockXml2.tagName.toLowerCase() != 'xml') {
- throw new Error('Expected two XML elements, recieved elements with tag ' +
- 'names: ' + blockXml1.tagName + ' and ' + blockXml2.tagName + '.');
- }
-
- // Compare the block elements directly. The XML tags may include other meta
- // information we want to igrore.
- var blockElement1 = blockXml1.getElementsByTagName('block')[0];
- var blockElement2 = blockXml2.getElementsByTagName('block')[0];
-
- if (!(blockElement1 && blockElement2)) {
- throw new Error('Could not get find block element in XML.');
- }
-
- var blockXmlText1 = Blockly.Xml.domToText(blockElement1);
- var blockXmlText2 = Blockly.Xml.domToText(blockElement2);
-
- // Strip white space.
- blockXmlText1 = blockXmlText1.replace(/\s+/g, '');
- blockXmlText2 = blockXmlText2.replace(/\s+/g, '');
-
- // Return whether or not changes have been saved.
- return blockXmlText1 == blockXmlText2;
-};
-
-/*
- * Checks if a block has a variable field. Blocks with variable fields cannot
- * be shadow blocks.
- * @param {Blockly.Block} block The block to check if a variable field exists.
- * @return {boolean} True if the block has a variable field, false otherwise.
- */
-FactoryUtils.hasVariableField = function(block) {
- if (!block) {
- return false;
- }
- return block.getVars().length > 0;
-};
-
-/**
- * Checks if a block is a procedures block. If procedures block names are
- * ever updated or expanded, this function should be updated as well (no
- * other known markers for procedure blocks beyond name).
- * @param {Blockly.Block} block The block to check.
- * @return {boolean} True if the block is a procedure block, false otherwise.
- */
-FactoryUtils.isProcedureBlock = function(block) {
- return block &&
- (block.type == 'procedures_defnoreturn' ||
- block.type == 'procedures_defreturn' ||
- block.type == 'procedures_callnoreturn' ||
- block.type == 'procedures_callreturn' ||
- block.type == 'procedures_ifreturn');
-};
-
-/**
- * Returns whether or not a modified block's changes has been saved to the
- * Block Library.
- * TODO(quachtina96): move into the Block Factory Controller once made.
- * @param {!BlockLibraryController} blockLibraryController Block Library
- * Controller storing custom blocks.
- * @return {boolean} True if all changes made to the block have been saved to
- * the given Block Library.
- */
-FactoryUtils.savedBlockChanges = function(blockLibraryController) {
- if (BlockFactory.isStarterBlock()) {
- return true;
- }
- var blockType = blockLibraryController.getCurrentBlockType();
- var currentXml = Blockly.Xml.workspaceToDom(BlockFactory.mainWorkspace);
-
- if (blockLibraryController.has(blockType)) {
- // Block is saved in block library.
- var savedXml = blockLibraryController.getBlockXml(blockType);
- return FactoryUtils.sameBlockXml(savedXml, currentXml);
- }
- return false;
-};
-
-/**
- * Given the root block of the factory, return the tooltip specified by the user
- * or the empty string if no tooltip is found.
- * @param {!Blockly.Block} rootBlock Factory_base block.
- * @return {string} The tooltip for the generated block, or the empty string.
- */
-FactoryUtils.getTooltipFromRootBlock_ = function(rootBlock) {
- var tooltipBlock = rootBlock.getInputTargetBlock('TOOLTIP');
- if (tooltipBlock && !tooltipBlock.disabled) {
- return tooltipBlock.getFieldValue('TEXT');
- }
- return '';
-};
-
-/**
- * Given the root block of the factory, return the help url specified by the
- * user or the empty string if no tooltip is found.
- * @param {!Blockly.Block} rootBlock Factory_base block.
- * @return {string} The help url for the generated block, or the empty string.
- */
-FactoryUtils.getHelpUrlFromRootBlock_ = function(rootBlock) {
- var helpUrlBlock = rootBlock.getInputTargetBlock('HELPURL');
- if (helpUrlBlock && !helpUrlBlock.disabled) {
- return helpUrlBlock.getFieldValue('TEXT');
- }
- return '';
-};
diff --git a/trunk/web/blockly/demos/blockfactory/icon.png b/trunk/web/blockly/demos/blockfactory/icon.png
deleted file mode 100644
index 2fcb25ed7fb..00000000000
Binary files a/trunk/web/blockly/demos/blockfactory/icon.png and /dev/null differ
diff --git a/trunk/web/blockly/demos/blockfactory/index.html b/trunk/web/blockly/demos/blockfactory/index.html
deleted file mode 100644
index 4a4423a82a1..00000000000
--- a/trunk/web/blockly/demos/blockfactory/index.html
+++ /dev/null
@@ -1,746 +0,0 @@
-
-
-
-
-
- Blockly Demo: Blockly Developer Tools
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 20
- 65
- 120
- 160
- 210
- 230
- 260
- 290
- 330
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 10
-
-
-
-
-
-
-
- 1
-
-
-
-
- 10
-
-
-
-
- 1
-
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
- 1
-
-
-
-
-
-
- 9
-
-
-
-
-
-
- 45
-
-
-
-
-
-
-
- 0
-
-
-
-
-
-
- 3.1
-
-
-
-
-
-
-
- 64
-
-
-
-
- 10
-
-
-
-
-
-
- 50
-
-
-
-
- 1
-
-
-
-
- 100
-
-
-
-
-
-
- 1
-
-
-
-
- 100
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- abc
-
-
-
-
-
-
-
-
-
-
-
-
-
- text
-
-
-
-
- abc
-
-
-
-
-
-
- text
-
-
-
-
-
-
- text
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
- abc
-
-
-
-
-
-
-
-
-
-
-
-
- 5
-
-
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- list
-
-
-
-
-
-
- ,
-
-
-
-
-
-
-
-
-
-
-
- 100
-
-
-
-
- 50
-
-
-
-
- 0
-
-
-
-
-
-
- #ff0000
-
-
-
-
- #3333ff
-
-
-
-
- 0.5
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/trunk/web/blockly/demos/blockfactory/link.png b/trunk/web/blockly/demos/blockfactory/link.png
deleted file mode 100644
index 11dfd82845e..00000000000
Binary files a/trunk/web/blockly/demos/blockfactory/link.png and /dev/null differ
diff --git a/trunk/web/blockly/demos/blockfactory/standard_categories.js b/trunk/web/blockly/demos/blockfactory/standard_categories.js
deleted file mode 100644
index 6b4072680c5..00000000000
--- a/trunk/web/blockly/demos/blockfactory/standard_categories.js
+++ /dev/null
@@ -1,395 +0,0 @@
-/**
- * @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 Contains a map of standard Blockly categories used to load
- * standard Blockly categories into the user's toolbox. The map is keyed by
- * the lower case name of the category, and contains the Category object for
- * that particular category. Also has a list of core block types provided
- * by Blockly.
- *
- * @author Emma Dauterman (evd2014)
- */
- 'use strict';
-
-/**
- * Namespace for StandardCategories
- */
-goog.provide('StandardCategories');
-
-
-// Map of standard category information necessary to add a standard category
-// to the toolbox.
-StandardCategories.categoryMap = Object.create(null);
-
-StandardCategories.categoryMap['logic'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Logic');
-StandardCategories.categoryMap['logic'].xml =
- Blockly.Xml.textToDom(
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '');
-StandardCategories.categoryMap['logic'].color ='#5C81A6';
-
-StandardCategories.categoryMap['loops'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Loops');
-StandardCategories.categoryMap['loops'].xml =
- Blockly.Xml.textToDom(
- '' +
- '' +
- '' +
- '' +
- '10' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '1' +
- '' +
- '' +
- '' +
- '' +
- '10' +
- '' +
- '' +
- '' +
- '' +
- '1' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '');
-StandardCategories.categoryMap['loops'].color = '#5CA65C';
-
-StandardCategories.categoryMap['math'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Math');
-StandardCategories.categoryMap['math'].xml =
- Blockly.Xml.textToDom(
- '' +
- '' +
- '' +
- '' +
- '' +
- '1' +
- '' +
- '' +
- '' +
- '' +
- '1' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '9' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '45' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '0' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '3.1' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '64' +
- '' +
- '' +
- '' +
- '' +
- '10'+
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '50' +
- '' +
- '' +
- '' +
- '' +
- '1' +
- '' +
- '' +
- '' +
- '' +
- '100' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '1' +
- '' +
- '' +
- '' +
- '' +
- '100' +
- '' +
- '' +
- '' +
- '' +
- '');
-StandardCategories.categoryMap['math'].color = '#5C68A6';
-
-StandardCategories.categoryMap['text'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Text');
-StandardCategories.categoryMap['text'].xml =
- Blockly.Xml.textToDom(
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'abc' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'text' +
- '' +
- '' +
- '' +
- '' +
- 'abc' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'text' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'text' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'abc' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'abc' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'abc' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'abc' +
- '' +
- '' +
- '' +
- '');
-StandardCategories.categoryMap['text'].color = '#5CA68D';
-
-StandardCategories.categoryMap['lists'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Lists');
-StandardCategories.categoryMap['lists'].xml =
- Blockly.Xml.textToDom(
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '5' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'list' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'list' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'list' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- 'list' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- ',' +
- '' +
- '' +
- '' +
- '' +
- '');
-StandardCategories.categoryMap['lists'].color = '#745CA6';
-
-StandardCategories.categoryMap['colour'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Colour');
-StandardCategories.categoryMap['colour'].xml =
- Blockly.Xml.textToDom(
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '100' +
- '' +
- '' +
- '' +
- '' +
- '50' +
- '' +
- '' +
- '' +
- '' +
- '0' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '#ff0000' +
- '' +
- '' +
- '' +
- '' +
- '#3333ff' +
- '' +
- '' +
- '' +
- '' +
- '0.5' +
- '' +
- '' +
- '' +
- '');
-StandardCategories.categoryMap['colour'].color = '#A6745C';
-
-StandardCategories.categoryMap['functions'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Functions');
-StandardCategories.categoryMap['functions'].color = '#9A5CA6'
-StandardCategories.categoryMap['functions'].custom = 'PROCEDURE';
-
-StandardCategories.categoryMap['variables'] =
- new ListElement(ListElement.TYPE_CATEGORY, 'Variables');
-StandardCategories.categoryMap['variables'].color = '#A65C81';
-StandardCategories.categoryMap['variables'].custom = 'VARIABLE';
-
-// All standard block types in provided in Blockly core.
-StandardCategories.coreBlockTypes = ["controls_if", "logic_compare",
- "logic_operation", "logic_negate", "logic_boolean", "logic_null",
- "logic_ternary", "controls_repeat_ext", "controls_whileUntil",
- "controls_for", "controls_forEach", "controls_flow_statements",
- "math_number", "math_arithmetic", "math_single", "math_trig",
- "math_constant", "math_number_property", "math_change", "math_round",
- "math_on_list", "math_modulo", "math_constrain", "math_random_int",
- "math_random_float", "text", "text_join", "text_append", "text_length",
- "text_isEmpty", "text_indexOf", "variables_get", "text_charAt",
- "text_getSubstring", "text_changeCase", "text_trim", "text_print",
- "text_prompt_ext", "colour_picker", "colour_random", "colour_rgb",
- "colour_blend", "lists_create_with", "lists_repeat", "lists_length",
- "lists_isEmpty", "lists_indexOf", "lists_getIndex", "lists_setIndex",
- "lists_getSublist", "lists_split", "lists_sort", "variables_set",
- "procedures_defreturn", "procedures_ifreturn", "procedures_defnoreturn",
- "procedures_callreturn"];
diff --git a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_controller.js b/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_controller.js
deleted file mode 100644
index 85b14d11bbe..00000000000
--- a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_controller.js
+++ /dev/null
@@ -1,1330 +0,0 @@
-/**
- * @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 Contains the controller code for workspace factory. Depends
- * on the model and view objects (created as internal variables) and interacts
- * with previewWorkspace and toolboxWorkspace (internal references stored to
- * both). Also depends on standard_categories.js for standard Blockly
- * categories. Provides the functionality for the actions the user can initiate:
- * - adding and removing categories
- * - switching between categories
- * - printing and downloading configuration xml
- * - updating the preview workspace
- * - changing a category name
- * - moving the position of a category.
- *
- * @author Emma Dauterman (evd2014)
- */
-
- goog.require('FactoryUtils');
- goog.require('StandardCategories');
-
-
-/**
- * Class for a WorkspaceFactoryController
- * @param {string} toolboxName Name of workspace toolbox XML.
- * @param {string} toolboxDiv Name of div to inject toolbox workspace in.
- * @param {string} previewDiv Name of div to inject preview workspace in.
- * @constructor
- */
-WorkspaceFactoryController = function(toolboxName, toolboxDiv, previewDiv) {
- // Toolbox XML element for the editing workspace.
- this.toolbox = document.getElementById(toolboxName);
-
- // Workspace for user to drag blocks in for a certain category.
- this.toolboxWorkspace = Blockly.inject(toolboxDiv,
- {grid:
- {spacing: 25,
- length: 3,
- colour: '#ccc',
- snap: true},
- media: '../../media/',
- toolbox: this.toolbox
- });
-
- // Workspace for user to preview their changes.
- this.previewWorkspace = Blockly.inject(previewDiv,
- {grid:
- {spacing: 25,
- length: 3,
- colour: '#ccc',
- snap: true},
- media: '../../media/',
- toolbox: '',
- zoom:
- {controls: true,
- wheel: true}
- });
-
- // Model to keep track of categories and blocks.
- this.model = new WorkspaceFactoryModel();
- // Updates the category tabs.
- this.view = new WorkspaceFactoryView();
- // Generates XML for categories.
- this.generator = new WorkspaceFactoryGenerator(this.model);
- // Tracks which editing mode the user is in. Toolbox mode on start.
- this.selectedMode = WorkspaceFactoryController.MODE_TOOLBOX;
- // True if key events are enabled, false otherwise.
- this.keyEventsEnabled = true;
- // True if there are unsaved changes in the toolbox, false otherwise.
- this.hasUnsavedToolboxChanges = false;
- // True if there are unsaved changes in the preloaded blocks, false otherwise.
- this.hasUnsavedPreloadChanges = false;
-};
-
-// Toolbox editing mode. Changes the user makes to the workspace updates the
-// toolbox.
-WorkspaceFactoryController.MODE_TOOLBOX = 'toolbox';
-// Pre-loaded workspace editing mode. Changes the user makes to the workspace
-// udpates the pre-loaded blocks.
-WorkspaceFactoryController.MODE_PRELOAD = 'preload';
-
-/**
- * Currently prompts the user for a name, checking that it's valid (not used
- * before), and then creates a tab and switches to it.
- */
-WorkspaceFactoryController.prototype.addCategory = function() {
- // Transfers the user's blocks to a flyout if it's the first category created.
- this.transferFlyoutBlocksToCategory();
-
- // After possibly creating a category, check again if it's the first category.
- var isFirstCategory = !this.model.hasElements();
- // Get name from user.
- var name = this.promptForNewCategoryName('Enter the name of your new category:');
- if (!name) { // Exit if cancelled.
- return;
- }
- // Create category.
- this.createCategory(name);
- // Switch to category.
- this.switchElement(this.model.getCategoryIdByName(name));
-
- // Sets the default options for injecting the workspace
- // when there are categories if adding the first category.
- if (isFirstCategory) {
- this.view.setCategoryOptions(this.model.hasElements());
- this.generateNewOptions();
- }
- // Update preview.
- this.updatePreview();
-};
-
-/**
- * Helper method for addCategory. Adds a category to the view given a name, ID,
- * and a boolean for if it's the first category created. Assumes the category
- * has already been created in the model. Does not switch to category.
- * @param {string} name Name of category being added.
- * @param {string} id The ID of the category being added.
- */
-WorkspaceFactoryController.prototype.createCategory = function(name) {
- // Create empty category
- var category = new ListElement(ListElement.TYPE_CATEGORY, name);
- this.model.addElementToList(category);
- // Create new category.
- var tab = this.view.addCategoryRow(name, category.id);
- this.addClickToSwitch(tab, category.id);
-};
-
-/**
- * Given a tab and a ID to be associated to that tab, adds a listener to
- * that tab so that when the user clicks on the tab, it switches to the
- * element associated with that ID.
- * @param {!Element} tab The DOM element to add the listener to.
- * @param {string} id The ID of the element to switch to when tab is clicked.
- */
-WorkspaceFactoryController.prototype.addClickToSwitch = function(tab, id) {
- var self = this;
- var clickFunction = function(id) { // Keep this in scope for switchElement.
- return function() {
- self.switchElement(id);
- };
- };
- this.view.bindClick(tab, clickFunction(id));
-};
-
-/**
- * Transfers the blocks in the user's flyout to a new category if
- * the user is creating their first category and their workspace is not
- * empty. Should be called whenever it is possible to switch from single flyout
- * to categories (not including importing).
- */
-WorkspaceFactoryController.prototype.transferFlyoutBlocksToCategory =
- function() {
- // Saves the user's blocks from the flyout in a category if there is no
- // toolbox and the user has dragged in blocks.
- if (!this.model.hasElements() &&
- this.toolboxWorkspace.getAllBlocks().length > 0) {
- // Create the new category.
- this.createCategory('Category 1', true);
- // Set the new category as selected.
- var id = this.model.getCategoryIdByName('Category 1');
- this.model.setSelectedById(id);
- this.view.setCategoryTabSelection(id, true);
- // Allow user to use the default options for injecting with categories.
- this.view.setCategoryOptions(this.model.hasElements());
- this.generateNewOptions();
- // Update preview here in case exit early.
- this.updatePreview();
- }
-};
-
-/**
- * Attached to "-" button. Checks if the user wants to delete
- * the current element. Removes the element and switches to another element.
- * When the last element is removed, it switches to a single flyout mode.
- */
-WorkspaceFactoryController.prototype.removeElement = function() {
- // Check that there is a currently selected category to remove.
- if (!this.model.getSelected()) {
- return;
- }
-
- // Check if user wants to remove current category.
- var check = confirm('Are you sure you want to delete the currently selected '
- + this.model.getSelected().type + '?');
- if (!check) { // If cancelled, exit.
- return;
- }
-
- var selectedId = this.model.getSelectedId();
- var selectedIndex = this.model.getIndexByElementId(selectedId);
- // Delete element visually.
- this.view.deleteElementRow(selectedId, selectedIndex);
- // Delete element in model.
- this.model.deleteElementFromList(selectedIndex);
-
- // Find next logical element to switch to.
- var next = this.model.getElementByIndex(selectedIndex);
- if (!next && this.model.hasElements()) {
- next = this.model.getElementByIndex(selectedIndex - 1);
- }
- var nextId = next ? next.id : null;
-
- // Open next element.
- this.clearAndLoadElement(nextId);
-
- // If no element to switch to, display message, clear the workspace, and
- // set a default selected element not in toolbox list in the model.
- if (!nextId) {
- alert('You currently have no categories or separators. All your blocks' +
- ' will be displayed in a single flyout.');
- this.toolboxWorkspace.clear();
- this.toolboxWorkspace.clearUndo();
- this.model.createDefaultSelectedIfEmpty();
- }
- // Update preview.
- this.updatePreview();
-};
-
-/**
- * Gets a valid name for a new category from the user.
- * @param {string} promptString Prompt for the user to enter a name.
- * @param {string=} opt_oldName The current name.
- * @return {string?} Valid name for a new category, or null if cancelled.
- */
-WorkspaceFactoryController.prototype.promptForNewCategoryName =
- function(promptString, opt_oldName) {
- var defaultName = opt_oldName;
- do {
- var name = prompt(promptString, defaultName);
- if (!name) { // If cancelled.
- return null;
- }
- defaultName = name;
- } while (this.model.hasCategoryByName(name));
- return name;
-};
-
-/**
- * Switches to a new tab for the element given by ID. Stores XML and blocks
- * to reload later, updates selected accordingly, and clears the workspace
- * and clears undo, then loads the new element.
- * @param {string} id ID of tab to be opened, must be valid element ID.
- */
-WorkspaceFactoryController.prototype.switchElement = function(id) {
- // Disables events while switching so that Blockly delete and create events
- // don't update the preview repeatedly.
- Blockly.Events.disable();
- // Caches information to reload or generate XML if switching to/from element.
- // Only saves if a category is selected.
- if (this.model.getSelectedId() != null && id != null) {
- this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace);
- }
- // Load element.
- this.clearAndLoadElement(id);
- // Enable Blockly events again.
- Blockly.Events.enable();
-};
-
-/**
- * Switches to a new tab for the element by ID. Helper for switchElement.
- * Updates selected, clears the workspace and clears undo, loads a new element.
- * @param {string} id ID of category to load.
- */
-WorkspaceFactoryController.prototype.clearAndLoadElement = function(id) {
- // Unselect current tab if switching to and from an element.
- if (this.model.getSelectedId() != null && id != null) {
- this.view.setCategoryTabSelection(this.model.getSelectedId(), false);
- }
-
- // If switching to another category, set category selection in the model and
- // view.
- if (id != null) {
- // Set next category.
- this.model.setSelectedById(id);
-
- // Clears workspace and loads next category.
- this.clearAndLoadXml_(this.model.getSelectedXml());
-
- // Selects the next tab.
- this.view.setCategoryTabSelection(id, true);
-
- // Order blocks as shown in flyout.
- this.toolboxWorkspace.cleanUp();
-
- // Update category editing buttons.
- this.view.updateState(this.model.getIndexByElementId
- (this.model.getSelectedId()), this.model.getSelected());
- } else {
- // Update category editing buttons for no categories.
- this.view.updateState(-1, null);
- }
-};
-
-/**
- * Tied to "Export" button. Gets a file name from the user and downloads
- * the corresponding configuration XML to that file.
- * @param {string} exportMode The type of file to export
- * (WorkspaceFactoryController.MODE_TOOLBOX for the toolbox configuration,
- * and WorkspaceFactoryController.MODE_PRELOAD for the pre-loaded workspace
- * configuration)
- */
-WorkspaceFactoryController.prototype.exportXmlFile = function(exportMode) {
- // Get file name.
- if (exportMode == WorkspaceFactoryController.MODE_TOOLBOX) {
- var fileName = prompt('File Name for toolbox XML:', 'toolbox.xml');
- } else {
- var fileName = prompt('File Name for pre-loaded workspace XML:',
- 'workspace.xml');
- }
- if (!fileName) { // If cancelled.
- return;
- }
-
- // Generate XML.
- if (exportMode == WorkspaceFactoryController.MODE_TOOLBOX) {
- // Export the toolbox XML.
- var configXml = Blockly.Xml.domToPrettyText
- (this.generator.generateToolboxXml());
- this.hasUnsavedToolboxChanges = false;
- } else if (exportMode == WorkspaceFactoryController.MODE_PRELOAD) {
- // Export the pre-loaded block XML.
- var configXml = Blockly.Xml.domToPrettyText
- (this.generator.generateWorkspaceXml());
- this.hasUnsavedPreloadChanges = false;
- } else {
- // Unknown mode. Throw error.
- throw new Error ("Unknown export mode: " + exportMode);
- }
-
- // Download file.
- var data = new Blob([configXml], {type: 'text/xml'});
- this.view.createAndDownloadFile(fileName, data);
- };
-
-/**
- * Export the options object to be used for the Blockly inject call. Gets a
- * file name from the user and downloads the options object to that file.
- */
-WorkspaceFactoryController.prototype.exportInjectFile = function() {
- var fileName = prompt('File Name for starter Blockly workspace code:',
- 'workspace.js');
- if (!fileName) { // If cancelled.
- return;
- }
- // Generate new options to remove toolbox XML from options object (if
- // necessary).
- this.generateNewOptions();
- var printableOptions = this.generator.generateInjectString()
- var data = new Blob([printableOptions], {type: 'text/javascript'});
- this.view.createAndDownloadFile(fileName, data);
-};
-
-/**
- * Tied to "Print" button. Mainly used for debugging purposes. Prints
- * the configuration XML to the console.
- */
-WorkspaceFactoryController.prototype.printConfig = function() {
- // Capture any changes made by user before generating XML.
- this.saveStateFromWorkspace();
- // Print XML.
- window.console.log(Blockly.Xml.domToPrettyText
- (this.generator.generateToolboxXml()));
-};
-
-/**
- * Updates the preview workspace based on the toolbox workspace. If switching
- * from no categories to categories or categories to no categories, reinjects
- * Blockly with reinjectPreview, otherwise just updates without reinjecting.
- * Called whenever a list element is created, removed, or modified and when
- * Blockly move and delete events are fired. Do not call on create events
- * or disabling will cause the user to "drop" their current blocks. Make sure
- * that no changes have been made to the workspace since updating the model
- * (if this might be the case, call saveStateFromWorkspace).
- */
-WorkspaceFactoryController.prototype.updatePreview = function() {
- // Disable events to stop updatePreview from recursively calling itself
- // through event handlers.
- Blockly.Events.disable();
-
- // Only update the toolbox if not in read only mode.
- if (!this.model.options['readOnly']) {
- // Get toolbox XML.
- var tree = Blockly.Options.parseToolboxTree(
- this.generator.generateToolboxXml());
-
- // No categories, creates a simple flyout.
- if (tree.getElementsByTagName('category').length == 0) {
- // No categories, creates a simple flyout.
- if (this.previewWorkspace.toolbox_) {
- this.reinjectPreview(tree); // Switch to simple flyout, expensive.
- } else {
- this.previewWorkspace.updateToolbox(tree);
- }
- } else {
- // Uses categories, creates a toolbox.
- if (!this.previewWorkspace.toolbox_) {
- this.reinjectPreview(tree); // Create a toolbox, expensive.
- } else {
- // Close the toolbox before updating it so that the user has to reopen
- // the flyout and see their updated toolbox (open flyout doesn't update)
- this.previewWorkspace.toolbox_.clearSelection();
- this.previewWorkspace.updateToolbox(tree);
- }
- }
- }
-
- // Update pre-loaded blocks in the preview workspace.
- this.previewWorkspace.clear();
- Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
- this.previewWorkspace);
-
- // Reenable events.
- Blockly.Events.enable();
-};
-
-/**
- * Saves the state from the workspace depending on the current mode. Should
- * be called after making changes to the workspace.
- */
-WorkspaceFactoryController.prototype.saveStateFromWorkspace = function() {
- if (this.selectedMode == WorkspaceFactoryController.MODE_TOOLBOX) {
- // If currently editing the toolbox.
- // Update flags if toolbox has been changed.
- if (this.model.getSelectedXml() !=
- Blockly.Xml.workspaceToDom(this.toolboxWorkspace)) {
- this.hasUnsavedToolboxChanges = true;
- }
-
- this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace);
-
- } else if (this.selectedMode == WorkspaceFactoryController.MODE_PRELOAD) {
- // If currently editing the pre-loaded workspace.
- // Update flags if preloaded blocks have been changed.
- if (this.model.getPreloadXml() !=
- Blockly.Xml.workspaceToDom(this.toolboxWorkspace)) {
- this.hasUnsavedPreloadChanges = true;
- }
-
- this.model.savePreloadXml(
- Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
- }
-};
-
-/**
- * Used to completely reinject the preview workspace. This should be used only
- * when switching from simple flyout to categories, or categories to simple
- * flyout. More expensive than simply updating the flyout or toolbox.
- * @param {!Element} Tree of XML elements
- * @package
- */
-WorkspaceFactoryController.prototype.reinjectPreview = function(tree) {
- this.previewWorkspace.dispose();
- var injectOptions = this.readOptions_();
- injectOptions['toolbox'] = Blockly.Xml.domToPrettyText(tree);
- this.previewWorkspace = Blockly.inject('preview_blocks', injectOptions);
- Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
- this.previewWorkspace);
-};
-
-/**
- * Tied to "change name" button. Changes the name of the selected category.
- * Continues prompting the user until they input a category name that is not
- * currently in use, exits if user presses cancel.
- */
-WorkspaceFactoryController.prototype.changeCategoryName = function() {
- var selected = this.model.getSelected();
- // Return if a category is not selected.
- if (selected.type != ListElement.TYPE_CATEGORY) {
- return;
- }
- // Get new name from user.
- window.foo = selected;
- var newName = this.promptForNewCategoryName('What do you want to change this'
- + ' category\'s name to?', selected.name);
- if (!newName) { // If cancelled.
- return;
- }
- // Change category name.
- selected.changeName(newName);
- this.view.updateCategoryName(newName, this.model.getSelectedId());
- // Update preview.
- this.updatePreview();
-};
-
-/**
- * Tied to arrow up and arrow down buttons. Swaps with the element above or
- * below the currently selected element (offset categories away from the
- * current element). Updates state to enable the correct element editing
- * buttons.
- * @param {number} offset The index offset from the currently selected element
- * to swap with. Positive if the element to be swapped with is below, negative
- * if the element to be swapped with is above.
- */
-WorkspaceFactoryController.prototype.moveElement = function(offset) {
- var curr = this.model.getSelected();
- if (!curr) { // Return if no selected element.
- return;
- }
- var currIndex = this.model.getIndexByElementId(curr.id);
- var swapIndex = this.model.getIndexByElementId(curr.id) + offset;
- var swap = this.model.getElementByIndex(swapIndex);
- if (!swap) { // Return if cannot swap in that direction.
- return;
- }
- // Move currently selected element to index of other element.
- // Indexes must be valid because confirmed that curr and swap exist.
- this.moveElementToIndex(curr, swapIndex, currIndex);
- // Update element editing buttons.
- this.view.updateState(swapIndex, this.model.getSelected());
- // Update preview.
- this.updatePreview();
-};
-
-/**
- * Moves a element to a specified index and updates the model and view
- * accordingly. Helper functions throw an error if indexes are out of bounds.
- * @param {!Element} element The element to move.
- * @param {number} newIndex The index to insert the element at.
- * @param {number} oldIndex The index the element is currently at.
- */
-WorkspaceFactoryController.prototype.moveElementToIndex = function(element,
- newIndex, oldIndex) {
- this.model.moveElementToIndex(element, newIndex, oldIndex);
- this.view.moveTabToIndex(element.id, newIndex, oldIndex);
-};
-
-/**
- * Changes the color of the selected category. Return if selected element is
- * a separator.
- * @param {string} color The color to change the selected category. Must be
- * a valid CSS string.
- */
-WorkspaceFactoryController.prototype.changeSelectedCategoryColor =
- function(color) {
- // Return if category is not selected.
- if (this.model.getSelected().type != ListElement.TYPE_CATEGORY) {
- return;
- }
- // Change color of selected category.
- this.model.getSelected().changeColor(color);
- this.view.setBorderColor(this.model.getSelectedId(), color);
- this.updatePreview();
-};
-
-/**
- * Tied to the "Standard Category" dropdown option, this function prompts
- * the user for a name of a standard Blockly category (case insensitive) and
- * loads it as a new category and switches to it. Leverages StandardCategories.
- */
-WorkspaceFactoryController.prototype.loadCategory = function() {
- // Prompt user for the name of the standard category to load.
- do {
- var name = prompt('Enter the name of the category you would like to import '
- + '(Logic, Loops, Math, Text, Lists, Colour, Variables, or Functions)');
- if (!name) {
- return; // Exit if cancelled.
- }
- } while (!this.isStandardCategoryName(name));
-
- // Load category.
- this.loadCategoryByName(name);
-};
-
-/**
- * Loads a Standard Category by name and switches to it. Leverages
- * StandardCategories. Returns if cannot load standard category.
- * @param {string} name Name of the standard category to load.
- */
-WorkspaceFactoryController.prototype.loadCategoryByName = function(name) {
- // Check if the user can load that standard category.
- if (!this.isStandardCategoryName(name)) {
- return;
- }
- if (this.model.hasVariables() && name.toLowerCase() == 'variables') {
- alert('A Variables category already exists. You cannot create multiple' +
- ' variables categories.');
- return;
- }
- if (this.model.hasProcedures() && name.toLowerCase() == 'functions') {
- alert('A Functions category already exists. You cannot create multiple' +
- ' functions categories.');
- return;
- }
- // Check if the user can create a category with that name.
- var standardCategory = StandardCategories.categoryMap[name.toLowerCase()]
- if (this.model.hasCategoryByName(standardCategory.name)) {
- alert('You already have a category with the name ' + standardCategory.name
- + '. Rename your category and try again.');
- return;
- }
- // Transfers current flyout blocks to a category if it's the first category
- // created.
- this.transferFlyoutBlocksToCategory();
-
- var isFirstCategory = !this.model.hasElements();
- // Copy the standard category in the model.
- var copy = standardCategory.copy();
-
- // Add it to the model.
- this.model.addElementToList(copy);
-
- // Update the copy in the view.
- var tab = this.view.addCategoryRow(copy.name, copy.id);
- this.addClickToSwitch(tab, copy.id);
- // Color the category tab in the view.
- if (copy.color) {
- this.view.setBorderColor(copy.id, copy.color);
- }
- // Switch to loaded category.
- this.switchElement(copy.id);
- // Convert actual shadow blocks to user-generated shadow blocks.
- this.convertShadowBlocks();
- // Save state from workspace before updating preview.
- this.saveStateFromWorkspace();
- if (isFirstCategory) {
- // Allow the user to use the default options for injecting the workspace
- // when there are categories.
- this.view.setCategoryOptions(this.model.hasElements());
- this.generateNewOptions();
- }
- // Update preview.
- this.updatePreview();
-};
-
-/**
- * Loads the standard Blockly toolbox into the editing space. Should only
- * be called when the mode is set to toolbox.
- */
-WorkspaceFactoryController.prototype.loadStandardToolbox = function() {
- this.loadCategoryByName('Logic');
- this.loadCategoryByName('Loops');
- this.loadCategoryByName('Math');
- this.loadCategoryByName('Text');
- this.loadCategoryByName('Lists');
- this.loadCategoryByName('Colour');
- this.addSeparator();
- this.loadCategoryByName('Variables');
- this.loadCategoryByName('Functions');
-};
-
-/**
- * Given the name of a category, determines if it's the name of a standard
- * category (case insensitive).
- * @param {string} name The name of the category that should be checked if it's
- * in StandardCategories categoryMap
- * @return {boolean} True if name is a standard category name, false otherwise.
- */
-WorkspaceFactoryController.prototype.isStandardCategoryName = function(name) {
- for (var category in StandardCategories.categoryMap) {
- if (name.toLowerCase() == category) {
- return true;
- }
- }
- return false;
-};
-
-/**
- * Connected to the "add separator" dropdown option. If categories already
- * exist, adds a separator to the model and view. Does not switch to select
- * the separator, and updates the preview.
- */
-WorkspaceFactoryController.prototype.addSeparator = function() {
- // If adding the first element in the toolbox, transfers the user's blocks
- // in a flyout to a category.
- this.transferFlyoutBlocksToCategory();
- // Create the separator in the model.
- var separator = new ListElement(ListElement.TYPE_SEPARATOR);
- this.model.addElementToList(separator);
- // Create the separator in the view.
- var tab = this.view.addSeparatorTab(separator.id);
- this.addClickToSwitch(tab, separator.id);
- // Switch to the separator and update the preview.
- this.switchElement(separator.id);
- this.updatePreview();
-};
-
-/**
- * Connected to the import button. Given the file path inputted by the user
- * from file input, if the import mode is for the toolbox, this function loads
- * that toolbox XML to the workspace, creating category and separator tabs as
- * necessary. If the import mode is for pre-loaded blocks in the workspace,
- * this function loads that XML to the workspace to be edited further. This
- * function switches mode to whatever the import mode is. Catches errors from
- * file reading and prints an error message alerting the user.
- * @param {string} file The path for the file to be imported into the workspace.
- * Should contain valid toolbox XML.
- * @param {string} importMode The mode corresponding to the type of file the
- * user is importing (WorkspaceFactoryController.MODE_TOOLBOX or
- * WorkspaceFactoryController.MODE_PRELOAD).
- */
-WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
- // Exit if cancelled.
- if (!file) {
- return;
- }
-
- Blockly.Events.disable();
- var controller = this;
- var reader = new FileReader();
-
- // To be executed when the reader has read the file.
- reader.onload = function() {
- // Try to parse XML from file and load it into toolbox editing area.
- // Print error message if fail.
- try {
- var tree = Blockly.Xml.textToDom(reader.result);
- if (importMode == WorkspaceFactoryController.MODE_TOOLBOX) {
- // Switch mode.
- controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
-
- // Confirm that the user wants to override their current toolbox.
- var hasToolboxElements = controller.model.hasElements() ||
- controller.toolboxWorkspace.getAllBlocks().length > 0;
- if (hasToolboxElements &&
- !confirm('Are you sure you want to import? You will lose your ' +
- 'current toolbox.')) {
- return;
- }
- // Import toolbox XML.
- controller.importToolboxFromTree_(tree);
-
- } else if (importMode == WorkspaceFactoryController.MODE_PRELOAD) {
- // Switch mode.
- controller.setMode(WorkspaceFactoryController.MODE_PRELOAD);
-
- // Confirm that the user wants to override their current blocks.
- if (controller.toolboxWorkspace.getAllBlocks().length > 0 &&
- !confirm('Are you sure you want to import? You will lose your ' +
- 'current workspace blocks.')) {
- return;
- }
-
- // Import pre-loaded workspace XML.
- controller.importPreloadFromTree_(tree);
- } else {
- // Throw error if invalid mode.
- throw new Error("Unknown import mode: " + importMode);
- }
- } catch(e) {
- alert('Cannot load XML from file.');
- console.log(e);
- } finally {
- Blockly.Events.enable();
- }
- }
-
- // Read the file asynchronously.
- reader.readAsText(file);
-};
-
-/**
- * Given a XML DOM tree, loads it into the toolbox editing area so that the
- * user can continue editing their work. Assumes that tree is in valid toolbox
- * XML format. Assumes that the mode is MODE_TOOLBOX.
- * @param {!Element} tree XML tree to be loaded to toolbox editing area.
- * @private
- */
-WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) {
- // Clear current editing area.
- this.model.clearToolboxList();
- this.view.clearToolboxTabs();
-
- if (tree.getElementsByTagName('category').length == 0) {
- // No categories present.
- // Load all the blocks into a single category evenly spaced.
- Blockly.Xml.domToWorkspace(tree, this.toolboxWorkspace);
- this.toolboxWorkspace.cleanUp();
-
- // Convert actual shadow blocks to user-generated shadow blocks.
- this.convertShadowBlocks();
-
- // Add message to denote empty category.
- this.view.addEmptyCategoryMessage();
-
- } else {
- // Categories/separators present.
- for (var i = 0, item; item = tree.children[i]; i++) {
-
- if (item.tagName == 'category') {
- // If the element is a category, create a new category and switch to it.
- this.createCategory(item.getAttribute('name'), false);
- var category = this.model.getElementByIndex(i);
- this.switchElement(category.id);
-
- // Load all blocks in that category to the workspace to be evenly
- // spaced and saved to that category.
- for (var j = 0, blockXml; blockXml = item.children[j]; j++) {
- Blockly.Xml.domToBlock(blockXml, this.toolboxWorkspace);
- }
-
- // Evenly space the blocks.
- this.toolboxWorkspace.cleanUp();
-
- // Convert actual shadow blocks to user-generated shadow blocks.
- this.convertShadowBlocks();
-
- // Set category color.
- if (item.getAttribute('colour')) {
- category.changeColor(item.getAttribute('colour'));
- this.view.setBorderColor(category.id, category.color);
- }
- // Set any custom tags.
- if (item.getAttribute('custom')) {
- this.model.addCustomTag(category, item.getAttribute('custom'));
- }
- } else {
- // If the element is a separator, add the separator and switch to it.
- this.addSeparator();
- this.switchElement(this.model.getElementByIndex(i).id);
- }
- }
- }
- this.view.updateState(this.model.getIndexByElementId
- (this.model.getSelectedId()), this.model.getSelected());
-
- this.saveStateFromWorkspace();
-
- // Set default configuration options for a single flyout or multiple
- // categories.
- this.view.setCategoryOptions(this.model.hasElements());
- this.generateNewOptions();
-
- this.updatePreview();
-};
-
-/**
- * Given a XML DOM tree, loads it into the pre-loaded workspace editing area.
- * Assumes that tree is in valid XML format and that the selected mode is
- * MODE_PRELOAD.
- * @param {!Element} tree XML tree to be loaded to pre-loaded block editing
- * area.
- */
-WorkspaceFactoryController.prototype.importPreloadFromTree_ = function(tree) {
- this.clearAndLoadXml_(tree);
- this.model.savePreloadXml(tree);
- this.updatePreview();
-};
-
-/**
- * Given a XML DOM tree, loads it into the pre-loaded workspace editing area.
- * Assumes that tree is in valid XML format and that the selected mode is
- * MODE_PRELOAD.
- * @param {!Element} tree XML tree to be loaded to pre-loaded block editing
- * area.
- */
-WorkspaceFactoryController.prototype.importPreloadFromTree_ = function(tree) {
- this.clearAndLoadXml_(tree);
- this.model.savePreloadXml(tree);
- this.saveStateFromWorkspace();
- this.updatePreview();
-};
-
-/**
- * Given a XML DOM tree, loads it into the pre-loaded workspace editing area.
- * Assumes that tree is in valid XML format and that the selected mode is
- * MODE_PRELOAD.
- * @param {!Element} tree XML tree to be loaded to pre-loaded block editing
- * area.
- */
-WorkspaceFactoryController.prototype.importPreloadFromTree_ = function(tree) {
- this.clearAndLoadXml_(tree);
- this.model.savePreloadXml(tree);
- this.saveStateFromWorkspace();
- this.updatePreview();
-};
-
-/**
- * Clears the editing area completely, deleting all categories and all
- * blocks in the model and view and all pre-loaded blocks. Tied to the
- * "Clear" button.
- */
-WorkspaceFactoryController.prototype.clearAll = function() {
- if (!confirm('Are you sure you want to clear all of your work in Workspace' +
- ' Factory?')) {
- return;
- }
- var hasCategories = this.model.hasElements();
- this.model.clearToolboxList();
- this.view.clearToolboxTabs();
- this.model.savePreloadXml(Blockly.Xml.textToDom(''));
- this.view.addEmptyCategoryMessage();
- this.view.updateState(-1, null);
- this.toolboxWorkspace.clear();
- this.toolboxWorkspace.clearUndo();
- this.saveStateFromWorkspace();
- this.hasUnsavedToolboxChanges = false;
- this.hasUnsavedPreloadChanges = false;
- this.view.setCategoryOptions(this.model.hasElements());
- this.generateNewOptions();
- this.updatePreview();
-};
-
-/*
- * Makes the currently selected block a user-generated shadow block. These
- * blocks are not made into real shadow blocks, but recorded in the model
- * and visually marked as shadow blocks, allowing the user to move and edit
- * them (which would be impossible with actual shadow blocks). Updates the
- * preview when done.
- */
-WorkspaceFactoryController.prototype.addShadow = function() {
- // No block selected to make a shadow block.
- if (!Blockly.selected) {
- return;
- }
- // Clear any previous warnings on the block (would only have warnings on
- // a non-shadow block if it was nested inside another shadow block).
- Blockly.selected.setWarningText(null);
- // Set selected block and all children as shadow blocks.
- this.addShadowForBlockAndChildren_(Blockly.selected);
-
- // Save and update the preview.
- this.saveStateFromWorkspace();
- this.updatePreview();
-};
-
-/**
- * Sets a block and all of its children to be user-generated shadow blocks,
- * both in the model and view.
- * @param {!Blockly.Block} block The block to be converted to a user-generated
- * shadow block.
- * @private
- */
-WorkspaceFactoryController.prototype.addShadowForBlockAndChildren_ =
- function(block) {
- // Convert to shadow block.
- this.view.markShadowBlock(block);
- this.model.addShadowBlock(block.id);
-
- if (FactoryUtils.hasVariableField(block)) {
- block.setWarningText('Cannot make variable blocks shadow blocks.');
- }
-
- // Convert all children to shadow blocks recursively.
- var children = block.getChildren();
- for (var i = 0; i < children.length; i++) {
- this.addShadowForBlockAndChildren_(children[i]);
- }
-};
-
-/**
- * If the currently selected block is a user-generated shadow block, this
- * function makes it a normal block again, removing it from the list of
- * shadow blocks and loading the workspace again. Updates the preview again.
- */
-WorkspaceFactoryController.prototype.removeShadow = function() {
- // No block selected to modify.
- if (!Blockly.selected) {
- return;
- }
- this.model.removeShadowBlock(Blockly.selected.id);
- this.view.unmarkShadowBlock(Blockly.selected);
-
- // If turning invalid shadow block back to normal block, remove warning.
- Blockly.selected.setWarningText(null);
-
- this.saveStateFromWorkspace();
- this.updatePreview();
-};
-
-/**
- * Given a unique block ID, uses the model to determine if a block is a
- * user-generated shadow block.
- * @param {string} blockId The unique ID of the block to examine.
- * @return {boolean} True if the block is a user-generated shadow block, false
- * otherwise.
- */
-WorkspaceFactoryController.prototype.isUserGenShadowBlock = function(blockId) {
- return this.model.isShadowBlock(blockId);
-};
-
-/**
- * Call when importing XML containing real shadow blocks. This function turns
- * all real shadow blocks loaded in the workspace into user-generated shadow
- * blocks, meaning they are marked as shadow blocks by the model and appear as
- * shadow blocks in the view but are still editable and movable.
- */
-WorkspaceFactoryController.prototype.convertShadowBlocks = function() {
- var blocks = this.toolboxWorkspace.getAllBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- if (block.isShadow()) {
- block.setShadow(false);
- // Delete the shadow DOM attached to the block so that the shadow block
- // does not respawn. Dependent on implementation details.
- var parentConnection = block.outputConnection ?
- block.outputConnection.targetConnection :
- block.previousConnection.targetConnection;
- if (parentConnection) {
- parentConnection.setShadowDom(null);
- }
- this.model.addShadowBlock(block.id);
- this.view.markShadowBlock(block);
- }
- }
-};
-
-/**
- * Sets the currently selected mode that determines what the toolbox workspace
- * is being used to edit. Updates the view and then saves and loads XML
- * to and from the toolbox and updates the help text.
- * @param {string} tab The type of tab being switched to
- * (WorkspaceFactoryController.MODE_TOOLBOX or
- * WorkspaceFactoryController.MODE_PRELOAD).
- */
-WorkspaceFactoryController.prototype.setMode = function(mode) {
- // No work to change mode that's currently set.
- if (this.selectedMode == mode) {
- return;
- }
-
- // No work to change mode that's currently set.
- if (this.selectedMode == mode) {
- return;
- }
-
- // Set tab selection and display appropriate tab.
- this.view.setModeSelection(mode);
-
- // Update selected tab.
- this.selectedMode = mode;
-
- // Update help text above workspace.
- this.view.updateHelpText(mode);
-
- if (mode == WorkspaceFactoryController.MODE_TOOLBOX) {
- // Open the toolbox editing space.
- this.model.savePreloadXml
- (Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
- this.clearAndLoadXml_(this.model.getSelectedXml());
- this.view.disableWorkspace(this.view.shouldDisableWorkspace
- (this.model.getSelected()));
- } else {
- // Open the pre-loaded workspace editing space.
- if (this.model.getSelected()) {
- this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace);
- }
- this.clearAndLoadXml_(this.model.getPreloadXml());
- this.view.disableWorkspace(false);
- }
-};
-
-/**
- * Clears the toolbox workspace and loads XML to it, marking shadow blocks
- * as necessary.
- * @private
- * @param {!Element} xml The XML to be loaded to the workspace.
- */
-WorkspaceFactoryController.prototype.clearAndLoadXml_ = function(xml) {
- this.toolboxWorkspace.clear();
- this.toolboxWorkspace.clearUndo();
- Blockly.Xml.domToWorkspace(xml, this.toolboxWorkspace);
- this.view.markShadowBlocks(this.model.getShadowBlocksInWorkspace
- (this.toolboxWorkspace.getAllBlocks()));
- this.warnForUndefinedBlocks_();
-};
-
-/**
- * Sets the standard default options for the options object and updates
- * the preview workspace. The default values depends on if categories are
- * present.
- */
-WorkspaceFactoryController.prototype.setStandardOptionsAndUpdate = function() {
- this.view.setBaseOptions();
- this.view.setCategoryOptions(this.model.hasElements());
- this.generateNewOptions();
-};
-
-/**
- * Generates a new options object for injecting a Blockly workspace based
- * on user input. Should be called every time a change has been made to
- * an input field. Updates the model and reinjects the preview workspace.
- */
-WorkspaceFactoryController.prototype.generateNewOptions = function() {
- this.model.setOptions(this.readOptions_());
-
- this.reinjectPreview(Blockly.Options.parseToolboxTree
- (this.generator.generateToolboxXml()));
-};
-
-/**
- * Generates a new options object for injecting a Blockly workspace based on
- * user input.
- * @return {!Object} Blockly injection options object.
- * @private
- */
-WorkspaceFactoryController.prototype.readOptions_ = function() {
- var optionsObj = Object.create(null);
-
- // Add all standard options to the options object.
- // Use parse int to get numbers from value inputs.
- var readonly = document.getElementById('option_readOnly_checkbox').checked;
- if (readonly) {
- optionsObj['readOnly'] = true;
- } else {
- optionsObj['collapse'] =
- document.getElementById('option_collapse_checkbox').checked;
- optionsObj['comments'] =
- document.getElementById('option_comments_checkbox').checked;
- optionsObj['disable'] =
- document.getElementById('option_disable_checkbox').checked;
- if (document.getElementById('option_infiniteBlocks_checkbox').checked) {
- optionsObj['maxBlocks'] = Infinity;
- } else {
- var maxBlocksValue =
- document.getElementById('option_maxBlocks_number').value;
- optionsObj['maxBlocks'] = typeof maxBlocksValue == 'string' ?
- parseInt(maxBlocksValue) : maxBlocksValue;
- }
- optionsObj['trashcan'] =
- document.getElementById('option_trashcan_checkbox').checked;
- optionsObj['horizontalLayout'] =
- document.getElementById('option_horizontalLayout_checkbox').checked;
- optionsObj['toolboxPosition'] =
- document.getElementById('option_toolboxPosition_checkbox').checked ?
- 'end' : 'start';
- }
-
- optionsObj['css'] = document.getElementById('option_css_checkbox').checked;
- optionsObj['media'] = document.getElementById('option_media_text').value;
- optionsObj['rtl'] = document.getElementById('option_rtl_checkbox').checked;
- optionsObj['scrollbars'] =
- document.getElementById('option_scrollbars_checkbox').checked;
- optionsObj['sounds'] =
- document.getElementById('option_sounds_checkbox').checked;
- optionsObj['oneBasedIndex'] =
- document.getElementById('option_oneBasedIndex_checkbox').checked;
-
- // If using a grid, add all grid options.
- if (document.getElementById('option_grid_checkbox').checked) {
- var grid = Object.create(null);
- var spacingValue =
- document.getElementById('gridOption_spacing_number').value;
- grid['spacing'] = typeof spacingValue == 'string' ?
- parseInt(spacingValue) : spacingValue;
- var lengthValue = document.getElementById('gridOption_length_number').value;
- grid['length'] = typeof lengthValue == 'string' ?
- parseInt(lengthValue) : lengthValue;
- grid['colour'] = document.getElementById('gridOption_colour_text').value;
- if (!readonly) {
- grid['snap'] =
- document.getElementById('gridOption_snap_checkbox').checked;
- }
- optionsObj['grid'] = grid;
- }
-
- // If using zoom, add all zoom options.
- if (document.getElementById('option_zoom_checkbox').checked) {
- var zoom = Object.create(null);
- zoom['controls'] =
- document.getElementById('zoomOption_controls_checkbox').checked;
- zoom['wheel'] =
- document.getElementById('zoomOption_wheel_checkbox').checked;
- var startScaleValue =
- document.getElementById('zoomOption_startScale_number').value;
- zoom['startScale'] = typeof startScaleValue == 'string' ?
- parseFloat(startScaleValue) : startScaleValue;
- var maxScaleValue =
- document.getElementById('zoomOption_maxScale_number').value;
- zoom['maxcale'] = typeof maxScaleValue == 'string' ?
- parseFloat(maxScaleValue) : maxScaleValue;
- var minScaleValue =
- document.getElementById('zoomOption_minScale_number').value;
- zoom['minScale'] = typeof minScaleValue == 'string' ?
- parseFloat(minScaleValue) : minScaleValue;
- var scaleSpeedValue =
- document.getElementById('zoomOption_scaleSpeed_number').value;
- zoom['startScale'] = typeof startScaleValue == 'string' ?
- parseFloat(scaleSpeedValue) : scaleSpeedValue;
- optionsObj['zoom'] = zoom;
- }
-
- return optionsObj;
-};
-
-/**
- * Imports blocks from a file, generating a category in the toolbox workspace
- * to allow the user to use imported blocks in the toolbox and in pre-loaded
- * blocks.
- * @param {!File} file File object for the blocks to import.
- * @param {string} format The format of the file to import, either 'JSON' or
- * 'JavaScript'.
- */
-WorkspaceFactoryController.prototype.importBlocks = function(file, format) {
- // Generate category name from file name.
- var categoryName = file.name;
-
- var controller = this;
- var reader = new FileReader();
-
- // To be executed when the reader has read the file.
- reader.onload = function() {
- try {
- // Define blocks using block types from file.
- var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result,
- format);
-
- // If an imported block type is already defined, check if the user wants
- // to override the current block definition.
- if (controller.model.hasDefinedBlockTypes(blockTypes) &&
- !confirm('An imported block uses the same name as a block '
- + 'already in your toolbox. Are you sure you want to override the '
- + 'currently defined block?')) {
- return;
- }
-
- var blocks = controller.generator.getDefinedBlocks(blockTypes);
- // Generate category XML and append to toolbox.
- var categoryXml = FactoryUtils.generateCategoryXml(blocks, categoryName);
- // Get random color for category between 0 and 360. Gives each imported
- // category a different color.
- var randomColor = Math.floor(Math.random() * 360);
- categoryXml.setAttribute('colour', randomColor);
- controller.toolbox.appendChild(categoryXml);
- controller.toolboxWorkspace.updateToolbox(controller.toolbox);
- // Update imported block types.
- controller.model.addImportedBlockTypes(blockTypes);
- // Reload current category to possibly reflect any newly defined blocks.
- controller.clearAndLoadXml_
- (Blockly.Xml.workspaceToDom(controller.toolboxWorkspace));
- } catch (e) {
- alert('Cannot read blocks from file.');
- window.console.log(e);
- }
- }
-
- // Read the file asynchronously.
- reader.readAsText(file);
-};
-
-/*
- * Updates the block library category in the toolbox workspace toolbox.
- * @param {!Element} categoryXml XML for the block library category.
- * @param {!Array.} libBlockTypes Array of block types from the block
- * library.
- */
-WorkspaceFactoryController.prototype.setBlockLibCategory =
- function(categoryXml, libBlockTypes) {
- var blockLibCategory = document.getElementById('blockLibCategory');
-
- // Set category ID so that it can be easily replaced, and set a standard,
- // arbitrary block library color.
- categoryXml.setAttribute('id', 'blockLibCategory');
- categoryXml.setAttribute('colour', 260);
-
- // Update the toolbox and toolboxWorkspace.
- this.toolbox.replaceChild(categoryXml, blockLibCategory);
- this.toolboxWorkspace.toolbox_.clearSelection();
- this.toolboxWorkspace.updateToolbox(this.toolbox);
-
- // Update the block library types.
- this.model.updateLibBlockTypes(libBlockTypes);
-
- // Reload XML on page to account for blocks now defined or undefined in block
- // library.
- this.clearAndLoadXml_(Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
-};
-
-/**
- * Return the block types used in the custom toolbox and pre-loaded workspace.
- * @return {!Array.} Block types used in the custom toolbox and
- * pre-loaded workspace.
- */
-WorkspaceFactoryController.prototype.getAllUsedBlockTypes = function() {
- return this.model.getAllUsedBlockTypes();
-};
-
-/**
- * Determines if a block loaded in the workspace has a definition (if it
- * is a standard block, is defined in the block library, or has a definition
- * imported).
- * @param {!Blockly.Block} block The block to examine.
- */
-WorkspaceFactoryController.prototype.isDefinedBlock = function(block) {
- return this.model.isDefinedBlockType(block.type);
-};
-
-/**
- * Sets a warning on blocks loaded to the workspace that are not defined.
- * @private
- */
-WorkspaceFactoryController.prototype.warnForUndefinedBlocks_ = function() {
- var blocks = this.toolboxWorkspace.getAllBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- if (!this.isDefinedBlock(block)) {
- block.setWarningText(block.type + ' is not defined (it is not a standard '
- + 'block, \nin your block library, or an imported block)');
- }
- }
-};
-
-/*
- * Determines if a standard variable category is in the custom toolbox.
- * @return {boolean} True if a variables category is in use, false otherwise.
- */
-WorkspaceFactoryController.prototype.hasVariablesCategory = function() {
- return this.model.hasVariables();
-};
-
-/**
- * Determines if a standard procedures category is in the custom toolbox.
- * @return {boolean} True if a procedures category is in use, false otherwise.
- */
-WorkspaceFactoryController.prototype.hasProceduresCategory = function() {
- return this.model.hasProcedures();
-};
-
-/**
- * Determines if there are any unsaved changes in workspace factory.
- * @return {boolean} True if there are unsaved changes, false otherwise.
- */
-WorkspaceFactoryController.prototype.hasUnsavedChanges = function() {
- return this.hasUnsavedToolboxChanges || this.hasUnsavedPreloadChanges;
-};
diff --git a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_generator.js b/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_generator.js
deleted file mode 100644
index 3b06ac282e1..00000000000
--- a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_generator.js
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * @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 Generates the configuration XML used to update the preview
- * workspace or print to the console or download to a file. Leverages
- * Blockly.Xml and depends on information in the model (holds a reference).
- * Depends on a hidden workspace created in the generator to load saved XML in
- * order to generate toolbox XML.
- *
- * @author Emma Dauterman (evd2014)
- */
-
-goog.require('FactoryUtils');
-
-
-/**
- * Class for a WorkspaceFactoryGenerator
- * @constructor
- */
-WorkspaceFactoryGenerator = function(model) {
- // Model to share information about categories and shadow blocks.
- this.model = model;
- // Create hidden workspace to load saved XML to generate toolbox XML.
- var hiddenBlocks = document.createElement('div');
- // Generate a globally unique ID for the hidden div element to avoid
- // collisions.
- var hiddenBlocksId = Blockly.utils.genUid();
- hiddenBlocks.id = hiddenBlocksId;
- hiddenBlocks.style.display = 'none';
- document.body.appendChild(hiddenBlocks);
- this.hiddenWorkspace = Blockly.inject(hiddenBlocksId);
-};
-
-/**
- * Generates the XML for the toolbox or flyout with information from
- * toolboxWorkspace and the model. Uses the hiddenWorkspace to generate XML.
- * Save state of workspace in model (saveFromWorkspace) before calling if
- * changes might have been made to the selected category.
- * @param {!Blockly.workspace} toolboxWorkspace Toolbox editing workspace where
- * blocks are added by user to be part of the toolbox.
- * @return {!Element} XML element representing toolbox or flyout corresponding
- * to toolbox workspace.
- */
-WorkspaceFactoryGenerator.prototype.generateToolboxXml = function() {
- // Create DOM for XML.
- var xmlDom = goog.dom.createDom('xml',
- {
- 'id' : 'toolbox',
- 'style' : 'display:none'
- });
- if (!this.model.hasElements()) {
- // Toolbox has no categories. Use XML directly from workspace.
- this.loadToHiddenWorkspace_(this.model.getSelectedXml());
- this.appendHiddenWorkspaceToDom_(xmlDom);
- } else {
- // Toolbox has categories.
- // Assert that selected != null
- if (!this.model.getSelected()) {
- throw new Error('Selected is null when the toolbox is empty.');
- }
-
- var xml = this.model.getSelectedXml();
- var toolboxList = this.model.getToolboxList();
-
- // Iterate through each category to generate XML for each using the
- // hidden workspace. Load each category to the hidden workspace to make sure
- // that all the blocks that are not top blocks are also captured as block
- // groups in the flyout.
- for (var i = 0; i < toolboxList.length; i++) {
- var element = toolboxList[i];
- if (element.type == ListElement.TYPE_SEPARATOR) {
- // If the next element is a separator.
- var nextElement = goog.dom.createDom('sep');
- } else if (element.type == ListElement.TYPE_CATEGORY) {
- // If the next element is a category.
- var nextElement = goog.dom.createDom('category');
- nextElement.setAttribute('name', element.name);
- // Add a colour attribute if one exists.
- if (element.color != null) {
- nextElement.setAttribute('colour', element.color);
- }
- // Add a custom attribute if one exists.
- if (element.custom != null) {
- nextElement.setAttribute('custom', element.custom);
- }
- // Load that category to hidden workspace, setting user-generated shadow
- // blocks as real shadow blocks.
- this.loadToHiddenWorkspace_(element.xml);
- this.appendHiddenWorkspaceToDom_(nextElement);
- }
- xmlDom.appendChild(nextElement);
- }
- }
- return xmlDom;
- };
-
-
- /**
- * Generates XML for the workspace (different from generateConfigXml in that
- * it includes XY and ID attributes). Uses a workspace and converts user
- * generated shadow blocks to actual shadow blocks.
- * @return {!Element} XML element representing toolbox or flyout corresponding
- * to toolbox workspace.
- */
-WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() {
- // Load workspace XML to hidden workspace with user-generated shadow blocks
- // as actual shadow blocks.
- this.hiddenWorkspace.clear();
- Blockly.Xml.domToWorkspace(this.model.getPreloadXml(), this.hiddenWorkspace);
- this.setShadowBlocksInHiddenWorkspace_();
-
- // Generate XML and set attributes.
- var generatedXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);
- generatedXml.setAttribute('id', 'workspaceBlocks');
- generatedXml.setAttribute('style', 'display:none');
- return generatedXml;
-};
-
-/**
- * Generates a string representation of the options object for injecting the
- * workspace and starter code.
- * @return {string} String representation of starter code for injecting.
- */
-WorkspaceFactoryGenerator.prototype.generateInjectString = function() {
- var addAttributes = function(obj, tabChar) {
- if (!obj) {
- return '{}\n';
- }
- var str = '';
- for (var key in obj) {
- if (key == 'grid' || key == 'zoom') {
- var temp = tabChar + key + ' : {\n' + addAttributes(obj[key],
- tabChar + '\t') + tabChar + '}, \n';
- } else if (typeof obj[key] == 'string') {
- var temp = tabChar + key + ' : \'' + obj[key] + '\', \n';
- } else {
- var temp = tabChar + key + ' : ' + obj[key] + ', \n';
- }
- str += temp;
- }
- var lastCommaIndex = str.lastIndexOf(',');
- str = str.slice(0, lastCommaIndex) + '\n';
- return str;
- };
-
- var attributes = addAttributes(this.model.options, '\t');
- if (!this.model.options['readOnly']) {
- attributes = '\ttoolbox : toolbox, \n' +
- attributes;
- }
- var finalStr = '/* TODO: Change toolbox XML ID if necessary. Can export ' +
- 'toolbox XML from Workspace Factory. */\n' +
- 'var toolbox = document.getElementById("toolbox");\n\n';
- finalStr += 'var options = { \n' + attributes + '};';
- finalStr += '\n\n/* Inject your workspace */ \nvar workspace = Blockly.' +
- 'inject(/* TODO: Add ID of div to inject Blockly into */, options);';
- finalStr += '\n\n/* Load Workspace Blocks from XML to workspace. ' +
- 'Remove all code below if no blocks to load */\n\n' +
- '/* TODO: Change workspace blocks XML ID if necessary. Can export' +
- ' workspace blocks XML from Workspace Factory. */\n' +
- 'var workspaceBlocks = document.getElementById("workspaceBlocks"); \n\n' +
- '/* Load blocks to workspace. */\n' +
- 'Blockly.Xml.domToWorkspace(workspace, workspaceBlocks);';
- return finalStr;
-};
-
-/**
- * Loads the given XML to the hidden workspace and sets any user-generated
- * shadow blocks to be actual shadow blocks.
- * @param {!Element} xml The XML to be loaded to the hidden workspace.
- * @private
- */
-WorkspaceFactoryGenerator.prototype.loadToHiddenWorkspace_ = function(xml) {
- this.hiddenWorkspace.clear();
- Blockly.Xml.domToWorkspace(xml, this.hiddenWorkspace);
- this.setShadowBlocksInHiddenWorkspace_();
-};
-
-/**
- * Encodes blocks in the hidden workspace in a XML DOM element. Very
- * similar to workspaceToDom, but doesn't capture IDs. Uses the top-level
- * blocks loaded in hiddenWorkspace.
- * @private
- * @param {!Element} xmlDom Tree of XML elements to be appended to.
- */
-WorkspaceFactoryGenerator.prototype.appendHiddenWorkspaceToDom_ =
- function(xmlDom) {
- var blocks = this.hiddenWorkspace.getTopBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- var blockChild = Blockly.Xml.blockToDom(block, /* opt_noId */ true);
- xmlDom.appendChild(blockChild);
- }
-};
-
-/**
- * Sets the user-generated shadow blocks loaded into hiddenWorkspace to be
- * actual shadow blocks. This is done so that blockToDom records them as
- * shadow blocks instead of regular blocks.
- * @private
- */
-WorkspaceFactoryGenerator.prototype.setShadowBlocksInHiddenWorkspace_ =
- function() {
- var blocks = this.hiddenWorkspace.getAllBlocks();
- for (var i = 0; i < blocks.length; i++) {
- if (this.model.isShadowBlock(blocks[i].id)) {
- blocks[i].setShadow(true);
- }
- }
-};
-
-/**
- * Given a set of block types, gets the Blockly.Block objects for each block
- * type.
- * @param {!Array.} blockTypes Array of blocks that have been defined.
- * @return {!Array.} Array of Blockly.Block objects corresponding
- * to the array of blockTypes.
- */
-WorkspaceFactoryGenerator.prototype.getDefinedBlocks = function(blockTypes) {
- var blocks = [];
- for (var i = 0; i < blockTypes.length ; i++) {
- blocks.push(FactoryUtils.getDefinedBlock(blockTypes[i],
- this.hiddenWorkspace));
- }
- return blocks;
-};
diff --git a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_init.js b/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_init.js
deleted file mode 100644
index 31069b0c50d..00000000000
--- a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_init.js
+++ /dev/null
@@ -1,604 +0,0 @@
-/**
- * @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 Contains the init functions for the workspace factory tab.
- * Adds click handlers to buttons and dropdowns, adds event listeners for
- * keydown events and Blockly events, and configures the initial setup of
- * the page.
- *
- * @author Emma Dauterman (evd2014)
- */
-
- goog.require('FactoryUtils');
-
-/**
- * Namespace for workspace factory initialization methods.
- * @namespace
- */
-WorkspaceFactoryInit = {};
-
-/**
- * Initialization for workspace factory tab.
- * @param {!FactoryController} controller The controller for the workspace
- * factory tab.
- */
-WorkspaceFactoryInit.initWorkspaceFactory = function(controller) {
- // Disable category editing buttons until categories are created.
- document.getElementById('button_remove').disabled = true;
- document.getElementById('button_up').disabled = true;
- document.getElementById('button_down').disabled = true;
- document.getElementById('button_editCategory').disabled = true;
-
- this.initColorPicker_(controller);
- this.addWorkspaceFactoryEventListeners_(controller);
- this.assignWorkspaceFactoryClickHandlers_(controller);
- this.addWorkspaceFactoryOptionsListeners_(controller);
-
- // Check standard options and apply the changes to update the view.
- controller.setStandardOptionsAndUpdate();
-};
-
-/**
- * Initialize the color picker in workspace factory.
- * @param {!FactoryController} controller The controller for the workspace
- * factory tab.
- * @private
- */
-WorkspaceFactoryInit.initColorPicker_ = function(controller) {
- // Array of Blockly category colours, consitent with the 15 degree default
- // of the block factory's colour wheel.
- var colours = [];
- for (var hue = 0; hue < 360; hue += 15) {
- colours.push(WorkspaceFactoryInit.hsvToHex_(hue,
- Blockly.HSV_SATURATION, Blockly.HSV_VALUE));
- }
-
- // Create color picker with specific set of Blockly colours.
- var colourPicker = new goog.ui.ColorPicker();
- colourPicker.setSize(6);
- colourPicker.setColors(colours);
-
- // Create and render the popup colour picker and attach to button.
- var popupPicker = new goog.ui.PopupColorPicker(null, colourPicker);
- popupPicker.render();
- popupPicker.attach(document.getElementById('dropdown_color'));
- popupPicker.setFocusable(true);
- goog.events.listen(popupPicker, 'change', function(e) {
- controller.changeSelectedCategoryColor(popupPicker.getSelectedColor());
- blocklyFactory.closeModal();
- });
-};
-
-/**
- * Converts from h,s,v values to a hex string
- * @param {number} h Hue, in [0, 360].
- * @param {number} s Saturation, in [0, 1].
- * @param {number} v Value, in [0, 1].
- * @return {string} hex representation of the color.
- * @private
- */
-WorkspaceFactoryInit.hsvToHex_ = function(h, s, v) {
- var brightness = v * 255;
- var red = 0;
- var green = 0;
- var blue = 0;
- if (s == 0) {
- red = brightness;
- green = brightness;
- blue = brightness;
- } else {
- var sextant = Math.floor(h / 60);
- var remainder = (h / 60) - sextant;
- var val1 = brightness * (1 - s);
- var val2 = brightness * (1 - (s * remainder));
- var val3 = brightness * (1 - (s * (1 - remainder)));
- switch (sextant) {
- case 1:
- red = val2;
- green = brightness;
- blue = val1;
- break;
- case 2:
- red = val1;
- green = brightness;
- blue = val3;
- break;
- case 3:
- red = val1;
- green = val2;
- blue = brightness;
- break;
- case 4:
- red = val3;
- green = val1;
- blue = brightness;
- break;
- case 5:
- red = brightness;
- green = val1;
- blue = val2;
- break;
- case 6:
- case 0:
- red = brightness;
- green = val3;
- blue = val1;
- break;
- }
- }
-
- var hexR = ('0' + Math.floor(red).toString(16)).slice(-2);
- var hexG = ('0' + Math.floor(green).toString(16)).slice(-2);
- var hexB = ('0' + Math.floor(blue).toString(16)).slice(-2);
- return '#' + hexR + hexG + hexB;
-};
-
-/**
- * Assign click handlers for workspace factory.
- * @param {!FactoryController} controller The controller for the workspace
- * factory tab.
- * @private
- */
-WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
- function(controller) {
-
- // Import Custom Blocks button.
- document.getElementById('button_importBlocks').addEventListener
- ('click',
- function() {
- blocklyFactory.openModal('dropdownDiv_importBlocks');
- });
- document.getElementById('input_importBlocksJson').addEventListener
- ('change',
- function() {
- controller.importBlocks(event.target.files[0], 'JSON');
- });
- document.getElementById('input_importBlocksJson').addEventListener
- ('click', function() {blocklyFactory.closeModal()});
- document.getElementById('input_importBlocksJs').addEventListener
- ('change',
- function() {
- controller.importBlocks(event.target.files[0], 'JavaScript');
- });
- document.getElementById('input_importBlocksJs').addEventListener
- ('click', function() {blocklyFactory.closeModal()});
-
- // Load to Edit button.
- document.getElementById('button_load').addEventListener
- ('click',
- function() {
- blocklyFactory.openModal('dropdownDiv_load');
- });
- document.getElementById('input_loadToolbox').addEventListener
- ('change',
- function() {
- controller.importFile(event.target.files[0],
- WorkspaceFactoryController.MODE_TOOLBOX);
- });
- document.getElementById('input_loadToolbox').addEventListener
- ('click', function() {blocklyFactory.closeModal()});
- document.getElementById('input_loadPreload').addEventListener
- ('change',
- function() {
- controller.importFile(event.target.files[0],
- WorkspaceFactoryController.MODE_PRELOAD);
- });
- document.getElementById('input_loadPreload').addEventListener
- ('click', function() {blocklyFactory.closeModal()});
-
- // Export button.
- document.getElementById('dropdown_exportOptions').addEventListener
- ('click',
- function() {
- controller.exportInjectFile();
- blocklyFactory.closeModal();
- });
- document.getElementById('dropdown_exportToolbox').addEventListener
- ('click',
- function() {
- controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
- blocklyFactory.closeModal();
- });
- document.getElementById('dropdown_exportPreload').addEventListener
- ('click',
- function() {
- controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD);
- blocklyFactory.closeModal();
- });
- document.getElementById('dropdown_exportAll').addEventListener
- ('click',
- function() {
- controller.exportInjectFile();
- controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
- controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD);
- blocklyFactory.closeModal();
- });
- document.getElementById('button_export').addEventListener
- ('click',
- function() {
- blocklyFactory.openModal('dropdownDiv_export');
- });
-
- // Clear button.
- document.getElementById('button_clear').addEventListener
- ('click',
- function() {
- controller.clearAll();
- });
-
- // Toolbox and Workspace tabs.
- document.getElementById('tab_toolbox').addEventListener
- ('click',
- function() {
- controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
- });
- document.getElementById('tab_preload').addEventListener
- ('click',
- function() {
- controller.setMode(WorkspaceFactoryController.MODE_PRELOAD);
- });
-
- // '+' button.
- document.getElementById('button_add').addEventListener
- ('click',
- function() {
- blocklyFactory.openModal('dropdownDiv_add');
- });
- document.getElementById('dropdown_newCategory').addEventListener
- ('click',
- function() {
- controller.addCategory();
- blocklyFactory.closeModal();
- });
- document.getElementById('dropdown_loadCategory').addEventListener
- ('click',
- function() {
- controller.loadCategory();
- blocklyFactory.closeModal();
- });
- document.getElementById('dropdown_separator').addEventListener
- ('click',
- function() {
- controller.addSeparator();
- blocklyFactory.closeModal();
- });
- document.getElementById('dropdown_loadStandardToolbox').addEventListener
- ('click',
- function() {
- controller.loadStandardToolbox();
- blocklyFactory.closeModal();
- });
-
- // '-' button.
- document.getElementById('button_remove').addEventListener
- ('click',
- function() {
- controller.removeElement();
- });
-
- // Up/Down buttons.
- document.getElementById('button_up').addEventListener
- ('click',
- function() {
- controller.moveElement(-1);
- });
- document.getElementById('button_down').addEventListener
- ('click',
- function() {
- controller.moveElement(1);
- });
-
- // Edit Category button.
- document.getElementById('button_editCategory').addEventListener
- ('click',
- function() {
- blocklyFactory.openModal('dropdownDiv_editCategory');
- });
- document.getElementById('dropdown_name').addEventListener
- ('click',
- function() {
- controller.changeCategoryName();
- blocklyFactory.closeModal();
- });
-
- // Make/Remove Shadow buttons.
- document.getElementById('button_addShadow').addEventListener
- ('click',
- function() {
- controller.addShadow();
- WorkspaceFactoryInit.displayAddShadow_(false);
- WorkspaceFactoryInit.displayRemoveShadow_(true);
- });
- document.getElementById('button_removeShadow').addEventListener
- ('click',
- function() {
- controller.removeShadow();
- WorkspaceFactoryInit.displayAddShadow_(true);
- WorkspaceFactoryInit.displayRemoveShadow_(false);
-
- // Disable shadow editing button if turning invalid shadow block back
- // to normal block.
- if (!Blockly.selected.getSurroundParent()) {
- document.getElementById('button_addShadow').disabled = true;
- }
- });
-
- // Help button on workspace tab.
- document.getElementById('button_optionsHelp').addEventListener
- ('click', function() {
- open('https://developers.google.com/blockly/guides/get-started/web#configuration');
- });
-
- // Reset to Default button on workspace tab.
- document.getElementById('button_standardOptions').addEventListener
- ('click', function() {
- controller.setStandardOptionsAndUpdate();
- });
-};
-
-/**
- * Add event listeners for workspace factory.
- * @param {!FactoryController} controller The controller for the workspace
- * factory tab.
- * @private
- */
-WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
- // Use up and down arrow keys to move categories.
- window.addEventListener('keydown', function(e) {
- // Don't let arrow keys have any effect if not in Workspace Factory
- // editing the toolbox.
- if (!(controller.keyEventsEnabled && controller.selectedMode
- == WorkspaceFactoryController.MODE_TOOLBOX)) {
- return;
- }
-
- if (e.keyCode == 38) {
- // Arrow up.
- controller.moveElement(-1);
- } else if (e.keyCode == 40) {
- // Arrow down.
- controller.moveElement(1);
- }
- });
-
- // Determines if a block breaks shadow block placement rules.
- // Breaks rules if (1) a shadow block no longer has a valid
- // parent, or (2) a normal block is inside of a shadow block.
- var isInvalidBlockPlacement = function(block) {
- return ((controller.isUserGenShadowBlock(block.id) &&
- !block.getSurroundParent()) ||
- (!controller.isUserGenShadowBlock(block.id) &&
- block.getSurroundParent() &&
- controller.isUserGenShadowBlock(block.getSurroundParent().id)));
- };
-
- // Add change listeners for toolbox workspace in workspace factory.
- controller.toolboxWorkspace.addChangeListener(function(e) {
- // Listen for Blockly move and delete events to update preview.
- // Not listening for Blockly create events because causes the user to drop
- // blocks when dragging them into workspace. Could cause problems if ever
- // load blocks into workspace directly without calling updatePreview.
- if (e.type == Blockly.Events.MOVE || e.type == Blockly.Events.DELETE ||
- e.type == Blockly.Events.CHANGE) {
- controller.saveStateFromWorkspace();
- controller.updatePreview();
- }
-
- // Listen for Blockly UI events to correctly enable the "Edit Block" button.
- // Only enable "Edit Block" when a block is selected and it has a
- // surrounding parent, meaning it is nested in another block (blocks that
- // are not nested in parents cannot be shadow blocks).
- if (e.type == Blockly.Events.MOVE || (e.type == Blockly.Events.UI &&
- e.element == 'selected')) {
- var selected = Blockly.selected;
-
- // Show shadow button if a block is selected. Show "Add Shadow" if
- // a block is not a shadow block, show "Remove Shadow" if it is a
- // shadow block.
- if (selected) {
- var isShadow = controller.isUserGenShadowBlock(selected.id);
- WorkspaceFactoryInit.displayAddShadow_(!isShadow);
- WorkspaceFactoryInit.displayRemoveShadow_(isShadow);
- } else {
- WorkspaceFactoryInit.displayAddShadow_(false);
- WorkspaceFactoryInit.displayRemoveShadow_(false);
- }
-
- if (selected != null && selected.getSurroundParent() != null &&
- !controller.isUserGenShadowBlock(selected.getSurroundParent().id)) {
- // Selected block is a valid shadow block or could be a valid shadow
- // block.
-
- // Enable block editing and remove warnings if the block is not a
- // variable user-generated shadow block.
- document.getElementById('button_addShadow').disabled = false;
- document.getElementById('button_removeShadow').disabled = false;
-
- if (!FactoryUtils.hasVariableField(selected) &&
- controller.isDefinedBlock(selected)) {
- selected.setWarningText(null);
- }
- } else {
- // Selected block cannot be a valid shadow block.
-
- if (selected != null && isInvalidBlockPlacement(selected)) {
- // Selected block breaks shadow block rules.
- // Invalid shadow block if (1) a shadow block no longer has a valid
- // parent, or (2) a normal block is inside of a shadow block.
-
- if (!controller.isUserGenShadowBlock(selected.id)) {
- // Warn if a non-shadow block is nested inside a shadow block.
- selected.setWarningText('Only shadow blocks can be nested inside\n'
- + 'other shadow blocks.');
- } else if (!FactoryUtils.hasVariableField(selected)) {
- // Warn if a shadow block is invalid only if not replacing
- // warning for variables.
- selected.setWarningText('Shadow blocks must be nested inside other'
- + ' blocks.')
- }
-
- // Give editing options so that the user can make an invalid shadow
- // block a normal block.
- document.getElementById('button_removeShadow').disabled = false;
- document.getElementById('button_addShadow').disabled = true;
- } else {
- // Selected block does not break any shadow block rules, but cannot
- // be a shadow block.
-
- // Remove possible 'invalid shadow block placement' warning.
- if (selected != null && controller.isDefinedBlock(selected) &&
- (!FactoryUtils.hasVariableField(selected) ||
- !controller.isUserGenShadowBlock(selected.id))) {
- selected.setWarningText(null);
- }
-
- // No block selected that is a shadow block or could be a valid shadow
- // block. Disable block editing.
- document.getElementById('button_addShadow').disabled = true;
- document.getElementById('button_removeShadow').disabled = true;
- }
- }
- }
-
- // Convert actual shadow blocks added from the toolbox to user-generated
- // shadow blocks.
- if (e.type == Blockly.Events.CREATE) {
- controller.convertShadowBlocks();
-
- // Let the user create a Variables or Functions category if they use
- // blocks from either category.
-
- // Get all children of a block and add them to childList.
- var getAllChildren = function(block, childList) {
- childList.push(block);
- var children = block.getChildren();
- for (var i = 0, child; child = children[i]; i++) {
- getAllChildren(child, childList);
- }
- };
-
- var newBaseBlock = controller.toolboxWorkspace.getBlockById(e.blockId);
- var allNewBlocks = [];
- getAllChildren(newBaseBlock, allNewBlocks);
- var variableCreated = false;
- var procedureCreated = false;
-
- // Check if the newly created block or any of its children are variable
- // or procedure blocks.
- for (var i = 0, block; block = allNewBlocks[i]; i++) {
- if (FactoryUtils.hasVariableField(block)) {
- variableCreated = true;
- } else if (FactoryUtils.isProcedureBlock(block)) {
- procedureCreated = true;
- }
- }
-
- // If any of the newly created blocks are variable or procedure blocks,
- // prompt the user to create the corresponding standard category.
- if (variableCreated && !controller.hasVariablesCategory()) {
- if (confirm('Your new block has a variables field. To use this block '
- + 'fully, you will need a Variables category. Do you want to add '
- + 'a Variables category to your custom toolbox?')) {
- controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
- controller.loadCategoryByName('variables');
- }
- }
-
- if (procedureCreated && !controller.hasProceduresCategory()) {
- if (confirm('Your new block is a function block. To use this block '
- + 'fully, you will need a Functions category. Do you want to add '
- + 'a Functions category to your custom toolbox?')) {
- controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
- controller.loadCategoryByName('functions');
- }
- }
- }
- });
-};
-
-/**
- * Display or hide the add shadow button.
- * @param {boolean} show True if the add shadow button should be shown, false
- * otherwise.
- */
-WorkspaceFactoryInit.displayAddShadow_ = function(show) {
- document.getElementById('button_addShadow').style.display =
- show ? 'inline-block' : 'none';
-};
-
-/**
- * Display or hide the remove shadow button.
- * @param {boolean} show True if the remove shadow button should be shown, false
- * otherwise.
- */
-WorkspaceFactoryInit.displayRemoveShadow_ = function(show) {
- document.getElementById('button_removeShadow').style.display =
- show ? 'inline-block' : 'none';
-};
-
-/**
- * Add listeners for workspace factory options input elements.
- * @param {!FactoryController} controller The controller for the workspace
- * factory tab.
- * @private
- */
-WorkspaceFactoryInit.addWorkspaceFactoryOptionsListeners_ =
- function(controller) {
- // Checking the grid checkbox displays grid options.
- document.getElementById('option_grid_checkbox').addEventListener('change',
- function(e) {
- document.getElementById('grid_options').style.display =
- document.getElementById('option_grid_checkbox').checked ?
- 'block' : 'none';
- });
-
- // Checking the zoom checkbox displays zoom options.
- document.getElementById('option_zoom_checkbox').addEventListener('change',
- function(e) {
- document.getElementById('zoom_options').style.display =
- document.getElementById('option_zoom_checkbox').checked ?
- 'block' : 'none';
- });
-
- // Checking the readonly checkbox enables/disables other options.
- document.getElementById('option_readOnly_checkbox').addEventListener('change',
- function(e) {
- var checkbox = document.getElementById('option_readOnly_checkbox');
- blocklyFactory.ifCheckedEnable(!checkbox.checked,
- ['readonly1', 'readonly2']);
- });
-
- document.getElementById('option_infiniteBlocks_checkbox').addEventListener('change',
- function(e) {
- document.getElementById('maxBlockNumber_option').style.display =
- document.getElementById('option_infiniteBlocks_checkbox').checked ?
- 'none' : 'block';
- });
-
- // Generate new options every time an options input is updated.
- var div = document.getElementById('workspace_options');
- var options = div.getElementsByTagName('input');
- for (var i = 0, option; option = options[i]; i++) {
- option.addEventListener('change', function() {
- controller.generateNewOptions();
- });
- }
-};
diff --git a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_model.js b/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_model.js
deleted file mode 100644
index 7b60810d38a..00000000000
--- a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_model.js
+++ /dev/null
@@ -1,563 +0,0 @@
-/**
- * @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 Stores and updates information about state and categories
- * in workspace factory. Each list element is either a separator or a category,
- * and each category stores its name, XML to load that category, color,
- * custom tags, and a unique ID making it possible to change category names and
- * move categories easily. Keeps track of the currently selected list
- * element. Also keeps track of all the user-created shadow blocks and
- * manipulates them as necessary.
- *
- * @author Emma Dauterman (evd2014)
- */
-
-/**
- * Class for a WorkspaceFactoryModel
- * @constructor
- */
-WorkspaceFactoryModel = function() {
- // Ordered list of ListElement objects. Empty if there is a single flyout.
- this.toolboxList = [];
- // ListElement for blocks in a single flyout. Null if a toolbox exists.
- this.flyout = new ListElement(ListElement.TYPE_FLYOUT);
- // Array of block IDs for all user created shadow blocks.
- this.shadowBlocks = [];
- // Reference to currently selected ListElement. Stored in this.toolboxList if
- // there are categories, or in this.flyout if blocks are displayed in a single
- // flyout.
- this.selected = this.flyout;
- // Boolean for if a Variable category has been added.
- this.hasVariableCategory = false;
- // Boolean for if a Procedure category has been added.
- this.hasProcedureCategory = false;
- // XML to be pre-loaded to workspace. Empty on default;
- this.preloadXml = Blockly.Xml.textToDom('');
- // Options object to be configured for Blockly inject call.
- this.options = new Object(null);
- // Block Library block types.
- this.libBlockTypes = [];
- // Imported block types.
- this.importedBlockTypes = [];
- //
-};
-
-/**
- * Given a name, determines if it is the name of a category already present.
- * Used when getting a valid category name from the user.
- * @param {string} name String name to be compared against.
- * @return {boolean} True if string is a used category name, false otherwise.
- */
-WorkspaceFactoryModel.prototype.hasCategoryByName = function(name) {
- for (var i = 0; i < this.toolboxList.length; i++) {
- if (this.toolboxList[i].type == ListElement.TYPE_CATEGORY &&
- this.toolboxList[i].name == name) {
- return true;
- }
- }
- return false;
-};
-
-/**
- * Determines if a category with the 'VARIABLE' tag exists.
- * @return {boolean} True if there exists a category with the Variables tag,
- * false otherwise.
- */
-WorkspaceFactoryModel.prototype.hasVariables = function() {
- return this.hasVariableCategory;
-};
-
-/**
- * Determines if a category with the 'PROCEDURE' tag exists.
- * @return {boolean} True if there exists a category with the Procedures tag,
- * false otherwise.
- */
-WorkspaceFactoryModel.prototype.hasProcedures = function() {
- return this.hasProcedureCategory;
-};
-
-/**
- * Determines if the user has any elements in the toolbox. Uses the length of
- * toolboxList.
- * @return {boolean} True if elements exist, false otherwise.
- */
-WorkspaceFactoryModel.prototype.hasElements = function() {
- return this.toolboxList.length > 0;
-};
-
-/**
- * Given a ListElement, adds it to the toolbox list.
- * @param {!ListElement} element The element to be added to the list.
- */
-WorkspaceFactoryModel.prototype.addElementToList = function(element) {
- // Update state if the copied category has a custom tag.
- this.hasVariableCategory = element.custom == 'VARIABLE' ? true :
- this.hasVariableCategory;
- this.hasProcedureCategory = element.custom == 'PROCEDURE' ? true :
- this.hasProcedureCategory;
- // Add element to toolboxList.
- this.toolboxList.push(element);
- // Empty single flyout.
- this.flyout = null;
-};
-
-/**
- * Given an index, deletes a list element and all associated data.
- * @param {number} index The index of the list element to delete.
- */
-WorkspaceFactoryModel.prototype.deleteElementFromList = function(index) {
- // Check if index is out of bounds.
- if (index < 0 || index >= this.toolboxList.length) {
- return; // No entry to delete.
- }
- // Check if need to update flags.
- this.hasVariableCategory = this.toolboxList[index].custom == 'VARIABLE' ?
- false : this.hasVariableCategory;
- this.hasProcedureCategory = this.toolboxList[index].custom == 'PROCEDURE' ?
- false : this.hasProcedureCategory;
- // Remove element.
- this.toolboxList.splice(index, 1);
-};
-
-/**
- * Sets selected to be an empty category not in toolbox list if toolbox list
- * is empty. Should be called when removing the last element from toolbox list.
- * If the toolbox list is empty, selected stores the XML for the single flyout
- * of blocks displayed.
- */
-WorkspaceFactoryModel.prototype.createDefaultSelectedIfEmpty = function() {
- if (this.toolboxList.length == 0) {
- this.flyout = new ListElement(ListElement.TYPE_FLYOUT);
- this.selected = this.flyout;
- }
-};
-
-/**
- * Moves a list element to a certain position in toolboxList by removing it
- * and then inserting it at the correct index. Checks that indices are in
- * bounds (throws error if not), but assumes that oldIndex is the correct index
- * for list element.
- * @param {!ListElement} element The element to move in toolboxList.
- * @param {number} newIndex The index to insert the element at.
- * @param {number} oldIndex The index the element is currently at.
- */
-WorkspaceFactoryModel.prototype.moveElementToIndex = function(element, newIndex,
- oldIndex) {
- // Check that indexes are in bounds.
- if (newIndex < 0 || newIndex >= this.toolboxList.length || oldIndex < 0 ||
- oldIndex >= this.toolboxList.length) {
- throw new Error('Index out of bounds when moving element in the model.');
- }
- this.deleteElementFromList(oldIndex);
- this.toolboxList.splice(newIndex, 0, element);
-};
-
-/**
- * Returns the ID of the currently selected element. Returns null if there are
- * no categories (if selected == null).
- * @return {string} The ID of the element currently selected.
- */
-WorkspaceFactoryModel.prototype.getSelectedId = function() {
- return this.selected ? this.selected.id : null;
-};
-
-/**
- * Returns the name of the currently selected category. Returns null if there
- * are no categories (if selected == null) or the selected element is not
- * a category (in which case its name is null).
- * @return {string} The name of the category currently selected.
- */
-WorkspaceFactoryModel.prototype.getSelectedName = function() {
- return this.selected ? this.selected.name : null;
-};
-
-/**
- * Returns the currently selected list element object.
- * @return {ListElement} The currently selected ListElement
- */
-WorkspaceFactoryModel.prototype.getSelected = function() {
- return this.selected;
-};
-
-/**
- * Sets list element currently selected by id.
- * @param {string} id ID of list element that should now be selected.
- */
-WorkspaceFactoryModel.prototype.setSelectedById = function(id) {
- this.selected = this.getElementById(id);
-};
-
-/**
- * Given an ID of a list element, returns the index of that list element in
- * toolboxList. Returns -1 if ID is not present.
- * @param {string} id The ID of list element to search for.
- * @return {number} The index of the list element in toolboxList, or -1 if it
- * doesn't exist.
- */
-WorkspaceFactoryModel.prototype.getIndexByElementId = function(id) {
- for (var i = 0; i < this.toolboxList.length; i++) {
- if (this.toolboxList[i].id == id) {
- return i;
- }
- }
- return -1; // ID not present in toolboxList.
-};
-
-/**
- * Given the ID of a list element, returns that ListElement object.
- * @param {string} id The ID of element to search for.
- * @return {ListElement} Corresponding ListElement object in toolboxList, or
- * null if that element does not exist.
- */
-WorkspaceFactoryModel.prototype.getElementById = function(id) {
- for (var i = 0; i < this.toolboxList.length; i++) {
- if (this.toolboxList[i].id == id) {
- return this.toolboxList[i];
- }
- }
- return null; // ID not present in toolboxList.
-};
-
-/**
- * Given the index of a list element in toolboxList, returns that ListElement
- * object.
- * @param {number} index The index of the element to return.
- * @return {ListElement} The corresponding ListElement object in toolboxList.
- */
-WorkspaceFactoryModel.prototype.getElementByIndex = function(index) {
- if (index < 0 || index >= this.toolboxList.length) {
- return null;
- }
- return this.toolboxList[index];
-};
-
-/**
- * Returns the XML to load the selected element.
- * @return {!Element} The XML of the selected element, or null if there is
- * no selected element.
- */
-WorkspaceFactoryModel.prototype.getSelectedXml = function() {
- return this.selected ? this.selected.xml : null;
-};
-
-/**
- * Return ordered list of ListElement objects.
- * @return {!Array.} ordered list of ListElement objects
- */
-WorkspaceFactoryModel.prototype.getToolboxList = function() {
- return this.toolboxList;
-};
-
-/**
- * Gets the ID of a category given its name.
- * @param {string} name Name of category.
- * @return {number} ID of category
- */
-WorkspaceFactoryModel.prototype.getCategoryIdByName = function(name) {
- for (var i = 0; i < this.toolboxList.length; i++) {
- if (this.toolboxList[i].name == name) {
- return this.toolboxList[i].id;
- }
- }
- return null; // Name not present in toolboxList.
-};
-
-/**
- * Clears the toolbox list, deleting all ListElements.
- */
-WorkspaceFactoryModel.prototype.clearToolboxList = function() {
- this.toolboxList = [];
- this.hasVariableCategory = false;
- this.hasProcedureCategory = false;
- this.shadowBlocks = [];
- this.selected.xml = Blockly.Xml.textToDom('');
-};
-
-/**
- * Class for a ListElement
- * Adds a shadow block to the list of shadow blocks.
- * @param {string} blockId The unique ID of block to be added.
- */
-WorkspaceFactoryModel.prototype.addShadowBlock = function(blockId) {
- this.shadowBlocks.push(blockId);
-};
-
-/**
- * Removes a shadow block ID from the list of shadow block IDs if that ID is
- * in the list.
- * @param {string} blockId The unique ID of block to be removed.
- */
-WorkspaceFactoryModel.prototype.removeShadowBlock = function(blockId) {
- for (var i = 0; i < this.shadowBlocks.length; i++) {
- if (this.shadowBlocks[i] == blockId) {
- this.shadowBlocks.splice(i, 1);
- return;
- }
- }
-};
-
-/**
- * Determines if a block is a shadow block given a unique block ID.
- * @param {string} blockId The unique ID of the block to examine.
- * @return {boolean} True if the block is a user-generated shadow block, false
- * otherwise.
- */
-WorkspaceFactoryModel.prototype.isShadowBlock = function(blockId) {
- for (var i = 0; i < this.shadowBlocks.length; i++) {
- if (this.shadowBlocks[i] == blockId) {
- return true;
- }
- }
- return false;
-};
-
-/**
- * Given a set of blocks currently loaded, returns all blocks in the workspace
- * that are user generated shadow blocks.
- * @param {!} blocks Array of blocks currently loaded.
- * @return {!} Array of user-generated shadow blocks currently
- * loaded.
- */
-WorkspaceFactoryModel.prototype.getShadowBlocksInWorkspace =
- function(workspaceBlocks) {
- var shadowsInWorkspace = [];
- for (var i = 0; i < workspaceBlocks.length; i++) {
- if (this.isShadowBlock(workspaceBlocks[i].id)) {
- shadowsInWorkspace.push(workspaceBlocks[i]);
- }
- }
- return shadowsInWorkspace;
-};
-
-/**
- * Adds a custom tag to a category, updating state variables accordingly.
- * Only accepts 'VARIABLE' and 'PROCEDURE' tags.
- * @param {!ListElement} category The category to add the tag to.
- * @param {string} tag The custom tag to add to the category.
- */
-WorkspaceFactoryModel.prototype.addCustomTag = function(category, tag) {
- // Only update list elements that are categories.
- if (category.type != ListElement.TYPE_CATEGORY) {
- return;
- }
- // Only update the tag to be 'VARIABLE' or 'PROCEDURE'.
- if (tag == 'VARIABLE') {
- this.hasVariableCategory = true;
- category.custom = 'VARIABLE';
- } else if (tag == 'PROCEDURE') {
- this.hasProcedureCategory = true;
- category.custom = 'PROCEDURE';
- }
-};
-
-/**
- * Have basic pre-loaded workspace working
- * Saves XML as XML to be pre-loaded into the workspace.
- * @param {!Element} xml The XML to be saved.
- */
-WorkspaceFactoryModel.prototype.savePreloadXml = function(xml) {
- this.preloadXml = xml
-};
-
-/**
- * Gets the XML to be pre-loaded into the workspace.
- * @return {!Element} The XML for the workspace.
- */
-WorkspaceFactoryModel.prototype.getPreloadXml = function() {
- return this.preloadXml;
-};
-
-/**
- * Sets a new options object for injecting a Blockly workspace.
- * @param {Object} options Options object for injecting a Blockly workspace.
- */
-WorkspaceFactoryModel.prototype.setOptions = function(options) {
- this.options = options;
-};
-
-/*
- * Returns an array of all the block types currently being used in the toolbox
- * and the pre-loaded blocks. No duplicates.
- * TODO(evd2014): Move pushBlockTypesToList to FactoryUtils.
- * @return {!Array.} Array of block types currently being used.
- */
-WorkspaceFactoryModel.prototype.getAllUsedBlockTypes = function() {
- var blockTypeList = [];
-
- // Given XML for the workspace, adds all block types included in the XML
- // to the list, not including duplicates.
- var pushBlockTypesToList = function(xml, list) {
- // Get all block XML nodes.
- var blocks = xml.getElementsByTagName('block');
-
- // Add block types if not already in list.
- for (var i = 0; i < blocks.length; i++) {
- var type = blocks[i].getAttribute('type');
- if (list.indexOf(type) == -1) {
- list.push(type);
- }
- }
- };
-
- if (this.flyout) {
- // If has a single flyout, add block types for the single flyout.
- pushBlockTypesToList(this.getSelectedXml(), blockTypeList);
- } else {
- // If has categories, add block types for each category.
-
- for (var i = 0, category; category = this.toolboxList[i]; i++) {
- if (category.type == ListElement.TYPE_CATEGORY) {
- pushBlockTypesToList(category.xml, blockTypeList);
- }
- }
- }
-
- // Add the block types from any pre-loaded blocks.
- pushBlockTypesToList(this.getPreloadXml(), blockTypeList);
-
- return blockTypeList;
-};
-
-/**
- * Adds new imported block types to the list of current imported block types.
- * @param {!Array.} blockTypes Array of block types imported.
- */
-WorkspaceFactoryModel.prototype.addImportedBlockTypes = function(blockTypes) {
- this.importedBlockTypes = this.importedBlockTypes.concat(blockTypes);
-};
-
-/**
- * Updates block types in block library.
- * @param {!Array.} blockTypes Array of block types in block library.
- */
-WorkspaceFactoryModel.prototype.updateLibBlockTypes = function(blockTypes) {
- this.libBlockTypes = blockTypes;
-};
-
-/**
- * Determines if a block type is defined as a standard block, in the block
- * library, or as an imported block.
- * @param {string} blockType Block type to check.
- * @return {boolean} True if blockType is defined, false otherwise.
- */
-WorkspaceFactoryModel.prototype.isDefinedBlockType = function(blockType) {
- var isStandardBlock = StandardCategories.coreBlockTypes.indexOf(blockType)
- != -1;
- var isLibBlock = this.libBlockTypes.indexOf(blockType) != -1;
- var isImportedBlock = this.importedBlockTypes.indexOf(blockType) != -1;
- return (isStandardBlock || isLibBlock || isImportedBlock);
-};
-
-/**
- * Checks if any of the block types are already defined.
- * @param {!Array.} blockTypes Array of block types.
- * @return {boolean} True if a block type in the array is already defined,
- * false if none of the blocks are already defined.
- */
-WorkspaceFactoryModel.prototype.hasDefinedBlockTypes = function(blockTypes) {
- for (var i = 0, blockType; blockType = blockTypes[i]; i++) {
- if (this.isDefinedBlockType(blockType)) {
- return true;
- }
- }
- return false;
-};
-
-/**
- * Class for a ListElement.
- * @constructor
- */
-ListElement = function(type, opt_name) {
- this.type = type;
- // XML DOM element to load the element.
- this.xml = Blockly.Xml.textToDom('');
- // Name of category. Can be changed by user. Null if separator.
- this.name = opt_name ? opt_name : null;
- // Unique ID of element. Does not change.
- this.id = Blockly.utils.genUid();
- // Color of category. Default is no color. Null if separator.
- this.color = null;
- // Stores a custom tag, if necessary. Null if no custom tag or separator.
- this.custom = null;
-};
-
-// List element types.
-ListElement.TYPE_CATEGORY = 'category';
-ListElement.TYPE_SEPARATOR = 'separator';
-ListElement.TYPE_FLYOUT = 'flyout';
-
-/**
- * Saves a category by updating its XML (does not save XML for
- * elements that are not categories).
- * @param {!Blockly.workspace} workspace The workspace to save category entry
- * from.
- */
-ListElement.prototype.saveFromWorkspace = function(workspace) {
- // Only save XML for categories and flyouts.
- if (this.type == ListElement.TYPE_FLYOUT ||
- this.type == ListElement.TYPE_CATEGORY) {
- this.xml = Blockly.Xml.workspaceToDom(workspace);
- }
-};
-
-
-/**
- * Changes the name of a category object given a new name. Returns if
- * not a category.
- * @param {string} name New name of category.
- */
-ListElement.prototype.changeName = function (name) {
- // Only update list elements that are categories.
- if (this.type != ListElement.TYPE_CATEGORY) {
- return;
- }
- this.name = name;
-};
-
-/**
- * Sets the color of a category. If tries to set the color of something other
- * than a category, returns.
- * @param {string} color The color that should be used for that category.
- */
-ListElement.prototype.changeColor = function (color) {
- if (this.type != ListElement.TYPE_CATEGORY) {
- return;
- }
- this.color = color;
-};
-
-/**
- * Makes a copy of the original element and returns it. Everything about the
- * copy is identical except for its ID.
- * @return {!ListElement} The copy of the ListElement.
- */
-ListElement.prototype.copy = function() {
- copy = new ListElement(this.type);
- // Generate a unique ID for the element.
- copy.id = Blockly.utils.genUid();
- // Copy all attributes except ID.
- copy.name = this.name;
- copy.xml = this.xml;
- copy.color = this.color;
- copy.custom = this.custom;
- // Return copy.
- return copy;
-};
diff --git a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_view.js b/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_view.js
deleted file mode 100644
index bf7463eb98f..00000000000
--- a/trunk/web/blockly/demos/blockfactory/workspacefactory/wfactory_view.js
+++ /dev/null
@@ -1,436 +0,0 @@
-/**
- * @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.
- */
-
-/**
- * Controls the UI elements for workspace factory, mainly the category tabs.
- * Also includes downloading files because that interacts directly with the DOM.
- * Depends on WorkspaceFactoryController (for adding mouse listeners). Tabs for
- * each category are stored in tab map, which associates a unique ID for a
- * category with a particular tab.
- *
- * @author Emma Dauterman (edauterman)
- */
-
-goog.require('FactoryUtils');
-
-/**
- * Class for a WorkspaceFactoryView
- * @constructor
- */
-WorkspaceFactoryView = function() {
- // For each tab, maps ID of a ListElement to the td DOM element.
- this.tabMap = Object.create(null);
-};
-
-/**
- * Adds a category tab to the UI, and updates tabMap accordingly.
- * @param {string} name The name of the category being created
- * @param {string} id ID of category being created
- * @return {!Element} DOM element created for tab
- */
-WorkspaceFactoryView.prototype.addCategoryRow = function(name, id) {
- var table = document.getElementById('categoryTable');
- var count = table.rows.length;
-
- // Delete help label and enable category buttons if it's the first category.
- if (count == 0) {
- document.getElementById('categoryHeader').textContent = 'Your categories:';
- }
-
- // Create tab.
- var row = table.insertRow(count);
- var nextEntry = row.insertCell(0);
- // Configure tab.
- nextEntry.id = this.createCategoryIdName(name);
- nextEntry.textContent = name;
- // Store tab.
- this.tabMap[id] = table.rows[count].cells[0];
- // Return tab.
- return nextEntry;
-};
-
-/**
- * Deletes a category tab from the UI and updates tabMap accordingly.
- * @param {string} id ID of category to be deleted.
- * @param {string} name The name of the category to be deleted.
- */
-WorkspaceFactoryView.prototype.deleteElementRow = function(id, index) {
- // Delete tab entry.
- delete this.tabMap[id];
- // Delete tab row.
- var table = document.getElementById('categoryTable');
- var count = table.rows.length;
- table.deleteRow(index);
-
- // If last category removed, add category help text and disable category
- // buttons.
- this.addEmptyCategoryMessage();
-};
-
-/**
- * If there are no toolbox elements created, adds a help message to show
- * where categories will appear. Should be called when deleting list elements
- * in case the last element is deleted.
- */
-WorkspaceFactoryView.prototype.addEmptyCategoryMessage = function() {
- var table = document.getElementById('categoryTable');
- if (!table.rows.length) {
- document.getElementById('categoryHeader').textContent =
- 'You currently have no categories.';
- }
-};
-
-/**
- * Given the index of the currently selected element, updates the state of
- * the buttons that allow the user to edit the list elements. Updates the edit
- * and arrow buttons. Should be called when adding or removing elements
- * or when changing to a new element or when swapping to a different element.
- * TODO(evd2014): Switch to using CSS to add/remove styles.
- * @param {number} selectedIndex The index of the currently selected category,
- * -1 if no categories created.
- * @param {ListElement} selected The selected ListElement.
- */
-WorkspaceFactoryView.prototype.updateState = function(selectedIndex, selected) {
- // Disable/enable editing buttons as necessary.
- document.getElementById('button_editCategory').disabled = selectedIndex < 0 ||
- selected.type != ListElement.TYPE_CATEGORY;
- document.getElementById('button_remove').disabled = selectedIndex < 0;
- document.getElementById('button_up').disabled = selectedIndex <= 0;
- var table = document.getElementById('categoryTable');
- document.getElementById('button_down').disabled = selectedIndex >=
- table.rows.length - 1 || selectedIndex < 0;
- // Disable/enable the workspace as necessary.
- this.disableWorkspace(this.shouldDisableWorkspace(selected));
-};
-
-/**
- * Determines the DOM ID for a category given its name.
- * @param {string} name Name of category
- * @return {string} ID of category tab
- */
-WorkspaceFactoryView.prototype.createCategoryIdName = function(name) {
- return 'tab_' + name;
-};
-
-/**
- * Switches a tab on or off.
- * @param {string} id ID of the tab to switch on or off.
- * @param {boolean} selected True if tab should be on, false if tab should be
- * off.
- */
-WorkspaceFactoryView.prototype.setCategoryTabSelection =
- function(id, selected) {
- if (!this.tabMap[id]) {
- return; // Exit if tab does not exist.
- }
- this.tabMap[id].className = selected ? 'tabon' : 'taboff';
-};
-
-/**
- * Used to bind a click to a certain DOM element (used for category tabs).
- * Taken directly from code.js
- * @param {string|!Element} e1 Tab element or corresponding ID string.
- * @param {!Function} func Function to be executed on click.
- */
-WorkspaceFactoryView.prototype.bindClick = function(el, func) {
- if (typeof el == 'string') {
- el = document.getElementById(el);
- }
- el.addEventListener('click', func, true);
- el.addEventListener('touchend', func, true);
-};
-
-/**
- * Creates a file and downloads it. In some browsers downloads, and in other
- * browsers, opens new tab with contents.
- * @param {string} filename Name of file
- * @param {!Blob} data Blob containing contents to download
- */
-WorkspaceFactoryView.prototype.createAndDownloadFile =
- function(filename, data) {
- var clickEvent = new MouseEvent('click', {
- 'view': window,
- 'bubbles': true,
- 'cancelable': false
- });
- var a = document.createElement('a');
- a.href = window.URL.createObjectURL(data);
- a.download = filename;
- a.textContent = 'Download file!';
- a.dispatchEvent(clickEvent);
-};
-
-/**
- * Given the ID of a certain category, updates the corresponding tab in
- * the DOM to show a new name.
- * @param {string} newName Name of string to be displayed on tab
- * @param {string} id ID of category to be updated
- */
-WorkspaceFactoryView.prototype.updateCategoryName = function(newName, id) {
- this.tabMap[id].textContent = newName;
- this.tabMap[id].id = this.createCategoryIdName(newName);
-};
-
-/**
- * Moves a tab from one index to another. Adjusts index inserting before
- * based on if inserting before or after. Checks that the indexes are in
- * bounds, throws error if not.
- * @param {string} id The ID of the category to move.
- * @param {number} newIndex The index to move the category to.
- * @param {number} oldIndex The index the category is currently at.
- */
-WorkspaceFactoryView.prototype.moveTabToIndex =
- function(id, newIndex, oldIndex) {
- var table = document.getElementById('categoryTable');
- // Check that indexes are in bounds.
- if (newIndex < 0 || newIndex >= table.rows.length || oldIndex < 0 ||
- oldIndex >= table.rows.length) {
- throw new Error('Index out of bounds when moving tab in the view.');
- }
-
- if (newIndex < oldIndex) {
- // Inserting before.
- var row = table.insertRow(newIndex);
- row.appendChild(this.tabMap[id]);
- table.deleteRow(oldIndex + 1);
- } else {
- // Inserting after.
- var row = table.insertRow(newIndex + 1);
- row.appendChild(this.tabMap[id]);
- table.deleteRow(oldIndex);
- }
-};
-
-/**
- * Given a category ID and color, use that color to color the left border of the
- * tab for that category.
- * @param {string} id The ID of the category to color.
- * @param {string} color The color for to be used for the border of the tab.
- * Must be a valid CSS string.
- */
-WorkspaceFactoryView.prototype.setBorderColor = function(id, color) {
- var tab = this.tabMap[id];
- tab.style.borderLeftWidth = '8px';
- tab.style.borderLeftStyle = 'solid';
- tab.style.borderColor = color;
-};
-
-/**
- * Given a separator ID, creates a corresponding tab in the view, updates
- * tab map, and returns the tab.
- * @param {string} id The ID of the separator.
- * @param {!Element} The td DOM element representing the separator.
- */
-WorkspaceFactoryView.prototype.addSeparatorTab = function(id) {
- var table = document.getElementById('categoryTable');
- var count = table.rows.length;
-
- if (count == 0) {
- document.getElementById('categoryHeader').textContent = 'Your categories:';
- }
- // Create separator.
- var row = table.insertRow(count);
- var nextEntry = row.insertCell(0);
- // Configure separator.
- nextEntry.style.height = '10px';
- // Store and return separator.
- this.tabMap[id] = table.rows[count].cells[0];
- return nextEntry;
-};
-
-/**
- * Disables or enables the workspace by putting a div over or under the
- * toolbox workspace, depending on the value of disable. Used when switching
- * to/from separators where the user shouldn't be able to drag blocks into
- * the workspace.
- * @param {boolean} disable True if the workspace should be disabled, false
- * if it should be enabled.
- */
-WorkspaceFactoryView.prototype.disableWorkspace = function(disable) {
- if (disable) {
- document.getElementById('toolbox_section').className = 'disabled';
- document.getElementById('toolbox_blocks').style.pointerEvents = 'none';
- } else {
- document.getElementById('toolbox_section').className = '';
- document.getElementById('toolbox_blocks').style.pointerEvents = 'auto';
- }
-
-};
-
-/**
- * Determines if the workspace should be disabled. The workspace should be
- * disabled if category is a separator or has VARIABLE or PROCEDURE tags.
- * @return {boolean} True if the workspace should be disabled, false otherwise.
- */
-WorkspaceFactoryView.prototype.shouldDisableWorkspace = function(category) {
- return category != null && category.type != ListElement.TYPE_FLYOUT &&
- (category.type == ListElement.TYPE_SEPARATOR ||
- category.custom == 'VARIABLE' || category.custom == 'PROCEDURE');
-};
-
-/**
- * Removes all categories and separators in the view. Clears the tabMap to
- * reflect this.
- */
-WorkspaceFactoryView.prototype.clearToolboxTabs = function() {
- this.tabMap = [];
- var oldCategoryTable = document.getElementById('categoryTable');
- var newCategoryTable = document.createElement('table');
- newCategoryTable.id = 'categoryTable';
- newCategoryTable.style.width = 'auto';
- oldCategoryTable.parentElement.replaceChild(newCategoryTable,
- oldCategoryTable);
-};
-
-/**
- * Given a set of blocks currently loaded user-generated shadow blocks, visually
- * marks them without making them actual shadow blocks (allowing them to still
- * be editable and movable).
- * @param {!Array.} blocks Array of user-generated shadow blocks
- * currently loaded.
- */
-WorkspaceFactoryView.prototype.markShadowBlocks = function(blocks) {
- for (var i = 0; i < blocks.length; i++) {
- this.markShadowBlock(blocks[i]);
- }
-};
-
-/**
- * Visually marks a user-generated shadow block as a shadow block in the
- * workspace without making the block an actual shadow block (allowing it
- * to be moved and edited).
- * @param {!Blockly.Block} block The block that should be marked as a shadow
- * block (must be rendered).
- */
-WorkspaceFactoryView.prototype.markShadowBlock = function(block) {
- // Add Blockly CSS for user-generated shadow blocks.
- Blockly.utils.addClass(block.svgGroup_, 'shadowBlock');
- // If not a valid shadow block, add a warning message.
- if (!block.getSurroundParent()) {
- block.setWarningText('Shadow blocks must be nested inside' +
- ' other blocks to be displayed.');
- }
- if (FactoryUtils.hasVariableField(block)) {
- block.setWarningText('Cannot make variable blocks shadow blocks.');
- }
-};
-
-/**
- * Removes visual marking for a shadow block given a rendered block.
- * @param {!Blockly.Block} block The block that should be unmarked as a shadow
- * block (must be rendered).
- */
-WorkspaceFactoryView.prototype.unmarkShadowBlock = function(block) {
- // Remove Blockly CSS for user-generated shadow blocks.
- Blockly.utils.removeClass(block.svgGroup_, 'shadowBlock');
-};
-
-/**
- * Sets the tabs for modes according to which mode the user is currenly
- * editing in.
- * @param {string} mode The mode being switched to
- * (WorkspaceFactoryController.MODE_TOOLBOX or WorkspaceFactoryController.MODE_PRELOAD).
- */
-WorkspaceFactoryView.prototype.setModeSelection = function(mode) {
- document.getElementById('tab_preload').className = mode ==
- WorkspaceFactoryController.MODE_PRELOAD ? 'tabon' : 'taboff';
- document.getElementById('preload_div').style.display = mode ==
- WorkspaceFactoryController.MODE_PRELOAD ? 'block' : 'none';
- document.getElementById('tab_toolbox').className = mode ==
- WorkspaceFactoryController.MODE_TOOLBOX ? 'tabon' : 'taboff';
- document.getElementById('toolbox_div').style.display = mode ==
- WorkspaceFactoryController.MODE_TOOLBOX ? 'block' : 'none';
-};
-
-/**
- * Updates the help text above the workspace depending on the selected mode.
- * @param {string} mode The selected mode (WorkspaceFactoryController.MODE_TOOLBOX or
- * WorkspaceFactoryController.MODE_PRELOAD).
- */
-WorkspaceFactoryView.prototype.updateHelpText = function(mode) {
- if (mode == WorkspaceFactoryController.MODE_TOOLBOX) {
- var helpText = 'Drag blocks into the workspace to configure the toolbox ' +
- 'in your custom workspace.';
- } else {
- var helpText = 'Drag blocks into the workspace to pre-load them in your ' +
- 'custom workspace.'
- }
- document.getElementById('editHelpText').textContent = helpText;
-};
-
-/**
- * Sets the basic options that are not dependent on if there are categories
- * or a single flyout of blocks. Updates checkboxes and text fields.
- */
-WorkspaceFactoryView.prototype.setBaseOptions = function() {
- // Readonly mode.
- document.getElementById('option_readOnly_checkbox').checked = false;
- blocklyFactory.ifCheckedEnable(true, ['readonly1', 'readonly2']);
-
- // Set basic options.
- document.getElementById('option_css_checkbox').checked = true;
- document.getElementById('option_maxBlocks_number').value = 100;
- document.getElementById('option_media_text').value =
- 'https://blockly-demo.appspot.com/static/media/';
- document.getElementById('option_rtl_checkbox').checked = false;
- document.getElementById('option_sounds_checkbox').checked = true;
- document.getElementById('option_oneBasedIndex_checkbox').checked = true;
- document.getElementById('option_horizontalLayout_checkbox').checked = false;
- document.getElementById('option_toolboxPosition_checkbox').checked = false;
-
- // Check infinite blocks and hide suboption.
- document.getElementById('option_infiniteBlocks_checkbox').checked = true;
- document.getElementById('maxBlockNumber_option').style.display =
- 'none';
-
- // Uncheck grid and zoom options and hide suboptions.
- document.getElementById('option_grid_checkbox').checked = false;
- document.getElementById('grid_options').style.display = 'none';
- document.getElementById('option_zoom_checkbox').checked = false;
- document.getElementById('zoom_options').style.display = 'none';
-
- // Set grid options.
- document.getElementById('gridOption_spacing_number').value = 20;
- document.getElementById('gridOption_length_number').value = 1;
- document.getElementById('gridOption_colour_text').value = '#888';
- document.getElementById('gridOption_snap_checkbox').checked = false;
-
- // Set zoom options.
- document.getElementById('zoomOption_controls_checkbox').checked = true;
- document.getElementById('zoomOption_wheel_checkbox').checked = true;
- document.getElementById('zoomOption_startScale_number').value = 1.0;
- document.getElementById('zoomOption_maxScale_number').value = 3;
- document.getElementById('zoomOption_minScale_number').value = 0.3;
- document.getElementById('zoomOption_scaleSpeed_number').value = 1.2;
-};
-
-/**
- * Updates category specific options depending on if there are categories
- * currently present. Updates checkboxes and text fields in the view.
- * @param {boolean} hasCategories True if categories are present, false if all
- * blocks are displayed in a single flyout.
- */
-WorkspaceFactoryView.prototype.setCategoryOptions = function(hasCategories) {
- document.getElementById('option_collapse_checkbox').checked = hasCategories;
- document.getElementById('option_comments_checkbox').checked = hasCategories;
- document.getElementById('option_disable_checkbox').checked = hasCategories;
- document.getElementById('option_scrollbars_checkbox').checked = hasCategories;
- document.getElementById('option_trashcan_checkbox').checked = hasCategories;
-};
diff --git a/trunk/web/blockly/demos/blockfactory_old/blocks.js b/trunk/web/blockly/demos/blockfactory_old/blocks.js
deleted file mode 100644
index 856780a526a..00000000000
--- a/trunk/web/blockly/demos/blockfactory_old/blocks.js
+++ /dev/null
@@ -1,826 +0,0 @@
-/**
- * Blockly Demos: Block Factory Blocks
- *
- * Copyright 2012 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 Blocks for Blockly's Block Factory application.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-Blockly.Blocks['factory_base'] = {
- // Base of new block.
- init: function() {
- this.setColour(120);
- this.appendDummyInput()
- .appendField('name')
- .appendField(new Blockly.FieldTextInput('block_type'), 'NAME');
- this.appendStatementInput('INPUTS')
- .setCheck('Input')
- .appendField('inputs');
- var dropdown = new Blockly.FieldDropdown([
- ['automatic inputs', 'AUTO'],
- ['external inputs', 'EXT'],
- ['inline inputs', 'INT']]);
- this.appendDummyInput()
- .appendField(dropdown, 'INLINE');
- dropdown = new Blockly.FieldDropdown([
- ['no connections', 'NONE'],
- ['← left output', 'LEFT'],
- ['↕ top+bottom connections', 'BOTH'],
- ['↑ top connection', 'TOP'],
- ['↓ bottom connection', 'BOTTOM']],
- function(option) {
- this.sourceBlock_.updateShape_(option);
- // Connect a shadow block to this new input.
- this.sourceBlock_.spawnOutputShadow_(option);
- });
- this.appendDummyInput()
- .appendField(dropdown, 'CONNECTIONS');
- this.appendValueInput('COLOUR')
- .setCheck('Colour')
- .appendField('colour');
- this.setTooltip('Build a custom block by plugging\n' +
- 'fields, inputs and other blocks here.');
- this.setHelpUrl(
- 'https://developers.google.com/blockly/guides/create-custom-blocks/block-factory');
- },
- mutationToDom: function() {
- var container = document.createElement('mutation');
- container.setAttribute('connections', this.getFieldValue('CONNECTIONS'));
- return container;
- },
- domToMutation: function(xmlElement) {
- var connections = xmlElement.getAttribute('connections');
- this.updateShape_(connections);
- },
- spawnOutputShadow_: function(option) {
- // Helper method for deciding which type of outputs this block needs
- // to attach shaddow blocks to.
- switch (option) {
- case 'LEFT':
- this.connectOutputShadow_('OUTPUTTYPE');
- break;
- case 'TOP':
- this.connectOutputShadow_('TOPTYPE');
- break;
- case 'BOTTOM':
- this.connectOutputShadow_('BOTTOMTYPE');
- break;
- case 'BOTH':
- this.connectOutputShadow_('TOPTYPE');
- this.connectOutputShadow_('BOTTOMTYPE');
- break;
- }
- },
- connectOutputShadow_: function(outputType) {
- // Helper method to create & connect shadow block.
- var type = this.workspace.newBlock('type_null');
- type.setShadow(true);
- type.outputConnection.connect(this.getInput(outputType).connection);
- type.initSvg();
- type.render();
- },
- updateShape_: function(option) {
- var outputExists = this.getInput('OUTPUTTYPE');
- var topExists = this.getInput('TOPTYPE');
- var bottomExists = this.getInput('BOTTOMTYPE');
- if (option == 'LEFT') {
- if (!outputExists) {
- this.addTypeInput_('OUTPUTTYPE', 'output type');
- }
- } else if (outputExists) {
- this.removeInput('OUTPUTTYPE');
- }
- if (option == 'TOP' || option == 'BOTH') {
- if (!topExists) {
- this.addTypeInput_('TOPTYPE', 'top type');
- }
- } else if (topExists) {
- this.removeInput('TOPTYPE');
- }
- if (option == 'BOTTOM' || option == 'BOTH') {
- if (!bottomExists) {
- this.addTypeInput_('BOTTOMTYPE', 'bottom type');
- }
- } else if (bottomExists) {
- this.removeInput('BOTTOMTYPE');
- }
- },
- addTypeInput_: function(name, label) {
- this.appendValueInput(name)
- .setCheck('Type')
- .appendField(label);
- this.moveInputBefore(name, 'COLOUR');
- }
-};
-
-var FIELD_MESSAGE = 'fields %1 %2';
-var FIELD_ARGS = [
- {
- "type": "field_dropdown",
- "name": "ALIGN",
- "options": [['left', 'LEFT'], ['right', 'RIGHT'], ['centre', 'CENTRE']],
- },
- {
- "type": "input_statement",
- "name": "FIELDS",
- "check": "Field"
- }
-];
-
-var TYPE_MESSAGE = 'type %1';
-var TYPE_ARGS = [
- {
- "type": "input_value",
- "name": "TYPE",
- "check": "Type",
- "align": "RIGHT"
- }
-];
-
-Blockly.Blocks['input_value'] = {
- // Value input.
- init: function() {
- this.jsonInit({
- "message0": "value input %1 %2",
- "args0": [
- {
- "type": "field_input",
- "name": "INPUTNAME",
- "text": "NAME"
- },
- {
- "type": "input_dummy"
- }
- ],
- "message1": FIELD_MESSAGE,
- "args1": FIELD_ARGS,
- "message2": TYPE_MESSAGE,
- "args2": TYPE_ARGS,
- "previousStatement": "Input",
- "nextStatement": "Input",
- "colour": 210,
- "tooltip": "A value socket for horizontal connections.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=71"
- });
- },
- onchange: function() {
- inputNameCheck(this);
- }
-};
-
-Blockly.Blocks['input_statement'] = {
- // Statement input.
- init: function() {
- this.jsonInit({
- "message0": "statement input %1 %2",
- "args0": [
- {
- "type": "field_input",
- "name": "INPUTNAME",
- "text": "NAME"
- },
- {
- "type": "input_dummy"
- },
- ],
- "message1": FIELD_MESSAGE,
- "args1": FIELD_ARGS,
- "message2": TYPE_MESSAGE,
- "args2": TYPE_ARGS,
- "previousStatement": "Input",
- "nextStatement": "Input",
- "colour": 210,
- "tooltip": "A statement socket for enclosed vertical stacks.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=246"
- });
- },
- onchange: function() {
- inputNameCheck(this);
- }
-};
-
-Blockly.Blocks['input_dummy'] = {
- // Dummy input.
- init: function() {
- this.jsonInit({
- "message0": "dummy input",
- "message1": FIELD_MESSAGE,
- "args1": FIELD_ARGS,
- "previousStatement": "Input",
- "nextStatement": "Input",
- "colour": 210,
- "tooltip": "For adding fields on a separate row with no " +
- "connections. Alignment options (left, right, centre) " +
- "apply only to multi-line fields.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293"
- });
- }
-};
-
-Blockly.Blocks['field_static'] = {
- // Text value.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('text')
- .appendField(new Blockly.FieldTextInput(''), 'TEXT');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Static text that serves as a label.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=88');
- }
-};
-
-Blockly.Blocks['field_input'] = {
- // Text input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('text input')
- .appendField(new Blockly.FieldTextInput('default'), 'TEXT')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('An input field for the user to enter text.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_number'] = {
- // Numeric input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('numeric input')
- .appendField(new Blockly.FieldNumber(0), 'VALUE')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.appendDummyInput()
- .appendField('min')
- .appendField(new Blockly.FieldNumber(-Infinity), 'MIN')
- .appendField('max')
- .appendField(new Blockly.FieldNumber(Infinity), 'MAX')
- .appendField('precision')
- .appendField(new Blockly.FieldNumber(0, 0), 'PRECISION');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('An input field for the user to enter a number.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_angle'] = {
- // Angle input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('angle input')
- .appendField(new Blockly.FieldAngle('90'), 'ANGLE')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('An input field for the user to enter an angle.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=372');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_dropdown'] = {
- // Dropdown menu.
- init: function() {
- this.appendDummyInput()
- .appendField('dropdown')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.optionCount_ = 3;
- this.updateShape_();
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setMutator(new Blockly.Mutator(['field_dropdown_option']));
- this.setColour(160);
- this.setTooltip('Dropdown menu with a list of options.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- },
- mutationToDom: function(workspace) {
- // Create XML to represent menu options.
- var container = document.createElement('mutation');
- container.setAttribute('options', this.optionCount_);
- return container;
- },
- domToMutation: function(container) {
- // Parse XML to restore the menu options.
- this.optionCount_ = parseInt(container.getAttribute('options'), 10);
- this.updateShape_();
- },
- decompose: function(workspace) {
- // Populate the mutator's dialog with this block's components.
- var containerBlock = workspace.newBlock('field_dropdown_container');
- containerBlock.initSvg();
- var connection = containerBlock.getInput('STACK').connection;
- for (var i = 0; i < this.optionCount_; i++) {
- var optionBlock = workspace.newBlock('field_dropdown_option');
- optionBlock.initSvg();
- connection.connect(optionBlock.previousConnection);
- connection = optionBlock.nextConnection;
- }
- return containerBlock;
- },
- compose: function(containerBlock) {
- // Reconfigure this block based on the mutator dialog's components.
- var optionBlock = containerBlock.getInputTargetBlock('STACK');
- // Count number of inputs.
- var data = [];
- while (optionBlock) {
- data.push([optionBlock.userData_, optionBlock.cpuData_]);
- optionBlock = optionBlock.nextConnection &&
- optionBlock.nextConnection.targetBlock();
- }
- this.optionCount_ = data.length;
- this.updateShape_();
- // Restore any data.
- for (var i = 0; i < this.optionCount_; i++) {
- this.setFieldValue(data[i][0] || 'option', 'USER' + i);
- this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
- }
- },
- saveConnections: function(containerBlock) {
- // Store names and values for each option.
- var optionBlock = containerBlock.getInputTargetBlock('STACK');
- var i = 0;
- while (optionBlock) {
- optionBlock.userData_ = this.getFieldValue('USER' + i);
- optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
- i++;
- optionBlock = optionBlock.nextConnection &&
- optionBlock.nextConnection.targetBlock();
- }
- },
- updateShape_: function() {
- // Modify this block to have the correct number of options.
- // Add new options.
- for (var i = 0; i < this.optionCount_; i++) {
- if (!this.getInput('OPTION' + i)) {
- this.appendDummyInput('OPTION' + i)
- .appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
- }
- }
- // Remove deleted options.
- while (this.getInput('OPTION' + i)) {
- this.removeInput('OPTION' + i);
- i++;
- }
- },
- onchange: function() {
- if (this.workspace && this.optionCount_ < 1) {
- this.setWarningText('Drop down menu must\nhave at least one option.');
- } else {
- fieldNameCheck(this);
- }
- }
-};
-
-Blockly.Blocks['field_dropdown_container'] = {
- // Container.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('add options');
- this.appendStatementInput('STACK');
- this.setTooltip('Add, remove, or reorder options\n' +
- 'to reconfigure this dropdown menu.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- this.contextMenu = false;
- }
-};
-
-Blockly.Blocks['field_dropdown_option'] = {
- // Add option.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('option');
- this.setPreviousStatement(true);
- this.setNextStatement(true);
- this.setTooltip('Add a new option to the dropdown menu.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
- this.contextMenu = false;
- }
-};
-
-Blockly.Blocks['field_checkbox'] = {
- // Checkbox.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('checkbox')
- .appendField(new Blockly.FieldCheckbox('TRUE'), 'CHECKED')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Checkbox field.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=485');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_colour'] = {
- // Colour input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('colour')
- .appendField(new Blockly.FieldColour('#ff0000'), 'COLOUR')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Colour input field.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=495');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_date'] = {
- // Date input.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('date')
- .appendField(new Blockly.FieldDate(), 'DATE')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Date input field.');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_variable'] = {
- // Dropdown for variables.
- init: function() {
- this.setColour(160);
- this.appendDummyInput()
- .appendField('variable')
- .appendField(new Blockly.FieldTextInput('item'), 'TEXT')
- .appendField(',')
- .appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Dropdown menu for variable names.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=510');
- },
- onchange: function() {
- fieldNameCheck(this);
- }
-};
-
-Blockly.Blocks['field_image'] = {
- // Image.
- init: function() {
- this.setColour(160);
- var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
- this.appendDummyInput()
- .appendField('image')
- .appendField(new Blockly.FieldTextInput(src), 'SRC');
- this.appendDummyInput()
- .appendField('width')
- .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH')
- .appendField('height')
- .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT')
- .appendField('alt text')
- .appendField(new Blockly.FieldTextInput('*'), 'ALT');
- this.setPreviousStatement(true, 'Field');
- this.setNextStatement(true, 'Field');
- this.setTooltip('Static image (JPEG, PNG, GIF, SVG, BMP).\n' +
- 'Retains aspect ratio regardless of height and width.\n' +
- 'Alt text is for when collapsed.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=567');
- }
-};
-
-Blockly.Blocks['type_group'] = {
- // Group of types.
- init: function() {
- this.typeCount_ = 2;
- this.updateShape_();
- this.setOutput(true, 'Type');
- this.setMutator(new Blockly.Mutator(['type_group_item']));
- this.setColour(230);
- this.setTooltip('Allows more than one type to be accepted.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677');
- },
- mutationToDom: function(workspace) {
- // Create XML to represent a group of types.
- var container = document.createElement('mutation');
- container.setAttribute('types', this.typeCount_);
- return container;
- },
- domToMutation: function(container) {
- // Parse XML to restore the group of types.
- this.typeCount_ = parseInt(container.getAttribute('types'), 10);
- this.updateShape_();
- for (var i = 0; i < this.typeCount_; i++) {
- this.removeInput('TYPE' + i);
- }
- for (var i = 0; i < this.typeCount_; i++) {
- var input = this.appendValueInput('TYPE' + i)
- .setCheck('Type');
- if (i == 0) {
- input.appendField('any of');
- }
- }
- },
- decompose: function(workspace) {
- // Populate the mutator's dialog with this block's components.
- var containerBlock = workspace.newBlock('type_group_container');
- containerBlock.initSvg();
- var connection = containerBlock.getInput('STACK').connection;
- for (var i = 0; i < this.typeCount_; i++) {
- var typeBlock = workspace.newBlock('type_group_item');
- typeBlock.initSvg();
- connection.connect(typeBlock.previousConnection);
- connection = typeBlock.nextConnection;
- }
- return containerBlock;
- },
- compose: function(containerBlock) {
- // Reconfigure this block based on the mutator dialog's components.
- var typeBlock = containerBlock.getInputTargetBlock('STACK');
- // Count number of inputs.
- var connections = [];
- while (typeBlock) {
- connections.push(typeBlock.valueConnection_);
- typeBlock = typeBlock.nextConnection &&
- typeBlock.nextConnection.targetBlock();
- }
- // Disconnect any children that don't belong.
- for (var i = 0; i < this.typeCount_; i++) {
- var connection = this.getInput('TYPE' + i).connection.targetConnection;
- if (connection && connections.indexOf(connection) == -1) {
- connection.disconnect();
- }
- }
- this.typeCount_ = connections.length;
- this.updateShape_();
- // Reconnect any child blocks.
- for (var i = 0; i < this.typeCount_; i++) {
- Blockly.Mutator.reconnect(connections[i], this, 'TYPE' + i);
- }
- },
- saveConnections: function(containerBlock) {
- // Store a pointer to any connected child blocks.
- var typeBlock = containerBlock.getInputTargetBlock('STACK');
- var i = 0;
- while (typeBlock) {
- var input = this.getInput('TYPE' + i);
- typeBlock.valueConnection_ = input && input.connection.targetConnection;
- i++;
- typeBlock = typeBlock.nextConnection &&
- typeBlock.nextConnection.targetBlock();
- }
- },
- updateShape_: function() {
- // Modify this block to have the correct number of inputs.
- // Add new inputs.
- for (var i = 0; i < this.typeCount_; i++) {
- if (!this.getInput('TYPE' + i)) {
- var input = this.appendValueInput('TYPE' + i);
- if (i == 0) {
- input.appendField('any of');
- }
- }
- }
- // Remove deleted inputs.
- while (this.getInput('TYPE' + i)) {
- this.removeInput('TYPE' + i);
- i++;
- }
- }
-};
-
-Blockly.Blocks['type_group_container'] = {
- // Container.
- init: function() {
- this.jsonInit({
- "message0": "add types %1 %2",
- "args0": [
- {"type": "input_dummy"},
- {"type": "input_statement", "name": "STACK"}
- ],
- "colour": 230,
- "tooltip": "Add, or remove allowed type.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
- });
- }
-};
-
-Blockly.Blocks['type_group_item'] = {
- // Add type.
- init: function() {
- this.jsonInit({
- "message0": "type",
- "previousStatement": null,
- "nextStatement": null,
- "colour": 230,
- "tooltip": "Add a new allowed type.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
- });
- }
-};
-
-Blockly.Blocks['type_null'] = {
- // Null type.
- valueType: null,
- init: function() {
- this.jsonInit({
- "message0": "any",
- "output": "Type",
- "colour": 230,
- "tooltip": "Any type is allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_boolean'] = {
- // Boolean type.
- valueType: 'Boolean',
- init: function() {
- this.jsonInit({
- "message0": "Boolean",
- "output": "Type",
- "colour": 230,
- "tooltip": "Booleans (true/false) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_number'] = {
- // Number type.
- valueType: 'Number',
- init: function() {
- this.jsonInit({
- "message0": "Number",
- "output": "Type",
- "colour": 230,
- "tooltip": "Numbers (int/float) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_string'] = {
- // String type.
- valueType: 'String',
- init: function() {
- this.jsonInit({
- "message0": "String",
- "output": "Type",
- "colour": 230,
- "tooltip": "Strings (text) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_list'] = {
- // List type.
- valueType: 'Array',
- init: function() {
- this.jsonInit({
- "message0": "Array",
- "output": "Type",
- "colour": 230,
- "tooltip": "Arrays (lists) are allowed.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
- });
- }
-};
-
-Blockly.Blocks['type_other'] = {
- // Other type.
- init: function() {
- this.jsonInit({
- "message0": "other %1",
- "args0": [{"type": "field_input", "name": "TYPE", "text": ""}],
- "output": "Type",
- "colour": 230,
- "tooltip": "Custom type to allow.",
- "helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=702"
- });
- }
-};
-
-Blockly.Blocks['colour_hue'] = {
- // Set the colour of the block.
- init: function() {
- this.appendDummyInput()
- .appendField('hue:')
- .appendField(new Blockly.FieldAngle('0', this.validator), 'HUE');
- this.setOutput(true, 'Colour');
- this.setTooltip('Paint the block with this colour.');
- this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55');
- },
- validator: function(text) {
- // Update the current block's colour to match.
- var hue = parseInt(text, 10);
- if (!isNaN(hue)) {
- this.sourceBlock_.setColour(hue);
- }
- },
- mutationToDom: function(workspace) {
- var container = document.createElement('mutation');
- container.setAttribute('colour', this.getColour());
- return container;
- },
- domToMutation: function(container) {
- this.setColour(container.getAttribute('colour'));
- }
-};
-
-/**
- * Check to see if more than one field has this name.
- * Highly inefficient (On^2), but n is small.
- * @param {!Blockly.Block} referenceBlock Block to check.
- */
-function fieldNameCheck(referenceBlock) {
- if (!referenceBlock.workspace) {
- // Block has been deleted.
- return;
- }
- var name = referenceBlock.getFieldValue('FIELDNAME').toLowerCase();
- var count = 0;
- var blocks = referenceBlock.workspace.getAllBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- var otherName = block.getFieldValue('FIELDNAME');
- if (!block.disabled && !block.getInheritedDisabled() &&
- otherName && otherName.toLowerCase() == name) {
- count++;
- }
- }
- var msg = (count > 1) ?
- 'There are ' + count + ' field blocks\n with this name.' : null;
- referenceBlock.setWarningText(msg);
-}
-
-/**
- * Check to see if more than one input has this name.
- * Highly inefficient (On^2), but n is small.
- * @param {!Blockly.Block} referenceBlock Block to check.
- */
-function inputNameCheck(referenceBlock) {
- if (!referenceBlock.workspace) {
- // Block has been deleted.
- return;
- }
- var name = referenceBlock.getFieldValue('INPUTNAME').toLowerCase();
- var count = 0;
- var blocks = referenceBlock.workspace.getAllBlocks();
- for (var i = 0, block; block = blocks[i]; i++) {
- var otherName = block.getFieldValue('INPUTNAME');
- if (!block.disabled && !block.getInheritedDisabled() &&
- otherName && otherName.toLowerCase() == name) {
- count++;
- }
- }
- var msg = (count > 1) ?
- 'There are ' + count + ' input blocks\n with this name.' : null;
- referenceBlock.setWarningText(msg);
-}
diff --git a/trunk/web/blockly/demos/blockfactory_old/factory.js b/trunk/web/blockly/demos/blockfactory_old/factory.js
deleted file mode 100644
index 79df6f148fd..00000000000
--- a/trunk/web/blockly/demos/blockfactory_old/factory.js
+++ /dev/null
@@ -1,850 +0,0 @@
-/**
- * Blockly Demos: Block Factory
- *
- * Copyright 2012 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.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-/**
- * Workspace for user to build block.
- * @type {Blockly.Workspace}
- */
-var mainWorkspace = null;
-
-/**
- * Workspace for preview of block.
- * @type {Blockly.Workspace}
- */
-var previewWorkspace = null;
-
-/**
- * Name of block if not named.
- */
-var UNNAMED = 'unnamed';
-
-/**
- * Change the language code format.
- */
-function formatChange() {
- 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();
- updatePreview();
- } else {
- mask.style.display = 'none';
- languageTA.style.display = 'none';
- languagePre.style.display = 'block';
- updateLanguage();
- }
- disableEnableLink();
-}
-
-/**
- * Update the language code based on constructs made in Blockly.
- */
-function updateLanguage() {
- var rootBlock = getRootBlock();
- if (!rootBlock) {
- return;
- }
- var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
- if (!blockType) {
- blockType = UNNAMED;
- }
- blockType = blockType.replace(/\W/g, '_').replace(/^(\d)/, '_\\1');
- switch (document.getElementById('format').value) {
- case 'JSON':
- var code = formatJson_(blockType, rootBlock);
- break;
- case 'JavaScript':
- var code = formatJavaScript_(blockType, rootBlock);
- break;
- }
- injectCode(code, 'languagePre');
- updatePreview();
-}
-
-/**
- * Update the language code as JSON.
- * @param {string} blockType Name of block.
- * @param {!Blockly.Block} rootBlock Factory_base block.
- * @return {string} Generanted language code.
- * @private
- */
-function formatJson_(blockType, rootBlock) {
- var JS = {};
- // Type is not used by Blockly, but may be used by a loader.
- JS.type = blockType;
- // Generate inputs.
- var message = [];
- var args = [];
- var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
- var lastInput = null;
- while (contentsBlock) {
- if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
- var fields = getFieldsJson_(contentsBlock.getInputTargetBlock('FIELDS'));
- for (var i = 0; i < fields.length; i++) {
- if (typeof fields[i] == 'string') {
- message.push(fields[i].replace(/%/g, '%%'));
- } else {
- args.push(fields[i]);
- message.push('%' + args.length);
- }
- }
-
- var input = {type: contentsBlock.type};
- // Dummy inputs don't have names. Other inputs do.
- if (contentsBlock.type != 'input_dummy') {
- input.name = contentsBlock.getFieldValue('INPUTNAME');
- }
- var check = JSON.parse(getOptTypesFrom(contentsBlock, 'TYPE') || 'null');
- if (check) {
- input.check = check;
- }
- var align = contentsBlock.getFieldValue('ALIGN');
- if (align != 'LEFT') {
- input.align = align;
- }
- args.push(input);
- message.push('%' + args.length);
- lastInput = contentsBlock;
- }
- contentsBlock = contentsBlock.nextConnection &&
- contentsBlock.nextConnection.targetBlock();
- }
- // Remove last input if dummy and not empty.
- if (lastInput && lastInput.type == 'input_dummy') {
- var fields = lastInput.getInputTargetBlock('FIELDS');
- if (fields && getFieldsJson_(fields).join('').trim() != '') {
- var align = lastInput.getFieldValue('ALIGN');
- if (align != 'LEFT') {
- JS.lastDummyAlign0 = align;
- }
- args.pop();
- message.pop();
- }
- }
- JS.message0 = message.join(' ');
- if (args.length) {
- JS.args0 = args;
- }
- // Generate inline/external switch.
- if (rootBlock.getFieldValue('INLINE') == 'EXT') {
- JS.inputsInline = false;
- } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
- JS.inputsInline = true;
- }
- // Generate output, or next/previous connections.
- switch (rootBlock.getFieldValue('CONNECTIONS')) {
- case 'LEFT':
- JS.output =
- JSON.parse(getOptTypesFrom(rootBlock, 'OUTPUTTYPE') || 'null');
- break;
- case 'BOTH':
- JS.previousStatement =
- JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
- JS.nextStatement =
- JSON.parse(getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
- break;
- case 'TOP':
- JS.previousStatement =
- JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
- break;
- case 'BOTTOM':
- JS.nextStatement =
- JSON.parse(getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
- break;
- }
- // Generate colour.
- var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
- if (colourBlock && !colourBlock.disabled) {
- var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
- JS.colour = hue;
- }
- JS.tooltip = '';
- JS.helpUrl = 'http://www.example.com/';
- return JSON.stringify(JS, null, ' ');
-}
-
-/**
- * Update the language code as JavaScript.
- * @param {string} blockType Name of block.
- * @param {!Blockly.Block} rootBlock Factory_base block.
- * @return {string} Generanted language code.
- * @private
- */
-function formatJavaScript_(blockType, rootBlock) {
- var code = [];
- code.push("Blockly.Blocks['" + blockType + "'] = {");
- code.push(" init: function() {");
- // Generate inputs.
- var TYPES = {'input_value': 'appendValueInput',
- 'input_statement': 'appendStatementInput',
- 'input_dummy': 'appendDummyInput'};
- var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
- while (contentsBlock) {
- if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
- var name = '';
- // Dummy inputs don't have names. Other inputs do.
- if (contentsBlock.type != 'input_dummy') {
- name = escapeString(contentsBlock.getFieldValue('INPUTNAME'));
- }
- code.push(' this.' + TYPES[contentsBlock.type] + '(' + name + ')');
- var check = getOptTypesFrom(contentsBlock, 'TYPE');
- if (check) {
- code.push(' .setCheck(' + check + ')');
- }
- var align = contentsBlock.getFieldValue('ALIGN');
- if (align != 'LEFT') {
- code.push(' .setAlign(Blockly.ALIGN_' + align + ')');
- }
- var fields = getFieldsJs_(contentsBlock.getInputTargetBlock('FIELDS'));
- for (var i = 0; i < fields.length; i++) {
- code.push(' .appendField(' + fields[i] + ')');
- }
- // Add semicolon to last line to finish the statement.
- code[code.length - 1] += ';';
- }
- contentsBlock = contentsBlock.nextConnection &&
- contentsBlock.nextConnection.targetBlock();
- }
- // Generate inline/external switch.
- if (rootBlock.getFieldValue('INLINE') == 'EXT') {
- code.push(' this.setInputsInline(false);');
- } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
- code.push(' this.setInputsInline(true);');
- }
- // Generate output, or next/previous connections.
- switch (rootBlock.getFieldValue('CONNECTIONS')) {
- case 'LEFT':
- code.push(connectionLineJs_('setOutput', 'OUTPUTTYPE'));
- break;
- case 'BOTH':
- code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
- code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
- break;
- case 'TOP':
- code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
- break;
- case 'BOTTOM':
- code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
- break;
- }
- // Generate colour.
- var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
- if (colourBlock && !colourBlock.disabled) {
- var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
- if (!isNaN(hue)) {
- code.push(' this.setColour(' + hue + ');');
- }
- }
- code.push(" this.setTooltip('');");
- code.push(" this.setHelpUrl('http://www.example.com/');");
- code.push(' }');
- code.push('};');
- return code.join('\n');
-}
-
-/**
- * Create JS code required to create a top, bottom, or value connection.
- * @param {string} functionName JavaScript function name.
- * @param {string} typeName Name of type input.
- * @return {string} Line of JavaScript code to create connection.
- * @private
- */
-function connectionLineJs_(functionName, typeName) {
- var type = getOptTypesFrom(getRootBlock(), typeName);
- if (type) {
- type = ', ' + type;
- } else {
- type = '';
- }
- return ' this.' + functionName + '(true' + type + ');';
-}
-
-/**
- * Returns field strings and any config.
- * @param {!Blockly.Block} block Input block.
- * @return {!Array.} Field strings.
- * @private
- */
-function getFieldsJs_(block) {
- var fields = [];
- while (block) {
- if (!block.disabled && !block.getInheritedDisabled()) {
- switch (block.type) {
- case 'field_static':
- // Result: 'hello'
- fields.push(escapeString(block.getFieldValue('TEXT')));
- break;
- case 'field_input':
- // Result: new Blockly.FieldTextInput('Hello'), 'GREET'
- fields.push('new Blockly.FieldTextInput(' +
- escapeString(block.getFieldValue('TEXT')) + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_number':
- // Result: new Blockly.FieldNumber(10, 0, 100, 1), 'NUMBER'
- var args = [
- Number(block.getFieldValue('VALUE')),
- Number(block.getFieldValue('MIN')),
- Number(block.getFieldValue('MAX')),
- Number(block.getFieldValue('PRECISION'))
- ];
- // Remove any trailing arguments that aren't needed.
- if (args[3] == 0) {
- args.pop();
- if (args[2] == Infinity) {
- args.pop();
- if (args[1] == -Infinity) {
- args.pop();
- }
- }
- }
- fields.push('new Blockly.FieldNumber(' + args.join(', ') + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_angle':
- // Result: new Blockly.FieldAngle(90), 'ANGLE'
- fields.push('new Blockly.FieldAngle(' +
- Number(block.getFieldValue('ANGLE')) + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_checkbox':
- // Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
- fields.push('new Blockly.FieldCheckbox(' +
- escapeString(block.getFieldValue('CHECKED')) + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_colour':
- // Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
- fields.push('new Blockly.FieldColour(' +
- escapeString(block.getFieldValue('COLOUR')) + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_date':
- // Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
- fields.push('new Blockly.FieldDate(' +
- escapeString(block.getFieldValue('DATE')) + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_variable':
- // Result: new Blockly.FieldVariable('item'), 'VAR'
- var varname = escapeString(block.getFieldValue('TEXT') || null);
- fields.push('new Blockly.FieldVariable(' + varname + '), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- break;
- case 'field_dropdown':
- // Result:
- // new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
- var options = [];
- for (var i = 0; i < block.optionCount_; i++) {
- options[i] = '[' + escapeString(block.getFieldValue('USER' + i)) +
- ', ' + escapeString(block.getFieldValue('CPU' + i)) + ']';
- }
- if (options.length) {
- fields.push('new Blockly.FieldDropdown([' +
- options.join(', ') + ']), ' +
- escapeString(block.getFieldValue('FIELDNAME')));
- }
- break;
- case 'field_image':
- // Result: new Blockly.FieldImage('http://...', 80, 60, '*')
- var src = escapeString(block.getFieldValue('SRC'));
- var width = Number(block.getFieldValue('WIDTH'));
- var height = Number(block.getFieldValue('HEIGHT'));
- var alt = escapeString(block.getFieldValue('ALT'));
- fields.push('new Blockly.FieldImage(' +
- src + ', ' + width + ', ' + height + ', ' + alt + ')');
- break;
- }
- }
- block = block.nextConnection && block.nextConnection.targetBlock();
- }
- return fields;
-}
-
-/**
- * Returns field strings and any config.
- * @param {!Blockly.Block} block Input block.
- * @return {!Array.} Array of static text and field configs.
- * @private
- */
-function getFieldsJson_(block) {
- var fields = [];
- while (block) {
- if (!block.disabled && !block.getInheritedDisabled()) {
- switch (block.type) {
- case 'field_static':
- // Result: 'hello'
- fields.push(block.getFieldValue('TEXT'));
- break;
- case 'field_input':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- text: block.getFieldValue('TEXT')
- });
- break;
- case 'field_number':
- var obj = {
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- value: parseFloat(block.getFieldValue('VALUE'))
- };
- var min = parseFloat(block.getFieldValue('MIN'));
- if (min > -Infinity) {
- obj.min = min;
- }
- var max = parseFloat(block.getFieldValue('MAX'));
- if (max < Infinity) {
- obj.max = max;
- }
- var precision = parseFloat(block.getFieldValue('PRECISION'));
- if (precision) {
- obj.precision = precision;
- }
- fields.push(obj);
- break;
- case 'field_angle':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- angle: Number(block.getFieldValue('ANGLE'))
- });
- break;
- case 'field_checkbox':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- checked: block.getFieldValue('CHECKED') == 'TRUE'
- });
- break;
- case 'field_colour':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- colour: block.getFieldValue('COLOUR')
- });
- break;
- case 'field_date':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- date: block.getFieldValue('DATE')
- });
- break;
- case 'field_variable':
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- variable: block.getFieldValue('TEXT') || null
- });
- break;
- case 'field_dropdown':
- var options = [];
- for (var i = 0; i < block.optionCount_; i++) {
- options[i] = [block.getFieldValue('USER' + i),
- block.getFieldValue('CPU' + i)];
- }
- if (options.length) {
- fields.push({
- type: block.type,
- name: block.getFieldValue('FIELDNAME'),
- options: options
- });
- }
- break;
- case 'field_image':
- fields.push({
- type: block.type,
- src: block.getFieldValue('SRC'),
- width: Number(block.getFieldValue('WIDTH')),
- height: Number(block.getFieldValue('HEIGHT')),
- alt: block.getFieldValue('ALT')
- });
- break;
- }
- }
- block = block.nextConnection && block.nextConnection.targetBlock();
- }
- return fields;
-}
-
-/**
- * Escape a string.
- * @param {string} string String to escape.
- * @return {string} Escaped string surrouned by quotes.
- */
-function escapeString(string) {
- return JSON.stringify(string);
-}
-
-/**
- * Fetch the type(s) defined in the given input.
- * Format as a string for appending to the generated code.
- * @param {!Blockly.Block} block Block with input.
- * @param {string} name Name of the input.
- * @return {?string} String defining the types.
- */
-function getOptTypesFrom(block, name) {
- var types = getTypesFrom_(block, name);
- if (types.length == 0) {
- return undefined;
- } else if (types.indexOf('null') != -1) {
- return 'null';
- } else if (types.length == 1) {
- return types[0];
- } else {
- return '[' + types.join(', ') + ']';
- }
-}
-
-/**
- * Fetch the type(s) defined in the given input.
- * @param {!Blockly.Block} block Block with input.
- * @param {string} name Name of the input.
- * @return {!Array.} List of types.
- * @private
- */
-function getTypesFrom_(block, name) {
- var typeBlock = block.getInputTargetBlock(name);
- var types;
- if (!typeBlock || typeBlock.disabled) {
- types = [];
- } else if (typeBlock.type == 'type_other') {
- types = [escapeString(typeBlock.getFieldValue('TYPE'))];
- } else if (typeBlock.type == 'type_group') {
- types = [];
- for (var i = 0; i < typeBlock.typeCount_; i++) {
- types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + i));
- }
- // Remove duplicates.
- var hash = Object.create(null);
- for (var n = types.length - 1; n >= 0; n--) {
- if (hash[types[n]]) {
- types.splice(n, 1);
- }
- hash[types[n]] = true;
- }
- } else {
- types = [escapeString(typeBlock.valueType)];
- }
- return types;
-}
-
-/**
- * Update the generator code.
- * @param {!Blockly.Block} block Rendered block in preview workspace.
- */
-function updateGenerator(block) {
- function makeVar(root, name) {
- name = name.toLowerCase().replace(/\W/g, '_');
- return ' var ' + root + '_' + name;
- }
- var language = document.getElementById('language').value;
- var code = [];
- code.push("Blockly." + language + "['" + block.type +
- "'] = function(block) {");
-
- // Generate getters for any fields or inputs.
- for (var i = 0, input; input = block.inputList[i]; i++) {
- for (var j = 0, field; field = input.fieldRow[j]; j++) {
- var name = field.name;
- if (!name) {
- continue;
- }
- if (field instanceof Blockly.FieldVariable) {
- // Subclass of Blockly.FieldDropdown, must test first.
- code.push(makeVar('variable', name) +
- " = Blockly." + language +
- ".variableDB_.getName(block.getFieldValue('" + name +
- "'), Blockly.Variables.NAME_TYPE);");
- } else if (field instanceof Blockly.FieldAngle) {
- // Subclass of Blockly.FieldTextInput, must test first.
- code.push(makeVar('angle', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (Blockly.FieldDate && field instanceof Blockly.FieldDate) {
- // Blockly.FieldDate may not be compiled into Blockly.
- code.push(makeVar('date', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldColour) {
- code.push(makeVar('colour', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldCheckbox) {
- code.push(makeVar('checkbox', name) +
- " = block.getFieldValue('" + name + "') == 'TRUE';");
- } else if (field instanceof Blockly.FieldDropdown) {
- code.push(makeVar('dropdown', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldNumber) {
- code.push(makeVar('number', name) +
- " = block.getFieldValue('" + name + "');");
- } else if (field instanceof Blockly.FieldTextInput) {
- code.push(makeVar('text', name) +
- " = block.getFieldValue('" + name + "');");
- }
- }
- var name = input.name;
- if (name) {
- if (input.type == Blockly.INPUT_VALUE) {
- code.push(makeVar('value', name) +
- " = Blockly." + language + ".valueToCode(block, '" + name +
- "', Blockly." + language + ".ORDER_ATOMIC);");
- } else if (input.type == Blockly.NEXT_STATEMENT) {
- code.push(makeVar('statements', name) +
- " = Blockly." + language + ".statementToCode(block, '" +
- name + "');");
- }
- }
- }
- // Most languages end lines with a semicolon. Python does not.
- var lineEnd = {
- 'JavaScript': ';',
- 'Python': '',
- 'PHP': ';',
- 'Dart': ';'
- };
- code.push(" // TODO: Assemble " + language + " into code variable.");
- if (block.outputConnection) {
- code.push(" var code = '...';");
- code.push(" // TODO: Change ORDER_NONE to the correct strength.");
- code.push(" return [code, Blockly." + language + ".ORDER_NONE];");
- } else {
- code.push(" var code = '..." + (lineEnd[language] || '') + "\\n';");
- code.push(" return code;");
- }
- code.push("};");
-
- injectCode(code.join('\n'), 'generatorPre');
-}
-
-/**
- * Existing direction ('ltr' vs 'rtl') of preview.
- */
-var oldDir = null;
-
-/**
- * Update the preview display.
- */
-function updatePreview() {
- // Toggle between LTR/RTL if needed (also used in first display).
- var newDir = document.getElementById('direction').value;
- if (oldDir != newDir) {
- if (previewWorkspace) {
- previewWorkspace.dispose();
- }
- var rtl = newDir == 'rtl';
- previewWorkspace = Blockly.inject('preview',
- {rtl: rtl,
- media: '../../media/',
- scrollbars: true});
- oldDir = newDir;
- }
- 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 = {};
- for (var prop in backupBlocks) {
- Blockly.Blocks[prop] = backupBlocks[prop];
- }
-
- if (format == 'JSON') {
- var json = JSON.parse(code);
- Blockly.Blocks[json.type || 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 = previewWorkspace.newBlock(blockType);
- previewBlock.initSvg();
- previewBlock.render();
- previewBlock.setMovable(false);
- previewBlock.setDeletable(false);
- previewBlock.moveBy(15, 10);
- previewWorkspace.clearUndo();
-
- updateGenerator(previewBlock);
- } finally {
- Blockly.Blocks = backupBlocks;
- }
-}
-
-/**
- * Inject code into a pre tag, with syntax highlighting.
- * Safe from HTML/script injection.
- * @param {string} code Lines of code.
- * @param {string} id ID of
element to inject into.
- */
-function injectCode(code, id) {
- var pre = document.getElementById(id);
- pre.textContent = code;
- code = pre.textContent;
- code = prettyPrintOne(code, 'js');
- pre.innerHTML = code;
-}
-
-/**
- * Return the uneditable container block that everything else attaches to.
- * @return {Blockly.Block}
- */
-function getRootBlock() {
- var blocks = mainWorkspace.getTopBlocks(false);
- for (var i = 0, block; block = blocks[i]; i++) {
- if (block.type == 'factory_base') {
- return block;
- }
- }
- return null;
-}
-
-/**
- * Disable the link button if the format is 'Manual', enable otherwise.
- */
-function disableEnableLink() {
- var linkButton = document.getElementById('linkButton');
- linkButton.disabled = document.getElementById('format').value == 'Manual';
-}
-
-/**
- * Initialize Blockly and layout. Called on page load.
- */
-function init() {
- if ('BlocklyStorage' in window) {
- BlocklyStorage.HTTPREQUEST_ERROR =
- 'There was a problem with the request.\n';
- BlocklyStorage.LINK_ALERT =
- 'Share your blocks with this link:\n\n%1';
- BlocklyStorage.HASH_ERROR =
- 'Sorry, "%1" doesn\'t correspond with any saved Blockly file.';
- BlocklyStorage.XML_ERROR = 'Could not load your saved file.\n'+
- 'Perhaps it was created with a different version of Blockly?';
- var linkButton = document.getElementById('linkButton');
- linkButton.style.display = 'inline-block';
- linkButton.addEventListener('click',
- function() {BlocklyStorage.link(mainWorkspace);});
- disableEnableLink();
- }
-
- document.getElementById('helpButton').addEventListener('click',
- function() {
- open('https://developers.google.com/blockly/guides/create-custom-blocks/block-factory',
- 'BlockFactoryHelp');
- });
-
- var expandList = [
- document.getElementById('blockly'),
- document.getElementById('blocklyMask'),
- document.getElementById('preview'),
- document.getElementById('languagePre'),
- document.getElementById('languageTA'),
- document.getElementById('generatorPre')
- ];
- var onresize = function(e) {
- for (var i = 0, expand; expand = expandList[i]; i++) {
- expand.style.width = (expand.parentNode.offsetWidth - 2) + 'px';
- expand.style.height = (expand.parentNode.offsetHeight - 2) + 'px';
- }
- };
- onresize();
- window.addEventListener('resize', onresize);
-
- var toolbox = document.getElementById('toolbox');
- mainWorkspace = Blockly.inject('blockly',
- {collapse: false,
- toolbox: toolbox,
- media: '../../media/'});
-
- // Create the root block.
- if ('BlocklyStorage' in window && window.location.hash.length > 1) {
- BlocklyStorage.retrieveXml(window.location.hash.substring(1),
- mainWorkspace);
- } else {
- var xml = '';
- Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), mainWorkspace);
- }
- mainWorkspace.clearUndo();
-
- mainWorkspace.addChangeListener(Blockly.Events.disableOrphans);
- mainWorkspace.addChangeListener(updateLanguage);
- document.getElementById('direction')
- .addEventListener('change', updatePreview);
- document.getElementById('languageTA')
- .addEventListener('change', updatePreview);
- document.getElementById('languageTA')
- .addEventListener('keyup', updatePreview);
- document.getElementById('format')
- .addEventListener('change', formatChange);
- document.getElementById('language')
- .addEventListener('change', updatePreview);
-}
-window.addEventListener('load', init);
diff --git a/trunk/web/blockly/demos/blockfactory_old/icon.png b/trunk/web/blockly/demos/blockfactory_old/icon.png
deleted file mode 100644
index d4d19b45768..00000000000
Binary files a/trunk/web/blockly/demos/blockfactory_old/icon.png and /dev/null differ
diff --git a/trunk/web/blockly/demos/blockfactory_old/index.html b/trunk/web/blockly/demos/blockfactory_old/index.html
deleted file mode 100644
index 449b9a5e889..00000000000
--- a/trunk/web/blockly/demos/blockfactory_old/index.html
+++ /dev/null
@@ -1,230 +0,0 @@
-
-
-
-
-
- Blockly Demo: Block Factory
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- i
- j
- k
-
-
-
-
-
-
diff --git a/trunk/web/blockly/demos/plane/README.txt b/trunk/web/blockly/demos/plane/README.txt
deleted file mode 100644
index 6da3fa65d6a..00000000000
--- a/trunk/web/blockly/demos/plane/README.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-This Blockly demo uses Closure Templates to create a multilingual application.
-Any changes to the template.soy file require a recompile. Here is the command
-to generate a quick English version for debugging:
-
-java -jar soy/SoyToJsSrcCompiler.jar --outputPathFormat generated/en.js --srcs template.soy
-
-To generate a full set of language translations, first extract all the strings
-from template.soy using this command:
-
-java -jar soy/SoyMsgExtractor.jar --outputFile xlf/extracted_msgs.xlf template.soy
-
-This generates xlf/extracted_msgs.xlf, which may then be used by any
-XLIFF-compatible translation console to generate a set of files with the
-translated strings. These should be placed in the xlf directory.
-
-Finally, generate all the language versions wih this command:
-
-java -jar soy/SoyToJsSrcCompiler.jar --locales ar,be-tarask,br,ca,da,de,el,en,es,fa,fr,he,hrx,hu,ia,is,it,ja,ko,ms,nb,nl,pl,pms,pt-br,ro,ru,sc,sv,th,tr,uk,vi,zh-hans,zh-hant --messageFilePathFormat xlf/translated_msgs_{LOCALE}.xlf --outputPathFormat "generated/{LOCALE}.js" template.soy
-
-This is the process that Google uses for maintaining Blockly Games in 40+
-languages. The XLIFF fromat is simple enough that it is trival to write a
-Python script to reformat it into some other format (such as JSON) for
-compatability with other translation consoles.
-
-For more information, see message translation for Closure Templates:
-https://developers.google.com/closure/templates/docs/translation
diff --git a/trunk/web/blockly/demos/plane/blocks.js b/trunk/web/blockly/demos/plane/blocks.js
deleted file mode 100644
index 18be29c5dd8..00000000000
--- a/trunk/web/blockly/demos/plane/blocks.js
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * Blockly Demos: Plane Seat Calculator Blocks
- *
- * Copyright 2013 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 Blocks for Blockly's Plane Seat Calculator application.
- * @author fraser@google.com (Neil Fraser)
- */
-'use strict';
-
-Blockly.Blocks['plane_set_seats'] = {
- // Block seat variable setter.
- init: function() {
- this.setHelpUrl(Blockly.Msg.VARIABLES_SET_HELPURL);
- this.setColour(330);
- this.appendValueInput('VALUE')
- .appendField(Plane.getMsg('Plane_setSeats'));
- this.setTooltip(Blockly.Msg.VARIABLES_SET_TOOLTIP);
- this.setDeletable(false);
- }
-};
-
-Blockly.JavaScript['plane_set_seats'] = function(block) {
- // Generate JavaScript for seat variable setter.
- var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE',
- Blockly.JavaScript.ORDER_ASSIGNMENT) || 'NaN';
- return argument0 + ';';
-};
-
-Blockly.Blocks['plane_get_rows'] = {
- // Block for row variable getter.
- init: function() {
- this.setHelpUrl(Blockly.Msg.VARIABLES_GET_HELPURL);
- this.setColour(330);
- this.appendDummyInput()
- .appendField(Plane.getMsg('Plane_getRows'), 'title');
- this.setOutput(true, 'Number');
- },
- customUpdate: function() {
- this.setFieldValue(
- Plane.getMsg('Plane_getRows').replace('%1', Plane.rows1st), 'title');
- }
-};
-
-Blockly.JavaScript['plane_get_rows'] = function(block) {
- // Generate JavaScript for row variable getter.
- return ['Plane.rows1st', Blockly.JavaScript.ORDER_MEMBER];
-};
-
-Blockly.Blocks['plane_get_rows1st'] = {
- // Block for first class row variable getter.
- init: function() {
- this.setHelpUrl(Blockly.Msg.VARIABLES_GET_HELPURL);
- this.setColour(330);
- this.appendDummyInput()
- .appendField(Plane.getMsg('Plane_getRows1'), 'title');
- this.setOutput(true, 'Number');
- },
- customUpdate: function() {
- this.setFieldValue(
- Plane.getMsg('Plane_getRows1').replace('%1', Plane.rows1st), 'title');
- }
-};
-
-Blockly.JavaScript['plane_get_rows1st'] = function(block) {
- // Generate JavaScript for first class row variable getter.
- return ['Plane.rows1st', Blockly.JavaScript.ORDER_MEMBER];
-};
-
-Blockly.Blocks['plane_get_rows2nd'] = {
- // Block for second class row variable getter.
- init: function() {
- this.setHelpUrl(Blockly.Msg.VARIABLES_GET_HELPURL);
- this.setColour(330);
- this.appendDummyInput()
- .appendField(Plane.getMsg('Plane_getRows2'), 'title');
- this.setOutput(true, 'Number');
- },
- customUpdate: function() {
- this.setFieldValue(
- Plane.getMsg('Plane_getRows2').replace('%1', Plane.rows2nd), 'title');
- }
-};
-
-Blockly.JavaScript['plane_get_rows2nd'] = function(block) {
- // Generate JavaScript for second class row variable getter.
- return ['Plane.rows2nd', Blockly.JavaScript.ORDER_MEMBER];
-};
diff --git a/trunk/web/blockly/demos/plane/generated/ar.js b/trunk/web/blockly/demos/plane/generated/ar.js
deleted file mode 100644
index 13109490495..00000000000
--- a/trunk/web/blockly/demos/plane/generated/ar.js
+++ /dev/null
@@ -1,37 +0,0 @@
-// This file was automatically generated from template.soy.
-// Please don't edit this file by hand.
-
-if (typeof planepage == 'undefined') { var planepage = {}; }
-
-
-planepage.messages = function(opt_data, opt_ignored, opt_ijData) {
- return '
الصفوف: %1الصفوف (%1)صفوف الطبقة الأولى: %1صفوف الطبقة الأولى (%1)صفوف الفئة الثانية: %1صفوف الفئة الثانية: (%1)المقاعد: %1؟المقاعد =