From d0e91a15a2fd1168aebbd4ad253b0c514f3f7fb8 Mon Sep 17 00:00:00 2001 From: alschmiedt Date: Wed, 13 May 2020 08:16:53 -0700 Subject: [PATCH] Toolbox definition (#3891) * Add support for creating a toolbox with JSON --- blockly_uncompressed.js | 329 +++++++++++----------- core/flyout_base.js | 198 +++++++++---- core/flyout_button.js | 14 +- core/inject.js | 2 +- core/mutator.js | 4 +- core/options.js | 20 +- core/toolbox.js | 249 ++++++++++------ core/utils/toolbox.js | 159 +++++++++++ core/workspace_svg.js | 24 +- tests/mocha/.eslintrc.json | 5 +- tests/mocha/flyout_test.js | 105 +++++++ tests/mocha/index.html | 9 + tests/mocha/run_mocha_tests_in_browser.js | 2 +- tests/mocha/toolbox_helper.js | 84 ++++++ tests/mocha/toolbox_test.js | 185 ++++++++++-- tests/mocha/workspace_svg_test.js | 34 +++ 16 files changed, 1059 insertions(+), 364 deletions(-) create mode 100644 core/utils/toolbox.js create mode 100644 tests/mocha/flyout_test.js create mode 100644 tests/mocha/toolbox_helper.js diff --git a/blockly_uncompressed.js b/blockly_uncompressed.js index ed5c17e2c..04a368a0a 100644 --- a/blockly_uncompressed.js +++ b/blockly_uncompressed.js @@ -1,4 +1,4 @@ -// Do not edit this file; automatically generated by build.py. +// Do not edit this file; automatically generated by gulp. 'use strict'; this.IS_NODE_JS = !!(typeof module !== 'undefined' && module.exports); @@ -21,168 +21,171 @@ this.BLOCKLY_DIR = (function(root) { this.BLOCKLY_BOOT = function(root) { // Execute after Closure has loaded. -goog.addDependency("../../core/block.js", ['Blockly.Block'], ['Blockly.Blocks', 'Blockly.Connection', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Extensions', 'Blockly.fieldRegistry', 'Blockly.Input', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.string', 'Blockly.Workspace']); -goog.addDependency("../../core/block_animations.js", ['Blockly.blockAnimations'], ['Blockly.utils.dom']); -goog.addDependency("../../core/block_drag_surface.js", ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../core/block_dragger.js", ['Blockly.BlockDragger'], ['Blockly.blockAnimations', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.InsertionMarkerManager', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../core/block_events.js", ['Blockly.Events.BlockBase', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Events.Change', 'Blockly.Events.Create', 'Blockly.Events.Delete', 'Blockly.Events.Move'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml']); -goog.addDependency("../../core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.ContextMenu', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Events.BlockMove', 'Blockly.Msg', 'Blockly.navigation', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect']); -goog.addDependency("../../core/blockly.js", ['Blockly'], ['Blockly.constants', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.inject', 'Blockly.navigation', 'Blockly.Procedures', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.colour', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); -goog.addDependency("../../core/blocks.js", ['Blockly.Blocks'], []); -goog.addDependency("../../core/bubble.js", ['Blockly.Bubble'], ['Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent', 'Blockly.Workspace']); -goog.addDependency("../../core/bubble_dragger.js", ['Blockly.BubbleDragger'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate']); -goog.addDependency("../../core/comment.js", ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent', 'Blockly.Warning']); -goog.addDependency("../../core/components/component.js", ['Blockly.Component', 'Blockly.Component.Error'], ['Blockly.utils.dom', 'Blockly.utils.IdGenerator', 'Blockly.utils.style']); -goog.addDependency("../../core/components/tree/basenode.js", ['Blockly.tree.BaseNode'], ['Blockly.Component', 'Blockly.utils.aria', 'Blockly.utils.object', 'Blockly.utils.KeyCodes', 'Blockly.utils.style']); -goog.addDependency("../../core/components/tree/treecontrol.js", ['Blockly.tree.TreeControl'], ['Blockly.tree.TreeNode', 'Blockly.tree.BaseNode', 'Blockly.utils.aria', 'Blockly.utils.object', 'Blockly.utils.style']); -goog.addDependency("../../core/components/tree/treenode.js", ['Blockly.tree.TreeNode'], ['Blockly.tree.BaseNode', 'Blockly.utils.object', 'Blockly.utils.KeyCodes']); -goog.addDependency("../../core/connection.js", ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml']); -goog.addDependency("../../core/connection_db.js", ['Blockly.ConnectionDB'], ['Blockly.RenderedConnection']); -goog.addDependency("../../core/constants.js", ['Blockly.constants'], []); -goog.addDependency("../../core/contextmenu.js", ['Blockly.ContextMenu'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.utils.userAgent', 'Blockly.Xml']); -goog.addDependency("../../core/css.js", ['Blockly.Css'], []); -goog.addDependency("../../core/dropdowndiv.js", ['Blockly.DropDownDiv'], ['Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.style']); -goog.addDependency("../../core/events.js", ['Blockly.Events'], ['Blockly.utils']); -goog.addDependency("../../core/events_abstract.js", ['Blockly.Events.Abstract'], ['Blockly.Events']); -goog.addDependency("../../core/extensions.js", ['Blockly.Extensions'], ['Blockly.utils']); -goog.addDependency("../../core/field.js", ['Blockly.Field'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Gesture', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.Size', 'Blockly.utils.style', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/field_angle.js", ['Blockly.FieldAngle'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.fieldRegistry', 'Blockly.FieldTextInput', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.object', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/field_checkbox.js", ['Blockly.FieldCheckbox'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Size']); -goog.addDependency("../../core/field_colour.js", ['Blockly.FieldColour'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.IdGenerator', 'Blockly.utils.KeyCodes', 'Blockly.utils.object', 'Blockly.utils.Size']); -goog.addDependency("../../core/field_dropdown.js", ['Blockly.FieldDropdown'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.utils.object', 'Blockly.utils.Size', 'Blockly.utils.string', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/field_image.js", ['Blockly.FieldImage'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Size']); -goog.addDependency("../../core/field_label.js", ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Size']); -goog.addDependency("../../core/field_label_serializable.js", ['Blockly.FieldLabelSerializable'], ['Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.object']); -goog.addDependency("../../core/field_multilineinput.js", ['Blockly.FieldMultilineInput'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.FieldTextInput', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.KeyCodes', 'Blockly.utils.object', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/field_number.js", ['Blockly.FieldNumber'], ['Blockly.fieldRegistry', 'Blockly.FieldTextInput', 'Blockly.utils.aria', 'Blockly.utils.object']); -goog.addDependency("../../core/field_registry.js", ['Blockly.fieldRegistry'], []); -goog.addDependency("../../core/field_textinput.js", ['Blockly.FieldTextInput'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.KeyCodes', 'Blockly.utils.object', 'Blockly.utils.Size', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/field_variable.js", ['Blockly.FieldVariable'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.fieldRegistry', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.object', 'Blockly.utils.Size', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml']); -goog.addDependency("../../core/flyout_base.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.blockRendering', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutCursor', 'Blockly.Gesture', 'Blockly.Marker', 'Blockly.Scrollbar', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); -goog.addDependency("../../core/flyout_button.js", ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../core/flyout_dragger.js", ['Blockly.FlyoutDragger'], ['Blockly.utils.object', 'Blockly.WorkspaceDragger']); -goog.addDependency("../../core/flyout_horizontal.js", ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.utils', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.WidgetDiv']); -goog.addDependency("../../core/flyout_vertical.js", ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.utils', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.utils.userAgent', 'Blockly.WidgetDiv']); -goog.addDependency("../../core/generator.js", ['Blockly.Generator'], ['Blockly.Block']); -goog.addDependency("../../core/gesture.js", ['Blockly.Gesture'], ['Blockly.ASTNode', 'Blockly.blockAnimations', 'Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.constants', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.FlyoutDragger', 'Blockly.navigation', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.WorkspaceDragger']); -goog.addDependency("../../core/grid.js", ['Blockly.Grid'], ['Blockly.utils.dom', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/icon.js", ['Blockly.Icon'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.Size']); -goog.addDependency("../../core/inject.js", ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Component', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.user.keyMap', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.userAgent', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg']); -goog.addDependency("../../core/input.js", ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel']); -goog.addDependency("../../core/insertion_marker_manager.js", ['Blockly.InsertionMarkerManager'], ['Blockly.blockAnimations', 'Blockly.Events']); -goog.addDependency("../../core/interfaces/i_copyable.js", ['Blockly.ICopyable'], ['Blockly.ISelectable']); -goog.addDependency("../../core/interfaces/i_deletable.js", ['Blockly.IDeletable'], []); -goog.addDependency("../../core/interfaces/i_movable.js", ['Blockly.IMovable'], []); -goog.addDependency("../../core/interfaces/i_selectable.js", ['Blockly.ISelectable'], ['Blockly.IDeletable', 'Blockly.IMovable']); -goog.addDependency("../../core/keyboard_nav/action.js", ['Blockly.Action'], []); -goog.addDependency("../../core/keyboard_nav/ast_node.js", ['Blockly.ASTNode'], ['Blockly.utils.Coordinate']); -goog.addDependency("../../core/keyboard_nav/basic_cursor.js", ['Blockly.BasicCursor'], ['Blockly.ASTNode', 'Blockly.Cursor']); -goog.addDependency("../../core/keyboard_nav/cursor.js", ['Blockly.Cursor'], ['Blockly.Action', 'Blockly.ASTNode', 'Blockly.Marker', 'Blockly.navigation', 'Blockly.utils.object']); -goog.addDependency("../../core/keyboard_nav/flyout_cursor.js", ['Blockly.FlyoutCursor'], ['Blockly.Cursor', 'Blockly.navigation', 'Blockly.utils.object']); -goog.addDependency("../../core/keyboard_nav/key_map.js", ['Blockly.user.keyMap'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object']); -goog.addDependency("../../core/keyboard_nav/marker.js", ['Blockly.Marker'], ['Blockly.ASTNode', 'Blockly.navigation']); -goog.addDependency("../../core/keyboard_nav/navigation.js", ['Blockly.navigation'], ['Blockly.Action', 'Blockly.ASTNode', 'Blockly.utils.Coordinate', 'Blockly.user.keyMap']); -goog.addDependency("../../core/keyboard_nav/tab_navigate_cursor.js", ['Blockly.TabNavigateCursor'], ['Blockly.ASTNode', 'Blockly.BasicCursor', 'Blockly.utils.object']); -goog.addDependency("../../core/marker_manager.js", ['Blockly.MarkerManager'], ['Blockly.Cursor', 'Blockly.Marker']); -goog.addDependency("../../core/menu.js", ['Blockly.Menu'], ['Blockly.utils.aria', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.KeyCodes', 'Blockly.utils.style']); -goog.addDependency("../../core/menuitem.js", ['Blockly.MenuItem'], ['Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.IdGenerator']); -goog.addDependency("../../core/msg.js", ['Blockly.Msg'], ['Blockly.utils.global']); -goog.addDependency("../../core/mutator.js", ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.xml', 'Blockly.WorkspaceSvg', 'Blockly.Xml']); -goog.addDependency("../../core/names.js", ['Blockly.Names'], ['Blockly.Msg']); -goog.addDependency("../../core/options.js", ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.user.keyMap', 'Blockly.utils.userAgent', 'Blockly.Xml']); -goog.addDependency("../../core/procedures.js", ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.Names', 'Blockly.utils.xml', 'Blockly.Workspace', 'Blockly.Xml']); -goog.addDependency("../../core/rendered_connection.js", ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/common/block_rendering.js", ['Blockly.blockRendering'], ['Blockly.utils.object']); -goog.addDependency("../../core/renderers/common/constants.js", ['Blockly.blockRendering.ConstantProvider'], ['Blockly.utils', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.svgPaths', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/renderers/common/debugger.js", ['Blockly.blockRendering.Debug'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types']); -goog.addDependency("../../core/renderers/common/drawer.js", ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths']); -goog.addDependency("../../core/renderers/common/i_path_object.js", ['Blockly.blockRendering.IPathObject'], []); -goog.addDependency("../../core/renderers/common/info.js", ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types']); -goog.addDependency("../../core/renderers/common/marker_svg.js", ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode']); -goog.addDependency("../../core/renderers/common/path_object.js", ['Blockly.blockRendering.PathObject'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.IPathObject', 'Blockly.Theme', 'Blockly.utils.dom']); -goog.addDependency("../../core/renderers/common/renderer.js", ['Blockly.blockRendering.Renderer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.InsertionMarkerManager']); -goog.addDependency("../../core/renderers/geras/constants.js", ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/geras/drawer.js", ['Blockly.geras.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.geras.Highlighter', 'Blockly.geras.RenderInfo', 'Blockly.utils.object', 'Blockly.utils.svgPaths']); -goog.addDependency("../../core/renderers/geras/highlight_constants.js", ['Blockly.geras.HighlightConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.svgPaths']); -goog.addDependency("../../core/renderers/geras/highlighter.js", ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths']); -goog.addDependency("../../core/renderers/geras/info.js", ['Blockly.geras', 'Blockly.geras.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.Types', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/geras/measurables/inputs.js", ['Blockly.geras.InlineInput', 'Blockly.geras.StatementInput'], ['Blockly.utils.object']); -goog.addDependency("../../core/renderers/geras/path_object.js", ['Blockly.geras.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.geras.ConstantProvider', 'Blockly.Theme', 'Blockly.utils.dom', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/geras/renderer.js", ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/measurables/base.js", ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.Types']); -goog.addDependency("../../core/renderers/measurables/connections.js", ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/measurables/inputs.js", ['Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.StatementInput'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/measurables/row_elements.js", ['Blockly.blockRendering.Field', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.Icon', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.JaggedEdge', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.SquareCorner'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/measurables/rows.js", ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow'], ['Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/measurables/types.js", ['Blockly.blockRendering.Types'], []); -goog.addDependency("../../core/renderers/minimalist/constants.js", ['Blockly.minimalist.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/minimalist/drawer.js", ['Blockly.minimalist.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.utils.object', 'Blockly.minimalist.RenderInfo']); -goog.addDependency("../../core/renderers/minimalist/info.js", ['Blockly.minimalist', 'Blockly.minimalist.RenderInfo'], ['Blockly.utils.object']); -goog.addDependency("../../core/renderers/minimalist/renderer.js", ['Blockly.minimalist.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo']); -goog.addDependency("../../core/renderers/thrasos/info.js", ['Blockly.thrasos', 'Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/thrasos/renderer.js", ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/zelos/constants.js", ['Blockly.zelos.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgPaths']); -goog.addDependency("../../core/renderers/zelos/drawer.js", ['Blockly.zelos.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.Types', 'Blockly.utils.object', 'Blockly.zelos.RenderInfo']); -goog.addDependency("../../core/renderers/zelos/info.js", ['Blockly.zelos', 'Blockly.zelos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow']); -goog.addDependency("../../core/renderers/zelos/marker_svg.js", ['Blockly.zelos.MarkerSvg'], ['Blockly.blockRendering.MarkerSvg']); -goog.addDependency("../../core/renderers/zelos/measurables/inputs.js", ['Blockly.zelos.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/zelos/measurables/row_elements.js", ['Blockly.zelos.RightConnectionShape'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/zelos/measurables/rows.js", ['Blockly.zelos.BottomRow', 'Blockly.zelos.TopRow'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.SpacerRow', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/zelos/path_object.js", ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.zelos.ConstantProvider', 'Blockly.utils.dom', 'Blockly.utils.object']); -goog.addDependency("../../core/renderers/zelos/renderer.js", ['Blockly.zelos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.InsertionMarkerManager', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo', 'Blockly.zelos.MarkerSvg']); -goog.addDependency("../../core/requires.js", ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.HorizontalFlyout', 'Blockly.VerticalFlyout', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.ZoomControls', 'Blockly.Mutator', 'Blockly.Warning', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldLabelSerializable', 'Blockly.FieldImage', 'Blockly.FieldTextInput', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.geras.Renderer', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer', 'Blockly.Themes.Classic', 'Blockly.Themes.Dark', 'Blockly.Themes.Deuteranopia', 'Blockly.Themes.HighContrast', 'Blockly.Themes.Tritanopia']); -goog.addDependency("../../core/scrollbar.js", ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../core/theme.js", ['Blockly.Theme'], ['Blockly.utils', 'Blockly.utils.colour', 'Blockly.utils.object']); -goog.addDependency("../../core/theme/classic.js", ['Blockly.Themes.Classic'], ['Blockly.Theme']); -goog.addDependency("../../core/theme/dark.js", ['Blockly.Themes.Dark'], ['Blockly.Theme']); -goog.addDependency("../../core/theme/deuteranopia.js", ['Blockly.Themes.Deuteranopia'], ['Blockly.Theme']); -goog.addDependency("../../core/theme/highcontrast.js", ['Blockly.Themes.HighContrast'], ['Blockly.Theme']); -goog.addDependency("../../core/theme/modern.js", ['Blockly.Themes.Modern'], ['Blockly.Theme']); -goog.addDependency("../../core/theme/tritanopia.js", ['Blockly.Themes.Tritanopia'], ['Blockly.Theme']); -goog.addDependency("../../core/theme/zelos.js", ['Blockly.Themes.Zelos'], ['Blockly.Theme']); -goog.addDependency("../../core/theme_manager.js", ['Blockly.ThemeManager'], ['Blockly.Theme']); -goog.addDependency("../../core/toolbox.js", ['Blockly.Toolbox'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.navigation', 'Blockly.Touch', 'Blockly.tree.TreeControl', 'Blockly.tree.TreeNode', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect']); -goog.addDependency("../../core/tooltip.js", ['Blockly.Tooltip'], ['Blockly.utils.string']); -goog.addDependency("../../core/touch.js", ['Blockly.Touch'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.string']); -goog.addDependency("../../core/touch_gesture.js", ['Blockly.TouchGesture'], ['Blockly.Gesture', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object']); -goog.addDependency("../../core/trashcan.js", ['Blockly.Trashcan'], ['Blockly.Scrollbar', 'Blockly.utils.dom', 'Blockly.utils.Rect', 'Blockly.Xml']); -goog.addDependency("../../core/ui_events.js", ['Blockly.Events.Ui'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.object']); -goog.addDependency("../../core/utils.js", ['Blockly.utils'], ['Blockly.Msg', 'Blockly.constants', 'Blockly.utils.colour', 'Blockly.utils.Coordinate', 'Blockly.utils.global', 'Blockly.utils.string', 'Blockly.utils.style', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/utils/aria.js", ['Blockly.utils.aria'], []); -goog.addDependency("../../core/utils/colour.js", ['Blockly.utils.colour'], []); -goog.addDependency("../../core/utils/coordinate.js", ['Blockly.utils.Coordinate'], []); -goog.addDependency("../../core/utils/dom.js", ['Blockly.utils.dom'], ['Blockly.utils.userAgent']); -goog.addDependency("../../core/utils/global.js", ['Blockly.utils.global'], []); -goog.addDependency("../../core/utils/idgenerator.js", ['Blockly.utils.IdGenerator'], []); -goog.addDependency("../../core/utils/keycodes.js", ['Blockly.utils.KeyCodes'], []); -goog.addDependency("../../core/utils/math.js", ['Blockly.utils.math'], []); -goog.addDependency("../../core/utils/object.js", ['Blockly.utils.object'], []); -goog.addDependency("../../core/utils/rect.js", ['Blockly.utils.Rect'], []); -goog.addDependency("../../core/utils/size.js", ['Blockly.utils.Size'], []); -goog.addDependency("../../core/utils/string.js", ['Blockly.utils.string'], []); -goog.addDependency("../../core/utils/style.js", ['Blockly.utils.style'], ['Blockly.utils.Coordinate', 'Blockly.utils.Size']); -goog.addDependency("../../core/utils/svg_paths.js", ['Blockly.utils.svgPaths'], []); -goog.addDependency("../../core/utils/useragent.js", ['Blockly.utils.userAgent'], ['Blockly.utils.global']); -goog.addDependency("../../core/utils/xml.js", ['Blockly.utils.xml'], []); -goog.addDependency("../../core/variable_events.js", ['Blockly.Events.VarBase', 'Blockly.Events.VarCreate', 'Blockly.Events.VarDelete', 'Blockly.Events.VarRename'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.object']); -goog.addDependency("../../core/variable_map.js", ['Blockly.VariableMap'], ['Blockly.Events', 'Blockly.Events.VarDelete', 'Blockly.Events.VarRename', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.object']); -goog.addDependency("../../core/variable_model.js", ['Blockly.VariableModel'], ['Blockly.Events', 'Blockly.Events.VarCreate', 'Blockly.utils']); -goog.addDependency("../../core/variables.js", ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.xml', 'Blockly.VariableModel', 'Blockly.Xml']); -goog.addDependency("../../core/variables_dynamic.js", ['Blockly.VariablesDynamic'], ['Blockly.Variables', 'Blockly.Blocks', 'Blockly.Msg', 'Blockly.utils.xml', 'Blockly.VariableModel']); -goog.addDependency("../../core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.dom', 'Blockly.utils.object']); -goog.addDependency("../../core/widgetdiv.js", ['Blockly.WidgetDiv'], ['Blockly.utils.style']); -goog.addDependency("../../core/workspace.js", ['Blockly.Workspace'], ['Blockly.Events', 'Blockly.Options', 'Blockly.utils', 'Blockly.utils.math', 'Blockly.VariableMap']); -goog.addDependency("../../core/workspace_audio.js", ['Blockly.WorkspaceAudio'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent']); -goog.addDependency("../../core/workspace_comment.js", ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml']); -goog.addDependency("../../core/workspace_comment_render_svg.js", ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom']); -goog.addDependency("../../core/workspace_comment_svg.js", ['Blockly.WorkspaceCommentSvg'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Ui', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.WorkspaceComment']); -goog.addDependency("../../core/workspace_drag_surface_svg.js", ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.dom']); -goog.addDependency("../../core/workspace_dragger.js", ['Blockly.WorkspaceDragger'], ['Blockly.utils.Coordinate']); -goog.addDependency("../../core/workspace_events.js", ['Blockly.Events.FinishedLoading'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.utils.object']); -goog.addDependency("../../core/workspace_svg.js", ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.blockRendering', 'Blockly.ConnectionDB', 'Blockly.constants', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.MarkerManager', 'Blockly.Msg', 'Blockly.navigation', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.Rect', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml']); -goog.addDependency("../../core/ws_comment_events.js", ['Blockly.Events.CommentBase', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml']); -goog.addDependency("../../core/xml.js", ['Blockly.Xml'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.VarCreate', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.xml']); -goog.addDependency("../../core/zoom_controls.js", ['Blockly.ZoomControls'], ['Blockly.Css', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.utils.dom']); +goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.Blocks', 'Blockly.Connection', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Extensions', 'Blockly.Input', 'Blockly.Workspace', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.string'], {}); +goog.addDependency('../../core/block_animations.js', ['Blockly.blockAnimations'], ['Blockly.utils.dom'], {}); +goog.addDependency('../../core/block_drag_surface.js', ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/block_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/block_events.js', ['Blockly.Events.BlockBase', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Events.Change', 'Blockly.Events.Create', 'Blockly.Events.Delete', 'Blockly.Events.Move'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ContextMenu', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Procedures', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.constants', 'Blockly.inject', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.colour'], {}); +goog.addDependency('../../core/blocks.js', ['Blockly.Blocks'], [], {}); +goog.addDependency('../../core/bubble.js', ['Blockly.Bubble'], ['Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/bubble_dragger.js', ['Blockly.BubbleDragger'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate'], {}); +goog.addDependency('../../core/category_registry.js', ['Blockly.categoryRegistry'], [], {}); +goog.addDependency('../../core/comment.js', ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.Warning', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/components/component.js', ['Blockly.Component', 'Blockly.Component.Error'], ['Blockly.utils.IdGenerator', 'Blockly.utils.dom', 'Blockly.utils.style'], {}); +goog.addDependency('../../core/components/tree/basenode.js', ['Blockly.tree.BaseNode'], ['Blockly.Component', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.object', 'Blockly.utils.style'], {}); +goog.addDependency('../../core/components/tree/treecontrol.js', ['Blockly.tree.TreeControl'], ['Blockly.tree.BaseNode', 'Blockly.tree.TreeNode', 'Blockly.utils.aria', 'Blockly.utils.object', 'Blockly.utils.style'], {}); +goog.addDependency('../../core/components/tree/treenode.js', ['Blockly.tree.TreeNode'], ['Blockly.tree.BaseNode', 'Blockly.utils.KeyCodes', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml'], {}); +goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.RenderedConnection'], {}); +goog.addDependency('../../core/constants.js', ['Blockly.constants'], [], {}); +goog.addDependency('../../core/contextmenu.js', ['Blockly.ContextMenu'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.Msg', 'Blockly.Xml', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/css.js', ['Blockly.Css'], [], {'lang': 'es5'}); +goog.addDependency('../../core/dropdowndiv.js', ['Blockly.DropDownDiv'], ['Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.style'], {}); +goog.addDependency('../../core/events.js', ['Blockly.Events'], ['Blockly.utils'], {}); +goog.addDependency('../../core/events_abstract.js', ['Blockly.Events.Abstract'], ['Blockly.Events'], {}); +goog.addDependency('../../core/extensions.js', ['Blockly.Extensions'], ['Blockly.utils'], {}); +goog.addDependency('../../core/field.js', ['Blockly.Field'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Gesture', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.style', 'Blockly.utils.userAgent'], {'lang': 'es5'}); +goog.addDependency('../../core/field_angle.js', ['Blockly.FieldAngle'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.FieldTextInput', 'Blockly.fieldRegistry', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/field_checkbox.js', ['Blockly.FieldCheckbox'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/field_colour.js', ['Blockly.FieldColour'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils.IdGenerator', 'Blockly.utils.KeyCodes', 'Blockly.utils.Size', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/field_dropdown.js', ['Blockly.FieldDropdown'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.string', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/field_image.js', ['Blockly.FieldImage'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/field_label.js', ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/field_label_serializable.js', ['Blockly.FieldLabelSerializable'], ['Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/field_multilineinput.js', ['Blockly.FieldMultilineInput'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.FieldTextInput', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es5'}); +goog.addDependency('../../core/field_number.js', ['Blockly.FieldNumber'], ['Blockly.FieldTextInput', 'Blockly.fieldRegistry', 'Blockly.utils.aria', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/field_registry.js', ['Blockly.fieldRegistry'], [], {}); +goog.addDependency('../../core/field_textinput.js', ['Blockly.FieldTextInput'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.Size', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/field_variable.js', ['Blockly.FieldVariable'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml', 'Blockly.fieldRegistry', 'Blockly.utils', 'Blockly.utils.Size', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/flyout_base.js', ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.FlyoutCursor', 'Blockly.Gesture', 'Blockly.Marker', 'Blockly.Scrollbar', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/flyout_button.js', ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {'lang': 'es5'}); +goog.addDependency('../../core/flyout_dragger.js', ['Blockly.FlyoutDragger'], ['Blockly.WorkspaceDragger', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly.Block'], {}); +goog.addDependency('../../core/gesture.js', ['Blockly.Gesture'], ['Blockly.ASTNode', 'Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.FlyoutDragger', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceDragger', 'Blockly.blockAnimations', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate'], {}); +goog.addDependency('../../core/grid.js', ['Blockly.Grid'], ['Blockly.utils.dom', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/inject.js', ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Component', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg', 'Blockly.user.keyMap', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel'], {}); +goog.addDependency('../../core/insertion_marker_manager.js', ['Blockly.InsertionMarkerManager'], ['Blockly.Events', 'Blockly.blockAnimations'], {'lang': 'es5'}); +goog.addDependency('../../core/interfaces/i_copyable.js', ['Blockly.ICopyable'], ['Blockly.ISelectable'], {}); +goog.addDependency('../../core/interfaces/i_deletable.js', ['Blockly.IDeletable'], [], {}); +goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {}); +goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], ['Blockly.IDeletable', 'Blockly.IMovable'], {}); +goog.addDependency('../../core/keyboard_nav/action.js', ['Blockly.Action'], [], {}); +goog.addDependency('../../core/keyboard_nav/ast_node.js', ['Blockly.ASTNode'], ['Blockly.utils.Coordinate'], {'lang': 'es5'}); +goog.addDependency('../../core/keyboard_nav/basic_cursor.js', ['Blockly.BasicCursor'], ['Blockly.ASTNode', 'Blockly.Cursor'], {'lang': 'es5'}); +goog.addDependency('../../core/keyboard_nav/cursor.js', ['Blockly.Cursor'], ['Blockly.ASTNode', 'Blockly.Action', 'Blockly.Marker', 'Blockly.navigation', 'Blockly.utils.object'], {'lang': 'es5'}); +goog.addDependency('../../core/keyboard_nav/flyout_cursor.js', ['Blockly.FlyoutCursor'], ['Blockly.Cursor', 'Blockly.navigation', 'Blockly.utils.object'], {'lang': 'es5'}); +goog.addDependency('../../core/keyboard_nav/key_map.js', ['Blockly.user.keyMap'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/keyboard_nav/marker.js', ['Blockly.Marker'], ['Blockly.ASTNode', 'Blockly.navigation'], {}); +goog.addDependency('../../core/keyboard_nav/navigation.js', ['Blockly.navigation'], ['Blockly.ASTNode', 'Blockly.Action', 'Blockly.user.keyMap', 'Blockly.utils.Coordinate'], {}); +goog.addDependency('../../core/keyboard_nav/tab_navigate_cursor.js', ['Blockly.TabNavigateCursor'], ['Blockly.ASTNode', 'Blockly.BasicCursor', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/marker_manager.js', ['Blockly.MarkerManager'], ['Blockly.Cursor', 'Blockly.Marker'], {}); +goog.addDependency('../../core/menu.js', ['Blockly.Menu'], ['Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.style'], {}); +goog.addDependency('../../core/menuitem.js', ['Blockly.MenuItem'], ['Blockly.utils.IdGenerator', 'Blockly.utils.aria', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/msg.js', ['Blockly.Msg'], ['Blockly.utils.global'], {}); +goog.addDependency('../../core/mutator.js', ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/names.js', ['Blockly.Names'], ['Blockly.Msg'], {}); +goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.Xml', 'Blockly.user.keyMap', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/procedures.js', ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.Msg', 'Blockly.Names', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.constants', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/common/block_rendering.js', ['Blockly.blockRendering'], ['Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/common/constants.js', ['Blockly.blockRendering.ConstantProvider'], ['Blockly.utils', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.svgPaths', 'Blockly.utils.userAgent'], {'lang': 'es5'}); +goog.addDependency('../../core/renderers/common/debugger.js', ['Blockly.blockRendering.Debug'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types'], {'lang': 'es5'}); +goog.addDependency('../../core/renderers/common/drawer.js', ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {}); +goog.addDependency('../../core/renderers/common/i_path_object.js', ['Blockly.blockRendering.IPathObject'], [], {}); +goog.addDependency('../../core/renderers/common/info.js', ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types'], {}); +goog.addDependency('../../core/renderers/common/marker_svg.js', ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode'], {}); +goog.addDependency('../../core/renderers/common/path_object.js', ['Blockly.blockRendering.PathObject'], ['Blockly.Theme', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.IPathObject', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/renderers/common/renderer.js', ['Blockly.blockRendering.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo'], {}); +goog.addDependency('../../core/renderers/geras/constants.js', ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es5'}); +goog.addDependency('../../core/renderers/geras/drawer.js', ['Blockly.geras.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.geras.Highlighter', 'Blockly.geras.RenderInfo', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {}); +goog.addDependency('../../core/renderers/geras/highlight_constants.js', ['Blockly.geras.HighlightConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.svgPaths'], {'lang': 'es5'}); +goog.addDependency('../../core/renderers/geras/highlighter.js', ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {}); +goog.addDependency('../../core/renderers/geras/info.js', ['Blockly.geras', 'Blockly.geras.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/geras/measurables/inputs.js', ['Blockly.geras.InlineInput', 'Blockly.geras.StatementInput'], ['Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/geras/path_object.js', ['Blockly.geras.PathObject'], ['Blockly.Theme', 'Blockly.blockRendering.PathObject', 'Blockly.geras.ConstantProvider', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/geras/renderer.js', ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/measurables/base.js', ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.Types'], {}); +goog.addDependency('../../core/renderers/measurables/connections.js', ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/measurables/inputs.js', ['Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.StatementInput'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/measurables/row_elements.js', ['Blockly.blockRendering.Field', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.Icon', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.JaggedEdge', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.SquareCorner'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/measurables/rows.js', ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow'], ['Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/measurables/types.js', ['Blockly.blockRendering.Types'], [], {}); +goog.addDependency('../../core/renderers/minimalist/constants.js', ['Blockly.minimalist.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/minimalist/drawer.js', ['Blockly.minimalist.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/minimalist/info.js', ['Blockly.minimalist', 'Blockly.minimalist.RenderInfo'], ['Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/minimalist/renderer.js', ['Blockly.minimalist.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/thrasos/info.js', ['Blockly.thrasos', 'Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/thrasos/renderer.js', ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/zelos/constants.js', ['Blockly.zelos.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es5'}); +goog.addDependency('../../core/renderers/zelos/drawer.js', ['Blockly.zelos.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.Types', 'Blockly.utils.object', 'Blockly.zelos.RenderInfo'], {}); +goog.addDependency('../../core/renderers/zelos/info.js', ['Blockly.zelos', 'Blockly.zelos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {}); +goog.addDependency('../../core/renderers/zelos/marker_svg.js', ['Blockly.zelos.MarkerSvg'], ['Blockly.blockRendering.MarkerSvg'], {}); +goog.addDependency('../../core/renderers/zelos/measurables/inputs.js', ['Blockly.zelos.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['Blockly.zelos.RightConnectionShape'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/zelos/measurables/rows.js', ['Blockly.zelos.BottomRow', 'Blockly.zelos.TopRow'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider'], {}); +goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {}); +goog.addDependency('../../core/requires.js', ['Blockly.requires'], ['Blockly', 'Blockly.Comment', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.FlyoutButton', 'Blockly.Generator', 'Blockly.HorizontalFlyout', 'Blockly.Mutator', 'Blockly.Themes.Classic', 'Blockly.Themes.Dark', 'Blockly.Themes.Deuteranopia', 'Blockly.Themes.HighContrast', 'Blockly.Themes.Tritanopia', 'Blockly.Toolbox', 'Blockly.Trashcan', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.ZoomControls', 'Blockly.geras.Renderer', 'Blockly.thrasos.Renderer', 'Blockly.zelos.Renderer'], {}); +goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.utils', 'Blockly.utils.colour', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/theme/classic.js', ['Blockly.Themes.Classic'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme/dark.js', ['Blockly.Themes.Dark'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme/deuteranopia.js', ['Blockly.Themes.Deuteranopia'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme/highcontrast.js', ['Blockly.Themes.HighContrast'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme/modern.js', ['Blockly.Themes.Modern'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme/tritanopia.js', ['Blockly.Themes.Tritanopia'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme/zelos.js', ['Blockly.Themes.Zelos'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/theme_manager.js', ['Blockly.ThemeManager'], ['Blockly.Theme'], {}); +goog.addDependency('../../core/toolbox.js', ['Blockly.Toolbox'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Touch', 'Blockly.navigation', 'Blockly.tree.TreeControl', 'Blockly.tree.TreeNode', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {}); +goog.addDependency('../../core/toolbox_category.js', ['Blockly.ToolboxCategory'], ['Blockly.Toolbox'], {}); +goog.addDependency('../../core/tooltip.js', ['Blockly.Tooltip'], ['Blockly.utils.string'], {}); +goog.addDependency('../../core/touch.js', ['Blockly.Touch'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.string'], {}); +goog.addDependency('../../core/touch_gesture.js', ['Blockly.TouchGesture'], ['Blockly.Gesture', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.Scrollbar', 'Blockly.Xml', 'Blockly.utils.Rect', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/ui_events.js', ['Blockly.Events.Ui'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/utils.js', ['Blockly.utils'], ['Blockly.Msg', 'Blockly.constants', 'Blockly.utils.Coordinate', 'Blockly.utils.colour', 'Blockly.utils.global', 'Blockly.utils.string', 'Blockly.utils.style', 'Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/utils/aria.js', ['Blockly.utils.aria'], [], {}); +goog.addDependency('../../core/utils/colour.js', ['Blockly.utils.colour'], [], {}); +goog.addDependency('../../core/utils/coordinate.js', ['Blockly.utils.Coordinate'], [], {}); +goog.addDependency('../../core/utils/dom.js', ['Blockly.utils.dom'], ['Blockly.utils.userAgent'], {}); +goog.addDependency('../../core/utils/global.js', ['Blockly.utils.global'], [], {}); +goog.addDependency('../../core/utils/idgenerator.js', ['Blockly.utils.IdGenerator'], [], {}); +goog.addDependency('../../core/utils/keycodes.js', ['Blockly.utils.KeyCodes'], [], {}); +goog.addDependency('../../core/utils/math.js', ['Blockly.utils.math'], [], {}); +goog.addDependency('../../core/utils/object.js', ['Blockly.utils.object'], [], {}); +goog.addDependency('../../core/utils/rect.js', ['Blockly.utils.Rect'], [], {}); +goog.addDependency('../../core/utils/size.js', ['Blockly.utils.Size'], [], {}); +goog.addDependency('../../core/utils/string.js', ['Blockly.utils.string'], [], {}); +goog.addDependency('../../core/utils/style.js', ['Blockly.utils.style'], ['Blockly.utils.Coordinate', 'Blockly.utils.Size'], {}); +goog.addDependency('../../core/utils/svg_paths.js', ['Blockly.utils.svgPaths'], [], {}); +goog.addDependency('../../core/utils/toolbox.js', ['Blockly.utils.toolbox'], [], {}); +goog.addDependency('../../core/utils/useragent.js', ['Blockly.utils.userAgent'], ['Blockly.utils.global'], {}); +goog.addDependency('../../core/utils/xml.js', ['Blockly.utils.xml'], [], {}); +goog.addDependency('../../core/variable_events.js', ['Blockly.Events.VarBase', 'Blockly.Events.VarCreate', 'Blockly.Events.VarDelete', 'Blockly.Events.VarRename'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/variable_map.js', ['Blockly.VariableMap'], ['Blockly.Events', 'Blockly.Events.VarDelete', 'Blockly.Events.VarRename', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/variable_model.js', ['Blockly.VariableModel'], ['Blockly.Events', 'Blockly.Events.VarCreate', 'Blockly.utils'], {}); +goog.addDependency('../../core/variables.js', ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Xml', 'Blockly.utils', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/variables_dynamic.js', ['Blockly.VariablesDynamic'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/warning.js', ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/widgetdiv.js', ['Blockly.WidgetDiv'], ['Blockly.utils.style'], {}); +goog.addDependency('../../core/workspace.js', ['Blockly.Workspace'], ['Blockly.Events', 'Blockly.Options', 'Blockly.VariableMap', 'Blockly.utils', 'Blockly.utils.math'], {}); +goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es5'}); +goog.addDependency('../../core/workspace_comment.js', ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/workspace_comment_render_svg.js', ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCommentSvg'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Ui', 'Blockly.WorkspaceComment', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.object'], {}); +goog.addDependency('../../core/workspace_drag_surface_svg.js', ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.dom'], {}); +goog.addDependency('../../core/workspace_dragger.js', ['Blockly.WorkspaceDragger'], ['Blockly.utils.Coordinate'], {}); +goog.addDependency('../../core/workspace_events.js', ['Blockly.Events.FinishedLoading'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.utils.object'], {'lang': 'es5'}); +goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ConnectionDB', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.MarkerManager', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.TouchGesture', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {}); +goog.addDependency('../../core/ws_comment_events.js', ['Blockly.Events.CommentBase', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/xml.js', ['Blockly.Xml'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.VarCreate', 'Blockly.utils', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.xml'], {}); +goog.addDependency('../../core/zoom_controls.js', ['Blockly.ZoomControls'], ['Blockly.Css', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.utils.dom'], {'lang': 'es5'}); goog.addDependency("base.js", [], []); // Load Blockly. diff --git a/core/flyout_base.js b/core/flyout_base.js index 5d0628700..13020a307 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -426,82 +426,40 @@ Blockly.Flyout.prototype.hide = function() { /** * Show and populate the flyout. - * @param {!Array|!NodeList|string} xmlList List of blocks to show. - * Variables and procedures have a custom set of blocks. + * @param {!Blockly.utils.toolbox.ToolboxDefinition|string} flyoutDef + * List of contents to display in the flyout as an array of xml an + * array of Nodes, a NodeList or a string with the name of the dynamic category. + * Variables and procedures have a custom set of blocks. */ -Blockly.Flyout.prototype.show = function(xmlList) { +Blockly.Flyout.prototype.show = function(flyoutDef) { this.workspace_.setResizesEnabled(false); this.hide(); this.clearOldBlocks_(); - // Handle dynamic categories, represented by a name instead of a list of XML. + // Handle dynamic categories, represented by a name instead of a list. // Look up the correct category generation function and call that to get a // valid XML list. - if (typeof xmlList == 'string') { + if (typeof flyoutDef == 'string') { var fnToApply = this.workspace_.targetWorkspace.getToolboxCategoryCallback( - xmlList); + flyoutDef); if (typeof fnToApply != 'function') { throw TypeError('Couldn\'t find a callback function when opening' + ' a toolbox category.'); } - xmlList = fnToApply(this.workspace_.targetWorkspace); - if (!Array.isArray(xmlList)) { + flyoutDef = fnToApply(this.workspace_.targetWorkspace); + if (!Array.isArray(flyoutDef)) { throw TypeError('Result of toolbox category callback must be an array.'); } } + // Parse the Array or NodeList passed in into an Array of + // Blockly.utils.toolbox.Toolbox. + var parsedContent = Blockly.utils.toolbox.convertToolboxToJSON(flyoutDef); + var flyoutInfo = /** @type {{contents:Array, gaps:Array}} */ + (this.createFlyoutInfo_(parsedContent)); this.setVisible(true); - // Create the blocks to be shown in this flyout. - var contents = []; - var gaps = []; - this.permanentlyDisabled_.length = 0; - var default_gap = this.horizontalLayout_ ? this.GAP_X : this.GAP_Y; - for (var i = 0, xml; (xml = xmlList[i]); i++) { - if (!xml.tagName) { - continue; - } - switch (xml.tagName.toUpperCase()) { - case 'BLOCK': - var curBlock = Blockly.Xml.domToBlock(xml, this.workspace_); - if (!curBlock.isEnabled()) { - // Record blocks that were initially disabled. - // Do not enable these blocks as a result of capacity filtering. - this.permanentlyDisabled_.push(curBlock); - } - contents.push({type: 'block', block: curBlock}); - // This is a deprecated method for adding gap to a block. - // - var gap = parseInt(xml.getAttribute('gap'), 10); - gaps.push(isNaN(gap) ? default_gap : gap); - break; - case 'SEP': - // Change the gap between two toolbox elements. - // - // The default gap is 24, can be set larger or smaller. - // This overwrites the gap attribute on the previous element. - var newGap = parseInt(xml.getAttribute('gap'), 10); - // Ignore gaps before the first block. - if (!isNaN(newGap) && gaps.length > 0) { - gaps[gaps.length - 1] = newGap; - } else { - gaps.push(default_gap); - } - break; - case 'LABEL': - case 'BUTTON': - var isLabel = xml.tagName.toUpperCase() == 'LABEL'; - if (!Blockly.FlyoutButton) { - throw Error('Missing require for Blockly.FlyoutButton'); - } - var curButton = new Blockly.FlyoutButton(this.workspace_, - this.targetWorkspace_, xml, isLabel); - contents.push({type: 'button', button: curButton}); - gaps.push(default_gap); - break; - } - } - this.layout_(contents, gaps); + this.layout_(flyoutInfo.contents, flyoutInfo.gaps); // IE 11 is an incompetent browser that fails to fire mouseout events. // When the mouse is over the background, deselect all blocks. @@ -532,6 +490,130 @@ Blockly.Flyout.prototype.show = function(xmlList) { this.workspace_.addChangeListener(this.reflowWrapper_); }; +/** + * Create the contents array and gaps array necessary to create the layout for + * the flyout. + * @param {Array.} parsedContent The array + * of objects to show in the flyout. + * @return {{contents:Array, gaps:Array}} The list of contents + * and gaps needed to lay out the flyout. + * @private + */ +Blockly.Flyout.prototype.createFlyoutInfo_ = function(parsedContent) { + var contents = []; + var gaps = []; + this.permanentlyDisabled_.length = 0; + var defaultGap = this.horizontalLayout_ ? this.GAP_X : this.GAP_Y; + for (var i = 0, contentInfo; (contentInfo = parsedContent[i]); i++) { + switch (contentInfo.kind.toUpperCase()) { + case 'BLOCK': + var blockInfo = /** @type {Blockly.utils.toolbox.Block} */ (contentInfo); + var blockXml = this.getBlockXml_(blockInfo); + var block = this.createBlock_(blockXml); + // This is a deprecated method for adding gap to a block. + // + var gap = parseInt(blockXml.getAttribute('gap'), 10); + gaps.push(isNaN(gap) ? defaultGap : gap); + contents.push({type: 'block', block: block}); + break; + case 'SEP': + var sepInfo = /** @type {Blockly.utils.toolbox.Separator} */ (contentInfo); + this.addSeparatorGap_(sepInfo, gaps, defaultGap); + break; + case 'LABEL': + var labelInfo = /** @type {Blockly.utils.toolbox.Label} */ (contentInfo); + // A label is a button with different styling. + // Rename this function. + var label = this.createButton_(labelInfo, /** isLabel */ true); + contents.push({type: 'button', button: label}); + gaps.push(defaultGap); + break; + case 'BUTTON': + var buttonInfo = /** @type {Blockly.utils.toolbox.Button} */ (contentInfo); + var button = this.createButton_(buttonInfo, /** isLabel */ false); + contents.push({type: 'button', button: button}); + gaps.push(defaultGap); + break; + } + } + return {contents: contents, gaps: gaps}; +}; + +/** + * Creates a flyout button or a flyout label. + * @param {!Blockly.utils.toolbox.Button|!Blockly.utils.toolbox.Label} btnInfo + * The object holding information about a button or a label. + * @param {boolean} isLabel True if the button is a label, false otherwise. + * @return {!Blockly.FlyoutButton} The object used to display the button in the + * flyout. + * @private + */ +Blockly.Flyout.prototype.createButton_ = function(btnInfo, isLabel) { + if (!Blockly.FlyoutButton) { + throw Error('Missing require for Blockly.FlyoutButton'); + } + var curButton = new Blockly.FlyoutButton(this.workspace_, + this.targetWorkspace_, btnInfo, isLabel); + return curButton; +}; + +/** + * Create a block from the xml and permanently disable any blocks that were + * defined as disabled. + * @param {!Element} blockXml The xml of the block. + * @return {!Blockly.Block} The block created from the blockXml. + * @private + */ +Blockly.Flyout.prototype.createBlock_ = function(blockXml) { + var curBlock = Blockly.Xml.domToBlock(blockXml, this.workspace_); + if (!curBlock.isEnabled()) { + // Record blocks that were initially disabled. + // Do not enable these blocks as a result of capacity filtering. + this.permanentlyDisabled_.push(curBlock); + } + return curBlock; +}; + +/** + * Get the xml from the block info object. + * @param {!Blockly.utils.toolbox.Block} blockInfo The object holding + * information about a block. + * @return {!Element} The xml for the block. + * @throws {Error} if the xml is not a valid block definition. + * @private + */ +Blockly.Flyout.prototype.getBlockXml_ = function(blockInfo) { + var blockXml = blockInfo['blockxml']; + if (blockXml) { + blockXml = Blockly.Xml.textToDom(blockInfo['blockxml']); + } else { + throw Error('Error: Invalid block definition. Block definition must have blockxml.'); + } + return blockXml; +}; + +/** + * Add the necessary gap in the flyout for a separator. + * @param {!Blockly.utils.toolbox.Separator} sepInfo The object holding + * information about a separator. + * @param {!Array.} gaps The list gaps between items in the flyout. + * @param {number} defaultGap The default gap between the button and next element. + * @private + */ +Blockly.Flyout.prototype.addSeparatorGap_ = function(sepInfo, gaps, defaultGap) { + // Change the gap between two toolbox elements. + // + // The default gap is 24, can be set larger or smaller. + // This overwrites the gap attribute on the previous element. + var newGap = parseInt(sepInfo['gap'], 10); + // Ignore gaps before the first block. + if (!isNaN(newGap) && gaps.length > 0) { + gaps[gaps.length - 1] = newGap; + } else { + gaps.push(defaultGap); + } +}; + /** * Delete blocks, mats and buttons from a previous showing of the flyout. * @private diff --git a/core/flyout_button.js b/core/flyout_button.js index e0c6c010b..8585c7187 100644 --- a/core/flyout_button.js +++ b/core/flyout_button.js @@ -23,11 +23,13 @@ goog.require('Blockly.utils.dom'); * @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this * button. * @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace. - * @param {!Element} xml The XML specifying the label/button. + * @param {!Blockly.utils.toolbox.Button|!Blockly.utils.toolbox.Label} json + * The JSON specifying the label/button. * @param {boolean} isLabel Whether this button should be styled as a label. * @constructor + * @package */ -Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) { +Blockly.FlyoutButton = function(workspace, targetWorkspace, json, isLabel) { // Labels behave the same as buttons, but are styled differently. /** @@ -46,7 +48,7 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) { * @type {string} * @private */ - this.text_ = xml.getAttribute('text'); + this.text_ = json['text']; /** * @type {!Blockly.utils.Coordinate} @@ -66,16 +68,16 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) { * @type {string} * @private */ - this.callbackKey_ = xml.getAttribute('callbackKey') || + this.callbackKey_ = json['callbackKey'] || /* Check the lower case version too to satisfy IE */ - xml.getAttribute('callbackkey'); + json['callbackkey']; /** * If specified, a CSS class to add to this button. * @type {?string} * @private */ - this.cssClass_ = xml.getAttribute('web-class') || null; + this.cssClass_ = json['web-class'] || null; /** * Mouse up event data. diff --git a/core/inject.js b/core/inject.js index cc599ab9f..a8c483bb3 100644 --- a/core/inject.js +++ b/core/inject.js @@ -364,7 +364,7 @@ Blockly.init_ = function(mainWorkspace) { } else if (flyout) { // Build a fixed flyout with the root blocks. flyout.init(mainWorkspace); - flyout.show(options.languageTree.childNodes); + flyout.show(options.languageTree); flyout.scrollToStart(); } } diff --git a/core/mutator.js b/core/mutator.js index 540178d05..972809928 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -165,7 +165,7 @@ Blockly.Mutator.prototype.createEditor_ = function() { })); workspaceOptions.toolboxPosition = this.block_.RTL ? Blockly.TOOLBOX_AT_RIGHT : Blockly.TOOLBOX_AT_LEFT; - workspaceOptions.languageTree = quarkXml; + workspaceOptions.languageTree = Blockly.utils.toolbox.convertToolboxToJSON(quarkXml); workspaceOptions.getMetrics = this.getFlyoutMetrics_.bind(this); this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions); this.workspace_.isMutator = true; @@ -286,7 +286,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) { var flyout = this.workspace_.getFlyout(); if (tree) { flyout.init(this.workspace_); - flyout.show(tree.childNodes); + flyout.show(tree); } this.rootBlock_ = this.block_.decompose(this.workspace_); diff --git a/core/options.js b/core/options.js index 84fce92dc..cbb60edba 100644 --- a/core/options.js +++ b/core/options.js @@ -16,6 +16,7 @@ goog.require('Blockly.Theme'); goog.require('Blockly.Themes.Classic'); goog.require('Blockly.user.keyMap'); goog.require('Blockly.utils.userAgent'); +goog.require('Blockly.utils.toolbox'); goog.require('Blockly.Xml'); @@ -29,7 +30,7 @@ goog.require('Blockly.Xml'); Blockly.Options = function(options) { var readOnly = !!options['readOnly']; if (readOnly) { - var languageTree = null; + var toolboxContents = null; var hasCategories = false; var hasTrashcan = false; var hasCollapse = false; @@ -37,10 +38,12 @@ Blockly.Options = function(options) { var hasDisable = false; var hasSounds = false; } else { - var languageTree = - Blockly.Options.parseToolboxTree(options['toolbox'] || null); - var hasCategories = Boolean(languageTree && - languageTree.getElementsByTagName('category').length); + var toolboxDef = options['toolbox']; + if (!Array.isArray(toolboxDef)) { + toolboxDef = Blockly.Options.parseToolboxTree(toolboxDef || null); + } + var toolboxContents = Blockly.utils.toolbox.convertToolboxToJSON(toolboxDef); + var hasCategories = Blockly.utils.toolbox.hasCategories(toolboxContents); var hasTrashcan = options['trashcan']; if (hasTrashcan === undefined) { hasTrashcan = hasCategories; @@ -140,8 +143,8 @@ Blockly.Options = function(options) { this.hasCss = hasCss; /** @type {boolean} */ this.horizontalLayout = horizontalLayout; - /** @type {Node} */ - this.languageTree = languageTree; + /** @type {Array.} */ + this.languageTree = toolboxContents; /** @type {!Object} */ this.gridOptions = Blockly.Options.parseGridOptions_(options); /** @type {!Object} */ @@ -312,7 +315,8 @@ Blockly.Options.parseThemeOptions_ = function(options) { /** * Parse the provided toolbox tree into a consistent DOM format. - * @param {Node|string} tree DOM tree of blocks, or text representation of same. + * @param {Node|NodeList|?string} tree DOM tree of blocks, or text representation + * of same. * @return {Node} DOM tree of blocks, or null. */ Blockly.Options.parseToolboxTree = function(tree) { diff --git a/core/toolbox.js b/core/toolbox.js index 38ce94f01..fcf2b7cf1 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -25,6 +25,7 @@ goog.require('Blockly.utils.colour'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); +goog.require('Blockly.utils.toolbox'); /** @@ -127,12 +128,11 @@ Blockly.Toolbox = function(workspace) { this.selectedOption_ = null; /** - * The tree node most recently selected. + * The TreeNode most recently selected. * @type {Blockly.tree.BaseNode} * @private */ this.lastCategory_ = null; - }; /** @@ -208,10 +208,11 @@ Blockly.Toolbox.prototype.init = function() { /** * Fill the toolbox with categories and blocks. - * @param {Node} languageTree DOM tree of blocks. + * @param {Array.} toolboxDef Array holding objects + * containing information on the contents of the toolbox. * @package */ -Blockly.Toolbox.prototype.renderTree = function(languageTree) { +Blockly.Toolbox.prototype.renderTree = function(toolboxDef) { if (this.tree_) { this.tree_.dispose(); // Delete any existing content. this.lastCategory_ = null; @@ -223,11 +224,10 @@ Blockly.Toolbox.prototype.renderTree = function(languageTree) { tree.onBeforeSelected(this.handleBeforeTreeSelected_); tree.onAfterSelected(this.handleAfterTreeSelected_); var openNode = null; - if (languageTree) { + if (toolboxDef) { this.tree_.contents = []; this.hasColours_ = false; - openNode = this.syncTrees_( - languageTree, this.tree_, this.workspace_.options.pathToMedia); + openNode = this.createTree_(toolboxDef, this.tree_); if (this.tree_.contents.length) { throw Error('Toolbox cannot have both blocks and categories ' + @@ -252,6 +252,156 @@ Blockly.Toolbox.prototype.renderTree = function(languageTree) { } }; +/** + * Create the toolbox tree. + * @param {Array.} toolboxDef List of objects + * holding information on toolbox contents. + * @param {!Blockly.tree.BaseNode} treeOut The output tree for the toolbox. Due + * to the recursive nature of this function, treeOut can be either the root of + * the tree (Blockly.tree.TreeControl) or a child node of the tree + * (Blockly.tree.TreeNode). These nodes are built from the toolboxDef. + * @return {Blockly.tree.BaseNode} The TreeNode to expand when the toolbox is + * first loaded (or null). + * @private + */ +Blockly.Toolbox.prototype.createTree_ = function(toolboxDef, treeOut) { + var openNode = null; + var lastElement = null; + if (!toolboxDef) { + return null; + } + + for (var i = 0, childIn; (childIn = toolboxDef[i]); i++) { + switch (childIn.kind.toUpperCase()) { + case 'CATEGORY': + var categoryInfo = /** @type {Blockly.utils.toolbox.Category} */ (childIn); + openNode = this.addCategory_(categoryInfo, treeOut) || openNode; + lastElement = childIn; + break; + case 'SEP': + var separatorInfo = /** @type {Blockly.utils.toolbox.Separator} */ (childIn); + lastElement = this.addSeparator_(separatorInfo, treeOut, lastElement) || lastElement; + break; + case 'BLOCK': + case 'SHADOW': + case 'LABEL': + case 'BUTTON': + treeOut.contents.push(childIn); + lastElement = childIn; + break; + } + } + return openNode; +}; + +/** + * Add a category to the toolbox tree. + * @param {!Blockly.utils.toolbox.Category} categoryInfo The object holding + * information on the category. + * @param {!Blockly.tree.BaseNode} treeOut The TreeControl or TreeNode + * object built from the childNodes. + * @return {Blockly.tree.BaseNode} TreeNode to open at startup (or null). + * @private + */ +Blockly.Toolbox.prototype.addCategory_ = function(categoryInfo, treeOut) { + var openNode = null; + // Decode the category name for any potential message references + // (eg. `%{BKY_CATEGORY_NAME_LOGIC}`). + var categoryName = Blockly.utils.replaceMessageReferences(categoryInfo['name']); + + // Create and add the tree node for the category. + var childOut = this.tree_.createNode(categoryName); + childOut.onSizeChanged(this.handleNodeSizeChanged_); + childOut.contents = []; + treeOut.add(childOut); + + var custom = categoryInfo['custom']; + + if (custom) { + // Variables and procedures are special dynamic categories. + childOut.contents = custom; + } else { + openNode = this.createTree_(categoryInfo.contents, childOut) || openNode; + } + this.setColourOrStyle_(categoryInfo, childOut, categoryName); + openNode = this.setExpanded_(categoryInfo, childOut) || openNode; + return openNode; +}; + +/** + * Add either the colour or the style for a category. + * @param {!Blockly.utils.toolbox.Category} categoryInfo The object holding + * information on the category. + * @param {!Blockly.tree.TreeNode} childOut The TreeNode for a category. + * @param {string} categoryName The name of the category. + * @private + */ +Blockly.Toolbox.prototype.setColourOrStyle_ = function( + categoryInfo, childOut, categoryName) { + var styleName = categoryInfo['categorystyle']; + var colour = categoryInfo['colour']; + + if (colour && styleName) { + childOut.hexColour = ''; + console.warn('Toolbox category "' + categoryName + + '" must not have both a style and a colour'); + } else if (styleName) { + this.setColourFromStyle_(styleName, childOut, categoryName); + } else { + this.setColour_(colour, childOut, categoryName); + } +}; + +/** + * Add a separator to the toolbox tree if it is between categories. Otherwise, + * add the separator to the list of contents. + * @param {!Blockly.utils.toolbox.Separator} separatorInfo The object holding + * information on the separator. + * @param {!Blockly.tree.BaseNode} treeOut The TreeControl or TreeNode + * object built from the childNodes. + * @param {Object} lastElement The last element to be added to the tree. + * @return {Object} The last element to be added to the tree, or + * null. + * @private + */ +Blockly.Toolbox.prototype.addSeparator_ = function( + separatorInfo, treeOut, lastElement) { + if (lastElement && lastElement.kind.toUpperCase() == 'CATEGORY') { + // Separator between two categories. + // + treeOut.add(new Blockly.Toolbox.TreeSeparator( + /** @type {!Blockly.tree.BaseNode.Config} */ + (this.treeSeparatorConfig_))); + } else { + // Otherwise add to contents array. + treeOut.contents.push(separatorInfo); + return separatorInfo; + } + return null; +}; + +/** + * Checks whether a node should be expanded, and expands if necessary. + * @param {!Blockly.utils.toolbox.Category} categoryInfo The child to expand. + * @param {!Blockly.tree.TreeNode} childOut The TreeNode created from childIn. + * @return {Blockly.tree.BaseNode} TreeNode to open at startup (or null). + * @private + */ +Blockly.Toolbox.prototype.setExpanded_ = function(categoryInfo, childOut) { + var openNode = null; + if (categoryInfo['expanded'] == 'true') { + if (childOut.contents.length) { + // This is a category that directly contains blocks. + // After the tree is rendered, open this category and show flyout. + openNode = childOut; + } + childOut.setExpanded(true); + } else { + childOut.setExpanded(false); + } + return openNode; +}; + /** * Handle the before tree item selected action. * @param {Blockly.tree.BaseNode} node The newly selected node. @@ -409,91 +559,6 @@ Blockly.Toolbox.prototype.position = function() { this.flyout_.position(); }; -/** - * Sync trees of the toolbox. - * @param {!Node} treeIn DOM tree of blocks. - * @param {!Blockly.tree.BaseNode} treeOut The TreeControl or TreeNode - * object built from treeIn. - * @param {string} pathToMedia The path to the Blockly media directory. - * @return {Blockly.tree.BaseNode} Tree node to open at startup (or null). - * @private - */ -Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { - var openNode = null; - var lastElement = null; - for (var i = 0, childIn; (childIn = treeIn.childNodes[i]); i++) { - if (!childIn.tagName) { - // Skip over text. - continue; - } - switch (childIn.tagName.toUpperCase()) { - case 'CATEGORY': - // Decode the category name for any potential message references - // (eg. `%{BKY_CATEGORY_NAME_LOGIC}`). - var categoryName = Blockly.utils.replaceMessageReferences( - childIn.getAttribute('name')); - var childOut = this.tree_.createNode(categoryName); - childOut.onSizeChanged(this.handleNodeSizeChanged_); - childOut.contents = []; - treeOut.add(childOut); - var custom = childIn.getAttribute('custom'); - if (custom) { - // Variables and procedures are special dynamic categories. - childOut.contents = custom; - } else { - var newOpenNode = this.syncTrees_(childIn, childOut, pathToMedia); - if (newOpenNode) { - openNode = newOpenNode; - } - } - - var styleName = childIn.getAttribute('categorystyle'); - var colour = childIn.getAttribute('colour'); - - if (colour && styleName) { - childOut.hexColour = ''; - console.warn('Toolbox category "' + categoryName + - '" can not have both a style and a colour'); - } else if (styleName) { - this.setColourFromStyle_(styleName, childOut, categoryName); - } else { - this.setColour_(colour, childOut, categoryName); - } - - if (childIn.getAttribute('expanded') == 'true') { - if (childOut.contents.length) { - // This is a category that directly contains blocks. - // After the tree is rendered, open this category and show flyout. - openNode = childOut; - } - childOut.setExpanded(true); - } else { - childOut.setExpanded(false); - } - lastElement = childIn; - break; - case 'SEP': - if (lastElement && lastElement.tagName.toUpperCase() == 'CATEGORY') { - // Separator between two categories. - // - treeOut.add(new Blockly.Toolbox.TreeSeparator( - /** @type {!Blockly.tree.BaseNode.Config} */ - (this.treeSeparatorConfig_))); - break; - } - // Otherwise falls through. - case 'BLOCK': - case 'SHADOW': - case 'LABEL': - case 'BUTTON': - treeOut.contents.push(childIn); - lastElement = childIn; - break; - } - } - return openNode; -}; - /** * Sets the colour on the category. * @param {number|string} colourValue HSV hue value (0 to 360), #RRGGBB string, diff --git a/core/utils/toolbox.js b/core/utils/toolbox.js new file mode 100644 index 000000000..c5e1fb71a --- /dev/null +++ b/core/utils/toolbox.js @@ -0,0 +1,159 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Utility functions for the toolbox and flyout. + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + +goog.provide('Blockly.utils.toolbox'); + + +/** + * The information needed to create a block in the toolbox. + * @typedef {{ + * kind:string, + * blockxml:(string|Node) + * }} + */ +Blockly.utils.toolbox.Block; + +/** + * The information needed to create a separator in the toolbox. + * @typedef {{ + * kind:string, + * gap:?number + * }} + */ +Blockly.utils.toolbox.Separator; + +/** + * The information needed to create a button in the toolbox. + * @typedef {{ + * kind:string, + * text:string, + * callbackkey:string + * }} + */ +Blockly.utils.toolbox.Button; + +/** + * The information needed to create a label in the toolbox. + * @typedef {{ + * kind:string, + * text:string + * }} + */ +Blockly.utils.toolbox.Label; + +/** + * The information needed to create a category in the toolbox. + * @typedef {{ + * kind:string, + * name:string, + * categorystyle:?string, + * colour:?string, + * contents:Array. + * }} + */ +Blockly.utils.toolbox.Category; + +/** + * Any information that can be used to create an item in the toolbox. + * @typedef {Blockly.utils.toolbox.Block| + * Blockly.utils.toolbox.Separator| + * Blockly.utils.toolbox.Button| + * Blockly.utils.toolbox.Label| + * Blockly.utils.toolbox.Category} + */ +Blockly.utils.toolbox.Toolbox; + +/** + * All of the different types that can create a toolbox. + * @typedef {Node| + * NodeList| + * Array.| + * Array.} + */ +Blockly.utils.toolbox.ToolboxDefinition; + + +/** + * Parse the provided toolbox definition into a consistent format. + * @param {Blockly.utils.toolbox.ToolboxDefinition} toolboxDef The definition of the + * toolbox in one of its many forms. + * @return {Array.} Array of JSON holding + * information on toolbox contents. + * @package + */ +Blockly.utils.toolbox.convertToolboxToJSON = function(toolboxDef) { + if (!toolboxDef) { + return null; + } + // If it is an array of JSON, then it is already in the correct format. + if (Array.isArray(toolboxDef) && toolboxDef.length && !(toolboxDef[0].nodeType)) { + return /** @type {!Array.} */ (toolboxDef); + } + + return Blockly.utils.toolbox.toolboxXmlToJson_(toolboxDef); +}; + +/** + * Convert the xml for a toolbox to JSON. + * @param {!NodeList|!Node|!Array} toolboxDef The + * definition of the toolbox in one of its many forms. + * @return {!Array.} A list of objects in the + * toolbox. + * @private + */ +Blockly.utils.toolbox.toolboxXmlToJson_ = function(toolboxDef) { + var arr = []; + // If it is a node it will have children. + var childNodes = toolboxDef.childNodes; + if (!childNodes) { + // Otherwise the toolboxDef is an array or collection. + childNodes = toolboxDef; + } + for (var i = 0, child; (child = childNodes[i]); i++) { + if (!child.tagName) { + continue; + } + var obj = {}; + obj['kind'] = child.tagName.toUpperCase(); + // Store the xml for a block + if (child.tagName.toUpperCase() == 'BLOCK') { + obj['blockxml'] = Blockly.utils.xml.domToText(child); + } + // Get the contents for a category. + if (child.tagName.toUpperCase() == 'CATEGORY') { + obj['contents'] = Blockly.utils.toolbox.toolboxXmlToJson_(child); + } + // Add xml attributes to object + for (var j = 0; j < child.attributes.length; j++) { + var attr = child.attributes[j]; + obj[attr.nodeName] = attr.value; + } + arr.push(obj); + } + return arr; +}; + +/** + * Whether or not the toolbox definition has categories or not. + * @param {Node|Array.} toolboxDef The definition + * of the toolbox. Either in xml or JSON. + * @return {boolean} True if the toolbox has categories. + * @package + */ +Blockly.utils.toolbox.hasCategories = function(toolboxDef) { + if (Array.isArray(toolboxDef)) { + // Search for categories + return !!(toolboxDef.length && toolboxDef[0]['kind'].toUpperCase() == 'CATEGORY'); + } else { + return !!(toolboxDef && toolboxDef.getElementsByTagName('category').length); + } +}; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 060ec942c..47b7445a5 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -32,6 +32,7 @@ goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); +goog.require('Blockly.utils.toolbox'); goog.require('Blockly.Workspace'); goog.require('Blockly.WorkspaceAudio'); goog.require('Blockly.WorkspaceDragSurfaceSvg'); @@ -1815,11 +1816,16 @@ Blockly.WorkspaceSvg.prototype.showContextMenu = function(e) { /** * Modify the block tree on the existing toolbox. - * @param {Node|string} tree DOM tree of blocks, or text representation of same. + * @param {Blockly.utils.toolbox.ToolboxDefinition|string} toolboxDef + * DOM tree of toolbox contents, string of toolbox contents, or array of JSON + * representing toolbox contents. */ -Blockly.WorkspaceSvg.prototype.updateToolbox = function(tree) { - tree = Blockly.Options.parseToolboxTree(tree); - if (!tree) { +Blockly.WorkspaceSvg.prototype.updateToolbox = function(toolboxDef) { + if (!Array.isArray(toolboxDef)) { + toolboxDef = Blockly.Options.parseToolboxTree(toolboxDef); + } + toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(toolboxDef); + if (!toolboxDef) { if (this.options.languageTree) { throw Error('Can\'t nullify an existing toolbox.'); } @@ -1828,18 +1834,18 @@ Blockly.WorkspaceSvg.prototype.updateToolbox = function(tree) { if (!this.options.languageTree) { throw Error('Existing toolbox is null. Can\'t create new toolbox.'); } - if (tree.getElementsByTagName('category').length) { + if (Blockly.utils.toolbox.hasCategories(toolboxDef)) { if (!this.toolbox_) { throw Error('Existing toolbox has no categories. Can\'t change mode.'); } - this.options.languageTree = tree; - this.toolbox_.renderTree(tree); + this.options.languageTree = toolboxDef; + this.toolbox_.renderTree(toolboxDef); } else { if (!this.flyout_) { throw Error('Existing toolbox has categories. Can\'t change mode.'); } - this.options.languageTree = tree; - this.flyout_.show(tree.childNodes); + this.options.languageTree = toolboxDef; + this.flyout_.show(toolboxDef); } }; diff --git a/tests/mocha/.eslintrc.json b/tests/mocha/.eslintrc.json index 6caceffa4..aa7377a34 100644 --- a/tests/mocha/.eslintrc.json +++ b/tests/mocha/.eslintrc.json @@ -14,7 +14,10 @@ "defineStackBlock": true, "defineStatementBlock": true, "createEventsFireStub": true, - "testAWorkspace": true + "testAWorkspace": true, + "getSimpleJSON": true, + "getXmlArray": true, + "getCategoryJSON": true }, "extends": "../../.eslintrc.json" } diff --git a/tests/mocha/flyout_test.js b/tests/mocha/flyout_test.js new file mode 100644 index 000000000..1a73f095b --- /dev/null +++ b/tests/mocha/flyout_test.js @@ -0,0 +1,105 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +suite('Flyout', function() { + setup(function() { + Blockly.defineBlocksWithJsonArray([{ + "type": "basic_block", + "message0": "%1", + "args0": [ + { + "type": "field_input", + "name": "TEXT", + "text": "default" + } + ] + }]); + this.toolboxXml = document.getElementById('toolbox-simple'); + this.workspace = Blockly.inject('blocklyDiv', + { + toolbox: this.toolboxXml + }); + }); + + teardown(function() { + this.workspace.dispose(); + delete Blockly.Blocks['basic_block']; + sinon.restore(); + }); + + suite('createFlyoutInfo_', function() { + setup(function() { + this.simpleToolboxJSON = getSimpleJSON(); + this.flyout = this.workspace.getFlyout(); + this.createFlyoutSpy = sinon.spy(this.flyout, 'createFlyoutInfo_'); + + }); + + function checkLayoutContents(actual, expected, opt_message) { + chai.assert.equal(actual.length, expected.length, opt_message); + for (var i = 0; i < actual.length; i++) { + chai.assert.equal(actual[i].type, expected[i].type, opt_message); + if (actual[i].type == 'BLOCK') { + chai.assert.typeOf(actual[i]['block'], 'Blockly.Block'); + } else if (actual[i].type == 'BUTTON' || actual[i].type == 'LABEL') { + chai.assert.typeOf(actual[i]['block'], 'Blockly.FlyoutButton'); + } + } + } + + function checkFlyoutInfo(flyoutSpy) { + var expectedContents = [ + {type: "block"}, + {type: "button"}, + {type: "button"} + ]; + var expectedGaps = [20,24,24]; + var flyoutInfo = flyoutSpy.returnValues[0]; + var contents = flyoutInfo.contents; + var gaps = flyoutInfo.gaps; + chai.assert.deepEqual(gaps, expectedGaps); + checkLayoutContents(contents, expectedContents, 'Contents'); + } + + test('Node', function() { + this.flyout.show(this.toolboxXml); + checkFlyoutInfo(this.createFlyoutSpy); + }); + test('NodeList', function() { + var nodeList = document.getElementById('toolbox-simple').childNodes; + this.flyout.show(nodeList); + checkFlyoutInfo(this.createFlyoutSpy); + }); + test('Array of JSON', function() { + this.flyout.show(this.simpleToolboxJSON); + checkFlyoutInfo(this.createFlyoutSpy); + }); + test('Array of xml', function() { + this.flyout.show(getXmlArray()); + checkFlyoutInfo(this.createFlyoutSpy); + }); + test('Custom Toolbox: No Category Available', function() { + chai.assert.throws(function() { + this.flyout.show('someString'); + }.bind(this), 'Couldn\'t find a callback function when opening' + + ' a toolbox category.'); + }); + test('Custom Toolbox: Function does not return array', function() { + sinon.stub(this.flyout.workspace_.targetWorkspace, + 'getToolboxCategoryCallback').returns(function(){return null;}); + chai.assert.throws(function() { + this.flyout.show('someString'); + }.bind(this), 'Result of toolbox category callback must be an array.'); + }); + test('Custom Toolbox: Returns Array', function() { + sinon.stub(this.flyout.workspace_.targetWorkspace, + 'getToolboxCategoryCallback').returns(function(){return getXmlArray();}); + chai.assert.doesNotThrow(function() { + this.flyout.show('someString'); + }.bind(this)); + }); + }); +}); diff --git a/tests/mocha/index.html b/tests/mocha/index.html index 5cc469558..078dc71d7 100644 --- a/tests/mocha/index.html +++ b/tests/mocha/index.html @@ -32,6 +32,7 @@ + @@ -55,6 +56,7 @@ + @@ -82,6 +84,13 @@
+ + diff --git a/tests/mocha/run_mocha_tests_in_browser.js b/tests/mocha/run_mocha_tests_in_browser.js index f78871811..5a0b0b7f6 100644 --- a/tests/mocha/run_mocha_tests_in_browser.js +++ b/tests/mocha/run_mocha_tests_in_browser.js @@ -40,7 +40,7 @@ async function runMochaTestsInBrowser() { var elem = await browser.$('#failureCount'); var text = await elem.getAttribute('tests_failed'); return text != 'unset'; - }, 20000); + }, 22000); const elem = await browser.$('#failureCount'); const numOfFailure = await elem.getAttribute('tests_failed'); diff --git a/tests/mocha/toolbox_helper.js b/tests/mocha/toolbox_helper.js new file mode 100644 index 000000000..7f0f1d304 --- /dev/null +++ b/tests/mocha/toolbox_helper.js @@ -0,0 +1,84 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + + +/* exported getCategoryJSON, getSimpleJSON, getXmlArray */ + +/** + * Get JSON for a toolbox that contains categories. + * @return {Array.} The array holding information + * for a toolbox. + */ +function getCategoryJSON() { + return [ + { + "kind": "CATEGORY", + "contents": [ + { + "kind": "BLOCK", + "blockxml": 'FirstCategory-FirstBlock' + }, + { + "kind": "BLOCK", + "blockxml": 'FirstCategory-SecondBlock' + } + ], + "name": "First" + }, + { + "kind": "CATEGORY", + "contents": [ + { + "kind": "BLOCK", + "blockxml": 'SecondCategory-FirstBlock' + } + ], + "name": "Second" + }]; +} + +/** + * Get JSON for a simple toolbox. + * @return {Array.} The array holding information + * for a simple toolbox. + */ +function getSimpleJSON() { + return [ + { + "kind":"BLOCK", + "blockxml": "", + "type":"logic_operation" + }, + { + "kind":"SEP", + "gap":"20" + }, + { + "kind":"BUTTON", + "text": "insert", + "callbackkey": "insertConnectionRows" + }, + { + "kind":"LABEL", + "text":"tooltips" + } + ]; +} + +/** + * Get an array filled with xml elements. + * @return {Array.} Array holding xml elements for a toolbox. + */ +function getXmlArray() { + // Need to use HTMLElement instead of Element so parser output is + // consistent with other tests + var block = document.createElement('block'); + block.setAttribute('type', 'logic_operation'); + var separator = Blockly.Xml.textToDom(''); + var button = Blockly.Xml.textToDom(''); + var label = Blockly.Xml.textToDom(''); + return [block, separator, button, label]; +} diff --git a/tests/mocha/toolbox_test.js b/tests/mocha/toolbox_test.js index 6e89a69b9..d4a9546e7 100644 --- a/tests/mocha/toolbox_test.js +++ b/tests/mocha/toolbox_test.js @@ -23,9 +23,8 @@ suite('Toolbox', function() { toolbox: this.toolboxXml }); this.toolbox = this.workspace.getToolbox(); - this.toolbox.init(); }); - + teardown(function() { this.workspace.dispose(); delete Blockly.Blocks['basic_block']; @@ -33,6 +32,9 @@ suite('Toolbox', function() { }); suite('init', function() { + setup(function() { + this.toolbox.init(); + }); test('HtmlDiv is created', function() { chai.assert.isDefined(this.toolbox.HtmlDiv); }); @@ -52,15 +54,47 @@ suite('Toolbox', function() { }); suite('renderTree', function() { + setup(function() { + this.toolboxXml = Blockly.utils.toolbox.convertToolboxToJSON(this.toolboxXml); + this.toolbox.selectFirstCategory(); + this.firstChild = this.toolbox.tree_.getChildAt(0); + this.secondChild = this.toolbox.tree_.getChildAt(1); + this.toolbox.handleBeforeTreeSelected_(this.secondChild); + }); test('Tree is created and set', function() { this.toolbox.renderTree(this.toolboxXml); chai.assert.isDefined(this.toolbox.tree_); }); test('Throws error if a toolbox has both blocks and categories at root level', function() { - var badXml = document.getElementById('toolbox-incorrect'); var toolbox = this.toolbox; + var badToolboxDef = [ + { + "kind": "block", + "blockxml": "" + }, + { + "kind": "category", + "name": "loops", + "categorystyle": "math_category", + "contents": [ + { + "kind": "block", + "blockxml": "" + }, + { + "kind": "button", + "text": "insert", + "callbackkey":"insertConnectionRows" + }, + { + "kind": "label", + "text": "Something" + } + ] + } + ]; chai.assert.throws(function() { - toolbox.renderTree(badXml); + toolbox.renderTree(badToolboxDef); }, 'Toolbox cannot have both blocks and categories in the root level.'); }); test('Select any open nodes', function() { @@ -75,6 +109,36 @@ suite('Toolbox', function() { .getAttribute('aria-orientation'); chai.assert.equal(orientationAttribute, 'horizontal'); }); + test('Create a toolbox from JSON', function() { + var jsonDef = [ + { + "kind": "category", + "contents": [ + { + "kind": "block", + "blockxml": 'FirstCategory-FirstBlock' + }, + { + "kind": "label", + "text": "Input/Output:", + "web-class": "ioLabel" + }, + { + "kind": "button", + "text": "insert", + "callbackkey": "insertConnectionStacks", + "web-class": "ioLabel" + }, + { + "kind": "sep", + "gap": "7" + } + ] + } + ]; + this.toolbox.renderTree(jsonDef); + chai.assert.lengthOf(this.toolbox.tree_.children_, 1); + }); }); suite('handleBeforeTreeSelected_', function() { @@ -186,7 +250,7 @@ suite('Toolbox', function() { }); }); - suite('syncTrees_', function() { + suite('createTree_', function() { setup(function() { this.tree = new Blockly.tree.TreeControl(this.toolbox, this.toolbox.config_); this.tree.contents = []; @@ -195,44 +259,119 @@ suite('Toolbox', function() { this.buttonIdx = 1; this.dynamicCategoryIdx = 3; this.categorySeparatorIdx = 2; + this.toolboxXml = Blockly.utils.toolbox.convertToolboxToJSON(this.toolboxXml); }); test('Having a dynamic category', function() { - this.toolbox.syncTrees_(this.toolboxXml, this.tree, - this.toolbox.workspace_.options.pathToMedia); + this.toolbox.createTree_(this.toolboxXml, this.tree); chai.assert.equal(this.tree.children_[this.dynamicCategoryIdx].contents, 'VARIABLE'); }); test('Node is expanded', function() { - var openNode = this.toolbox.syncTrees_(this.toolboxXml, this.tree, - this.toolbox.workspace_.options.pathToMedia); + var openNode = this.toolbox.createTree_(this.toolboxXml, this.tree); chai.assert.exists(openNode); }); test('Having a tree separator', function() { - this.toolbox.syncTrees_(this.toolboxXml, this.tree, - this.toolbox.workspace_.options.pathToMedia); - var sepString = Blockly.utils.xml.domToText( - this.tree.children_[0].contents[this.separatorIdx]); - chai.assert.equal(sepString, ''); + this.toolbox.createTree_(this.toolboxXml, this.tree); + var sepObj = this.tree.children_[0].contents[this.separatorIdx]; + chai.assert.isNotNull(sepObj); + chai.assert.equal(sepObj['gap'], -1); }); test('Separator between two categories', function() { - this.toolbox.syncTrees_(this.toolboxXml, this.tree, - this.toolbox.workspace_.options.pathToMedia); + this.toolbox.createTree_(this.toolboxXml, this.tree); chai.assert.instanceOf(this.tree.children_[this.categorySeparatorIdx], Blockly.Toolbox.TreeSeparator); }); test('Having a button', function() { - this.toolbox.syncTrees_(this.toolboxXml, this.tree, - this.toolbox.workspace_.options.pathToMedia); - var btnString = Blockly.utils.xml.domToText(this.tree.children_[0].contents[this.buttonIdx]); - chai.assert.equal(btnString, - ''); + this.toolbox.createTree_(this.toolboxXml, this.tree); + var btnObj = this.tree.children_[0].contents[this.buttonIdx]; + chai.assert.isNotNull(btnObj); + chai.assert.equal(btnObj['text'], 'insert'); + chai.assert.equal(btnObj['callbackkey'], 'insertConnectionRows'); }); test('Colours are set using correct method', function() { var setColourFromStyleStub = sinon.stub(this.toolbox, "setColourFromStyle_"); var setColourStub = sinon.stub(this.toolbox, "setColour_"); - this.toolbox.syncTrees_(this.toolboxXml, this.tree, - this.toolbox.workspace_.options.pathToMedia); + this.toolbox.createTree_(this.toolboxXml, this.tree); sinon.assert.calledOnce(setColourFromStyleStub); sinon.assert.called(setColourStub); }); }); + + suite('parseToolbox', function() { + setup(function() { + this.categoryToolboxJSON = getCategoryJSON(); + this.simpleToolboxJSON = getSimpleJSON(); + }); + + function checkValue(actual, expected, value) { + var actualVal = actual[value]; + var expectedVal = expected[value]; + chai.assert.equal(actualVal.toUpperCase(), expectedVal.toUpperCase(), 'Checknig value for: ' + value); + } + function checkContents(actualContents, expectedContents) { + chai.assert.equal(actualContents.length, expectedContents.length); + console.log(actualContents); + for (var i = 0; i < actualContents.length; i++) { + chai.assert.containsAllKeys(actualContents[i], Object.keys(expectedContents[i])); + } + } + function checkCategory(actual, expected) { + checkValue(actual, expected, 'kind'); + checkValue(actual, expected, 'name'); + checkContents(actual.contents, expected.contents); + } + function checkCategoryToolbox(actual, expected) { + chai.assert.equal(actual.length, expected.length); + for (var i = 0; i < expected.length; i++) { + checkCategory(actual[i], expected[i]); + } + } + function checkSimpleToolbox(actual, expected) { + checkContents(actual, expected); + } + + test('Simple Toolbox: Array with xml', function() { + var xmlList = getXmlArray(); + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(xmlList); + checkSimpleToolbox(toolboxDef, this.simpleToolboxJSON); + }); + test('Category Toolbox: Array with xml', function() { + var categoryOne = Blockly.Xml.textToDom('FirstCategory-FirstBlockFirstCategory-SecondBlock'); + var categoryTwo = Blockly.Xml.textToDom('SecondCategory-FirstBlock'); + var xmlList = [categoryOne, categoryTwo]; + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(xmlList); + checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON); + }); + test('Category Toolbox: Array with JSON', function() { + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(this.categoryToolboxJSON); + chai.assert.isNotNull(toolboxDef); + checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON); + }); + test('Simple Toolbox: Array with JSON', function() { + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(this.simpleToolboxJSON); + chai.assert.isNotNull(toolboxDef); + checkSimpleToolbox(toolboxDef, this.simpleToolboxJSON); + }); + test('Category Toolbox: NodeList', function() { + var nodeList = document.getElementById('toolbox-categories').childNodes; + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(nodeList); + checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON); + }); + test('Simple Toolbox: NodeList', function() { + var nodeList = document.getElementById('toolbox-simple').childNodes; + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(nodeList); + checkSimpleToolbox(toolboxDef, this.simpleToolboxJSON); + }); + test('Category Toolbox: xml', function() { + var toolboxXml = document.getElementById('toolbox-categories'); + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(toolboxXml); + chai.assert.isNotNull(toolboxDef); + checkCategoryToolbox(toolboxDef, this.categoryToolboxJSON); + }); + test('Simple Toolbox: xml', function() { + var toolboxXml = document.getElementById('toolbox-simple'); + var toolboxDef = Blockly.utils.toolbox.convertToolboxToJSON(toolboxXml); + chai.assert.isNotNull(toolboxDef); + checkSimpleToolbox(toolboxDef, this.simpleToolboxJSON); + }); + }); }); diff --git a/tests/mocha/workspace_svg_test.js b/tests/mocha/workspace_svg_test.js index ad61e6d15..5bfe4375f 100644 --- a/tests/mocha/workspace_svg_test.js +++ b/tests/mocha/workspace_svg_test.js @@ -29,6 +29,7 @@ suite('WorkspaceSvg', function() { delete Blockly.Blocks['simple_test_block']; delete Blockly.Blocks['test_val_in']; this.workspace.dispose(); + sinon.restore(); }); test('appendDomToWorkspace alignment', function() { @@ -82,6 +83,39 @@ suite('WorkspaceSvg', function() { chai.assert.notExists(shadowBlock.getSvgRoot()); }); + suite('updateToolbox', function() { + test('Passes in null when toolbox exists', function() { + chai.assert.throws(function() { + this.workspace.updateToolbox(null); + }.bind(this), 'Can\'t nullify an existing toolbox.'); + }); + test('Passes in toolbox def when current toolbox is null', function() { + this.workspace.options.languageTree = null; + chai.assert.throws(function() { + this.workspace.updateToolbox([]); + }.bind(this), 'Existing toolbox is null. Can\'t create new toolbox.'); + }); + test('Existing toolbox has no categories', function() { + sinon.stub(Blockly.utils.toolbox, 'hasCategories').returns(true); + this.workspace.toolbox_ = null; + chai.assert.throws(function() { + this.workspace.updateToolbox([]); + }.bind(this), 'Existing toolbox has no categories. Can\'t change mode.'); + }); + test('Existing toolbox has categories', function() { + sinon.stub(Blockly.utils.toolbox, 'hasCategories').returns(false); + this.workspace.flyout_ = null; + chai.assert.throws(function() { + this.workspace.updateToolbox([]); + }.bind(this), 'Existing toolbox has categories. Can\'t change mode.'); + }); + test('Passing in string as toolboxdef', function() { + var parseToolboxFake = sinon.spy(Blockly.Options, 'parseToolboxTree'); + this.workspace.updateToolbox(''); + sinon.assert.calledOnce(parseToolboxFake); + }); + }); + suite('Workspace Base class', function() { testAWorkspace(); });