summaryrefslogtreecommitdiff
path: root/blockly/demos/blocklyfactory/workspacefactory/wfactory_init.js
diff options
context:
space:
mode:
Diffstat (limited to 'blockly/demos/blocklyfactory/workspacefactory/wfactory_init.js')
-rw-r--r--blockly/demos/blocklyfactory/workspacefactory/wfactory_init.js617
1 files changed, 617 insertions, 0 deletions
diff --git a/blockly/demos/blocklyfactory/workspacefactory/wfactory_init.js b/blockly/demos/blocklyfactory/workspacefactory/wfactory_init.js
new file mode 100644
index 0000000..78f8b7d
--- /dev/null
+++ b/blockly/demos/blocklyfactory/workspacefactory/wfactory_init.js
@@ -0,0 +1,617 @@
+/**
+ * @license
+ * Visual Blocks Editor
+ *
+ * Copyright 2016 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview 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.
+ * @private
+ *
+ * @param {!FactoryController} controller The controller for the workspace
+ * factory tab.
+ */
+WorkspaceFactoryInit.initColorPicker_ = function(controller) {
+ // Array of Blockly category colors, variety of hues with saturation 45%
+ // and value 65% as specified in Blockly Developer documentation:
+ // https://developers.google.com/blockly/guides/create-custom-blocks/define-blocks
+ var colors = ['#A65C5C',
+ '#A6635C',
+ '#A66A5C',
+ '#A6725C',
+ '#A6795C',
+ '#A6815C',
+ '#A6885C',
+ '#A6905C',
+ '#A6975C',
+ '#A69F5C',
+ '#A6A65C',
+ '#9FA65C',
+ '#97A65C',
+ '#90A65C',
+ '#88A65C',
+ '#81A65C',
+ '#79A65C',
+ '#6FA65C',
+ '#66A65C',
+ '#5EA65C',
+ '#5CA661',
+ '#5CA668',
+ '#5CA66F',
+ '#5CA677',
+ '#5CA67E',
+ '#5CA686',
+ '#5CA68D',
+ '#5CA695',
+ '#5CA69C',
+ '#5CA6A4',
+ '#5CA1A6',
+ '#5C9AA6',
+ '#5C92A6',
+ '#5C8BA6',
+ '#5C83A6',
+ '#5C7CA6',
+ '#5C74A6',
+ '#5C6AA6',
+ '#5C61A6',
+ '#5E5CA6',
+ '#665CA6',
+ '#6D5CA6',
+ '#745CA6',
+ '#7C5CA6',
+ '#835CA6',
+ '#8B5CA6',
+ '#925CA6',
+ '#9A5CA6',
+ '#A15CA6',
+ '#A65CA4',
+ '#A65C9C',
+ '#A65C95',
+ '#A65C8D',
+ '#A65C86',
+ '#A65C7E',
+ '#A65C77',
+ '#A65C6F',
+ '#A65C66',
+ '#A65C61',
+ '#A65C5E'];
+
+ // Create color picker with specific set of Blockly colors.
+ var colorPicker = new goog.ui.ColorPicker();
+ colorPicker.setColors(colors);
+
+ // Create and render the popup color picker and attach to button.
+ var popupPicker = new goog.ui.PopupColorPicker(null, colorPicker);
+ popupPicker.render();
+ popupPicker.attach(document.getElementById('dropdown_color'));
+ popupPicker.setFocusable(true);
+ goog.events.listen(popupPicker, 'change', function(e) {
+ controller.changeSelectedCategoryColor(popupPicker.getSelectedColor());
+ document.getElementById('dropdownDiv_editCategory').classList.remove
+ ("show");
+ });
+};
+
+/**
+ * Assign click handlers for workspace factory.
+ * @private
+ *
+ * @param {!FactoryController} controller The controller for the workspace
+ * factory tab.
+ */
+WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
+ function(controller) {
+ document.getElementById('tab_toolbox').addEventListener
+ ('click',
+ function() {
+ controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
+ });
+
+ document.getElementById('tab_preload').addEventListener
+ ('click',
+ function() {
+ controller.setMode(WorkspaceFactoryController.MODE_PRELOAD);
+ });
+
+ document.getElementById('button_add').addEventListener
+ ('click',
+ function() {
+ document.getElementById('dropdownDiv_add').classList.toggle("show");
+ });
+
+ document.getElementById('dropdown_newCategory').addEventListener
+ ('click',
+ function() {
+ controller.addCategory();
+ document.getElementById('dropdownDiv_add').classList.remove("show");
+ });
+
+ document.getElementById('dropdown_loadCategory').addEventListener
+ ('click',
+ function() {
+ controller.loadCategory();
+ document.getElementById('dropdownDiv_add').classList.remove("show");
+ });
+
+ document.getElementById('dropdown_separator').addEventListener
+ ('click',
+ function() {
+ controller.addSeparator();
+ document.getElementById('dropdownDiv_add').classList.remove("show");
+ });
+
+ document.getElementById('dropdown_loadStandardToolbox').addEventListener
+ ('click',
+ function() {
+ controller.loadStandardToolbox();
+ document.getElementById('dropdownDiv_add').classList.remove("show");
+ });
+
+ document.getElementById('button_remove').addEventListener
+ ('click',
+ function() {
+ controller.removeElement();
+ });
+
+ document.getElementById('dropdown_exportToolbox').addEventListener
+ ('click',
+ function() {
+ controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ });
+
+ document.getElementById('dropdown_exportPreload').addEventListener
+ ('click',
+ function() {
+ controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD);
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ });
+
+ document.getElementById('dropdown_exportOptions').addEventListener
+ ('click',
+ function() {
+ controller.exportInjectFile();
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ });
+
+ document.getElementById('dropdown_exportAll').addEventListener
+ ('click',
+ function() {
+ controller.exportInjectFile();
+ controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
+ controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD);
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ });
+
+ document.getElementById('button_export').addEventListener
+ ('click',
+ function() {
+ document.getElementById('dropdownDiv_export').classList.toggle("show");
+ document.getElementById('dropdownDiv_load').classList.remove("show");
+ document.getElementById('dropdownDiv_importBlocks').classList.
+ remove("show");
+ });
+
+ document.getElementById('button_up').addEventListener
+ ('click',
+ function() {
+ controller.moveElement(-1);
+ });
+
+ document.getElementById('button_down').addEventListener
+ ('click',
+ function() {
+ controller.moveElement(1);
+ });
+
+ document.getElementById('button_editCategory').addEventListener
+ ('click',
+ function() {
+ document.getElementById('dropdownDiv_editCategory').classList.
+ toggle("show");
+ });
+
+ document.getElementById('dropdown_name').addEventListener
+ ('click',
+ function() {
+ controller.changeCategoryName();
+ document.getElementById('dropdownDiv_editCategory').classList.
+ remove("show");
+ });
+
+document.getElementById('button_importBlocks').addEventListener
+ ('click',
+ function() {
+ document.getElementById('dropdownDiv_importBlocks').classList.
+ toggle("show");
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ document.getElementById('dropdownDiv_load').classList.remove("show");
+ });
+
+ document.getElementById('button_load').addEventListener
+ ('click',
+ function() {
+ document.getElementById('dropdownDiv_load').classList.toggle("show");
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ document.getElementById('dropdownDiv_importBlocks').classList.
+ remove("show");
+ });
+
+ document.getElementById('input_loadToolbox').addEventListener
+ ('change',
+ function() {
+ controller.importFile(event.target.files[0],
+ WorkspaceFactoryController.MODE_TOOLBOX);
+ document.getElementById('dropdownDiv_load').classList.remove("show");
+ });
+
+ document.getElementById('input_loadPreload').addEventListener
+ ('change',
+ function() {
+ controller.importFile(event.target.files[0],
+ WorkspaceFactoryController.MODE_PRELOAD);
+ document.getElementById('dropdownDiv_load').classList.remove("show");
+ });
+
+ document.getElementById('input_importBlocksJson').addEventListener
+ ('change',
+ function() {
+ controller.importBlocks(event.target.files[0],'JSON');
+ document.getElementById('dropdownDiv_importBlocks').classList.
+ remove("show");
+ });
+
+ document.getElementById('input_importBlocksJs').addEventListener
+ ('change',
+ function() {
+ controller.importBlocks(event.target.files[0],'JavaScript');
+ document.getElementById('dropdownDiv_importBlocks').classList.
+ remove("show");
+ });
+
+ document.getElementById('button_clear').addEventListener
+ ('click',
+ function() {
+ document.getElementById('dropdownDiv_importBlocks').classList.
+ remove("show");
+ document.getElementById('dropdownDiv_export').classList.remove("show");
+ document.getElementById('dropdownDiv_load').classList.remove("show");
+ controller.clearAll();
+ });
+
+ 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;
+ }
+ });
+
+ document.getElementById('button_standardOptions').addEventListener
+ ('click', function() {
+ controller.setStandardOptionsAndUpdate();
+ });
+
+ document.getElementById('button_optionsHelp').addEventListener
+ ('click', function() {
+ open('https://developers.google.com/blockly/guides/get-started/web');
+ });
+};
+
+/**
+ * Add event listeners for workspace factory.
+ * @private
+ *
+ * @param {!FactoryController} controller The controller for the workspace
+ * factory tab.
+ */
+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.
+ * @private
+ *
+ * @param {!FactoryController} controller The controller for the workspace
+ * factory tab.
+ */
+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 grid 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';
+ });
+
+ document.getElementById('option_readOnly_checkbox').addEventListener('change',
+ function(e) {
+ document.getElementById('trashcan_option').style.display =
+ document.getElementById('option_readOnly_checkbox').checked ?
+ 'none' : 'block';
+ });
+
+ 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 optionsElements = document.getElementsByClassName('optionsInput');
+ for (var i = 0; i < optionsElements.length; i++) {
+ optionsElements[i].addEventListener('change', function() {
+ controller.generateNewOptions();
+ });
+ }
+};