mirror of
https://github.com/google/blockly.git
synced 2026-06-17 08:35:12 +02:00
@@ -2,6 +2,7 @@
|
||||
*_uncompressed*.js
|
||||
gulpfile.js
|
||||
/msg/*
|
||||
/dist/*
|
||||
/core/utils/global.js
|
||||
/tests/blocks/*
|
||||
/tests/themes/*
|
||||
|
||||
+346
-324
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+27
-16
@@ -21,44 +21,41 @@ 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.Input', 'Blockly.Workspace', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.string'], {});
|
||||
goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.ASTNode', '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'], {'lang': 'es5'});
|
||||
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/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.Size', '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/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/menu/menu.js', ['Blockly.Menu'], ['Blockly.Component', 'Blockly.utils.Coordinate', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/components/menu/menuitem.js', ['Blockly.MenuItem'], ['Blockly.Component', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
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.uiMenu', 'Blockly.utils.userAgent'], {});
|
||||
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.Rect', '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.js', ['Blockly.Field'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.Gesture', 'Blockly.utils', 'Blockly.utils.Rect', '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_date.js', ['Blockly.FieldDate'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.string', 'goog.date', 'goog.date.DateTime', 'goog.events', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_he', 'goog.ui.DatePicker'], {});
|
||||
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_registry.js', ['Blockly.fieldRegistry'], ['Blockly.registry'], {});
|
||||
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'], {});
|
||||
@@ -73,6 +70,16 @@ goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.utils', 'Bl
|
||||
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_accessibility.js', ['Blockly.IASTNodeLocation', 'Blockly.IASTNodeLocationSvg', 'Blockly.IASTNodeLocationWithBlock', 'Blockly.IBlocklyActionable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_bounded_element.js', ['Blockly.IBoundedElement'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_copyable.js', ['Blockly.ICopyable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_deletable.js', ['Blockly.IDeletable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_deletearea.js', ['Blockly.IDeleteArea'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistrable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], [], {});
|
||||
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'});
|
||||
@@ -83,13 +90,16 @@ goog.addDependency('../../core/keyboard_nav/marker.js', ['Blockly.Marker'], ['Bl
|
||||
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.userAgent'], {});
|
||||
goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.Xml', 'Blockly.registry', 'Blockly.user.keyMap', 'Blockly.utils.IdGenerator', 'Blockly.utils.Metrics', '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/registry.js', ['Blockly.registry'], [], {});
|
||||
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/block_rendering.js', ['Blockly.blockRendering'], ['Blockly.registry', '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'], {});
|
||||
@@ -128,8 +138,8 @@ goog.addDependency('../../core/renderers/zelos/measurables/rows.js', ['Blockly.z
|
||||
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/scrollbar.js', ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['Blockly.Touch', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', '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'], {});
|
||||
@@ -138,14 +148,13 @@ goog.addDependency('../../core/theme/modern.js', ['Blockly.Themes.Modern'], ['Bl
|
||||
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'], {});
|
||||
goog.addDependency('../../core/toolbox.js', ['Blockly.Toolbox'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Touch', 'Blockly.navigation', 'Blockly.registry', '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/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/ui_menu_utils.js', ['Blockly.utils.uiMenu'], ['Blockly.utils.style'], {});
|
||||
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.js', ['Blockly.utils'], ['Blockly.Msg', 'Blockly.constants', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', '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'], [], {});
|
||||
@@ -154,12 +163,14 @@ 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/metrics.js', ['Blockly.utils.Metrics'], [], {});
|
||||
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'], {});
|
||||
@@ -177,7 +188,7 @@ goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCom
|
||||
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'], {});
|
||||
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.registry', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Metrics', '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'});
|
||||
|
||||
+10
-1
@@ -805,6 +805,14 @@ Blockly.Blocks['procedures_callnoreturn'] = {
|
||||
}
|
||||
this.setProcedureParameters_(args, paramIds);
|
||||
},
|
||||
/**
|
||||
* Return all variables referenced by this block.
|
||||
* @return {!Array.<string>} List of variable names.
|
||||
* @this {Blockly.Block}
|
||||
*/
|
||||
getVars: function() {
|
||||
return this.arguments_;
|
||||
},
|
||||
/**
|
||||
* Return all variables referenced by this block.
|
||||
* @return {!Array.<!Blockly.VariableModel>} List of variable models.
|
||||
@@ -836,7 +844,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
|
||||
var name = this.getProcedureCall();
|
||||
var def = Blockly.Procedures.getDefinition(name, this.workspace);
|
||||
if (def && (def.type != this.defType_ ||
|
||||
JSON.stringify(def.arguments_) != JSON.stringify(this.arguments_))) {
|
||||
JSON.stringify(def.getVars()) != JSON.stringify(this.arguments_))) {
|
||||
// The signatures don't match.
|
||||
def = null;
|
||||
}
|
||||
@@ -959,6 +967,7 @@ Blockly.Blocks['procedures_callreturn'] = {
|
||||
updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_,
|
||||
mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom,
|
||||
domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation,
|
||||
getVars: Blockly.Blocks['procedures_callnoreturn'].getVars,
|
||||
getVarModels: Blockly.Blocks['procedures_callnoreturn'].getVarModels,
|
||||
onchange: Blockly.Blocks['procedures_callnoreturn'].onchange,
|
||||
customContextMenu:
|
||||
|
||||
+22
-9
@@ -1,8 +1,16 @@
|
||||
// Do not edit this file; automatically generated by gulp.
|
||||
'use strict';
|
||||
|
||||
|
||||
Blockly.Blocks.colour={};Blockly.Constants={};Blockly.Constants.Colour={};Blockly.Constants.Colour.HUE=20;
|
||||
/* eslint-disable */
|
||||
;(function(root, factory) {
|
||||
if (typeof define === 'function' && define.amd) { // AMD
|
||||
define(['./blockly_compressed.js'], factory);
|
||||
} else if (typeof exports === 'object') { // Node.js
|
||||
module.exports = factory(require('./blockly_compressed.js'));
|
||||
} else { // Browser
|
||||
root.Blockly.Blocks = factory(root.Blockly);
|
||||
}
|
||||
}(this, function(Blockly) {
|
||||
'use strict';Blockly.Blocks.colour={};Blockly.Constants={};Blockly.Constants.Colour={};Blockly.Constants.Colour.HUE=20;
|
||||
Blockly.defineBlocksWithJsonArray([{type:"colour_picker",message0:"%1",args0:[{type:"field_colour",name:"COLOUR",colour:"#ff0000"}],output:"Colour",helpUrl:"%{BKY_COLOUR_PICKER_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_PICKER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"colour_random",message0:"%{BKY_COLOUR_RANDOM_TITLE}",output:"Colour",helpUrl:"%{BKY_COLOUR_RANDOM_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RANDOM_TOOLTIP}"},{type:"colour_rgb",message0:"%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3",
|
||||
args0:[{type:"input_value",name:"RED",check:"Number",align:"RIGHT"},{type:"input_value",name:"GREEN",check:"Number",align:"RIGHT"},{type:"input_value",name:"BLUE",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_RGB_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RGB_TOOLTIP}"},{type:"colour_blend",message0:"%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} %1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3",args0:[{type:"input_value",name:"COLOUR1",check:"Colour",
|
||||
align:"RIGHT"},{type:"input_value",name:"COLOUR2",check:"Colour",align:"RIGHT"},{type:"input_value",name:"RATIO",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_BLEND_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_BLEND_TOOLTIP}"}]);Blockly.Blocks.lists={};Blockly.Constants.Lists={};Blockly.Constants.Lists.HUE=260;
|
||||
@@ -113,13 +121,13 @@ this.getProcedureCall())&&(this.setFieldValue(b,"NAME"),this.setTooltip((this.ou
|
||||
this.setCollapsed(!1);this.quarkIds_||(this.quarkConnections_={},this.quarkIds_=[]);c=this.rendered;this.rendered=!1;for(var e=0;e<this.arguments_.length;e++){var f=this.getInput("ARG"+e);f&&(f=f.connection.targetConnection,this.quarkConnections_[this.quarkIds_[e]]=f,d&&f&&-1==b.indexOf(this.quarkIds_[e])&&(f.disconnect(),f.getSourceBlock().bumpNeighbours()))}this.arguments_=[].concat(a);this.argumentVarModels_=[];for(e=0;e<this.arguments_.length;e++)a=Blockly.Variables.getOrCreateVariablePackage(this.workspace,
|
||||
null,this.arguments_[e],""),this.argumentVarModels_.push(a);this.updateShape_();if(this.quarkIds_=b)for(e=0;e<this.arguments_.length;e++)b=this.quarkIds_[e],b in this.quarkConnections_&&(f=this.quarkConnections_[b],Blockly.Mutator.reconnect(f,this,"ARG"+e)||delete this.quarkConnections_[b]);(this.rendered=c)&&this.render()}},updateShape_:function(){for(var a=0;a<this.arguments_.length;a++){var b=this.getField("ARGNAME"+a);if(b){Blockly.Events.disable();try{b.setValue(this.arguments_[a])}finally{Blockly.Events.enable()}}else b=
|
||||
new Blockly.FieldLabel(this.arguments_[a]),this.appendValueInput("ARG"+a).setAlign(Blockly.ALIGN_RIGHT).appendField(b,"ARGNAME"+a).init()}for(;this.getInput("ARG"+a);)this.removeInput("ARG"+a),a++;if(a=this.getInput("TOPROW"))this.arguments_.length?this.getField("WITH")||(a.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS,"WITH"),a.init()):this.getField("WITH")&&a.removeField("WITH")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("name",this.getProcedureCall());
|
||||
for(var b=0;b<this.arguments_.length;b++){var c=Blockly.utils.xml.createElement("arg");c.setAttribute("name",this.arguments_[b]);a.appendChild(c)}return a},domToMutation:function(a){var b=a.getAttribute("name");this.renameProcedure(this.getProcedureCall(),b);b=[];for(var c=[],d=0,e;e=a.childNodes[d];d++)"arg"==e.nodeName.toLowerCase()&&(b.push(e.getAttribute("name")),c.push(e.getAttribute("paramId")));this.setProcedureParameters_(b,c)},getVarModels:function(){return this.argumentVarModels_},onchange:function(a){if(this.workspace&&
|
||||
!this.workspace.isFlyout&&a.recordUndo)if(a.type==Blockly.Events.BLOCK_CREATE&&-1!=a.ids.indexOf(this.id)){var b=this.getProcedureCall();b=Blockly.Procedures.getDefinition(b,this.workspace);!b||b.type==this.defType_&&JSON.stringify(b.arguments_)==JSON.stringify(this.arguments_)||(b=null);if(!b){Blockly.Events.setGroup(a.group);a=Blockly.utils.xml.createElement("xml");b=Blockly.utils.xml.createElement("block");b.setAttribute("type",this.defType_);var c=this.getRelativeToSurfaceXY(),d=c.y+2*Blockly.SNAP_RADIUS;
|
||||
b.setAttribute("x",c.x+Blockly.SNAP_RADIUS*(this.RTL?-1:1));b.setAttribute("y",d);c=this.mutationToDom();b.appendChild(c);c=Blockly.utils.xml.createElement("field");c.setAttribute("name","NAME");c.appendChild(Blockly.utils.xml.createTextNode(this.getProcedureCall()));b.appendChild(c);a.appendChild(b);Blockly.Xml.domToWorkspace(a,this.workspace);Blockly.Events.setGroup(!1)}}else a.type==Blockly.Events.BLOCK_DELETE?(b=this.getProcedureCall(),b=Blockly.Procedures.getDefinition(b,this.workspace),b||(Blockly.Events.setGroup(a.group),
|
||||
this.dispose(!0),Blockly.Events.setGroup(!1))):a.type==Blockly.Events.CHANGE&&"disabled"==a.element&&(b=this.getProcedureCall(),(b=Blockly.Procedures.getDefinition(b,this.workspace))&&b.id==a.blockId&&((b=Blockly.Events.getGroup())&&console.log("Saw an existing group while responding to a definition change"),Blockly.Events.setGroup(a.group),a.newValue?(this.previousEnabledState_=this.isEnabled(),this.setEnabled(!1)):this.setEnabled(this.previousEnabledState_),Blockly.Events.setGroup(b)))},customContextMenu:function(a){if(this.workspace.isMovable()){var b=
|
||||
{enabled:!0};b.text=Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;var c=this.getProcedureCall(),d=this.workspace;b.callback=function(){var a=Blockly.Procedures.getDefinition(c,d);a&&(d.centerOnBlock(a.id),a.select())};a.push(b)}},defType_:"procedures_defnoreturn"};
|
||||
for(var b=0;b<this.arguments_.length;b++){var c=Blockly.utils.xml.createElement("arg");c.setAttribute("name",this.arguments_[b]);a.appendChild(c)}return a},domToMutation:function(a){var b=a.getAttribute("name");this.renameProcedure(this.getProcedureCall(),b);b=[];for(var c=[],d=0,e;e=a.childNodes[d];d++)"arg"==e.nodeName.toLowerCase()&&(b.push(e.getAttribute("name")),c.push(e.getAttribute("paramId")));this.setProcedureParameters_(b,c)},getVars:function(){return this.arguments_},getVarModels:function(){return this.argumentVarModels_},
|
||||
onchange:function(a){if(this.workspace&&!this.workspace.isFlyout&&a.recordUndo)if(a.type==Blockly.Events.BLOCK_CREATE&&-1!=a.ids.indexOf(this.id)){var b=this.getProcedureCall();b=Blockly.Procedures.getDefinition(b,this.workspace);!b||b.type==this.defType_&&JSON.stringify(b.getVars())==JSON.stringify(this.arguments_)||(b=null);if(!b){Blockly.Events.setGroup(a.group);a=Blockly.utils.xml.createElement("xml");b=Blockly.utils.xml.createElement("block");b.setAttribute("type",this.defType_);var c=this.getRelativeToSurfaceXY(),
|
||||
d=c.y+2*Blockly.SNAP_RADIUS;b.setAttribute("x",c.x+Blockly.SNAP_RADIUS*(this.RTL?-1:1));b.setAttribute("y",d);c=this.mutationToDom();b.appendChild(c);c=Blockly.utils.xml.createElement("field");c.setAttribute("name","NAME");c.appendChild(Blockly.utils.xml.createTextNode(this.getProcedureCall()));b.appendChild(c);a.appendChild(b);Blockly.Xml.domToWorkspace(a,this.workspace);Blockly.Events.setGroup(!1)}}else a.type==Blockly.Events.BLOCK_DELETE?(b=this.getProcedureCall(),b=Blockly.Procedures.getDefinition(b,
|
||||
this.workspace),b||(Blockly.Events.setGroup(a.group),this.dispose(!0),Blockly.Events.setGroup(!1))):a.type==Blockly.Events.CHANGE&&"disabled"==a.element&&(b=this.getProcedureCall(),(b=Blockly.Procedures.getDefinition(b,this.workspace))&&b.id==a.blockId&&((b=Blockly.Events.getGroup())&&console.log("Saw an existing group while responding to a definition change"),Blockly.Events.setGroup(a.group),a.newValue?(this.previousEnabledState_=this.isEnabled(),this.setEnabled(!1)):this.setEnabled(this.previousEnabledState_),
|
||||
Blockly.Events.setGroup(b)))},customContextMenu:function(a){if(this.workspace.isMovable()){var b={enabled:!0};b.text=Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;var c=this.getProcedureCall(),d=this.workspace;b.callback=function(){var a=Blockly.Procedures.getDefinition(c,d);a&&(d.centerOnBlock(a.id),a.select())};a.push(b)}},defType_:"procedures_defnoreturn"};
|
||||
Blockly.Blocks.procedures_callreturn={init:function(){this.appendDummyInput("TOPROW").appendField("","NAME");this.setOutput(!0);this.setStyle("procedure_blocks");this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);this.arguments_=[];this.quarkConnections_={};this.quarkIds_=null;this.previousEnabledState_=!0},getProcedureCall:Blockly.Blocks.procedures_callnoreturn.getProcedureCall,renameProcedure:Blockly.Blocks.procedures_callnoreturn.renameProcedure,setProcedureParameters_:Blockly.Blocks.procedures_callnoreturn.setProcedureParameters_,
|
||||
updateShape_:Blockly.Blocks.procedures_callnoreturn.updateShape_,mutationToDom:Blockly.Blocks.procedures_callnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_callnoreturn.domToMutation,getVarModels:Blockly.Blocks.procedures_callnoreturn.getVarModels,onchange:Blockly.Blocks.procedures_callnoreturn.onchange,customContextMenu:Blockly.Blocks.procedures_callnoreturn.customContextMenu,defType_:"procedures_defreturn"};
|
||||
updateShape_:Blockly.Blocks.procedures_callnoreturn.updateShape_,mutationToDom:Blockly.Blocks.procedures_callnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_callnoreturn.domToMutation,getVars:Blockly.Blocks.procedures_callnoreturn.getVars,getVarModels:Blockly.Blocks.procedures_callnoreturn.getVarModels,onchange:Blockly.Blocks.procedures_callnoreturn.onchange,customContextMenu:Blockly.Blocks.procedures_callnoreturn.customContextMenu,defType_:"procedures_defreturn"};
|
||||
Blockly.Blocks.procedures_ifreturn={init:function(){this.appendValueInput("CONDITION").setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_IFRETURN_HELPURL);this.hasReturnValue_=!0},mutationToDom:function(){var a=
|
||||
Blockly.utils.xml.createElement("mutation");a.setAttribute("value",Number(this.hasReturnValue_));return a},domToMutation:function(a){this.hasReturnValue_=1==a.getAttribute("value");this.hasReturnValue_||(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN))},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()){a=!1;var b=this;do{if(-1!=this.FUNCTION_TYPES.indexOf(b.type)){a=!0;break}b=b.getSurroundParent()}while(b);
|
||||
a?("procedures_defnoreturn"==b.type&&this.hasReturnValue_?(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!1):"procedures_defreturn"!=b.type||this.hasReturnValue_||(this.removeInput("VALUE"),this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!0),this.setWarningText(null),this.isInFlyout||this.setEnabled(!0)):(this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING),
|
||||
@@ -167,3 +175,8 @@ Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MI
|
||||
d=Blockly.utils.xml.createElement("field");d.setAttribute("name","VAR");d.setAttribute("variabletype",c);d.appendChild(Blockly.utils.xml.createTextNode(f));f=Blockly.utils.xml.createElement("block");f.setAttribute("type",b);f.appendChild(d);e.callback=Blockly.ContextMenu.callbackFactory(this,f);a.push(e)}else if("variables_get_dynamic"==this.type||"variables_get_reporter_dynamic"==this.type)b={text:Blockly.Msg.RENAME_VARIABLE,enabled:!0,callback:Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)},
|
||||
f=this.getField("VAR").getText(),e={text:Blockly.Msg.DELETE_VARIABLE.replace("%1",f),enabled:!0,callback:Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)},a.unshift(b),a.unshift(e)},onchange:function(a){a=this.getFieldValue("VAR");a=Blockly.Variables.getVariable(this.workspace,a);"variables_get_dynamic"==this.type?this.outputConnection.setCheck(a.type):this.getInput("VALUE").connection.setCheck(a.type)}};
|
||||
Blockly.Constants.VariablesDynamic.RENAME_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();Blockly.Variables.renameVariable(b,c)}};Blockly.Constants.VariablesDynamic.DELETE_OPTION_CALLBACK_FACTORY=function(a){return function(){var b=a.workspace,c=a.getField("VAR").getVariable();b.deleteVariableById(c.getId());b.refreshToolboxSelection()}};Blockly.Extensions.registerMixin("contextMenu_variableDynamicSetterGetter",Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);
|
||||
return Blockly.Blocks;
|
||||
}));
|
||||
|
||||
|
||||
//# sourceMappingURL=blocks_compressed.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
+111
-19
@@ -12,6 +12,7 @@
|
||||
|
||||
goog.provide('Blockly.Block');
|
||||
|
||||
goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.Blocks');
|
||||
goog.require('Blockly.Connection');
|
||||
goog.require('Blockly.Events');
|
||||
@@ -29,6 +30,8 @@ goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.string');
|
||||
goog.require('Blockly.Workspace');
|
||||
|
||||
goog.requireType('Blockly.IASTNodeLocation');
|
||||
|
||||
|
||||
/**
|
||||
* Class for one block.
|
||||
@@ -39,6 +42,7 @@ goog.require('Blockly.Workspace');
|
||||
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
|
||||
* create a new ID.
|
||||
* @constructor
|
||||
* @implements {Blockly.IASTNodeLocation}
|
||||
* @throws When block is not valid or block name is not allowed.
|
||||
*/
|
||||
Blockly.Block = function(workspace, prototypeName, opt_id) {
|
||||
@@ -170,6 +174,9 @@ Blockly.Block = function(workspace, prototypeName, opt_id) {
|
||||
*/
|
||||
this.hat = undefined;
|
||||
|
||||
/** @type {?boolean} */
|
||||
this.rendered = null;
|
||||
|
||||
/**
|
||||
* A count of statement inputs on the block.
|
||||
* @type {number}
|
||||
@@ -229,6 +236,17 @@ Blockly.Block = function(workspace, prototypeName, opt_id) {
|
||||
*/
|
||||
Blockly.Block.CommentModel;
|
||||
|
||||
/**
|
||||
* The language-neutral id given to the collapsed input.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.Block.COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
|
||||
/**
|
||||
* The language-neutral id given to the collapsed field.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.Block.COLLAPSED_FIELD_NAME = '_TEMP_COLLAPSED_FIELD';
|
||||
|
||||
/**
|
||||
* Optional text data that round-trips between blocks and XML.
|
||||
* Has no effect. May be used by 3rd parties for meta information.
|
||||
@@ -550,7 +568,9 @@ Blockly.Block.prototype.bumpNeighbours = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the parent block or null if this block is at the top level.
|
||||
* Return the parent block or null if this block is at the top level. The parent
|
||||
* block is either the block connected to the previous connection (for a statement
|
||||
* block) or the block connected to the output connection (for a value block).
|
||||
* @return {Blockly.Block} The block that holds the current block.
|
||||
*/
|
||||
Blockly.Block.prototype.getParent = function() {
|
||||
@@ -601,7 +621,7 @@ Blockly.Block.prototype.getNextBlock = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the previous statement block directly connected to this block.
|
||||
* Returns the block connected to the previous connection.
|
||||
* @return {Blockly.Block} The previous statement block or null.
|
||||
*/
|
||||
Blockly.Block.prototype.getPreviousBlock = function() {
|
||||
@@ -974,7 +994,6 @@ Blockly.Block.prototype.getField = function(name) {
|
||||
/**
|
||||
* Return all variables referenced by this block.
|
||||
* @return {!Array.<string>} List of variable names.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Block.prototype.getVars = function() {
|
||||
var vars = [];
|
||||
@@ -1295,24 +1314,94 @@ Blockly.Block.prototype.setCollapsed = function(collapsed) {
|
||||
Blockly.Block.prototype.toString = function(opt_maxLength, opt_emptyToken) {
|
||||
var text = [];
|
||||
var emptyFieldPlaceholder = opt_emptyToken || '?';
|
||||
if (this.collapsed_) {
|
||||
text.push(this.getInput('_TEMP_COLLAPSED_INPUT').fieldRow[0].getText());
|
||||
} else {
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
|
||||
text.push(field.getText());
|
||||
}
|
||||
if (input.connection) {
|
||||
var child = input.connection.targetBlock();
|
||||
if (child) {
|
||||
text.push(child.toString(undefined, opt_emptyToken));
|
||||
} else {
|
||||
|
||||
// Temporarily set flag to navigate to all fields.
|
||||
var prevNavigateFields = Blockly.ASTNode.NAVIGATE_ALL_FIELDS;
|
||||
Blockly.ASTNode.NAVIGATE_ALL_FIELDS = true;
|
||||
|
||||
var node = Blockly.ASTNode.createBlockNode(this);
|
||||
var rootNode = node;
|
||||
|
||||
/**
|
||||
* Whether or not to add parentheses around an input.
|
||||
* @param {!Blockly.Connection} connection The connection.
|
||||
* @return {boolean} True if we should add parentheses around the input.
|
||||
*/
|
||||
function shouldAddParentheses(connection) {
|
||||
var checks = connection.getCheck();
|
||||
if (!checks && connection.targetConnection) {
|
||||
checks = connection.targetConnection.getCheck();
|
||||
}
|
||||
return !!checks && (checks.indexOf('Boolean') != -1 ||
|
||||
checks.indexOf('Number') != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we haven't circled back to the original root node.
|
||||
*/
|
||||
function checkRoot() {
|
||||
if (node && node.getType() == rootNode.getType() &&
|
||||
node.getLocation() == rootNode.getLocation()) {
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse the AST building up our text string.
|
||||
while (node) {
|
||||
switch (node.getType()) {
|
||||
case Blockly.ASTNode.types.INPUT:
|
||||
var connection = /** @type {!Blockly.Connection} */ (node.getLocation());
|
||||
if (!node.in()) {
|
||||
text.push(emptyFieldPlaceholder);
|
||||
} else if (shouldAddParentheses(connection)) {
|
||||
text.push('(');
|
||||
}
|
||||
break;
|
||||
case Blockly.ASTNode.types.FIELD:
|
||||
var field = /** @type {Blockly.Field} */ (node.getLocation());
|
||||
if (field.name != Blockly.Block.COLLAPSED_FIELD_NAME) {
|
||||
text.push(field.getText());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var current = node;
|
||||
node = current.in() || current.next();
|
||||
if (!node) {
|
||||
// Can't go in or next, keep going out until we can go next.
|
||||
node = current.out();
|
||||
checkRoot();
|
||||
while (node && !node.next()) {
|
||||
node = node.out();
|
||||
checkRoot();
|
||||
// If we hit an input on the way up, possibly close out parentheses.
|
||||
if (node && node.getType() == Blockly.ASTNode.types.INPUT &&
|
||||
shouldAddParentheses(
|
||||
/** @type {!Blockly.Connection} */ (node.getLocation()))) {
|
||||
text.push(')');
|
||||
}
|
||||
}
|
||||
if (node) {
|
||||
node = node.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
text = text.join(' ').trim() || '???';
|
||||
|
||||
// Restore state of NAVIGATE_ALL_FIELDS.
|
||||
Blockly.ASTNode.NAVIGATE_ALL_FIELDS = prevNavigateFields;
|
||||
|
||||
// Run through our text array and simplify expression to remove parentheses
|
||||
// around single field blocks.
|
||||
for (var i = 2, l = text.length; i < l; i++) {
|
||||
if (text[i - 2] == '(' && text[i] == ')') {
|
||||
text[i - 2] = text[i - 1];
|
||||
text.splice(i - 1, 2);
|
||||
l -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Join the text array, removing spaces around added paranthesis.
|
||||
text = text.join(' ').replace(/(\() | (\))/gmi, '$1$2').trim() || '???';
|
||||
if (opt_maxLength) {
|
||||
// TODO: Improve truncation so that text from this block is given priority.
|
||||
// E.g. "1+2+3+4+5+6+7+8+9=0" should be "...6+7+8+9=0", not "1+2+3+4+5...".
|
||||
@@ -1716,7 +1805,8 @@ Blockly.Block.prototype.moveNumberedInputBefore = function(
|
||||
/**
|
||||
* Remove an input from this block.
|
||||
* @param {string} name The name of the input.
|
||||
* @param {boolean=} opt_quiet True to prevent error if input is not present.
|
||||
* @param {boolean=} opt_quiet True to prevent an error if input is not present.
|
||||
* @return {boolean} True if operation succeeds, false if input is not present and opt_quiet is true
|
||||
* @throws {Error} if the input is not present and opt_quiet is not true.
|
||||
*/
|
||||
Blockly.Block.prototype.removeInput = function(name, opt_quiet) {
|
||||
@@ -1727,10 +1817,12 @@ Blockly.Block.prototype.removeInput = function(name, opt_quiet) {
|
||||
}
|
||||
input.dispose();
|
||||
this.inputList.splice(i, 1);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!opt_quiet) {
|
||||
if (opt_quiet) {
|
||||
return false;
|
||||
} else {
|
||||
throw Error('Input not found: ' + name);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,6 +162,7 @@ Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY,
|
||||
|
||||
this.draggingBlock_.translate(newLoc.x, newLoc.y);
|
||||
Blockly.blockAnimations.disconnectUiEffect(this.draggingBlock_);
|
||||
this.draggedConnectionManager_.updateAvailableConnections();
|
||||
}
|
||||
this.draggingBlock_.setDragging(true);
|
||||
// For future consideration: we may be able to put moveToDragSurface inside
|
||||
@@ -170,7 +171,7 @@ Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY,
|
||||
this.draggingBlock_.moveToDragSurface();
|
||||
|
||||
var toolbox = this.workspace_.getToolbox();
|
||||
if (toolbox) {
|
||||
if (toolbox && typeof toolbox.addStyle == 'function') {
|
||||
var style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' :
|
||||
'blocklyToolboxGrab';
|
||||
toolbox.addStyle(style);
|
||||
@@ -220,7 +221,7 @@ Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) {
|
||||
this.dragBlock(e, currentDragDeltaXY);
|
||||
this.dragIconData_ = [];
|
||||
this.fireDragEndEvent_();
|
||||
|
||||
|
||||
Blockly.utils.dom.stopTextWidthCache();
|
||||
|
||||
Blockly.blockAnimations.disconnectUiStop();
|
||||
@@ -246,7 +247,7 @@ Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) {
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
var toolbox = this.workspace_.getToolbox();
|
||||
if (toolbox) {
|
||||
if (toolbox && typeof toolbox.removeStyle == 'function') {
|
||||
var style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' :
|
||||
'blocklyToolboxGrab';
|
||||
toolbox.removeStyle(style);
|
||||
|
||||
+125
-93
@@ -32,6 +32,10 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
|
||||
goog.requireType('Blockly.IASTNodeLocationSvg');
|
||||
goog.requireType('Blockly.IBoundedElement');
|
||||
goog.requireType('Blockly.ICopyable');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a block's SVG representation.
|
||||
@@ -42,15 +46,19 @@ goog.require('Blockly.utils.Rect');
|
||||
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
|
||||
* create a new ID.
|
||||
* @extends {Blockly.Block}
|
||||
* @implements {Blockly.IASTNodeLocationSvg}
|
||||
* @implements {Blockly.IBoundedElement}
|
||||
* @implements {Blockly.ICopyable}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
||||
// Create core elements for the block.
|
||||
/**
|
||||
* @type {!SVGElement}
|
||||
* @type {!SVGGElement}
|
||||
* @private
|
||||
*/
|
||||
this.svgGroup_ = Blockly.utils.dom.createSvgElement('g', {}, null);
|
||||
this.svgGroup_ = /** @type {!SVGGElement} */ (
|
||||
Blockly.utils.dom.createSvgElement('g', {}, null));
|
||||
this.svgGroup_.translate_ = '';
|
||||
|
||||
/**
|
||||
@@ -69,6 +77,14 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
||||
|
||||
/** @type {boolean} */
|
||||
this.rendered = false;
|
||||
/**
|
||||
* Is this block currently rendering? Used to stop recursive render calls
|
||||
* from actually triggering a re-render.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.renderIsInProgress_ = false;
|
||||
|
||||
|
||||
/** @type {!Blockly.WorkspaceSvg} */
|
||||
this.workspace = workspace;
|
||||
@@ -114,13 +130,6 @@ Blockly.BlockSvg.prototype.height = 0;
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.width = 0;
|
||||
|
||||
/**
|
||||
* Original location of block being dragged.
|
||||
* @type {Blockly.utils.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.dragStartXY_ = null;
|
||||
|
||||
/**
|
||||
* Map from IDs for warnings text to PIDs of functions to apply them.
|
||||
* Used to be able to maintain multiple warnings.
|
||||
@@ -602,60 +611,51 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) {
|
||||
if (this.collapsed_ == collapsed) {
|
||||
return;
|
||||
}
|
||||
var renderList = [];
|
||||
// Show/hide the inputs.
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
renderList.push.apply(renderList, input.setVisible(!collapsed));
|
||||
}
|
||||
|
||||
var COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT';
|
||||
if (collapsed) {
|
||||
var icons = this.getIcons();
|
||||
for (var i = 0; i < icons.length; i++) {
|
||||
icons[i].setVisible(false);
|
||||
}
|
||||
var text = this.toString(Blockly.COLLAPSE_CHARS);
|
||||
this.appendDummyInput(COLLAPSED_INPUT_NAME).appendField(text).init();
|
||||
|
||||
// Add any warnings on enclosed blocks to this block.
|
||||
var descendants = this.getDescendants(true);
|
||||
var nextBlock = this.getNextBlock();
|
||||
if (nextBlock) {
|
||||
var index = descendants.indexOf(nextBlock);
|
||||
descendants.splice(index, descendants.length - index);
|
||||
}
|
||||
for (var i = 1, block; (block = descendants[i]); i++) {
|
||||
if (block.warning) {
|
||||
this.setWarningText(Blockly.Msg['COLLAPSED_WARNINGS_WARNING'],
|
||||
Blockly.BlockSvg.COLLAPSED_WARNING_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.removeInput(COLLAPSED_INPUT_NAME);
|
||||
// Clear any warnings inherited from enclosed blocks.
|
||||
if (this.warning) {
|
||||
this.warning.setText('', Blockly.BlockSvg.COLLAPSED_WARNING_ID);
|
||||
if (!Object.keys(this.warning.text_).length) {
|
||||
this.setWarningText(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Blockly.BlockSvg.superClass_.setCollapsed.call(this, collapsed);
|
||||
if (!collapsed) {
|
||||
this.updateCollapsed_();
|
||||
} else if (this.rendered) {
|
||||
this.render();
|
||||
// Don't bump neighbours. Users like to store collapsed functions together
|
||||
// and bumping makes them go out of alignment.
|
||||
}
|
||||
};
|
||||
|
||||
if (!renderList.length) {
|
||||
// No child blocks, just render this block.
|
||||
renderList[0] = this;
|
||||
}
|
||||
if (this.rendered) {
|
||||
for (var i = 0, block; (block = renderList[i]); i++) {
|
||||
block.render();
|
||||
/**
|
||||
* Makes sure that when the block is collapsed, it is rendered correctly
|
||||
* for that state.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.updateCollapsed_ = function() {
|
||||
var collapsed = this.isCollapsed();
|
||||
var collapsedInputName = Blockly.Block.COLLAPSED_INPUT_NAME;
|
||||
var collapsedFieldName = Blockly.Block.COLLAPSED_FIELD_NAME;
|
||||
|
||||
for (var i = 0, input; (input = this.inputList[i]); i++) {
|
||||
if (input.name != collapsedInputName) {
|
||||
input.setVisible(!collapsed);
|
||||
}
|
||||
// Don't bump neighbours.
|
||||
// Although bumping neighbours would make sense, users often collapse
|
||||
// all their functions and store them next to each other. Expanding and
|
||||
// bumping causes all their definitions to go out of alignment.
|
||||
}
|
||||
|
||||
if (!collapsed) {
|
||||
this.removeInput(collapsedInputName);
|
||||
return;
|
||||
}
|
||||
|
||||
var icons = this.getIcons();
|
||||
for (var i = 0, icon; (icon = icons[i]); i++) {
|
||||
icon.setVisible(false);
|
||||
}
|
||||
|
||||
var text = this.toString(Blockly.COLLAPSE_CHARS);
|
||||
var field = this.getField(collapsedFieldName);
|
||||
if (field) {
|
||||
field.setValue(text);
|
||||
return;
|
||||
}
|
||||
var input = this.getInput(collapsedInputName) ||
|
||||
this.appendDummyInput(collapsedInputName);
|
||||
input.appendField(new Blockly.FieldLabel(text), collapsedFieldName);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -925,7 +925,7 @@ Blockly.BlockSvg.prototype.setInsertionMarker = function(insertionMarker) {
|
||||
|
||||
/**
|
||||
* Return the root node of the SVG or null if none exists.
|
||||
* @return {!SVGElement} The root SVG node (probably a group).
|
||||
* @return {!SVGGElement} The root SVG node (probably a group).
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.getSvgRoot = function() {
|
||||
return this.svgGroup_;
|
||||
@@ -992,6 +992,26 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
||||
Blockly.utils.dom.stopTextWidthCache();
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a block for copying.
|
||||
* @return {!Blockly.ICopyable.CopyData} Copy metadata.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.toCopyData = function() {
|
||||
var xml = Blockly.Xml.blockToDom(this, true);
|
||||
// Copy only the selected block and internal blocks.
|
||||
Blockly.Xml.deleteNext(xml);
|
||||
// Encode start position in XML.
|
||||
var xy = this.getRelativeToSurfaceXY();
|
||||
xml.setAttribute('x', this.RTL ? -xy.x : xy.x);
|
||||
xml.setAttribute('y', xy.y);
|
||||
return {
|
||||
xml: xml,
|
||||
source: this.workspace,
|
||||
typeCounts: Blockly.utils.getBlockTypeCounts(this, true)
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the colour of a block.
|
||||
* @package
|
||||
@@ -1109,22 +1129,22 @@ Blockly.BlockSvg.prototype.setWarningText = function(text, opt_id) {
|
||||
text = null;
|
||||
}
|
||||
|
||||
// Bubble up to add a warning on top-most collapsed block.
|
||||
var parent = this.getSurroundParent();
|
||||
var collapsedParent = null;
|
||||
while (parent) {
|
||||
if (parent.isCollapsed()) {
|
||||
collapsedParent = parent;
|
||||
}
|
||||
parent = parent.getSurroundParent();
|
||||
}
|
||||
if (collapsedParent) {
|
||||
collapsedParent.setWarningText(Blockly.Msg['COLLAPSED_WARNINGS_WARNING'],
|
||||
Blockly.BlockSvg.COLLAPSED_WARNING_ID);
|
||||
}
|
||||
|
||||
var changedState = false;
|
||||
if (typeof text == 'string') {
|
||||
// Bubble up to add a warning on top-most collapsed block.
|
||||
var parent = this.getSurroundParent();
|
||||
var collapsedParent = null;
|
||||
while (parent) {
|
||||
if (parent.isCollapsed()) {
|
||||
collapsedParent = parent;
|
||||
}
|
||||
parent = parent.getSurroundParent();
|
||||
}
|
||||
if (collapsedParent) {
|
||||
collapsedParent.setWarningText(Blockly.Msg['COLLAPSED_WARNINGS_WARNING'],
|
||||
Blockly.BlockSvg.COLLAPSED_WARNING_ID);
|
||||
}
|
||||
|
||||
if (!this.warning) {
|
||||
this.warning = new Blockly.Warning(this);
|
||||
changedState = true;
|
||||
@@ -1369,17 +1389,20 @@ Blockly.BlockSvg.prototype.setInputsInline = function(newBoolean) {
|
||||
* Remove an input from this block.
|
||||
* @param {string} name The name of the input.
|
||||
* @param {boolean=} opt_quiet True to prevent error if input is not present.
|
||||
* @return {boolean} True if operation succeeds, false if input is not present and opt_quiet is true
|
||||
* @throws {Error} if the input is not present and
|
||||
* opt_quiet is not true.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.removeInput = function(name, opt_quiet) {
|
||||
Blockly.BlockSvg.superClass_.removeInput.call(this, name, opt_quiet);
|
||||
var removed = Blockly.BlockSvg.superClass_.removeInput.call(this, name, opt_quiet);
|
||||
|
||||
if (this.rendered) {
|
||||
this.render();
|
||||
// Removing an input will cause the block to change shape.
|
||||
this.bumpNeighbours();
|
||||
}
|
||||
|
||||
return removed;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1647,31 +1670,40 @@ Blockly.BlockSvg.prototype.getRootBlock = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the block.
|
||||
* Lays out and reflows a block based on its contents and settings.
|
||||
* @param {boolean=} opt_bubble If false, just render this block.
|
||||
* If true, also render block's parent, grandparent, etc. Defaults to true.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.render = function(opt_bubble) {
|
||||
Blockly.utils.dom.startTextWidthCache();
|
||||
this.rendered = true;
|
||||
(/** @type {!Blockly.WorkspaceSvg} */ (this.workspace))
|
||||
.getRenderer().render(this);
|
||||
// No matter how we rendered, connection locations should now be correct.
|
||||
this.updateConnectionLocations_();
|
||||
if (opt_bubble !== false) {
|
||||
// Render all blocks above this one (propagate a reflow).
|
||||
var parentBlock = this.getParent();
|
||||
if (parentBlock) {
|
||||
parentBlock.render(true);
|
||||
} else {
|
||||
// Top-most block. Fire an event to allow scrollbars to resize.
|
||||
this.workspace.resizeContents();
|
||||
}
|
||||
if (this.renderIsInProgress_) {
|
||||
return; // Don't allow recursive renders.
|
||||
}
|
||||
Blockly.utils.dom.stopTextWidthCache();
|
||||
this.renderIsInProgress_ = true;
|
||||
try {
|
||||
this.rendered = true;
|
||||
Blockly.utils.dom.startTextWidthCache();
|
||||
|
||||
this.updateMarkers_();
|
||||
if (this.isCollapsed()) {
|
||||
this.updateCollapsed_();
|
||||
}
|
||||
this.workspace.getRenderer().render(this);
|
||||
this.updateConnectionLocations_();
|
||||
|
||||
if (opt_bubble !== false) {
|
||||
var parentBlock = this.getParent();
|
||||
if (parentBlock) {
|
||||
parentBlock.render(true);
|
||||
} else {
|
||||
// Top-most block. Fire an event to allow scrollbars to resize.
|
||||
this.workspace.resizeContents();
|
||||
}
|
||||
}
|
||||
|
||||
Blockly.utils.dom.stopTextWidthCache();
|
||||
this.updateMarkers_();
|
||||
} finally {
|
||||
this.renderIsInProgress_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+18
-27
@@ -26,6 +26,7 @@ goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.colour');
|
||||
goog.require('Blockly.utils.Size');
|
||||
goog.require('Blockly.Variables');
|
||||
goog.require('Blockly.WidgetDiv');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
@@ -51,7 +52,7 @@ Blockly.mainWorkspace = null;
|
||||
|
||||
/**
|
||||
* Currently selected block.
|
||||
* @type {Blockly.Block}
|
||||
* @type {?Blockly.ICopyable}
|
||||
*/
|
||||
Blockly.selected = null;
|
||||
|
||||
@@ -107,13 +108,11 @@ Blockly.EventData;
|
||||
/**
|
||||
* Returns the dimensions of the specified SVG image.
|
||||
* @param {!SVGElement} svg SVG image.
|
||||
* @return {!Object} Contains width and height properties.
|
||||
* @return {!Blockly.utils.Size} Contains width and height properties.
|
||||
*/
|
||||
Blockly.svgSize = function(svg) {
|
||||
return {
|
||||
width: svg.cachedWidth_,
|
||||
height: svg.cachedHeight_
|
||||
};
|
||||
svg = /** @type {?} */ (svg);
|
||||
return new Blockly.utils.Size(svg.cachedWidth_, svg.cachedHeight_);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,7 +159,7 @@ Blockly.svgResize = function(workspace) {
|
||||
/**
|
||||
* Handle a key-down on SVG drawing surface. Does nothing if the main workspace
|
||||
* is not visible.
|
||||
* @param {!Event} e Key down event.
|
||||
* @param {!KeyboardEvent} e Key down event.
|
||||
* @package
|
||||
*/
|
||||
// TODO (https://github.com/google/blockly/issues/1998) handle cases where there
|
||||
@@ -190,7 +189,7 @@ Blockly.onKeyDown = function(e) {
|
||||
// Pressing esc closes the context menu.
|
||||
Blockly.hideChaff();
|
||||
Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_EXIT);
|
||||
} else if (Blockly.navigation.onKeyPress(e)) {
|
||||
} else if (!Blockly.Gesture.inProgress() && Blockly.navigation.onKeyPress(e)) {
|
||||
// If the keyboard or field handled the key press return.
|
||||
return;
|
||||
} else if (e.keyCode == Blockly.utils.KeyCodes.BACKSPACE ||
|
||||
@@ -249,6 +248,10 @@ Blockly.onKeyDown = function(e) {
|
||||
// 'z' for undo 'Z' is for redo.
|
||||
Blockly.hideChaff();
|
||||
mainWorkspace.undo(e.shiftKey);
|
||||
} else if (e.ctrlKey && e.keyCode == Blockly.utils.KeyCodes.Y) {
|
||||
// Ctrl-y is redo in Windows. Command-y is never valid on Macs.
|
||||
Blockly.hideChaff();
|
||||
mainWorkspace.undo(true);
|
||||
}
|
||||
}
|
||||
// Common code for delete and cut.
|
||||
@@ -264,32 +267,20 @@ Blockly.onKeyDown = function(e) {
|
||||
|
||||
/**
|
||||
* Copy a block or workspace comment onto the local clipboard.
|
||||
* @param {!Blockly.Block | !Blockly.WorkspaceComment} toCopy Block or
|
||||
* Workspace Comment to be copied.
|
||||
* @param {!Blockly.ICopyable} toCopy Block or Workspace Comment to be copied.
|
||||
* @private
|
||||
*/
|
||||
Blockly.copy_ = function(toCopy) {
|
||||
if (toCopy.isComment) {
|
||||
var xml = toCopy.toXmlWithXY();
|
||||
} else {
|
||||
var xml = Blockly.Xml.blockToDom(toCopy, true);
|
||||
// Copy only the selected block and internal blocks.
|
||||
Blockly.Xml.deleteNext(xml);
|
||||
// Encode start position in XML.
|
||||
var xy = toCopy.getRelativeToSurfaceXY();
|
||||
xml.setAttribute('x', toCopy.RTL ? -xy.x : xy.x);
|
||||
xml.setAttribute('y', xy.y);
|
||||
}
|
||||
Blockly.clipboardXml_ = xml;
|
||||
Blockly.clipboardSource_ = toCopy.workspace;
|
||||
Blockly.clipboardTypeCounts_ = toCopy.isComment ? null :
|
||||
Blockly.utils.getBlockTypeCounts(toCopy, true);
|
||||
var data = toCopy.toCopyData();
|
||||
Blockly.clipboardXml_ = data.xml;
|
||||
Blockly.clipboardSource_ = data.source;
|
||||
Blockly.clipboardTypeCounts_ = data.typeCounts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Duplicate this block and its children, or a workspace comment.
|
||||
* @param {!Blockly.Block | !Blockly.WorkspaceComment} toDuplicate Block or
|
||||
* Workspace Comment to be copied.
|
||||
* @param {!Blockly.ICopyable} toDuplicate Block or Workspace Comment to be
|
||||
* copied.
|
||||
* @package
|
||||
*/
|
||||
Blockly.duplicate = function(toDuplicate) {
|
||||
|
||||
+10
-8
@@ -21,6 +21,8 @@ goog.require('Blockly.utils.math');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
goog.require('Blockly.Workspace');
|
||||
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* Class for UI bubble.
|
||||
@@ -489,8 +491,8 @@ Blockly.Bubble.prototype.layoutBubble_ = function() {
|
||||
* workspace (what percentage of the bubble is visible).
|
||||
* @param {!{x: number, y: number}} relativeMin The position of the top-left
|
||||
* corner of the bubble relative to the anchor point.
|
||||
* @param {!Object} metrics The metrics of the workspace the bubble will
|
||||
* appear in.
|
||||
* @param {!Blockly.utils.Metrics} metrics The metrics of the workspace the
|
||||
* bubble will appear in.
|
||||
* @return {number} The percentage of the bubble that is visible.
|
||||
* @private
|
||||
*/
|
||||
@@ -535,10 +537,10 @@ Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, metrics) {
|
||||
* Calculate what the optimal horizontal position of the top-left corner of the
|
||||
* bubble is (relative to the anchor point) so that the most area of the
|
||||
* bubble is shown.
|
||||
* @param {!Object} metrics The metrics of the workspace the bubble will
|
||||
* appear in.
|
||||
* @param {!Blockly.utils.Metrics} metrics The metrics of the workspace the
|
||||
* bubble will appear in.
|
||||
* @return {number} The optimal horizontal position of the top-left corner
|
||||
* of the bubble.
|
||||
* of the bubble.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(metrics) {
|
||||
@@ -593,10 +595,10 @@ Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(metrics) {
|
||||
* Calculate what the optimal vertical position of the top-left corner of
|
||||
* the bubble is (relative to the anchor point) so that the most area of the
|
||||
* bubble is shown.
|
||||
* @param {!Object} metrics The metrics of the workspace the bubble will
|
||||
* appear in.
|
||||
* @param {!Blockly.utils.Metrics} metrics The metrics of the workspace the
|
||||
* bubble will appear in.
|
||||
* @return {number} The optimal vertical position of the top-left corner
|
||||
* of the bubble.
|
||||
* of the bubble.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Bubble.prototype.getOptimalRelativeTop_ = function(metrics) {
|
||||
|
||||
@@ -107,7 +107,7 @@ Blockly.BubbleDragger.prototype.startBubbleDrag = function() {
|
||||
this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(true);
|
||||
|
||||
var toolbox = this.workspace_.getToolbox();
|
||||
if (toolbox) {
|
||||
if (toolbox && typeof toolbox.addStyle == 'function') {
|
||||
var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' :
|
||||
'blocklyToolboxGrab';
|
||||
toolbox.addStyle(style);
|
||||
@@ -208,10 +208,11 @@ Blockly.BubbleDragger.prototype.endBubbleDrag = function(
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
if (this.workspace_.getToolbox()) {
|
||||
var toolbox = this.workspace_.getToolbox();
|
||||
if (toolbox && typeof toolbox.removeStyle == 'function') {
|
||||
var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' :
|
||||
'blocklyToolboxGrab';
|
||||
this.workspace_.getToolbox().removeStyle(style);
|
||||
toolbox.removeStyle(style);
|
||||
}
|
||||
Blockly.Events.setGroup(false);
|
||||
};
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ Blockly.utils.object.inherits(Blockly.Comment, Blockly.Icon);
|
||||
/**
|
||||
* Draw the comment icon.
|
||||
* @param {!Element} group The icon group.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Comment.prototype.drawIcon_ = function(group) {
|
||||
// Circle.
|
||||
|
||||
@@ -84,6 +84,13 @@ Blockly.Component = function() {
|
||||
* @private
|
||||
*/
|
||||
this.childIndex_ = {};
|
||||
|
||||
/**
|
||||
* Whether or not the component has been disposed.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.disposed_ = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -115,7 +122,12 @@ Blockly.Component.Error = {
|
||||
* Error when an attempt is made to add a child component at an out-of-bounds
|
||||
* index. We don't support sparse child arrays.
|
||||
*/
|
||||
CHILD_INDEX_OUT_OF_BOUNDS: 'Child component index out of bounds'
|
||||
CHILD_INDEX_OUT_OF_BOUNDS: 'Child component index out of bounds',
|
||||
|
||||
/**
|
||||
* Error when calling an abstract method that should be overriden.
|
||||
*/
|
||||
ABSTRACT_METHOD: 'Unimplemented abstract method'
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -195,12 +207,11 @@ Blockly.Component.prototype.isInDocument = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the initial DOM representation for the component. The default
|
||||
* implementation is to set this.element_ = div.
|
||||
* Creates the initial DOM representation for the component.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Component.prototype.createDom = function() {
|
||||
this.element_ = document.createElement('div');
|
||||
throw Error(Blockly.Component.Error.ABSTRACT_METHOD);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -223,19 +234,6 @@ Blockly.Component.prototype.render = function(opt_parentElement) {
|
||||
this.render_(opt_parentElement);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the component before another element. The other element should be in
|
||||
* the document already.
|
||||
*
|
||||
* Throws an Error if the component is already rendered.
|
||||
*
|
||||
* @param {Node} sibling Node to render the component before.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Component.prototype.renderBefore = function(sibling) {
|
||||
this.render_(/** @type {Element} */ (sibling.parentNode), sibling);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the component. If a parent element is supplied, the component's
|
||||
* element will be appended to it. If there is no optional parent element and
|
||||
@@ -476,7 +474,8 @@ Blockly.Component.prototype.addChildAt = function(child, index, opt_render) {
|
||||
child.element_.parentNode &&
|
||||
// Under some circumstances, IE8 implicitly creates a Document Fragment
|
||||
// for detached nodes, so ensure the parent is an Element as it should be.
|
||||
child.element_.parentNode.nodeType == Blockly.utils.dom.Node.ELEMENT_NODE) {
|
||||
child.element_.parentNode.nodeType ==
|
||||
Blockly.utils.dom.NodeType.ELEMENT_NODE) {
|
||||
// We don't touch the DOM, but if the parent is in the document, and the
|
||||
// child element is in the document but not marked as such, then we call
|
||||
// enterDocument on the child.
|
||||
@@ -496,21 +495,6 @@ Blockly.Component.prototype.getContentElement = function() {
|
||||
return this.element_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set is right-to-left. This function should be used if the component needs
|
||||
* to know the rendering direction during DOM creation (i.e. before
|
||||
* {@link #enterDocument} is called and is right-to-left is set).
|
||||
* @param {boolean} rightToLeft Whether the component is rendered
|
||||
* right-to-left.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Component.prototype.setRightToLeft = function(rightToLeft) {
|
||||
if (this.inDocument_) {
|
||||
throw Error(Blockly.Component.Error.ALREADY_RENDERED);
|
||||
}
|
||||
this.rightToLeft_ = rightToLeft;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the component has children.
|
||||
* @return {boolean} True if the component has children.
|
||||
@@ -568,14 +552,3 @@ Blockly.Component.prototype.forEachChild = function(f, opt_obj) {
|
||||
f.call(/** @type {?} */ (opt_obj), this.children_[i], i);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the 0-based index of the given child component, or -1 if no such
|
||||
* child is found.
|
||||
* @param {?Blockly.Component} child The child component.
|
||||
* @return {number} 0-based index of the child component; -1 if not found.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Component.prototype.indexOfChild = function(child) {
|
||||
return this.children_.indexOf(child);
|
||||
};
|
||||
|
||||
@@ -1,543 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Blockly menu similar to Closure's goog.ui.Menu
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Menu');
|
||||
|
||||
goog.require('Blockly.Component');
|
||||
goog.require('Blockly.utils.aria');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
|
||||
/**
|
||||
* A basic menu class.
|
||||
* @constructor
|
||||
* @extends {Blockly.Component}
|
||||
*/
|
||||
Blockly.Menu = function() {
|
||||
Blockly.Component.call(this);
|
||||
|
||||
/**
|
||||
* Coordinates of the mousedown event that caused this menu to open. Used to
|
||||
* prevent the consequent mouseup event due to a simple click from activating
|
||||
* a menu item immediately.
|
||||
* @type {?Blockly.utils.Coordinate}
|
||||
* @package
|
||||
*/
|
||||
this.openingCoords = null;
|
||||
|
||||
/**
|
||||
* This is the element that we will listen to the real focus events on.
|
||||
* A value of -1 means no menuitem is highlighted.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.highlightedIndex_ = -1;
|
||||
|
||||
/**
|
||||
* Mouse over event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.mouseOverHandler_ = null;
|
||||
|
||||
/**
|
||||
* Click event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.clickHandler_ = null;
|
||||
|
||||
/**
|
||||
* Mouse enter event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.mouseEnterHandler_ = null;
|
||||
|
||||
/**
|
||||
* Mouse leave event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.mouseLeaveHandler_ = null;
|
||||
|
||||
/**
|
||||
* Key down event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.onKeyDownWrapper_ = null;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.Menu, Blockly.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Creates the menu DOM.
|
||||
* @override
|
||||
*/
|
||||
Blockly.Menu.prototype.createDom = function() {
|
||||
var element = document.createElement('div');
|
||||
element.id = this.getId();
|
||||
this.setElementInternal(element);
|
||||
|
||||
// Set class
|
||||
element.className = 'goog-menu goog-menu-vertical blocklyNonSelectable';
|
||||
element.tabIndex = 0;
|
||||
|
||||
// Initialize ARIA role.
|
||||
Blockly.utils.aria.setRole(element,
|
||||
this.roleName_ || Blockly.utils.aria.Role.MENU);
|
||||
};
|
||||
|
||||
/**
|
||||
* Focus the menu element.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.focus = function() {
|
||||
var el = this.getElement();
|
||||
if (el) {
|
||||
el.focus({preventScroll:true});
|
||||
Blockly.utils.dom.addClass(el, 'focused');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Blur the menu element.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.blur = function() {
|
||||
var el = this.getElement();
|
||||
if (el) {
|
||||
el.blur();
|
||||
Blockly.utils.dom.removeClass(el, 'focused');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the menu accessibility role.
|
||||
* @param {!Blockly.utils.aria.Role} roleName role name.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.setRole = function(roleName) {
|
||||
this.roleName_ = roleName;
|
||||
};
|
||||
|
||||
/** @override */
|
||||
Blockly.Menu.prototype.enterDocument = function() {
|
||||
Blockly.Menu.superClass_.enterDocument.call(this);
|
||||
|
||||
this.forEachChild(function(child) {
|
||||
if (child.isInDocument()) {
|
||||
this.registerChildId_(child);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.attachEvents_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleans up the container before its DOM is removed from the document, and
|
||||
* removes event handlers. Overrides {@link Blockly.Component#exitDocument}.
|
||||
* @override
|
||||
*/
|
||||
Blockly.Menu.prototype.exitDocument = function() {
|
||||
// {@link #setHighlightedIndex} has to be called before
|
||||
// {@link Blockly.Component#exitDocument}, otherwise it has no effect.
|
||||
this.setHighlightedIndex(-1);
|
||||
|
||||
Blockly.Menu.superClass_.exitDocument.call(this);
|
||||
};
|
||||
|
||||
/** @override */
|
||||
Blockly.Menu.prototype.disposeInternal = function() {
|
||||
Blockly.Menu.superClass_.disposeInternal.call(this);
|
||||
|
||||
this.detachEvents_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the event listeners to the menu.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.attachEvents_ = function() {
|
||||
var el = /** @type {!EventTarget} */ (this.getElement());
|
||||
|
||||
this.mouseOverHandler_ = Blockly.bindEventWithChecks_(el,
|
||||
'mouseover', this, this.handleMouseOver_, true);
|
||||
this.clickHandler_ = Blockly.bindEventWithChecks_(el,
|
||||
'click', this, this.handleClick_, true);
|
||||
this.mouseEnterHandler_ = Blockly.bindEventWithChecks_(el,
|
||||
'mouseenter', this, this.handleMouseEnter_, true);
|
||||
this.mouseLeaveHandler_ = Blockly.bindEventWithChecks_(el,
|
||||
'mouseleave', this, this.handleMouseLeave_, true);
|
||||
this.onKeyDownWrapper_ = Blockly.bindEventWithChecks_(el,
|
||||
'keydown', this, this.handleKeyEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the event listeners from the menu.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.detachEvents_ = function() {
|
||||
if (this.mouseOverHandler_) {
|
||||
Blockly.unbindEvent_(this.mouseOverHandler_);
|
||||
this.mouseOverHandler_ = null;
|
||||
}
|
||||
if (this.clickHandler_) {
|
||||
Blockly.unbindEvent_(this.clickHandler_);
|
||||
this.clickHandler_ = null;
|
||||
}
|
||||
if (this.mouseEnterHandler_) {
|
||||
Blockly.unbindEvent_(this.mouseEnterHandler_);
|
||||
this.mouseEnterHandler_ = null;
|
||||
}
|
||||
if (this.mouseLeaveHandler_) {
|
||||
Blockly.unbindEvent_(this.mouseLeaveHandler_);
|
||||
this.mouseLeaveHandler_ = null;
|
||||
}
|
||||
if (this.onKeyDownWrapper_) {
|
||||
Blockly.unbindEvent_(this.onKeyDownWrapper_);
|
||||
this.onKeyDownWrapper_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Child component management.
|
||||
|
||||
/**
|
||||
* Map of DOM IDs to child menuitems. Each key is the DOM ID of a child
|
||||
* menuitems's root element; each value is a reference to the child menu
|
||||
* item itself.
|
||||
* @type {?Object}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.childElementIdMap_ = null;
|
||||
|
||||
/**
|
||||
* Creates a DOM ID for the child menuitem and registers it to an internal
|
||||
* hash table to be able to find it fast by id.
|
||||
* @param {Blockly.Component} child The child menuitem. Its root element has
|
||||
* to be created yet.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.registerChildId_ = function(child) {
|
||||
// Map the DOM ID of the menuitem's root element to the menuitem itself.
|
||||
var childElem = child.getElement();
|
||||
|
||||
// If the menuitem's root element doesn't have a DOM ID assign one.
|
||||
var id = childElem.id || (childElem.id = child.getId());
|
||||
|
||||
// Lazily create the child element ID map on first use.
|
||||
if (!this.childElementIdMap_) {
|
||||
this.childElementIdMap_ = {};
|
||||
}
|
||||
this.childElementIdMap_[id] = child;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the child menuitem that owns the given DOM node, or null if no such
|
||||
* menuitem is found.
|
||||
* @param {Node} node DOM node whose owner is to be returned.
|
||||
* @return {?Blockly.MenuItem} menuitem for which the DOM node belongs to.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.getMenuItem = function(node) {
|
||||
// Ensure that this menu actually has child menuitems before
|
||||
// looking up the menuitem.
|
||||
if (this.childElementIdMap_) {
|
||||
var elem = this.getElement();
|
||||
while (node && node !== elem) {
|
||||
var id = node.id;
|
||||
if (id in this.childElementIdMap_) {
|
||||
return this.childElementIdMap_[id];
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Highlight management.
|
||||
|
||||
/**
|
||||
* Unhighlight the current highlighted item.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.unhighlightCurrent = function() {
|
||||
var highlighted = this.getHighlighted();
|
||||
if (highlighted) {
|
||||
highlighted.setHighlighted(false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the currently highlighted item.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.clearHighlighted = function() {
|
||||
this.unhighlightCurrent();
|
||||
this.setHighlightedIndex(-1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the currently highlighted item (if any).
|
||||
* @return {?Blockly.Component} Highlighted item (null if none).
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.getHighlighted = function() {
|
||||
return this.getChildAt(this.highlightedIndex_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the item at the given 0-based index (if any). If another item
|
||||
* was previously highlighted, it is un-highlighted.
|
||||
* @param {number} index Index of item to highlight (-1 removes the current
|
||||
* highlight).
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.setHighlightedIndex = function(index) {
|
||||
var child = this.getChildAt(index);
|
||||
if (child) {
|
||||
child.setHighlighted(true);
|
||||
this.highlightedIndex_ = index;
|
||||
} else if (this.highlightedIndex_ > -1) {
|
||||
this.getHighlighted().setHighlighted(false);
|
||||
this.highlightedIndex_ = -1;
|
||||
}
|
||||
|
||||
// Bring the highlighted item into view. This has no effect if the menu is not
|
||||
// scrollable.
|
||||
if (child) {
|
||||
Blockly.utils.style.scrollIntoContainerView(
|
||||
/** @type {!Element} */ (child.getElement()),
|
||||
/** @type {!Element} */ (this.getElement()));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the given item if it exists and is a child of the container;
|
||||
* otherwise un-highlights the currently highlighted item.
|
||||
* @param {Blockly.MenuItem} item Item to highlight.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.setHighlighted = function(item) {
|
||||
this.setHighlightedIndex(this.indexOfChild(item));
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the next highlightable item (or the first if nothing is currently
|
||||
* highlighted).
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightNext = function() {
|
||||
this.unhighlightCurrent();
|
||||
this.highlightHelper(function(index, max) {
|
||||
return (index + 1) % max;
|
||||
}, this.highlightedIndex_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the previous highlightable item (or the last if nothing is
|
||||
* currently highlighted).
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightPrevious = function() {
|
||||
this.unhighlightCurrent();
|
||||
this.highlightHelper(function(index, max) {
|
||||
index--;
|
||||
return index < 0 ? max - 1 : index;
|
||||
}, this.highlightedIndex_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function that manages the details of moving the highlight among
|
||||
* child menuitems in response to keyboard events.
|
||||
* @param {function(this: Blockly.Component, number, number) : number} fn
|
||||
* Function that accepts the current and maximum indices, and returns the
|
||||
* next index to check.
|
||||
* @param {number} startIndex Start index.
|
||||
* @return {boolean} Whether the highlight has changed.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightHelper = function(fn, startIndex) {
|
||||
// If the start index is -1 (meaning there's nothing currently highlighted),
|
||||
// try starting from the currently open item, if any.
|
||||
var curIndex =
|
||||
startIndex < 0 ? -1 : startIndex;
|
||||
var numItems = this.getChildCount();
|
||||
|
||||
curIndex = fn.call(this, curIndex, numItems);
|
||||
var visited = 0;
|
||||
while (visited <= numItems) {
|
||||
var menuItem = /** @type {Blockly.MenuItem} */ (this.getChildAt(curIndex));
|
||||
if (menuItem && this.canHighlightItem(menuItem)) {
|
||||
this.setHighlightedIndex(curIndex);
|
||||
return true;
|
||||
}
|
||||
visited++;
|
||||
curIndex = fn.call(this, curIndex, numItems);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the given item can be highlighted.
|
||||
* @param {Blockly.MenuItem} item The item to check.
|
||||
* @return {boolean} Whether the item can be highlighted.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.canHighlightItem = function(item) {
|
||||
return item.isEnabled();
|
||||
};
|
||||
|
||||
// Mouse events.
|
||||
|
||||
/**
|
||||
* Handles mouseover events. Highlight menuitems as the user
|
||||
* hovers over them.
|
||||
* @param {Event} e Mouse event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleMouseOver_ = function(e) {
|
||||
var menuItem = this.getMenuItem(/** @type {Node} */ (e.target));
|
||||
|
||||
if (menuItem) {
|
||||
if (menuItem.isEnabled()) {
|
||||
var currentHighlighted = this.getHighlighted();
|
||||
if (currentHighlighted === menuItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.unhighlightCurrent();
|
||||
this.setHighlighted(menuItem);
|
||||
} else {
|
||||
this.unhighlightCurrent();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles click events. Pass the event onto the child
|
||||
* menuitem to handle.
|
||||
* @param {Event} e Click to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleClick_ = function(e) {
|
||||
var oldCoords = this.openingCoords;
|
||||
// Clear out the saved opening coords immediately so they're not used twice.
|
||||
this.openingCoords = null;
|
||||
if (oldCoords && typeof e.clientX === 'number') {
|
||||
var newCoords = new Blockly.utils.Coordinate(e.clientX, e.clientY);
|
||||
if (Blockly.utils.Coordinate.distance(oldCoords, newCoords) < 1) {
|
||||
// This menu was opened by a mousedown and we're handling the consequent
|
||||
// click event. The coords haven't changed, meaning this was the same
|
||||
// opening event. Don't do the usual behavior because the menu just popped
|
||||
// up under the mouse and the user didn't mean to activate this item.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var menuItem = this.getMenuItem(/** @type {Node} */ (e.target));
|
||||
|
||||
if (menuItem && menuItem.handleClick(e)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles mouse enter events. Focus the element.
|
||||
* @param {Event} _e Mouse event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleMouseEnter_ = function(_e) {
|
||||
this.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles mouse leave events. Blur and clear highlight.
|
||||
* @param {Event} _e Mouse event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleMouseLeave_ = function(_e) {
|
||||
if (this.getElement()) {
|
||||
this.blur();
|
||||
this.clearHighlighted();
|
||||
}
|
||||
};
|
||||
|
||||
// Keyboard events.
|
||||
|
||||
/**
|
||||
* Attempts to handle a keyboard event, if the menuitem is enabled, by calling
|
||||
* {@link handleKeyEventInternal}. Considered protected; should only be used
|
||||
* within this package and by subclasses.
|
||||
* @param {Event} e Key event to handle.
|
||||
* @return {boolean} Whether the key event was handled.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.handleKeyEvent = function(e) {
|
||||
if (this.getChildCount() != 0 &&
|
||||
this.handleKeyEventInternal(e)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to handle a keyboard event; returns true if the event was handled,
|
||||
* false otherwise. If the container is enabled, and a child is highlighted,
|
||||
* calls the child menuitem's `handleKeyEvent` method to give the menuitem
|
||||
* a chance to handle the event first.
|
||||
* @param {Event} e Key event to handle.
|
||||
* @return {boolean} Whether the event was handled by the container (or one of
|
||||
* its children).
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Menu.prototype.handleKeyEventInternal = function(e) {
|
||||
// Give the highlighted menuitem the chance to handle the key event.
|
||||
var highlighted = this.getHighlighted();
|
||||
if (highlighted && typeof highlighted.handleKeyEvent == 'function' &&
|
||||
highlighted.handleKeyEvent(e)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not handle the key event if any modifier key is pressed.
|
||||
if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Either nothing is highlighted, or the highlighted menuitem didn't handle
|
||||
// the key event, so attempt to handle it here.
|
||||
switch (e.keyCode) {
|
||||
case Blockly.utils.KeyCodes.ENTER:
|
||||
if (highlighted) {
|
||||
highlighted.performActionInternal(e);
|
||||
}
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.UP:
|
||||
this.highlightPrevious();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.DOWN:
|
||||
this.highlightNext();
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -1,282 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Blockly menu item similar to Closure's goog.ui.MenuItem
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.MenuItem');
|
||||
|
||||
goog.require('Blockly.Component');
|
||||
goog.require('Blockly.utils.aria');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
|
||||
/**
|
||||
* Class representing an item in a menu.
|
||||
*
|
||||
* @param {string} content Text caption to display as the content of
|
||||
* the item.
|
||||
* @param {string=} opt_value Data/model associated with the menu item.
|
||||
* @constructor
|
||||
* @extends {Blockly.Component}
|
||||
*/
|
||||
Blockly.MenuItem = function(content, opt_value) {
|
||||
Blockly.Component.call(this);
|
||||
|
||||
this.setContentInternal(content);
|
||||
this.setValue(opt_value);
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.enabled_ = true;
|
||||
|
||||
/**
|
||||
* @type {Blockly.MenuItem}
|
||||
* @private
|
||||
*/
|
||||
this.previousSibling_;
|
||||
|
||||
/**
|
||||
* @type {Blockly.MenuItem}
|
||||
* @private
|
||||
*/
|
||||
this.nextSibling_;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.MenuItem, Blockly.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Creates the menuitem's DOM.
|
||||
* @override
|
||||
*/
|
||||
Blockly.MenuItem.prototype.createDom = function() {
|
||||
var element = document.createElement('div');
|
||||
element.id = this.getId();
|
||||
this.setElementInternal(element);
|
||||
|
||||
// Set class and style
|
||||
element.className = 'goog-menuitem goog-option ' +
|
||||
(!this.enabled_ ? 'goog-menuitem-disabled ' : '') +
|
||||
(this.checked_ ? 'goog-option-selected ' : '') +
|
||||
(this.rightToLeft_ ? 'goog-menuitem-rtl ' : '');
|
||||
|
||||
var content = this.getContentWrapperDom();
|
||||
element.appendChild(content);
|
||||
|
||||
// Add a checkbox for checkable menu items.
|
||||
var checkboxDom = this.getCheckboxDom();
|
||||
if (checkboxDom) {
|
||||
content.appendChild(checkboxDom);
|
||||
}
|
||||
|
||||
content.appendChild(this.getContentDom());
|
||||
|
||||
// Initialize ARIA role and state.
|
||||
Blockly.utils.aria.setRole(element, this.roleName_ || (this.checkable_ ?
|
||||
Blockly.utils.aria.Role.MENUITEMCHECKBOX :
|
||||
Blockly.utils.aria.Role.MENUITEM));
|
||||
Blockly.utils.aria.setState(element, Blockly.utils.aria.State.SELECTED,
|
||||
(this.checkable_ && this.checked_) || false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Element} The HTML element for the checkbox.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getCheckboxDom = function() {
|
||||
if (!this.checkable_) {
|
||||
return null;
|
||||
}
|
||||
var menuItemCheckbox = document.createElement('div');
|
||||
menuItemCheckbox.className = 'goog-menuitem-checkbox';
|
||||
return menuItemCheckbox;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!Element} The HTML for the content.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getContentDom = function() {
|
||||
var content = this.content_;
|
||||
if (typeof content === 'string') {
|
||||
content = document.createTextNode(content);
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {!Element} The HTML for the content wrapper.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getContentWrapperDom = function() {
|
||||
var contentWrapper = document.createElement('div');
|
||||
contentWrapper.className = 'goog-menuitem-content';
|
||||
return contentWrapper;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the content associated with the menu item.
|
||||
* @param {string} content Text caption to set as the
|
||||
* menuitem's contents.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setContentInternal = function(content) {
|
||||
this.content_ = content;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the value associated with the menu item.
|
||||
* @param {*} value Value to be associated with the menu item.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setValue = function(value) {
|
||||
this.value_ = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the value associated with the menu item.
|
||||
* @return {*} value Value associated with the menu item.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getValue = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the menu accessibility role.
|
||||
* @param {!Blockly.utils.aria.Role} roleName Role name.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setRole = function(roleName) {
|
||||
this.roleName_ = roleName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the menu item to be checkable or not. Set to true for menu items
|
||||
* that represent checkable options.
|
||||
* @param {boolean} checkable Whether the menu item is checkable.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setCheckable = function(checkable) {
|
||||
this.checkable_ = checkable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks or unchecks the component.
|
||||
* @param {boolean} checked Whether to check or uncheck the component.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setChecked = function(checked) {
|
||||
if (!this.checkable_) {
|
||||
return;
|
||||
}
|
||||
this.checked_ = checked;
|
||||
|
||||
var el = this.getElement();
|
||||
if (el && this.isEnabled()) {
|
||||
if (checked) {
|
||||
Blockly.utils.dom.addClass(el, 'goog-option-selected');
|
||||
Blockly.utils.aria.setState(el,
|
||||
Blockly.utils.aria.State.SELECTED, true);
|
||||
} else {
|
||||
Blockly.utils.dom.removeClass(el, 'goog-option-selected');
|
||||
Blockly.utils.aria.setState(el,
|
||||
Blockly.utils.aria.State.SELECTED, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights or unhighlights the component.
|
||||
* @param {boolean} highlight Whether to highlight or unhighlight the component.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setHighlighted = function(highlight) {
|
||||
this.highlight_ = highlight;
|
||||
|
||||
var el = this.getElement();
|
||||
if (el && this.isEnabled()) {
|
||||
if (highlight) {
|
||||
Blockly.utils.dom.addClass(el, 'goog-menuitem-highlight');
|
||||
} else {
|
||||
Blockly.utils.dom.removeClass(el, 'goog-menuitem-highlight');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the menu item is enabled, false otherwise.
|
||||
* @return {boolean} Whether the menu item is enabled.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.isEnabled = function() {
|
||||
return this.enabled_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables or disables the menu item.
|
||||
* @param {boolean} enabled Whether to enable or disable the menu item.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setEnabled = function(enabled) {
|
||||
this.enabled_ = enabled;
|
||||
|
||||
var el = this.getElement();
|
||||
if (el) {
|
||||
if (!this.enabled_) {
|
||||
Blockly.utils.dom.addClass(el, 'goog-menuitem-disabled');
|
||||
} else {
|
||||
Blockly.utils.dom.removeClass(el, 'goog-menuitem-disabled');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles click events. If the component is enabled, trigger
|
||||
* the action associated with this menu item.
|
||||
* @param {Event} _e Mouse event to handle.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.handleClick = function(_e) {
|
||||
if (this.isEnabled()) {
|
||||
this.setHighlighted(true);
|
||||
this.performActionInternal();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs the appropriate action when the menu item is activated
|
||||
* by the user.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.MenuItem.prototype.performActionInternal = function() {
|
||||
if (this.checkable_) {
|
||||
this.setChecked(!this.checked_);
|
||||
}
|
||||
if (this.actionHandler_) {
|
||||
this.actionHandler_.call(/** @type {?} */ (this.actionHandlerObj_), this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the handler that's triggered when the menu item is activated
|
||||
* by the user. If `opt_obj` is provided, it will be used as the
|
||||
* 'this' object in the function when called.
|
||||
* @param {function(this:T,!Blockly.MenuItem):?} fn The handler.
|
||||
* @param {T=} opt_obj Used as the 'this' object in f when called.
|
||||
* @template T
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.onAction = function(fn, opt_obj) {
|
||||
this.actionHandler_ = fn;
|
||||
this.actionHandlerObj_ = opt_obj;
|
||||
};
|
||||
@@ -91,13 +91,6 @@ Blockly.tree.BaseNode = function(content, config) {
|
||||
*/
|
||||
this.expanded_ = false;
|
||||
|
||||
/**
|
||||
* Whether to allow user to collapse this node.
|
||||
* @type {boolean}
|
||||
* @protected
|
||||
*/
|
||||
this.isUserCollapsible_ = true;
|
||||
|
||||
/**
|
||||
* Nesting depth of this node; cached result of getDepth.
|
||||
* -1 if value has not been cached.
|
||||
@@ -174,8 +167,7 @@ Blockly.tree.BaseNode.prototype.initAccessibility = function() {
|
||||
|
||||
var ce = this.getChildrenElement();
|
||||
if (ce) {
|
||||
Blockly.utils.aria.setRole(ce,
|
||||
Blockly.utils.aria.Role.GROUP);
|
||||
Blockly.utils.aria.setRole(ce, Blockly.utils.aria.Role.GROUP);
|
||||
|
||||
// In case the children will be created lazily.
|
||||
if (ce.hasChildNodes()) {
|
||||
@@ -201,7 +193,7 @@ Blockly.tree.BaseNode.prototype.initAccessibility = function() {
|
||||
Blockly.tree.BaseNode.prototype.createDom = function() {
|
||||
var element = document.createElement('div');
|
||||
element.appendChild(this.toDom());
|
||||
this.setElementInternal(/** @type {!Element} */ (element));
|
||||
this.setElementInternal(/** @type {!HTMLElement} */ (element));
|
||||
};
|
||||
|
||||
|
||||
@@ -340,25 +332,24 @@ Blockly.tree.BaseNode.prototype.setDepth_ = function(depth) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the node is a descendant of this node
|
||||
* @param {Blockly.tree.BaseNode} node The node to check.
|
||||
* Returns true if the node is a descendant of this node.
|
||||
* @param {Blockly.Component} node The node to check.
|
||||
* @return {boolean} True if the node is a descendant of this node, false
|
||||
* otherwise.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.contains = function(node) {
|
||||
var current = node;
|
||||
while (current) {
|
||||
if (current == this) {
|
||||
while (node) {
|
||||
if (node == this) {
|
||||
return true;
|
||||
}
|
||||
current = current.getParent();
|
||||
node = node.getParent();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is re-defined here to indicate to the closure compiler the correct
|
||||
* This is re-defined here to indicate to the Closure Compiler the correct
|
||||
* child return type.
|
||||
* @param {number} index 0-based index.
|
||||
* @return {Blockly.tree.BaseNode} The child at the given index; null if none.
|
||||
@@ -377,6 +368,16 @@ Blockly.tree.BaseNode.prototype.getChildren = function() {
|
||||
return children;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the node's parent, if any.
|
||||
* @return {?Blockly.tree.BaseNode} The parent node.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.getParent = function() {
|
||||
return /** @type {Blockly.tree.BaseNode} */ (
|
||||
Blockly.tree.BaseNode.superClass_.getParent.call(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Blockly.tree.BaseNode} The previous sibling of this node.
|
||||
* @protected
|
||||
@@ -551,6 +552,7 @@ Blockly.tree.BaseNode.prototype.toDom = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates correct padding for each row. Nested categories are indented more.
|
||||
* @return {number} The pixel indent of the row.
|
||||
* @private
|
||||
*/
|
||||
@@ -559,6 +561,7 @@ Blockly.tree.BaseNode.prototype.getPixelIndent_ = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates row with icon and label dom.
|
||||
* @return {!Element} The HTML element for the row.
|
||||
* @protected
|
||||
*/
|
||||
@@ -575,6 +578,8 @@ Blockly.tree.BaseNode.prototype.getRowDom = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the selected class name to the default row class name if node is
|
||||
* selected.
|
||||
* @return {string} The class name for the row.
|
||||
* @protected
|
||||
*/
|
||||
@@ -613,10 +618,11 @@ Blockly.tree.BaseNode.prototype.getIconDom = function() {
|
||||
* @protected
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.getCalculatedIconClass = function() {
|
||||
throw Error('unimplemented abstract method');
|
||||
throw Error(Blockly.Component.Error.ABSTRACT_METHOD);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a string containing the x and y position of the node's background.
|
||||
* @return {string} The background position style value.
|
||||
* @protected
|
||||
*/
|
||||
@@ -626,7 +632,7 @@ Blockly.tree.BaseNode.prototype.getBackgroundPosition = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Element} The element for the tree node.
|
||||
* @return {HTMLElement} The element for the tree node.
|
||||
* @override
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.getElement = function() {
|
||||
@@ -635,7 +641,7 @@ Blockly.tree.BaseNode.prototype.getElement = function() {
|
||||
el = document.getElementById(this.getId());
|
||||
this.setElementInternal(el);
|
||||
}
|
||||
return el;
|
||||
return /** @type {!HTMLElement} */ (el);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -707,26 +713,6 @@ Blockly.tree.BaseNode.prototype.updateIcon_ = function() {
|
||||
this.getIconElement().className = this.getCalculatedIconClass();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles mouse down event.
|
||||
* @param {!Event} e The browser event.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.onMouseDown = function(e) {
|
||||
var el = e.target;
|
||||
// expand icon
|
||||
var type = el.getAttribute('type');
|
||||
if (type == 'expand' && this.hasChildren()) {
|
||||
if (this.isUserCollapsible_) {
|
||||
this.toggle();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.select();
|
||||
this.updateRow();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a click event.
|
||||
* @param {!Event} e The browser event.
|
||||
@@ -746,16 +732,10 @@ Blockly.tree.BaseNode.prototype.onKeyDown = function(e) {
|
||||
var handled = true;
|
||||
switch (e.keyCode) {
|
||||
case Blockly.utils.KeyCodes.RIGHT:
|
||||
if (e.altKey) {
|
||||
break;
|
||||
}
|
||||
handled = this.selectChild();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.LEFT:
|
||||
if (e.altKey) {
|
||||
break;
|
||||
}
|
||||
handled = this.selectParent();
|
||||
break;
|
||||
|
||||
@@ -767,6 +747,12 @@ Blockly.tree.BaseNode.prototype.onKeyDown = function(e) {
|
||||
handled = this.selectPrevious();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.ENTER:
|
||||
case Blockly.utils.KeyCodes.SPACE:
|
||||
this.toggle();
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
handled = false;
|
||||
}
|
||||
@@ -811,7 +797,7 @@ Blockly.tree.BaseNode.prototype.selectPrevious = function() {
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.BaseNode.prototype.selectParent = function() {
|
||||
if (this.hasChildren() && this.expanded_ && this.isUserCollapsible_) {
|
||||
if (this.hasChildren() && this.expanded_) {
|
||||
this.setExpanded(false);
|
||||
} else {
|
||||
var parent = this.getParent();
|
||||
@@ -862,18 +848,17 @@ Blockly.tree.BaseNode.prototype.getLastShownDescendant = function() {
|
||||
Blockly.tree.BaseNode.prototype.getNextShownNode = function() {
|
||||
if (this.hasChildren() && this.expanded_) {
|
||||
return this.getChildAt(0);
|
||||
} else {
|
||||
var parent = this;
|
||||
var next;
|
||||
while (parent != this.getTree()) {
|
||||
next = parent.getNextSibling();
|
||||
if (next != null) {
|
||||
return next;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var parent = this;
|
||||
var next;
|
||||
while (parent != this.getTree()) {
|
||||
next = parent.getNextSibling();
|
||||
if (next != null) {
|
||||
return next;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,20 +33,6 @@ goog.require('Blockly.utils.style');
|
||||
Blockly.tree.TreeControl = function(toolbox, config) {
|
||||
this.toolbox_ = toolbox;
|
||||
|
||||
/**
|
||||
* Focus event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.onFocusWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Blur event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.onBlurWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Click event data.
|
||||
* @type {?Blockly.EventData}
|
||||
@@ -66,13 +52,27 @@ Blockly.tree.TreeControl = function(toolbox, config) {
|
||||
// The root is open and selected by default.
|
||||
this.expanded_ = true;
|
||||
this.selected_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Currently selected item.
|
||||
* @type {Blockly.tree.BaseNode}
|
||||
* @private
|
||||
*/
|
||||
this.selectedItem_ = this;
|
||||
|
||||
/**
|
||||
* A handler that's triggered before a node is selected.
|
||||
* @type {?function(Blockly.tree.BaseNode):boolean}
|
||||
* @private
|
||||
*/
|
||||
this.onBeforeSelected_ = null;
|
||||
|
||||
/**
|
||||
* A handler that's triggered before a node is selected.
|
||||
* @type {?function(Blockly.tree.BaseNode, Blockly.tree.BaseNode):?}
|
||||
* @private
|
||||
*/
|
||||
this.onAfterSelected_ = null;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.tree.TreeControl, Blockly.tree.BaseNode);
|
||||
|
||||
@@ -101,41 +101,6 @@ Blockly.tree.TreeControl.prototype.getDepth = function() {
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles focus on the tree.
|
||||
* @param {!Event} _e The browser event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.tree.TreeControl.prototype.handleFocus_ = function(_e) {
|
||||
this.focused_ = true;
|
||||
var el = /** @type {!Element} */ (this.getElement());
|
||||
Blockly.utils.dom.addClass(el, 'focused');
|
||||
|
||||
if (this.selectedItem_) {
|
||||
this.selectedItem_.select();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles blur on the tree.
|
||||
* @param {!Event} _e The browser event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.tree.TreeControl.prototype.handleBlur_ = function(_e) {
|
||||
this.focused_ = false;
|
||||
var el = /** @type {!Element} */ (this.getElement());
|
||||
Blockly.utils.dom.removeClass(el, 'focused');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get whether this tree has focus or not.
|
||||
* @return {boolean} True if it has focus.
|
||||
* @package
|
||||
*/
|
||||
Blockly.tree.TreeControl.prototype.hasFocus = function() {
|
||||
return this.focused_;
|
||||
};
|
||||
|
||||
/** @override */
|
||||
Blockly.tree.TreeControl.prototype.setExpanded = function(expanded) {
|
||||
this.expanded_ = expanded;
|
||||
@@ -172,11 +137,6 @@ Blockly.tree.TreeControl.prototype.getCalculatedIconClass = function() {
|
||||
if (!expanded && iconClass) {
|
||||
return iconClass;
|
||||
}
|
||||
|
||||
// fall back on default icons
|
||||
if (expanded && this.config_.cssExpandedRootIcon) {
|
||||
return this.config_.cssTreeIcon + ' ' + this.config_.cssExpandedRootIcon;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
@@ -278,10 +238,6 @@ Blockly.tree.TreeControl.prototype.attachEvents_ = function() {
|
||||
var el = this.getElement();
|
||||
el.tabIndex = 0;
|
||||
|
||||
this.onFocusWrapper_ = Blockly.bindEvent_(el,
|
||||
'focus', this, this.handleFocus_);
|
||||
this.onBlurWrapper_ = Blockly.bindEvent_(el,
|
||||
'blur', this, this.handleBlur_);
|
||||
this.onClickWrapper_ = Blockly.bindEventWithChecks_(el,
|
||||
'click', this, this.handleMouseEvent_);
|
||||
this.onKeydownWrapper_ = Blockly.bindEvent_(el,
|
||||
@@ -293,14 +249,6 @@ Blockly.tree.TreeControl.prototype.attachEvents_ = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.tree.TreeControl.prototype.detachEvents_ = function() {
|
||||
if (this.onFocusWrapper_) {
|
||||
Blockly.unbindEvent_(this.onFocusWrapper_);
|
||||
this.onFocusWrapper_ = null;
|
||||
}
|
||||
if (this.onBlurWrapper_) {
|
||||
Blockly.unbindEvent_(this.onBlurWrapper_);
|
||||
this.onBlurWrapper_ = null;
|
||||
}
|
||||
if (this.onClickWrapper_) {
|
||||
Blockly.unbindEvent_(this.onClickWrapper_);
|
||||
this.onClickWrapper_ = null;
|
||||
@@ -318,15 +266,8 @@ Blockly.tree.TreeControl.prototype.detachEvents_ = function() {
|
||||
*/
|
||||
Blockly.tree.TreeControl.prototype.handleMouseEvent_ = function(e) {
|
||||
var node = this.getNodeFromEvent_(e);
|
||||
if (node) {
|
||||
switch (e.type) {
|
||||
case 'mousedown':
|
||||
node.onMouseDown(e);
|
||||
break;
|
||||
case 'click':
|
||||
node.onClick_(e);
|
||||
break;
|
||||
}
|
||||
if (node && e.type == 'click') {
|
||||
node.onClick_(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -337,10 +278,8 @@ Blockly.tree.TreeControl.prototype.handleMouseEvent_ = function(e) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.tree.TreeControl.prototype.handleKeyEvent_ = function(e) {
|
||||
var handled = false;
|
||||
|
||||
// Handle navigation keystrokes.
|
||||
handled = (this.selectedItem_ && this.selectedItem_.onKeyDown(e)) || handled;
|
||||
var handled = !!(this.selectedItem_ && this.selectedItem_.onKeyDown(e));
|
||||
|
||||
if (handled) {
|
||||
Blockly.utils.style.scrollIntoContainerView(
|
||||
@@ -363,7 +302,7 @@ Blockly.tree.TreeControl.prototype.getNodeFromEvent_ = function(e) {
|
||||
// find the right node
|
||||
var node = null;
|
||||
var target = e.target;
|
||||
while (target != null) {
|
||||
while (target) {
|
||||
var id = target.id;
|
||||
node = Blockly.tree.BaseNode.allNodes[id];
|
||||
if (node) {
|
||||
|
||||
@@ -32,6 +32,13 @@ goog.require('Blockly.utils.KeyCodes');
|
||||
Blockly.tree.TreeNode = function(toolbox, content, config) {
|
||||
this.toolbox_ = toolbox;
|
||||
Blockly.tree.BaseNode.call(this, content, config);
|
||||
|
||||
/**
|
||||
* A handler that's triggered when the size of node has changed.
|
||||
* @type {?function():?}
|
||||
* @private
|
||||
*/
|
||||
this.onSizeChanged_ = null;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.tree.TreeNode, Blockly.tree.BaseNode);
|
||||
|
||||
@@ -93,7 +100,7 @@ Blockly.tree.TreeNode.prototype.getCalculatedIconClass = function() {
|
||||
*/
|
||||
Blockly.tree.TreeNode.prototype.onClick_ = function(_e) {
|
||||
// Expand icon.
|
||||
if (this.hasChildren() && this.isUserCollapsible_) {
|
||||
if (this.hasChildren()) {
|
||||
this.toggle();
|
||||
this.select();
|
||||
} else if (this.isSelected()) {
|
||||
@@ -104,15 +111,6 @@ Blockly.tree.TreeNode.prototype.onClick_ = function(_e) {
|
||||
this.updateRow();
|
||||
};
|
||||
|
||||
/**
|
||||
* Suppress the inherited mouse down behaviour.
|
||||
* @param {!Event} _e The browser event.
|
||||
* @override
|
||||
* @private
|
||||
*/
|
||||
Blockly.tree.TreeNode.prototype.onMouseDown = function(_e) {
|
||||
// NOP
|
||||
};
|
||||
|
||||
/**
|
||||
* Remap event.keyCode in horizontalLayout so that arrow
|
||||
|
||||
+5
-2
@@ -16,12 +16,15 @@ goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.BlockMove');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.IASTNodeLocationWithBlock');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a connection between blocks.
|
||||
* @param {!Blockly.Block} source The block establishing this connection.
|
||||
* @param {number} type The type of the connection.
|
||||
* @constructor
|
||||
* @implements {Blockly.IASTNodeLocationWithBlock}
|
||||
*/
|
||||
Blockly.Connection = function(source, type) {
|
||||
/**
|
||||
@@ -242,7 +245,6 @@ Blockly.Connection.prototype.isConnected = function() {
|
||||
* @param {Blockly.Connection} target Connection to check compatibility with.
|
||||
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
|
||||
* an error code otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Connection.prototype.canConnectWithReason = function(target) {
|
||||
if (!target) {
|
||||
@@ -617,6 +619,7 @@ Blockly.Connection.prototype.checkType = function(otherConnection) {
|
||||
* @return {boolean} True if the connections share a type.
|
||||
* @private
|
||||
* @deprecated October 2019, use connection.checkType instead.
|
||||
* @suppress {unusedPrivateMembers}
|
||||
*/
|
||||
Blockly.Connection.prototype.checkType_ = function(otherConnection) {
|
||||
console.warn('Deprecated call to Blockly.Connection.prototype.checkType_, ' +
|
||||
@@ -639,7 +642,7 @@ Blockly.Connection.prototype.onCheckChanged_ = function() {
|
||||
|
||||
/**
|
||||
* Change a connection's compatibility.
|
||||
* @param {?(string|!Array<string>)} check Compatible value type or list of
|
||||
* @param {?(string|!Array.<string>)} check Compatible value type or list of
|
||||
* value types. Null if all types are compatible.
|
||||
* @return {!Blockly.Connection} The connection being modified
|
||||
* (to allow chaining).
|
||||
|
||||
+34
-23
@@ -24,7 +24,7 @@ goog.require('Blockly.Msg');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.uiMenu');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
@@ -36,11 +36,11 @@ goog.require('Blockly.Xml');
|
||||
Blockly.ContextMenu.currentBlock = null;
|
||||
|
||||
/**
|
||||
* Opaque data that can be passed to unbindEvent_.
|
||||
* @type {Array.<!Array>}
|
||||
* Menu object.
|
||||
* @type {Blockly.Menu}
|
||||
* @private
|
||||
*/
|
||||
Blockly.ContextMenu.eventWrapper_ = null;
|
||||
Blockly.ContextMenu.menu_ = null;
|
||||
|
||||
/**
|
||||
* Construct the menu based on the list of options and show the menu.
|
||||
@@ -49,17 +49,18 @@ Blockly.ContextMenu.eventWrapper_ = null;
|
||||
* @param {boolean} rtl True if RTL, false if LTR.
|
||||
*/
|
||||
Blockly.ContextMenu.show = function(e, options, rtl) {
|
||||
Blockly.WidgetDiv.show(Blockly.ContextMenu, rtl, null);
|
||||
Blockly.WidgetDiv.show(Blockly.ContextMenu, rtl, Blockly.ContextMenu.dispose);
|
||||
if (!options.length) {
|
||||
Blockly.ContextMenu.hide();
|
||||
return;
|
||||
}
|
||||
var menu = Blockly.ContextMenu.populate_(options, rtl);
|
||||
Blockly.ContextMenu.menu_ = menu;
|
||||
|
||||
Blockly.ContextMenu.position_(menu, e, rtl);
|
||||
// 1ms delay is required for focusing on context menus because some other
|
||||
// mouse event is still waiting in the queue and clears focus.
|
||||
setTimeout(function() {menu.getElement().focus();}, 1);
|
||||
setTimeout(function() {menu.focus();}, 1);
|
||||
Blockly.ContextMenu.currentBlock = null; // May be set by Blockly.Block.
|
||||
};
|
||||
|
||||
@@ -77,14 +78,15 @@ Blockly.ContextMenu.populate_ = function(options, rtl) {
|
||||
callback: Blockly.MakeItSo}
|
||||
*/
|
||||
var menu = new Blockly.Menu();
|
||||
menu.setRightToLeft(rtl);
|
||||
menu.setRole(Blockly.utils.aria.Role.MENU);
|
||||
for (var i = 0, option; (option = options[i]); i++) {
|
||||
var menuItem = new Blockly.MenuItem(option.text);
|
||||
menuItem.setRightToLeft(rtl);
|
||||
menu.addChild(menuItem, true);
|
||||
menuItem.setRole(Blockly.utils.aria.Role.MENUITEM);
|
||||
menu.addChild(menuItem);
|
||||
menuItem.setEnabled(option.enabled);
|
||||
if (option.enabled) {
|
||||
var actionHandler = function() {
|
||||
var actionHandler = function(_menuItem) {
|
||||
var option = this;
|
||||
Blockly.ContextMenu.hide();
|
||||
option.callback();
|
||||
@@ -108,25 +110,28 @@ Blockly.ContextMenu.position_ = function(menu, e, rtl) {
|
||||
var viewportBBox = Blockly.utils.getViewportBBox();
|
||||
// This one is just a point, but we'll pretend that it's a rect so we can use
|
||||
// some helper functions.
|
||||
var anchorBBox = {
|
||||
top: e.clientY + viewportBBox.top,
|
||||
bottom: e.clientY + viewportBBox.top,
|
||||
left: e.clientX + viewportBBox.left,
|
||||
right: e.clientX + viewportBBox.left
|
||||
};
|
||||
var anchorBBox = new Blockly.utils.Rect(
|
||||
e.clientY + viewportBBox.top,
|
||||
e.clientY + viewportBBox.top,
|
||||
e.clientX + viewportBBox.left,
|
||||
e.clientX + viewportBBox.left
|
||||
);
|
||||
|
||||
Blockly.ContextMenu.createWidget_(menu);
|
||||
var menuSize = Blockly.utils.uiMenu.getSize(menu);
|
||||
var menuSize = menu.getSize();
|
||||
|
||||
if (rtl) {
|
||||
Blockly.utils.uiMenu.adjustBBoxesForRTL(viewportBBox, anchorBBox, menuSize);
|
||||
anchorBBox.left += menuSize.width;
|
||||
anchorBBox.right += menuSize.width;
|
||||
viewportBBox.left += menuSize.width;
|
||||
viewportBBox.right += menuSize.width;
|
||||
}
|
||||
|
||||
Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, menuSize, rtl);
|
||||
// Calling menuDom.focus() has to wait until after the menu has been placed
|
||||
// correctly. Otherwise it will cause a page scroll to get the misplaced menu
|
||||
// in view. See issue #1329.
|
||||
menu.getElement().focus();
|
||||
menu.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -141,8 +146,8 @@ Blockly.ContextMenu.createWidget_ = function(menu) {
|
||||
Blockly.utils.dom.addClass(
|
||||
/** @type {!Element} */ (menuDom), 'blocklyContextMenu');
|
||||
// Prevent system context menu when right-clicking a Blockly context menu.
|
||||
Blockly.bindEventWithChecks_(
|
||||
/** @type {!EventTarget} */ (menuDom), 'contextmenu', null, Blockly.utils.noEvent);
|
||||
Blockly.bindEventWithChecks_(/** @type {!EventTarget} */ (menuDom),
|
||||
'contextmenu', null, Blockly.utils.noEvent);
|
||||
// Focus only after the initial render to avoid issue #1329.
|
||||
menu.focus();
|
||||
};
|
||||
@@ -153,9 +158,15 @@ Blockly.ContextMenu.createWidget_ = function(menu) {
|
||||
Blockly.ContextMenu.hide = function() {
|
||||
Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu);
|
||||
Blockly.ContextMenu.currentBlock = null;
|
||||
if (Blockly.ContextMenu.eventWrapper_) {
|
||||
Blockly.unbindEvent_(Blockly.ContextMenu.eventWrapper_);
|
||||
Blockly.ContextMenu.eventWrapper_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of the menu.
|
||||
*/
|
||||
Blockly.ContextMenu.dispose = function() {
|
||||
if (Blockly.ContextMenu.menu_) {
|
||||
Blockly.ContextMenu.menu_.dispose();
|
||||
Blockly.ContextMenu.menu_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+102
-218
@@ -74,13 +74,13 @@ Blockly.Css.inject = function(hasCss, pathToMedia) {
|
||||
|
||||
/**
|
||||
* Set the cursor to be displayed when over something draggable.
|
||||
* See See https://github.com/google/blockly/issues/981 for context.
|
||||
* See https://github.com/google/blockly/issues/981 for context.
|
||||
* @param {*} _cursor Enum.
|
||||
* @deprecated April 2017.
|
||||
*/
|
||||
Blockly.Css.setCursor = function(_cursor) {
|
||||
console.warn('Deprecated call to Blockly.Css.setCursor. ' +
|
||||
'See https://github.com/google/blockly/issues/981 for context');
|
||||
'See issue #981 for context');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -150,8 +150,7 @@ Blockly.Css.CONTENT = [
|
||||
'box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15);',
|
||||
'color: #000;',
|
||||
'display: none;',
|
||||
'font-family: sans-serif;',
|
||||
'font-size: 9pt;',
|
||||
'font: 9pt sans-serif;',
|
||||
'opacity: .9;',
|
||||
'padding: 2px;',
|
||||
'position: absolute;',
|
||||
@@ -169,11 +168,11 @@ Blockly.Css.CONTENT = [
|
||||
'background-color: #fff;',
|
||||
'border-radius: 2px;',
|
||||
'padding: 4px;',
|
||||
'box-shadow: 0px 0px 3px 1px rgba(0,0,0,.3);',
|
||||
'box-shadow: 0 0 3px 1px rgba(0,0,0,.3);',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv.focused {',
|
||||
'box-shadow: 0px 0px 6px 1px rgba(0,0,0,.3);',
|
||||
'.blocklyDropDownDiv.blocklyFocused {',
|
||||
'box-shadow: 0 0 6px 1px rgba(0,0,0,.3);',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownContent {',
|
||||
@@ -309,7 +308,7 @@ Blockly.Css.CONTENT = [
|
||||
'.blocklyInsertionMarker>.blocklyPathLight,',
|
||||
'.blocklyInsertionMarker>.blocklyPathDark {',
|
||||
'fill-opacity: .2;',
|
||||
'stroke: none',
|
||||
'stroke: none;',
|
||||
'}',
|
||||
|
||||
'.blocklyMultilineText {',
|
||||
@@ -333,7 +332,8 @@ Blockly.Css.CONTENT = [
|
||||
Don't allow users to select text. It gets annoying when trying to
|
||||
drag a block and selected text moves instead.
|
||||
*/
|
||||
'.blocklySvg text, .blocklyBlockDragSurface text {',
|
||||
'.blocklySvg text,',
|
||||
'.blocklyBlockDragSurface text {',
|
||||
'user-select: none;',
|
||||
'-ms-user-select: none;',
|
||||
'-webkit-user-select: none;',
|
||||
@@ -416,7 +416,8 @@ Blockly.Css.CONTENT = [
|
||||
'z-index: 30;',
|
||||
'}',
|
||||
|
||||
'.blocklyScrollbarHorizontal, .blocklyScrollbarVertical {',
|
||||
'.blocklyScrollbarHorizontal,',
|
||||
'.blocklyScrollbarVertical {',
|
||||
'position: absolute;',
|
||||
'outline: none;',
|
||||
'}',
|
||||
@@ -449,217 +450,10 @@ Blockly.Css.CONTENT = [
|
||||
'background: #faa;',
|
||||
'}',
|
||||
|
||||
'.blocklyContextMenu {',
|
||||
'border-radius: 4px;',
|
||||
'max-height: 100%;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropdownMenu {',
|
||||
'border-radius: 2px;',
|
||||
'padding: 0 !important;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .blocklyDropdownMenu .goog-menuitem,',
|
||||
'.blocklyDropDownDiv .blocklyDropdownMenu .goog-menuitem {',
|
||||
/* 28px on the left for icon or checkbox. */
|
||||
'padding-left: 28px;',
|
||||
'}',
|
||||
|
||||
/* BiDi override for the resting state. */
|
||||
/* #noflip */
|
||||
'.blocklyWidgetDiv .blocklyDropdownMenu .goog-menuitem.goog-menuitem-rtl,',
|
||||
'.blocklyDropDownDiv .blocklyDropdownMenu .goog-menuitem.goog-menuitem-rtl {',
|
||||
/* Flip left/right padding for BiDi. */
|
||||
'padding-left: 5px;',
|
||||
'padding-right: 28px;',
|
||||
'}',
|
||||
|
||||
'.blocklyVerticalMarker {',
|
||||
'stroke-width: 3px;',
|
||||
'fill: rgba(255,255,255,.5);',
|
||||
'pointer-events: none',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon,',
|
||||
'.blocklyDropDownDiv .goog-option-selected .goog-menuitem-checkbox,',
|
||||
'.blocklyDropDownDiv .goog-option-selected .goog-menuitem-icon {',
|
||||
'background: url(<<<PATH>>>/sprites.png) no-repeat -48px -16px;',
|
||||
'}',
|
||||
|
||||
/* Copied from: goog/css/menu.css */
|
||||
/*
|
||||
* Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by the Apache License, Version 2.0.
|
||||
* See the COPYING file for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Standard styling for menus created by goog.ui.MenuRenderer.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
'.blocklyWidgetDiv .goog-menu {',
|
||||
'background: #fff;',
|
||||
'border-color: transparent;',
|
||||
'border-style: solid;',
|
||||
'border-width: 1px;',
|
||||
'cursor: default;',
|
||||
'font: normal 13px Arial, sans-serif;',
|
||||
'margin: 0;',
|
||||
'outline: none;',
|
||||
'padding: 4px 0;',
|
||||
'position: absolute;',
|
||||
'overflow-y: auto;',
|
||||
'overflow-x: hidden;',
|
||||
'max-height: 100%;',
|
||||
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
|
||||
'box-shadow: 0px 0px 3px 1px rgba(0,0,0,.3);',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menu.focused {',
|
||||
'box-shadow: 0px 0px 6px 1px rgba(0,0,0,.3);',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv .goog-menu {',
|
||||
'cursor: default;',
|
||||
'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;',
|
||||
'outline: none;',
|
||||
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
|
||||
'}',
|
||||
|
||||
/* Copied from: goog/css/menuitem.css */
|
||||
/*
|
||||
* Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by the Apache License, Version 2.0.
|
||||
* See the COPYING file for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Standard styling for menus created by goog.ui.MenuItemRenderer.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
/**
|
||||
* State: resting.
|
||||
*
|
||||
* NOTE(mleibman,chrishenry):
|
||||
* The RTL support in Closure is provided via two mechanisms -- "rtl" CSS
|
||||
* classes and BiDi flipping done by the CSS compiler. Closure supports RTL
|
||||
* with or without the use of the CSS compiler. In order for them not to
|
||||
* conflict with each other, the "rtl" CSS classes need to have the #noflip
|
||||
* annotation. The non-rtl counterparts should ideally have them as well,
|
||||
* but, since .goog-menuitem existed without .goog-menuitem-rtl for so long
|
||||
* before being added, there is a risk of people having templates where they
|
||||
* are not rendering the .goog-menuitem-rtl class when in RTL and instead
|
||||
* rely solely on the BiDi flipping by the CSS compiler. That's why we're
|
||||
* not adding the #noflip to .goog-menuitem.
|
||||
*/
|
||||
'.blocklyWidgetDiv .goog-menuitem,',
|
||||
'.blocklyDropDownDiv .goog-menuitem {',
|
||||
'color: #000;',
|
||||
'font: normal 13px Arial, sans-serif;',
|
||||
'list-style: none;',
|
||||
'margin: 0;',
|
||||
/* 7em on the right for shortcut. */
|
||||
'min-width: 7em;',
|
||||
'border: none;',
|
||||
'padding: 6px 15px;',
|
||||
'white-space: nowrap;',
|
||||
'cursor: pointer;',
|
||||
'}',
|
||||
|
||||
/* If a menu doesn't have checkable items or items with icons,
|
||||
* remove padding.
|
||||
*/
|
||||
'.blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,',
|
||||
'.blocklyWidgetDiv .goog-menu-noicon .goog-menuitem,',
|
||||
'.blocklyDropDownDiv .goog-menu-nocheckbox .goog-menuitem,',
|
||||
'.blocklyDropDownDiv .goog-menu-noicon .goog-menuitem {',
|
||||
'padding-left: 12px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-content,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-content {',
|
||||
'font-family: Arial, sans-serif;',
|
||||
'font-size: 13px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-content {',
|
||||
'color: #000;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv .goog-menuitem-content {',
|
||||
'color: #000;',
|
||||
'}',
|
||||
|
||||
/* State: disabled. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-disabled {',
|
||||
'cursor: inherit;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-content {',
|
||||
'color: #ccc !important;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-icon {',
|
||||
'opacity: .3;',
|
||||
'filter: alpha(opacity=30);',
|
||||
'}',
|
||||
|
||||
/* State: hover. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-highlight ,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-highlight {',
|
||||
'background-color: rgba(0,0,0,.1);',
|
||||
'}',
|
||||
|
||||
/* State: selected/checked. */
|
||||
'.blocklyWidgetDiv .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-icon,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-checkbox,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-icon {',
|
||||
'background-repeat: no-repeat;',
|
||||
'height: 16px;',
|
||||
'left: 6px;',
|
||||
'position: absolute;',
|
||||
'right: auto;',
|
||||
'vertical-align: middle;',
|
||||
'width: 16px;',
|
||||
'}',
|
||||
|
||||
/* BiDi override for the selected/checked state. */
|
||||
/* #noflip */
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-checkbox,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-icon {',
|
||||
/* Flip left/right positioning. */
|
||||
'left: auto;',
|
||||
'right: 6px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon,',
|
||||
'.blocklyDropDownDiv .goog-option-selected .goog-menuitem-checkbox,',
|
||||
'.blocklyDropDownDiv .goog-option-selected .goog-menuitem-icon {',
|
||||
'position: static;', /* Scroll with the menu. */
|
||||
'float: left;',
|
||||
'margin-left: -24px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,',
|
||||
'.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-checkbox,',
|
||||
'.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-icon {',
|
||||
'float: right;',
|
||||
'margin-right: -24px;',
|
||||
'pointer-events: none;',
|
||||
'}',
|
||||
|
||||
'.blocklyComputeCanvas {',
|
||||
@@ -671,5 +465,95 @@ Blockly.Css.CONTENT = [
|
||||
'.blocklyNoPointerEvents {',
|
||||
'pointer-events: none;',
|
||||
'}',
|
||||
|
||||
'.blocklyContextMenu {',
|
||||
'border-radius: 4px;',
|
||||
'max-height: 100%;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropdownMenu {',
|
||||
'border-radius: 2px;',
|
||||
'padding: 0 !important;',
|
||||
'}',
|
||||
|
||||
'.blocklyDropdownMenu .blocklyMenuItem {',
|
||||
/* 28px on the left for icon or checkbox. */
|
||||
'padding-left: 28px;',
|
||||
'}',
|
||||
|
||||
/* BiDi override for the resting state. */
|
||||
'.blocklyDropdownMenu .blocklyMenuItemRtl {',
|
||||
/* Flip left/right padding for BiDi. */
|
||||
'padding-left: 5px;',
|
||||
'padding-right: 28px;',
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .blocklyMenu {',
|
||||
'background: #fff;',
|
||||
'border: 1px solid transparent;',
|
||||
'box-shadow: 0 0 3px 1px rgba(0,0,0,.3);',
|
||||
'font: normal 13px Arial, sans-serif;',
|
||||
'margin: 0;',
|
||||
'outline: none;',
|
||||
'padding: 4px 0;',
|
||||
'position: absolute;',
|
||||
'overflow-y: auto;',
|
||||
'overflow-x: hidden;',
|
||||
'max-height: 100%;',
|
||||
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
|
||||
'}',
|
||||
|
||||
'.blocklyWidgetDiv .blocklyMenu.blocklyFocused {',
|
||||
'box-shadow: 0 0 6px 1px rgba(0,0,0,.3);',
|
||||
'}',
|
||||
|
||||
'.blocklyDropDownDiv .blocklyMenu {',
|
||||
'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;',
|
||||
'outline: none;',
|
||||
'z-index: 20000;', /* Arbitrary, but some apps depend on it... */
|
||||
'}',
|
||||
|
||||
/* State: resting. */
|
||||
'.blocklyMenuItem {',
|
||||
'border: none;',
|
||||
'color: #000;',
|
||||
'cursor: pointer;',
|
||||
'list-style: none;',
|
||||
'margin: 0;',
|
||||
/* 7em on the right for shortcut. */
|
||||
'min-width: 7em;',
|
||||
'padding: 6px 15px;',
|
||||
'white-space: nowrap;',
|
||||
'}',
|
||||
|
||||
/* State: disabled. */
|
||||
'.blocklyMenuItemDisabled {',
|
||||
'color: #ccc;',
|
||||
'cursor: inherit;',
|
||||
'}',
|
||||
|
||||
/* State: hover. */
|
||||
'.blocklyMenuItemHighlight {',
|
||||
'background-color: rgba(0,0,0,.1);',
|
||||
'}',
|
||||
|
||||
/* State: selected/checked. */
|
||||
'.blocklyMenuItemCheckbox {',
|
||||
'height: 16px;',
|
||||
'position: absolute;',
|
||||
'width: 16px;',
|
||||
'}',
|
||||
|
||||
'.blocklyMenuItemSelected .blocklyMenuItemCheckbox {',
|
||||
'background: url(<<<PATH>>>/sprites.png) no-repeat -48px -16px;',
|
||||
'float: left;',
|
||||
'margin-left: -24px;',
|
||||
'position: static;', /* Scroll with the menu. */
|
||||
'}',
|
||||
|
||||
'.blocklyMenuItemRtl .blocklyMenuItemCheckbox {',
|
||||
'float: right;',
|
||||
'margin-right: -24px;',
|
||||
'}',
|
||||
/* eslint-enable indent */
|
||||
];
|
||||
|
||||
+82
-40
@@ -19,6 +19,9 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.math');
|
||||
goog.require('Blockly.utils.style');
|
||||
|
||||
goog.requireType('Blockly.utils.Rect');
|
||||
goog.requireType('Blockly.utils.Size');
|
||||
|
||||
|
||||
/**
|
||||
* Class for drop-down div.
|
||||
@@ -117,6 +120,35 @@ Blockly.DropDownDiv.rendererClassName_ = '';
|
||||
*/
|
||||
Blockly.DropDownDiv.themeClassName_ = '';
|
||||
|
||||
/**
|
||||
* Dropdown bounds info object used to encapsulate sizing information about a
|
||||
* bounding element (bounding box and width/height).
|
||||
* @typedef {{
|
||||
* top:number,
|
||||
* left:number,
|
||||
* bottom:number,
|
||||
* right:number,
|
||||
* width:number,
|
||||
* height:number
|
||||
* }}
|
||||
*/
|
||||
Blockly.DropDownDiv.BoundsInfo;
|
||||
|
||||
/**
|
||||
* Dropdown position metrics.
|
||||
* @typedef {{
|
||||
* initialX:number,
|
||||
* initialY:number,
|
||||
* finalX:number,
|
||||
* finalY:number,
|
||||
* arrowX:?number,
|
||||
* arrowY:?number,
|
||||
* arrowAtTop:?boolean,
|
||||
* arrowVisible:boolean
|
||||
* }}
|
||||
*/
|
||||
Blockly.DropDownDiv.PositionMetrics;
|
||||
|
||||
/**
|
||||
* Create and insert the DOM element for this div.
|
||||
* @package
|
||||
@@ -166,10 +198,10 @@ Blockly.DropDownDiv.createDom = function() {
|
||||
// Handle focusin/out events to add a visual indicator when
|
||||
// a child is focused or blurred.
|
||||
div.addEventListener('focusin', function() {
|
||||
Blockly.utils.dom.addClass(div, 'focused');
|
||||
Blockly.utils.dom.addClass(div, 'blocklyFocused');
|
||||
});
|
||||
div.addEventListener('focusout', function() {
|
||||
Blockly.utils.dom.removeClass(div, 'focused');
|
||||
Blockly.utils.dom.removeClass(div, 'blocklyFocused');
|
||||
});
|
||||
};
|
||||
|
||||
@@ -184,7 +216,7 @@ Blockly.DropDownDiv.setBoundsElement = function(boundsElement) {
|
||||
|
||||
/**
|
||||
* Provide the div for inserting content into the drop-down.
|
||||
* @return {Element} Div to populate with content
|
||||
* @return {!Element} Div to populate with content.
|
||||
*/
|
||||
Blockly.DropDownDiv.getContentDiv = function() {
|
||||
return Blockly.DropDownDiv.content_;
|
||||
@@ -214,7 +246,7 @@ Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) {
|
||||
* and the secondary position above the block. Drop-down will be
|
||||
* constrained to the block's workspace.
|
||||
* @param {!Blockly.Field} field The field showing the drop-down.
|
||||
* @param {!Blockly.Block} block Block to position the drop-down around.
|
||||
* @param {!Blockly.BlockSvg} block Block to position the drop-down around.
|
||||
* @param {Function=} opt_onHide Optional callback for when the drop-down is
|
||||
* hidden.
|
||||
* @param {number=} opt_secondaryYOffset Optional Y offset for above-block
|
||||
@@ -250,7 +282,7 @@ Blockly.DropDownDiv.showPositionedByField = function(field,
|
||||
|
||||
/**
|
||||
* Get the scaled bounding box of a block.
|
||||
* @param {!Blockly.Block} block The block.
|
||||
* @param {!Blockly.BlockSvg} block The block.
|
||||
* @return {!Blockly.utils.Rect} The scaled bounding box of the block.
|
||||
* @private
|
||||
*/
|
||||
@@ -302,10 +334,15 @@ Blockly.DropDownDiv.showPositionedByRect_ = function(bBox, field,
|
||||
if (opt_secondaryYOffset) {
|
||||
secondaryY += opt_secondaryYOffset;
|
||||
}
|
||||
var sourceBlock = field.getSourceBlock();
|
||||
// Set bounds to workspace; show the drop-down.
|
||||
var sourceBlock = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock());
|
||||
// Set bounds to main workspace; show the drop-down.
|
||||
var workspace = sourceBlock.workspace;
|
||||
while (workspace.options.parentWorkspace) {
|
||||
workspace = /** @type {!Blockly.WorkspaceSvg} */ (
|
||||
workspace.options.parentWorkspace);
|
||||
}
|
||||
Blockly.DropDownDiv.setBoundsElement(
|
||||
sourceBlock.workspace.getParentSvg().parentNode);
|
||||
/** @type {Element} */ (workspace.getParentSvg().parentNode));
|
||||
return Blockly.DropDownDiv.show(
|
||||
field, sourceBlock.RTL,
|
||||
primaryX, primaryY, secondaryX, secondaryY, opt_onHide);
|
||||
@@ -340,10 +377,11 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
|
||||
var div = Blockly.DropDownDiv.DIV_;
|
||||
div.style.direction = rtl ? 'rtl' : 'ltr';
|
||||
|
||||
var mainWorkspace =
|
||||
/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
|
||||
Blockly.DropDownDiv.rendererClassName_ =
|
||||
Blockly.getMainWorkspace().getRenderer().getClassName();
|
||||
Blockly.DropDownDiv.themeClassName_ =
|
||||
Blockly.getMainWorkspace().getTheme().getClassName();
|
||||
mainWorkspace.getRenderer().getClassName();
|
||||
Blockly.DropDownDiv.themeClassName_ = mainWorkspace.getTheme().getClassName();
|
||||
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.rendererClassName_);
|
||||
Blockly.utils.dom.addClass(div, Blockly.DropDownDiv.themeClassName_);
|
||||
|
||||
@@ -362,8 +400,8 @@ Blockly.DropDownDiv.show = function(owner, rtl, primaryX, primaryY,
|
||||
|
||||
/**
|
||||
* Get sizing info about the bounding element.
|
||||
* @return {!Object} An object containing size information about the bounding
|
||||
* element (bounding box and width/height).
|
||||
* @return {!Blockly.DropDownDiv.BoundsInfo} An object containing size
|
||||
* information about the bounding element (bounding box and width/height).
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.getBoundsInfo_ = function() {
|
||||
@@ -388,11 +426,11 @@ Blockly.DropDownDiv.getBoundsInfo_ = function() {
|
||||
* @param {number} primaryX Desired origin point x, in absolute px.
|
||||
* @param {number} primaryY Desired origin point y, in absolute px.
|
||||
* @param {number} secondaryX Secondary/alternative origin point x,
|
||||
* in absolute px.
|
||||
* in absolute px.
|
||||
* @param {number} secondaryY Secondary/alternative origin point y,
|
||||
* in absolute px.
|
||||
* @return {Object} Various final metrics, including rendered positions
|
||||
* for drop-down and arrow.
|
||||
* in absolute px.
|
||||
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
|
||||
* including rendered positions for drop-down and arrow.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY,
|
||||
@@ -431,12 +469,12 @@ Blockly.DropDownDiv.getPositionMetrics_ = function(primaryX, primaryY,
|
||||
* Get the metrics for positioning the div below the source.
|
||||
* @param {number} primaryX Desired origin point x, in absolute px.
|
||||
* @param {number} primaryY Desired origin point y, in absolute px.
|
||||
* @param {!Object} boundsInfo An object containing size information about the
|
||||
* bounding element (bounding box and width/height).
|
||||
* @param {!Object} divSize An object containing information about the size
|
||||
* of the DropDownDiv (width & height).
|
||||
* @return {Object} Various final metrics, including rendered positions
|
||||
* for drop-down and arrow.
|
||||
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
|
||||
* information about the bounding element (bounding box and width/height).
|
||||
* @param {!Blockly.utils.Size} divSize An object containing information about
|
||||
* the size of the DropDownDiv (width & height).
|
||||
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
|
||||
* including rendered positions for drop-down and arrow.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
|
||||
@@ -464,15 +502,15 @@ Blockly.DropDownDiv.getPositionBelowMetrics_ = function(
|
||||
/**
|
||||
* Get the metrics for positioning the div above the source.
|
||||
* @param {number} secondaryX Secondary/alternative origin point x,
|
||||
* in absolute px.
|
||||
* in absolute px.
|
||||
* @param {number} secondaryY Secondary/alternative origin point y,
|
||||
* in absolute px.
|
||||
* @param {!Object} boundsInfo An object containing size information about the
|
||||
* bounding element (bounding box and width/height).
|
||||
* @param {!Object} divSize An object containing information about the size
|
||||
* of the DropDownDiv (width & height).
|
||||
* @return {Object} Various final metrics, including rendered positions
|
||||
* for drop-down and arrow.
|
||||
* in absolute px.
|
||||
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
|
||||
* information about the bounding element (bounding box and width/height).
|
||||
* @param {!Blockly.utils.Size} divSize An object containing information about
|
||||
* the size of the DropDownDiv (width & height).
|
||||
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
|
||||
* including rendered positions for drop-down and arrow.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.getPositionAboveMetrics_ = function(
|
||||
@@ -501,12 +539,12 @@ Blockly.DropDownDiv.getPositionAboveMetrics_ = function(
|
||||
/**
|
||||
* Get the metrics for positioning the div at the top of the page.
|
||||
* @param {number} sourceX Desired origin point x, in absolute px.
|
||||
* @param {!Object} boundsInfo An object containing size information about the
|
||||
* bounding element (bounding box and width/height).
|
||||
* @param {!Object} divSize An object containing information about the size
|
||||
* of the DropDownDiv (width & height).
|
||||
* @return {Object} Various final metrics, including rendered positions
|
||||
* for drop-down and arrow.
|
||||
* @param {!Blockly.DropDownDiv.BoundsInfo} boundsInfo An object containing size
|
||||
* information about the bounding element (bounding box and width/height).
|
||||
* @param {!Blockly.utils.Size} divSize An object containing information about
|
||||
* the size of the DropDownDiv (width & height).
|
||||
* @return {!Blockly.DropDownDiv.PositionMetrics} Various final metrics,
|
||||
* including rendered positions for drop-down and arrow.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
|
||||
@@ -521,6 +559,9 @@ Blockly.DropDownDiv.getPositionTopOfPageMetrics_ = function(
|
||||
initialY : 0,
|
||||
finalX: xCoords.divX, // X position remains constant during animation.
|
||||
finalY: 0, // Y position remains constant during animation.
|
||||
arrowAtTop: null,
|
||||
arrowX: null,
|
||||
arrowY: null,
|
||||
arrowVisible: false
|
||||
};
|
||||
};
|
||||
@@ -649,7 +690,8 @@ Blockly.DropDownDiv.hideWithoutAnimation = function() {
|
||||
Blockly.utils.dom.removeClass(div, Blockly.DropDownDiv.themeClassName_);
|
||||
Blockly.DropDownDiv.themeClassName_ = '';
|
||||
}
|
||||
Blockly.getMainWorkspace().markFocused();
|
||||
(/** @type {!Blockly.WorkspaceSvg} */ (
|
||||
Blockly.getMainWorkspace())).markFocused();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -700,7 +742,7 @@ Blockly.DropDownDiv.positionInternal_ = function(
|
||||
var dy = finalY - initialY;
|
||||
div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)';
|
||||
|
||||
return metrics.arrowAtTop;
|
||||
return !!metrics.arrowAtTop;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -716,7 +758,7 @@ Blockly.DropDownDiv.repositionForWindowResize = function() {
|
||||
// it.
|
||||
if (Blockly.DropDownDiv.owner_) {
|
||||
var field = /** @type {!Blockly.Field} */ (Blockly.DropDownDiv.owner_);
|
||||
var block = Blockly.DropDownDiv.owner_.getSourceBlock();
|
||||
var block = /** @type {!Blockly.BlockSvg} */ (field.getSourceBlock());
|
||||
var bBox = Blockly.DropDownDiv.positionToField_ ?
|
||||
Blockly.DropDownDiv.getScaledBboxOfField_(field) :
|
||||
Blockly.DropDownDiv.getScaledBboxOfBlock_(block);
|
||||
|
||||
+34
-13
@@ -19,11 +19,16 @@ goog.require('Blockly.Events.BlockChange');
|
||||
goog.require('Blockly.Gesture');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.Size');
|
||||
goog.require('Blockly.utils.style');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
|
||||
goog.requireType('Blockly.blockRendering.ConstantProvider');
|
||||
goog.requireType('Blockly.IASTNodeLocationSvg');
|
||||
goog.requireType('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IRegistrable');
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,6 +41,10 @@ goog.requireType('Blockly.blockRendering.ConstantProvider');
|
||||
* the individual field's documentation for a list of properties this
|
||||
* parameter supports.
|
||||
* @constructor
|
||||
* @implements {Blockly.IASTNodeLocationSvg}
|
||||
* @implements {Blockly.IASTNodeLocationWithBlock}
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
* @implements {Blockly.IRegistrable}
|
||||
*/
|
||||
Blockly.Field = function(value, opt_validator, opt_config) {
|
||||
/**
|
||||
@@ -44,7 +53,7 @@ Blockly.Field = function(value, opt_validator, opt_config) {
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
this.value_ = null;
|
||||
this.value_ = this.DEFAULT_VALUE;
|
||||
|
||||
/**
|
||||
* Validation function called when user edits an editable field.
|
||||
@@ -131,6 +140,13 @@ Blockly.Field = function(value, opt_validator, opt_config) {
|
||||
opt_validator && this.setValidator(opt_validator);
|
||||
};
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Field.prototype.DEFAULT_VALUE = null;
|
||||
|
||||
/**
|
||||
* Name of field. Unique within each block.
|
||||
* Static labels are usually unnamed.
|
||||
@@ -708,8 +724,8 @@ Blockly.Field.prototype.getSize = function() {
|
||||
/**
|
||||
* Returns the bounding box of the rendered field, accounting for workspace
|
||||
* scaling.
|
||||
* @return {!Object} An object with top, bottom, left, and right in pixels
|
||||
* relative to the top left corner of the page (window coordinates).
|
||||
* @return {!Blockly.utils.Rect} An object with top, bottom, left, and right in
|
||||
* pixels relative to the top left corner of the page (window coordinates).
|
||||
* @package
|
||||
*/
|
||||
Blockly.Field.prototype.getScaledBBox = function() {
|
||||
@@ -742,12 +758,12 @@ Blockly.Field.prototype.getScaledBBox = function() {
|
||||
var scaledWidth = bBox.width;
|
||||
var scaledHeight = bBox.height;
|
||||
}
|
||||
return {
|
||||
top: xy.y,
|
||||
bottom: xy.y + scaledHeight,
|
||||
left: xy.x,
|
||||
right: xy.x + scaledWidth
|
||||
};
|
||||
return new Blockly.utils.Rect(
|
||||
xy.y,
|
||||
xy.y + scaledHeight,
|
||||
xy.x,
|
||||
xy.x + scaledWidth
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -858,16 +874,20 @@ Blockly.Field.prototype.setValue = function(newValue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var source = this.sourceBlock_;
|
||||
if (source && source.disposed) {
|
||||
doLogging && console.log('source disposed, return');
|
||||
return;
|
||||
}
|
||||
var oldValue = this.getValue();
|
||||
if (oldValue === newValue) {
|
||||
doLogging && console.log('same, return');
|
||||
// No change.
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
||||
if (source && Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name || null, oldValue, newValue));
|
||||
source, 'field', this.name || null, oldValue, newValue));
|
||||
}
|
||||
this.doValueUpdate_(newValue);
|
||||
if (this.isDirty_) {
|
||||
@@ -1101,7 +1121,8 @@ Blockly.Field.prototype.setMarkerSvg = function(markerSvg) {
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Field.prototype.updateMarkers_ = function() {
|
||||
var workspace = this.sourceBlock_.workspace;
|
||||
var workspace =
|
||||
/** @type {!Blockly.WorkspaceSvg} */ (this.sourceBlock_.workspace);
|
||||
if (workspace.keyboardAccessibilityMode && this.cursorSvg_) {
|
||||
workspace.getCursor().draw();
|
||||
}
|
||||
|
||||
+10
-2
@@ -71,7 +71,7 @@ Blockly.FieldAngle = function(opt_value, opt_validator, opt_config) {
|
||||
this.round_ = Blockly.FieldAngle.ROUND;
|
||||
|
||||
Blockly.FieldAngle.superClass_.constructor.call(
|
||||
this, opt_value || 0, opt_validator, opt_config);
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
|
||||
/**
|
||||
* The angle picker's gauge path depending on the value.
|
||||
@@ -108,6 +108,14 @@ Blockly.FieldAngle = function(opt_value, opt_validator, opt_config) {
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldAngle, Blockly.FieldTextInput);
|
||||
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldAngle.prototype.DEFAULT_VALUE = 0;
|
||||
|
||||
/**
|
||||
* Construct a FieldAngle from a JSON arg object.
|
||||
* @param {!Object} options A JSON object with options (angle).
|
||||
@@ -247,7 +255,7 @@ Blockly.FieldAngle.prototype.render_ = function() {
|
||||
* Create and show the angle field's editor.
|
||||
* @param {Event=} opt_e Optional mouse event that triggered the field to open,
|
||||
* or undefined if triggered programmatically.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldAngle.prototype.showEditor_ = function(opt_e) {
|
||||
// Mobile browsers have issues with in-line textareas (focus & keyboards).
|
||||
|
||||
@@ -44,14 +44,18 @@ Blockly.FieldCheckbox = function(opt_value, opt_validator, opt_config) {
|
||||
*/
|
||||
this.checkChar_ = null;
|
||||
|
||||
if (opt_value == null) {
|
||||
opt_value = 'FALSE';
|
||||
}
|
||||
Blockly.FieldCheckbox.superClass_.constructor.call(
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldCheckbox, Blockly.Field);
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldCheckbox.prototype.DEFAULT_VALUE = false;
|
||||
|
||||
/**
|
||||
* Construct a FieldCheckbox from a JSON arg object.
|
||||
* @param {!Object} options A JSON object with options (checked).
|
||||
|
||||
@@ -43,8 +43,7 @@ goog.require('Blockly.utils.Size');
|
||||
*/
|
||||
Blockly.FieldColour = function(opt_value, opt_validator, opt_config) {
|
||||
Blockly.FieldColour.superClass_.constructor.call(
|
||||
this, opt_value || Blockly.FieldColour.COLOURS[0],
|
||||
opt_validator, opt_config);
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
|
||||
/**
|
||||
* The field's colour picker element.
|
||||
@@ -268,6 +267,13 @@ Blockly.FieldColour.COLOURS = [
|
||||
'#ffccff', '#ff99ff', '#cc66cc', '#cc33cc', '#993399', '#663366', '#330033'
|
||||
];
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldColour.prototype.DEFAULT_VALUE = Blockly.FieldColour.COLOURS[0];
|
||||
|
||||
/**
|
||||
* An array of tooltip strings for the palette. If not the same length as
|
||||
* COLOURS, the colour's hex code will be used for any missing titles.
|
||||
@@ -311,7 +317,7 @@ Blockly.FieldColour.prototype.setColumns = function(columns) {
|
||||
|
||||
/**
|
||||
* Create and show the colour field's editor.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldColour.prototype.showEditor_ = function() {
|
||||
this.picker_ = this.dropdownCreate_();
|
||||
|
||||
@@ -1,344 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2015 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Date input field.
|
||||
* @author pkendall64@gmail.com (Paul Kendall)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.FieldDate');
|
||||
|
||||
goog.require('Blockly.Css');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Field');
|
||||
goog.require('Blockly.fieldRegistry');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.string');
|
||||
|
||||
goog.require('goog.date');
|
||||
goog.require('goog.date.DateTime');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.i18n.DateTimeSymbols');
|
||||
goog.require('goog.i18n.DateTimeSymbols_he');
|
||||
goog.require('goog.ui.DatePicker');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a date input field.
|
||||
* @param {string=} opt_value The initial value of the field. Should be in
|
||||
* 'YYYY-MM-DD' format. Defaults to the current date.
|
||||
* @param {Function=} opt_validator A function that is called to validate
|
||||
* changes to the field's value. Takes in a date string & returns a
|
||||
* validated date string ('YYYY-MM-DD' format), or null to abort the change.
|
||||
* @extends {Blockly.Field}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldDate = function(opt_value, opt_validator) {
|
||||
Blockly.FieldDate.superClass_.constructor.call(this,
|
||||
opt_value || new goog.date.Date().toIsoString(true), opt_validator);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldDate, Blockly.Field);
|
||||
|
||||
/**
|
||||
* Construct a FieldDate from a JSON arg object.
|
||||
* @param {!Object} options A JSON object with options (date).
|
||||
* @return {!Blockly.FieldDate} The new field instance.
|
||||
* @package
|
||||
* @nocollapse
|
||||
*/
|
||||
Blockly.FieldDate.fromJson = function(options) {
|
||||
return new Blockly.FieldDate(options['date']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializable fields are saved by the XML renderer, non-serializable fields
|
||||
* are not. Editable fields should also be serializable.
|
||||
* @type {boolean}
|
||||
*/
|
||||
Blockly.FieldDate.prototype.SERIALIZABLE = true;
|
||||
|
||||
/**
|
||||
* Mouse cursor style when over the hotspot that initiates the editor.
|
||||
*/
|
||||
Blockly.FieldDate.prototype.CURSOR = 'text';
|
||||
|
||||
/**
|
||||
* Border colour for the dropdown div showing the date picker. Must be a CSS
|
||||
* string.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.DROPDOWN_BORDER_COLOUR = 'silver';
|
||||
|
||||
/**
|
||||
* Background colour for the dropdown div showing the date picker. Must be a
|
||||
* CSS string.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.DROPDOWN_BACKGROUND_COLOUR = 'white';
|
||||
|
||||
/**
|
||||
* Ensure that the input value is a valid date.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A valid date, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldDate.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
if (!opt_newValue) {
|
||||
return null;
|
||||
}
|
||||
// Check if the new value is parsable or not.
|
||||
var date = goog.date.Date.fromIsoString(opt_newValue);
|
||||
if (!date || date.toIsoString(true) != opt_newValue) {
|
||||
return null;
|
||||
}
|
||||
return opt_newValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the field. If the picker is shown make sure it has the current
|
||||
* date selected.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldDate.prototype.render_ = function() {
|
||||
Blockly.FieldDate.superClass_.render_.call(this);
|
||||
if (this.picker_) {
|
||||
this.picker_.setDate(goog.date.Date.fromIsoString(this.getValue()));
|
||||
this.updateEditor_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the field's colours to match those of the block.
|
||||
* @package
|
||||
*/
|
||||
Blockly.FieldDate.prototype.applyColour = function() {
|
||||
this.todayColour_ = this.sourceBlock_.style.colourPrimary;
|
||||
this.selectedColour_ = this.sourceBlock_.style.colourSecondary;
|
||||
this.updateEditor_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the picker to show the current date and currently selected date.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.updateEditor_ = function() {
|
||||
if (!this.picker_) {
|
||||
// Nothing to update.
|
||||
return;
|
||||
}
|
||||
|
||||
// Updating today should come before updating selected, so that if the
|
||||
// current day is selected, it will appear so.
|
||||
if (this.oldTodayElement_) {
|
||||
this.oldTodayElement_.style.backgroundColor = null;
|
||||
this.oldTodayElement_.style.color = null;
|
||||
}
|
||||
var today = this.picker_.getElementByClass('goog-date-picker-today');
|
||||
this.oldTodayElement_ = today;
|
||||
if (today) {
|
||||
today.style.backgroundColor = this.todayColour_;
|
||||
today.style.color = 'white';
|
||||
}
|
||||
|
||||
if (this.oldSelectedElement_ && this.oldSelectedElement_ != today) {
|
||||
this.oldSelectedElement_.style.backgroundColor = null;
|
||||
this.oldSelectedElement_.style.color = null;
|
||||
}
|
||||
var selected = this.picker_.getElementByClass('goog-date-picker-selected');
|
||||
this.oldSelectedElement_ = selected;
|
||||
if (selected) {
|
||||
selected.style.backgroundColor = this.selectedColour_;
|
||||
selected.style.color = this.todayColour_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and show the date field's editor.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.showEditor_ = function() {
|
||||
this.picker_ = this.dropdownCreate_();
|
||||
this.picker_.render(Blockly.DropDownDiv.getContentDiv());
|
||||
Blockly.utils.dom.addClass(this.picker_.getElement(), 'blocklyDatePicker');
|
||||
Blockly.DropDownDiv.setColour(
|
||||
this.DROPDOWN_BACKGROUND_COLOUR, this.DROPDOWN_BORDER_COLOUR);
|
||||
|
||||
Blockly.DropDownDiv.showPositionedByField(
|
||||
this, this.dropdownDispose_.bind(this));
|
||||
|
||||
this.updateEditor_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the date dropdown editor.
|
||||
* @return {!goog.ui.DatePicker} The newly created date picker.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.dropdownCreate_ = function() {
|
||||
// Create the date picker using Closure.
|
||||
Blockly.FieldDate.loadLanguage_();
|
||||
var picker = new goog.ui.DatePicker();
|
||||
picker.setAllowNone(false);
|
||||
picker.setShowWeekNum(false);
|
||||
picker.setUseNarrowWeekdayNames(true);
|
||||
picker.setUseSimpleNavigationMenu(true);
|
||||
picker.setDate(goog.date.DateTime.fromIsoString(this.getValue()));
|
||||
|
||||
this.changeEventKey_ = goog.events.listen(
|
||||
picker,
|
||||
goog.ui.DatePicker.Events.CHANGE,
|
||||
this.onDateSelected_,
|
||||
null,
|
||||
this);
|
||||
this.activeMonthEventKey_ = goog.events.listen(
|
||||
picker,
|
||||
goog.ui.DatePicker.Events.CHANGE_ACTIVE_MONTH,
|
||||
this.updateEditor_,
|
||||
null,
|
||||
this);
|
||||
|
||||
return picker;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of references to DOM elements and events belonging
|
||||
* to the date editor.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.dropdownDispose_ = function() {
|
||||
goog.events.unlistenByKey(this.changeEventKey_);
|
||||
goog.events.unlistenByKey(this.activeMonthEventKey_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a CHANGE event in the date picker.
|
||||
* @param {!Event} event The CHANGE event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.prototype.onDateSelected_ = function(event) {
|
||||
var date = event.date ? event.date.toIsoString(true) : '';
|
||||
this.setValue(date);
|
||||
Blockly.DropDownDiv.hideIfOwner(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the best language pack by scanning the Blockly.Msg object for a
|
||||
* language that matches the available languages in Closure.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDate.loadLanguage_ = function() {
|
||||
for (var prop in goog.i18n) {
|
||||
if (Blockly.utils.string.startsWith(prop, 'DateTimeSymbols_')) {
|
||||
var lang = prop.substr(16).toLowerCase().replace('_', '.');
|
||||
// E.g. 'DateTimeSymbols_pt_BR' -> 'pt.br'
|
||||
if (goog.getObjectByName(lang, Blockly.Msg)) {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n[prop];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CSS for date picker. See css.js for use.
|
||||
*/
|
||||
Blockly.Css.register([
|
||||
/* eslint-disable indent */
|
||||
'.blocklyDatePicker,',
|
||||
'.blocklyDatePicker th,',
|
||||
'.blocklyDatePicker td {',
|
||||
'font: 13px Arial, sans-serif;',
|
||||
'color: #3c4043;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker th,',
|
||||
'.blocklyDatePicker td {',
|
||||
'text-align: center;',
|
||||
'vertical-align: middle;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker .goog-date-picker-wday,',
|
||||
'.blocklyDatePicker .goog-date-picker-date {',
|
||||
'padding: 6px 6px;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker button {',
|
||||
'cursor: pointer;',
|
||||
'padding: 6px 6px;',
|
||||
'margin: 1px 0;',
|
||||
'border: 0;',
|
||||
'color: #3c4043;',
|
||||
'font-weight: bold;',
|
||||
'background: transparent;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker .goog-date-picker-previousMonth,',
|
||||
'.blocklyDatePicker .goog-date-picker-nextMonth {',
|
||||
'height: 24px;',
|
||||
'width: 24px;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker .goog-date-picker-monthyear {',
|
||||
'font-weight: bold;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker .goog-date-picker-wday, ',
|
||||
'.blocklyDatePicker .goog-date-picker-other-month {',
|
||||
'color: #70757a;',
|
||||
'border-radius: 12px;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker button,',
|
||||
'.blocklyDatePicker .goog-date-picker-date {',
|
||||
'cursor: pointer;',
|
||||
'background-color: rgb(218, 220, 224, 0);',
|
||||
'border-radius: 12px;',
|
||||
'transition: background-color,opacity 100ms linear;',
|
||||
'}',
|
||||
|
||||
'.blocklyDatePicker button:hover,',
|
||||
'.blocklyDatePicker .goog-date-picker-date:hover {',
|
||||
'background-color: rgb(218, 220, 224, .5);',
|
||||
'}'
|
||||
/* eslint-enable indent */
|
||||
]);
|
||||
|
||||
Blockly.fieldRegistry.register('field_date', Blockly.FieldDate);
|
||||
|
||||
|
||||
/**
|
||||
* Back up original getMsg function.
|
||||
* @type {!Function}
|
||||
*/
|
||||
goog.getMsgOrig = goog.getMsg;
|
||||
|
||||
/**
|
||||
* Gets a localized message.
|
||||
* Overrides the default Closure function to check for a Blockly.Msg first.
|
||||
* Used infrequently, only known case is TODAY button in date picker.
|
||||
* @param {string} str Translatable string, places holders in the form {$foo}.
|
||||
* @param {Object.<string, string>=} opt_values Maps place holder name to value.
|
||||
* @return {string} Message with placeholders filled.
|
||||
* @suppress {duplicate}
|
||||
*/
|
||||
goog.getMsg = function(str, opt_values) {
|
||||
var key = goog.getMsg.blocklyMsgMap[str];
|
||||
if (key) {
|
||||
str = Blockly.Msg[key];
|
||||
}
|
||||
return goog.getMsgOrig(str, opt_values);
|
||||
};
|
||||
|
||||
/**
|
||||
* Mapping of Closure messages to Blockly.Msg names.
|
||||
*/
|
||||
goog.getMsg.blocklyMsgMap = {
|
||||
'Today': 'TODAY'
|
||||
};
|
||||
+21
-18
@@ -67,6 +67,20 @@ Blockly.FieldDropdown = function(menuGenerator, opt_validator, opt_config) {
|
||||
*/
|
||||
this.generatedOptions_ = null;
|
||||
|
||||
/**
|
||||
* The prefix field label, of common words set after options are trimmed.
|
||||
* @type {?string}
|
||||
* @package
|
||||
*/
|
||||
this.prefixField = null;
|
||||
|
||||
/**
|
||||
* The suffix field label, of common words set after options are trimmed.
|
||||
* @type {?string}
|
||||
* @package
|
||||
*/
|
||||
this.suffixField = null;
|
||||
|
||||
this.trimOptions_();
|
||||
|
||||
/**
|
||||
@@ -258,7 +272,7 @@ Blockly.FieldDropdown.prototype.createSVGArrow_ = function() {
|
||||
* Create a dropdown menu under the text.
|
||||
* @param {Event=} opt_e Optional mouse event that triggered the field to open,
|
||||
* or undefined if triggered programmatically.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
|
||||
this.menu_ = this.dropdownCreate_();
|
||||
@@ -270,8 +284,8 @@ Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
|
||||
}
|
||||
// Element gets created in render.
|
||||
this.menu_.render(Blockly.DropDownDiv.getContentDiv());
|
||||
Blockly.utils.dom.addClass(
|
||||
/** @type {!Element} */ (this.menu_.getElement()), 'blocklyDropdownMenu');
|
||||
var menuElement = /** @type {!Element} */ (this.menu_.getElement());
|
||||
Blockly.utils.dom.addClass(menuElement, 'blocklyDropdownMenu');
|
||||
|
||||
if (this.getConstants().FIELD_DROPDOWN_COLOURED_DIV) {
|
||||
var primaryColour = (this.sourceBlock_.isShadow()) ?
|
||||
@@ -291,11 +305,8 @@ Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
|
||||
// view. See issue #1329.
|
||||
this.menu_.focus();
|
||||
|
||||
// Scroll the dropdown to show the selected menu item.
|
||||
if (this.selectedMenuItem_) {
|
||||
Blockly.utils.style.scrollIntoContainerView(
|
||||
/** @type {!Element} */ (this.selectedMenuItem_.getElement()),
|
||||
/** @type {!Element} */ (this.menu_.getElement()));
|
||||
this.menu_.setHighlighted(this.selectedMenuItem_);
|
||||
}
|
||||
|
||||
this.applyColour();
|
||||
@@ -308,7 +319,6 @@ Blockly.FieldDropdown.prototype.showEditor_ = function(opt_e) {
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.dropdownCreate_ = function() {
|
||||
var menu = new Blockly.Menu();
|
||||
menu.setRightToLeft(this.sourceBlock_.RTL);
|
||||
menu.setRole(Blockly.utils.aria.Role.LISTBOX);
|
||||
|
||||
var options = this.getOptions(false);
|
||||
@@ -323,12 +333,11 @@ Blockly.FieldDropdown.prototype.dropdownCreate_ = function() {
|
||||
image.alt = content['alt'] || '';
|
||||
content = image;
|
||||
}
|
||||
var menuItem = new Blockly.MenuItem(content);
|
||||
var menuItem = new Blockly.MenuItem(content, value);
|
||||
menuItem.setRole(Blockly.utils.aria.Role.OPTION);
|
||||
menuItem.setRightToLeft(this.sourceBlock_.RTL);
|
||||
menuItem.setValue(value);
|
||||
menuItem.setCheckable(true);
|
||||
menu.addChild(menuItem, true);
|
||||
menu.addChild(menuItem);
|
||||
menuItem.setChecked(value == this.value_);
|
||||
if (value == this.value_) {
|
||||
this.selectedMenuItem_ = menuItem;
|
||||
@@ -336,10 +345,6 @@ Blockly.FieldDropdown.prototype.dropdownCreate_ = function() {
|
||||
menuItem.onAction(this.handleMenuActionEvent_, this);
|
||||
}
|
||||
|
||||
Blockly.utils.aria.setState(/** @type {!Element} */ (menu.getElement()),
|
||||
Blockly.utils.aria.State.ACTIVEDESCENDANT,
|
||||
this.selectedMenuItem_ ? this.selectedMenuItem_.getId() : '');
|
||||
|
||||
return menu;
|
||||
};
|
||||
|
||||
@@ -382,8 +387,6 @@ Blockly.FieldDropdown.prototype.onItemSelected_ = function(menu, menuItem) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.trimOptions_ = function() {
|
||||
this.prefixField = null;
|
||||
this.suffixField = null;
|
||||
var options = this.menuGenerator_;
|
||||
if (!Array.isArray(options)) {
|
||||
return;
|
||||
@@ -549,7 +552,7 @@ Blockly.FieldDropdown.prototype.applyColour = function() {
|
||||
|
||||
/**
|
||||
* Draws the border with the correct width.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.render_ = function() {
|
||||
// Hide both elements.
|
||||
|
||||
+9
-2
@@ -22,7 +22,7 @@ goog.require('Blockly.utils.Size');
|
||||
|
||||
/**
|
||||
* Class for an image on a block.
|
||||
* @param {string} src The URL of the image. Defaults to an empty string.
|
||||
* @param {string} src The URL of the image.
|
||||
* @param {!(string|number)} width Width of the image.
|
||||
* @param {!(string|number)} height Height of the image.
|
||||
* @param {string=} opt_alt Optional alt text for when block is collapsed.
|
||||
@@ -70,7 +70,7 @@ Blockly.FieldImage = function(src, width, height,
|
||||
this.altText_ = '';
|
||||
|
||||
Blockly.FieldImage.superClass_.constructor.call(
|
||||
this, src || '', null, opt_config);
|
||||
this, src, null, opt_config);
|
||||
|
||||
if (!opt_config) { // If the config wasn't passed, do old configuration.
|
||||
this.flipRtl_ = !!opt_flipRtl;
|
||||
@@ -114,6 +114,13 @@ Blockly.FieldImage = function(src, width, height,
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldImage, Blockly.Field);
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldImage.prototype.DEFAULT_VALUE = '';
|
||||
|
||||
/**
|
||||
* Construct a FieldImage from a JSON arg object,
|
||||
* dereferencing any string table references.
|
||||
|
||||
+7
-3
@@ -40,9 +40,6 @@ Blockly.FieldLabel = function(opt_value, opt_class, opt_config) {
|
||||
*/
|
||||
this.class_ = null;
|
||||
|
||||
if (opt_value == null) {
|
||||
opt_value = '';
|
||||
}
|
||||
Blockly.FieldLabel.superClass_.constructor.call(
|
||||
this, opt_value, null, opt_config);
|
||||
|
||||
@@ -52,6 +49,13 @@ Blockly.FieldLabel = function(opt_value, opt_class, opt_config) {
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldLabel, Blockly.Field);
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldLabel.prototype.DEFAULT_VALUE = '';
|
||||
|
||||
/**
|
||||
* Construct a FieldLabel from a JSON arg object,
|
||||
* dereferencing any string table references.
|
||||
|
||||
@@ -43,9 +43,6 @@ goog.require('Blockly.utils.userAgent');
|
||||
Blockly.FieldMultilineInput = function(opt_value, opt_validator, opt_config) {
|
||||
// TODO: Once this field is documented the opt_config link should point to its
|
||||
// creation documentation, rather than the text input field's.
|
||||
if (opt_value == null) {
|
||||
opt_value = '';
|
||||
}
|
||||
Blockly.FieldMultilineInput.superClass_.constructor.call(this,
|
||||
opt_value, opt_validator, opt_config);
|
||||
|
||||
@@ -59,7 +56,6 @@ Blockly.FieldMultilineInput = function(opt_value, opt_validator, opt_config) {
|
||||
Blockly.utils.object.inherits(Blockly.FieldMultilineInput,
|
||||
Blockly.FieldTextInput);
|
||||
|
||||
|
||||
/**
|
||||
* Construct a FieldMultilineInput from a JSON arg object,
|
||||
* dereferencing any string table references.
|
||||
|
||||
@@ -67,7 +67,7 @@ Blockly.FieldNumber = function(opt_value, opt_min, opt_max, opt_precision,
|
||||
this.decimalPlaces_ = null;
|
||||
|
||||
Blockly.FieldNumber.superClass_.constructor.call(
|
||||
this, opt_value || 0, opt_validator, opt_config);
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
|
||||
if (!opt_config) { // Only do one kind of configuration or the other.
|
||||
this.setConstraints(opt_min, opt_max, opt_precision);
|
||||
@@ -75,6 +75,13 @@ Blockly.FieldNumber = function(opt_value, opt_min, opt_max, opt_precision,
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldNumber, Blockly.FieldTextInput);
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.DEFAULT_VALUE = 0;
|
||||
|
||||
/**
|
||||
* Construct a FieldNumber from a JSON arg object.
|
||||
* @param {!Object} options A JSON object with options (value, min, max, and
|
||||
|
||||
+8
-29
@@ -14,39 +14,23 @@
|
||||
|
||||
goog.provide('Blockly.fieldRegistry');
|
||||
|
||||
goog.require('Blockly.registry');
|
||||
|
||||
/**
|
||||
* The set of all registered fields, keyed by field type as used in the JSON
|
||||
* definition of a block.
|
||||
* @type {!Object<string, !{fromJson: Function}>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.fieldRegistry.typeMap_ = {};
|
||||
|
||||
/**
|
||||
* Registers a field type.
|
||||
* Blockly.fieldRegistry.fromJson uses this registry to
|
||||
* find the appropriate field type.
|
||||
* @param {string} type The field type name as used in the JSON definition.
|
||||
* @param {!{fromJson: Function}} fieldClass The field class containing a
|
||||
* fromJson function that can construct an instance of the field.
|
||||
* @param {?function(new:Blockly.Field, ...?)} fieldClass The field class
|
||||
* containing a fromJson function that can construct an instance of the
|
||||
* field.
|
||||
* @throws {Error} if the type name is empty, the field is already
|
||||
* registered, or the fieldClass is not an object containing a fromJson
|
||||
* function.
|
||||
*/
|
||||
Blockly.fieldRegistry.register = function(type, fieldClass) {
|
||||
if ((typeof type != 'string') || (type.trim() == '')) {
|
||||
throw Error('Invalid field type "' + type + '". The type must be a' +
|
||||
' non-empty string.');
|
||||
}
|
||||
if (Blockly.fieldRegistry.typeMap_[type]) {
|
||||
throw Error('Error: Field "' + type + '" is already registered.');
|
||||
}
|
||||
if (!fieldClass || (typeof fieldClass.fromJson != 'function')) {
|
||||
throw Error('Field "' + fieldClass + '" must have a fromJson function');
|
||||
}
|
||||
type = type.toLowerCase();
|
||||
Blockly.fieldRegistry.typeMap_[type] = fieldClass;
|
||||
Blockly.registry.register(Blockly.registry.Type.FIELD, type, fieldClass);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -54,12 +38,7 @@ Blockly.fieldRegistry.register = function(type, fieldClass) {
|
||||
* @param {string} type The field type name as used in the JSON definition.
|
||||
*/
|
||||
Blockly.fieldRegistry.unregister = function(type) {
|
||||
if (Blockly.fieldRegistry.typeMap_[type]) {
|
||||
delete Blockly.fieldRegistry.typeMap_[type];
|
||||
} else {
|
||||
console.warn('No field mapping for type "' + type +
|
||||
'" found to unregister');
|
||||
}
|
||||
Blockly.registry.unregister(Blockly.registry.Type.FIELD, type);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -73,8 +52,8 @@ Blockly.fieldRegistry.unregister = function(type) {
|
||||
* @package
|
||||
*/
|
||||
Blockly.fieldRegistry.fromJson = function(options) {
|
||||
var type = options['type'].toLowerCase();
|
||||
var fieldClass = Blockly.fieldRegistry.typeMap_[type];
|
||||
var fieldClass = /** @type {{fromJson:function(!Object):!Blockly.Field}} */ (
|
||||
Blockly.registry.getClass(Blockly.registry.Type.FIELD, options['type']));
|
||||
if (!fieldClass) {
|
||||
console.warn('Blockly could not create a field of type ' + options['type'] +
|
||||
'. The field is probably not being registered. This could be because' +
|
||||
|
||||
+16
-4
@@ -48,9 +48,6 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
|
||||
*/
|
||||
this.spellcheck_ = true;
|
||||
|
||||
if (opt_value == null) {
|
||||
opt_value = '';
|
||||
}
|
||||
Blockly.FieldTextInput.superClass_.constructor.call(this,
|
||||
opt_value, opt_validator, opt_config);
|
||||
|
||||
@@ -80,9 +77,23 @@ Blockly.FieldTextInput = function(opt_value, opt_validator, opt_config) {
|
||||
* @type {?boolean}
|
||||
*/
|
||||
this.fullBlockClickTarget_ = false;
|
||||
|
||||
/**
|
||||
* The workspace that this field belongs to.
|
||||
* @type {?Blockly.WorkspaceSvg}
|
||||
* @protected
|
||||
*/
|
||||
this.workspace_ = null;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldTextInput, Blockly.Field);
|
||||
|
||||
/**
|
||||
* The default value for this field.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldTextInput.prototype.DEFAULT_VALUE = '';
|
||||
|
||||
/**
|
||||
* Construct a FieldTextInput from a JSON arg object,
|
||||
* dereferencing any string table references.
|
||||
@@ -275,7 +286,8 @@ Blockly.FieldTextInput.prototype.setSpellcheck = function(check) {
|
||||
*/
|
||||
Blockly.FieldTextInput.prototype.showEditor_ = function(_opt_e,
|
||||
opt_quietInput) {
|
||||
this.workspace_ = this.sourceBlock_.workspace;
|
||||
this.workspace_ =
|
||||
(/** @type {!Blockly.BlockSvg} */ (this.sourceBlock_)).workspace;
|
||||
var quietInput = opt_quietInput || false;
|
||||
if (!quietInput && (Blockly.utils.userAgent.MOBILE ||
|
||||
Blockly.utils.userAgent.ANDROID ||
|
||||
|
||||
@@ -98,13 +98,6 @@ Blockly.FieldVariable.fromJson = function(options) {
|
||||
varName, undefined, undefined, undefined, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* The workspace that this variable field belongs to.
|
||||
* @type {?Blockly.Workspace}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldVariable.prototype.workspace_ = null;
|
||||
|
||||
/**
|
||||
* Serializable fields are saved by the XML renderer, non-serializable fields
|
||||
* are not. Editable fields should also be serializable.
|
||||
|
||||
+275
-90
@@ -29,15 +29,24 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
* @param {!Blockly.Options} workspaceOptions Dictionary of options for the
|
||||
* workspace.
|
||||
* @constructor
|
||||
* @abstract
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
* @implements {Blockly.IDeleteArea}
|
||||
*/
|
||||
Blockly.Flyout = function(workspaceOptions) {
|
||||
workspaceOptions.getMetrics = this.getMetrics_.bind(this);
|
||||
workspaceOptions.getMetrics =
|
||||
/** @type {function():!Blockly.utils.Metrics} */ (
|
||||
this.getMetrics_.bind(this));
|
||||
workspaceOptions.setMetrics = this.setMetrics_.bind(this);
|
||||
|
||||
/**
|
||||
@@ -55,6 +64,13 @@ Blockly.Flyout = function(workspaceOptions) {
|
||||
*/
|
||||
this.RTL = !!workspaceOptions.RTL;
|
||||
|
||||
/**
|
||||
* Whether the flyout should be laid out horizontally or not.
|
||||
* @type {boolean}
|
||||
* @package
|
||||
*/
|
||||
this.horizontalLayout = false;
|
||||
|
||||
/**
|
||||
* Position of the toolbox and flyout relative to the workspace.
|
||||
* @type {number}
|
||||
@@ -105,6 +121,13 @@ Blockly.Flyout = function(workspaceOptions) {
|
||||
* @const
|
||||
*/
|
||||
this.tabWidth_ = this.workspace_.getRenderer().getConstants().TAB_WIDTH;
|
||||
|
||||
/**
|
||||
* The target workspace
|
||||
* @type {?Blockly.WorkspaceSvg}
|
||||
* @package
|
||||
*/
|
||||
this.targetWorkspace = null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -232,12 +255,15 @@ Blockly.Flyout.prototype.createDom = function(tagName) {
|
||||
* create new blocks.
|
||||
*/
|
||||
Blockly.Flyout.prototype.init = function(targetWorkspace) {
|
||||
this.targetWorkspace_ = targetWorkspace;
|
||||
this.targetWorkspace = targetWorkspace;
|
||||
this.workspace_.targetWorkspace = targetWorkspace;
|
||||
|
||||
// Add scrollbar.
|
||||
this.scrollbar_ = new Blockly.Scrollbar(this.workspace_,
|
||||
this.horizontalLayout_, false, 'blocklyFlyoutScrollbar');
|
||||
/**
|
||||
* @type {!Blockly.Scrollbar}
|
||||
* @package
|
||||
*/
|
||||
this.scrollbar = new Blockly.Scrollbar(this.workspace_,
|
||||
this.horizontalLayout, false, 'blocklyFlyoutScrollbar');
|
||||
|
||||
this.hide();
|
||||
|
||||
@@ -245,7 +271,7 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) {
|
||||
Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this, this.wheel_));
|
||||
if (!this.autoClose) {
|
||||
this.filterWrapper_ = this.filterForCapacity_.bind(this);
|
||||
this.targetWorkspace_.addChangeListener(this.filterWrapper_);
|
||||
this.targetWorkspace.addChangeListener(this.filterWrapper_);
|
||||
}
|
||||
|
||||
// Dragging the flyout up and down.
|
||||
@@ -255,10 +281,10 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) {
|
||||
|
||||
// A flyout connected to a workspace doesn't have its own current gesture.
|
||||
this.workspace_.getGesture =
|
||||
this.targetWorkspace_.getGesture.bind(this.targetWorkspace_);
|
||||
this.targetWorkspace.getGesture.bind(this.targetWorkspace);
|
||||
|
||||
// Get variables from the main workspace rather than the target workspace.
|
||||
this.workspace_.setVariableMap(this.targetWorkspace_.getVariableMap());
|
||||
this.workspace_.setVariableMap(this.targetWorkspace.getVariableMap());
|
||||
|
||||
this.workspace_.createPotentialVariableMap();
|
||||
};
|
||||
@@ -272,12 +298,12 @@ Blockly.Flyout.prototype.dispose = function() {
|
||||
this.hide();
|
||||
Blockly.unbindEvent_(this.eventWrappers_);
|
||||
if (this.filterWrapper_) {
|
||||
this.targetWorkspace_.removeChangeListener(this.filterWrapper_);
|
||||
this.targetWorkspace.removeChangeListener(this.filterWrapper_);
|
||||
this.filterWrapper_ = null;
|
||||
}
|
||||
if (this.scrollbar_) {
|
||||
this.scrollbar_.dispose();
|
||||
this.scrollbar_ = null;
|
||||
if (this.scrollbar) {
|
||||
this.scrollbar.dispose();
|
||||
this.scrollbar = null;
|
||||
}
|
||||
if (this.workspace_) {
|
||||
this.workspace_.getThemeManager().unsubscribe(this.svgBackground_);
|
||||
@@ -290,7 +316,7 @@ Blockly.Flyout.prototype.dispose = function() {
|
||||
this.svgGroup_ = null;
|
||||
}
|
||||
this.svgBackground_ = null;
|
||||
this.targetWorkspace_ = null;
|
||||
this.targetWorkspace = null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -367,7 +393,7 @@ Blockly.Flyout.prototype.updateDisplay_ = function() {
|
||||
this.svgGroup_.style.display = show ? 'block' : 'none';
|
||||
// Update the scrollbar's visibility too since it should mimic the
|
||||
// flyout's visibility.
|
||||
this.scrollbar_.setContainerVisible(show);
|
||||
this.scrollbar.setContainerVisible(show);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -392,14 +418,14 @@ Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) {
|
||||
}
|
||||
|
||||
// Update the scrollbar (if one exists).
|
||||
if (this.scrollbar_) {
|
||||
if (this.scrollbar) {
|
||||
// Set the scrollbars origin to be the top left of the flyout.
|
||||
this.scrollbar_.setOrigin(x, y);
|
||||
this.scrollbar_.resize();
|
||||
this.scrollbar.setOrigin(x, y);
|
||||
this.scrollbar.resize();
|
||||
// Set the position again so that if the metrics were the same (and the
|
||||
// resize failed) our position is still updated.
|
||||
this.scrollbar_.setPosition_(
|
||||
this.scrollbar_.position_.x, this.scrollbar_.position_.y);
|
||||
this.scrollbar.setPosition(
|
||||
this.scrollbar.position.x, this.scrollbar.position.y);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -426,82 +452,41 @@ 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.');
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// <block type="math_arithmetic" gap="8"></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.
|
||||
// <sep gap="36"></sep>
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.<!Object>, gaps:!Array.<number>}} */ (
|
||||
this.createFlyoutInfo_(parsedContent));
|
||||
|
||||
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.
|
||||
@@ -515,7 +500,7 @@ Blockly.Flyout.prototype.show = function(xmlList) {
|
||||
this.listeners_.push(Blockly.bindEventWithChecks_(this.svgBackground_,
|
||||
'mouseover', this, deselectAll));
|
||||
|
||||
if (this.horizontalLayout_) {
|
||||
if (this.horizontalLayout) {
|
||||
this.height_ = 0;
|
||||
} else {
|
||||
this.width_ = 0;
|
||||
@@ -532,6 +517,142 @@ 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.<Blockly.utils.toolbox.Toolbox>} parsedContent The array
|
||||
* of objects to show in the flyout.
|
||||
* @return {{contents:Array.<Object>, gaps:Array.<number>}} 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.
|
||||
// <block type="math_arithmetic" gap="8"></block>
|
||||
var gap = parseInt(blockInfo['gap'] || 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_,
|
||||
/** @type {!Blockly.WorkspaceSvg} */ (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.BlockSvg} The block created from the blockXml.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.createBlock_ = function(blockXml) {
|
||||
var curBlock = /** @type {!Blockly.BlockSvg} */ (
|
||||
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 blockElement = null;
|
||||
var blockXml = blockInfo['blockxml'];
|
||||
|
||||
if (blockXml && typeof blockXml != 'string') {
|
||||
blockElement = blockXml;
|
||||
} else if (blockXml && typeof blockXml == 'string') {
|
||||
blockElement = Blockly.Xml.textToDom(blockXml);
|
||||
} else if (blockInfo['type']) {
|
||||
blockElement = Blockly.utils.xml.createElement('xml');
|
||||
blockElement.setAttribute('type', blockInfo['type']);
|
||||
blockElement.setAttribute('disabled', blockInfo['disabled']);
|
||||
}
|
||||
|
||||
if (!blockElement) {
|
||||
throw Error('Error: Invalid block definition. Block definition must have blockxml or type.');
|
||||
}
|
||||
return blockElement;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.<number>} 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.
|
||||
// <sep gap="36"></sep>
|
||||
// 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
|
||||
@@ -566,7 +687,7 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() {
|
||||
/**
|
||||
* Add listeners to a block that has been added to the flyout.
|
||||
* @param {!SVGElement} root The root node of the SVG group the block is in.
|
||||
* @param {!Blockly.Block} block The block to add listeners for.
|
||||
* @param {!Blockly.BlockSvg} block The block to add listeners for.
|
||||
* @param {!SVGElement} rect The invisible rectangle under the block that acts
|
||||
* as a mat for that block.
|
||||
* @protected
|
||||
@@ -588,14 +709,14 @@ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) {
|
||||
|
||||
/**
|
||||
* Handle a mouse-down on an SVG block in a non-closing flyout.
|
||||
* @param {!Blockly.Block} block The flyout block to copy.
|
||||
* @param {!Blockly.BlockSvg} block The flyout block to copy.
|
||||
* @return {!Function} Function to call when block is clicked.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
|
||||
var flyout = this;
|
||||
return function(e) {
|
||||
var gesture = flyout.targetWorkspace_.getGesture(e);
|
||||
var gesture = flyout.targetWorkspace.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.setStartBlock(block);
|
||||
gesture.handleFlyoutStart(e, flyout);
|
||||
@@ -609,7 +730,7 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.onMouseDown_ = function(e) {
|
||||
var gesture = this.targetWorkspace_.getGesture(e);
|
||||
var gesture = this.targetWorkspace.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.handleFlyoutStart(e, this);
|
||||
}
|
||||
@@ -637,8 +758,8 @@ Blockly.Flyout.prototype.isBlockCreatable_ = function(block) {
|
||||
Blockly.Flyout.prototype.createBlock = function(originalBlock) {
|
||||
var newBlock = null;
|
||||
Blockly.Events.disable();
|
||||
var variablesBeforeCreation = this.targetWorkspace_.getAllVariables();
|
||||
this.targetWorkspace_.setResizesEnabled(false);
|
||||
var variablesBeforeCreation = this.targetWorkspace.getAllVariables();
|
||||
this.targetWorkspace.setResizesEnabled(false);
|
||||
try {
|
||||
newBlock = this.placeNewBlock_(originalBlock);
|
||||
// Close the flyout.
|
||||
@@ -647,7 +768,7 @@ Blockly.Flyout.prototype.createBlock = function(originalBlock) {
|
||||
Blockly.Events.enable();
|
||||
}
|
||||
|
||||
var newVariables = Blockly.Variables.getAddedVariables(this.targetWorkspace_,
|
||||
var newVariables = Blockly.Variables.getAddedVariables(this.targetWorkspace,
|
||||
variablesBeforeCreation);
|
||||
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
@@ -690,7 +811,7 @@ Blockly.Flyout.prototype.initFlyoutButton_ = function(button, x, y) {
|
||||
|
||||
/**
|
||||
* Create and place a rectangle corresponding to the given block.
|
||||
* @param {!Blockly.Block} block The block to associate the rect to.
|
||||
* @param {!Blockly.BlockSvg} block The block to associate the rect to.
|
||||
* @param {number} x The x position of the cursor during this layout pass.
|
||||
* @param {number} y The y position of the cursor during this layout pass.
|
||||
* @param {!{height: number, width: number}} blockHW The height and width of the
|
||||
@@ -749,7 +870,7 @@ Blockly.Flyout.prototype.filterForCapacity_ = function() {
|
||||
var blocks = this.workspace_.getTopBlocks(false);
|
||||
for (var i = 0, block; (block = blocks[i]); i++) {
|
||||
if (this.permanentlyDisabled_.indexOf(block) == -1) {
|
||||
var enable = this.targetWorkspace_
|
||||
var enable = this.targetWorkspace
|
||||
.isCapacityAvailable(Blockly.utils.getBlockTypeCounts(block));
|
||||
while (block) {
|
||||
block.setEnabled(enable);
|
||||
@@ -778,7 +899,7 @@ Blockly.Flyout.prototype.reflow = function() {
|
||||
* @package
|
||||
*/
|
||||
Blockly.Flyout.prototype.isScrollable = function() {
|
||||
return this.scrollbar_ ? this.scrollbar_.isVisible() : false;
|
||||
return this.scrollbar ? this.scrollbar.isVisible() : false;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -788,7 +909,7 @@ Blockly.Flyout.prototype.isScrollable = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) {
|
||||
var targetWorkspace = this.targetWorkspace_;
|
||||
var targetWorkspace = this.targetWorkspace;
|
||||
var svgRootOld = oldBlock.getSvgRoot();
|
||||
if (!svgRootOld) {
|
||||
throw Error('oldBlock is not rendered.');
|
||||
@@ -850,3 +971,67 @@ Blockly.Flyout.prototype.onBlocklyAction = function(action) {
|
||||
var cursor = this.workspace_.getCursor();
|
||||
return cursor.onBlocklyAction(action);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the deletion rectangle for this flyout in viewport coordinates.
|
||||
* @return {Blockly.utils.Rect} Rectangle in which to delete.
|
||||
*/
|
||||
Blockly.Flyout.prototype.getClientRect;
|
||||
|
||||
/**
|
||||
* Position the flyout.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.Flyout.prototype.position;
|
||||
|
||||
/**
|
||||
* Determine if a drag delta is toward the workspace, based on the position
|
||||
* and orientation of the flyout. This is used in determineDragIntention_ to
|
||||
* determine if a new block should be created or if the flyout should scroll.
|
||||
* @param {!Blockly.utils.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @return {boolean} True if the drag is toward the workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Flyout.prototype.isDragTowardWorkspace;
|
||||
|
||||
/**
|
||||
* Return an object with all the metrics required to size scrollbars for the
|
||||
* flyout.
|
||||
* @return {Blockly.utils.Metrics} Contains size and position metrics of the
|
||||
* flyout.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Flyout.prototype.getMetrics_;
|
||||
|
||||
/**
|
||||
* Sets the translation of the flyout to match the scrollbars.
|
||||
* @param {!{x:number,y:number}} xyRatio Contains a y property which is a float
|
||||
* between 0 and 1 specifying the degree of scrolling and a
|
||||
* similar x property.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Flyout.prototype.setMetrics_;
|
||||
|
||||
/**
|
||||
* Lay out the blocks in the flyout.
|
||||
* @param {!Array.<!Object>} contents The blocks and buttons to lay out.
|
||||
* @param {!Array.<number>} gaps The visible gaps between blocks.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Flyout.prototype.layout_;
|
||||
|
||||
/**
|
||||
* Scroll the flyout.
|
||||
* @param {!Event} e Mouse wheel scroll event.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Flyout.prototype.wheel_;
|
||||
|
||||
/**
|
||||
* Compute height of flyout. Position mat under each block.
|
||||
* For RTL: Lay out the blocks right-aligned.
|
||||
* @return {void}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Flyout.prototype.reflowInternal_;
|
||||
|
||||
@@ -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.
|
||||
@@ -166,7 +168,7 @@ Blockly.FlyoutButton.prototype.createDom = function() {
|
||||
var fontMetrics = Blockly.utils.dom.measureFontMetrics(text, fontSize,
|
||||
fontWeight, fontFamily);
|
||||
this.height = fontMetrics.height;
|
||||
|
||||
|
||||
if (!this.isLabel_) {
|
||||
this.width += 2 * Blockly.FlyoutButton.MARGIN_X;
|
||||
this.height += 2 * Blockly.FlyoutButton.MARGIN_Y;
|
||||
|
||||
@@ -37,7 +37,7 @@ Blockly.FlyoutDragger = function(flyout) {
|
||||
* @type {!Blockly.Scrollbar}
|
||||
* @private
|
||||
*/
|
||||
this.scrollbar_ = flyout.scrollbar_;
|
||||
this.scrollbar_ = flyout.scrollbar;
|
||||
|
||||
/**
|
||||
* Whether the flyout scrolls horizontally. If false, the flyout scrolls
|
||||
@@ -45,7 +45,7 @@ Blockly.FlyoutDragger = function(flyout) {
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = flyout.horizontalLayout_;
|
||||
this.horizontalLayout_ = flyout.horizontalLayout;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FlyoutDragger, Blockly.WorkspaceDragger);
|
||||
|
||||
|
||||
+26
-28
@@ -20,6 +20,8 @@ goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.WidgetDiv');
|
||||
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
@@ -29,17 +31,9 @@ goog.require('Blockly.WidgetDiv');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.HorizontalFlyout = function(workspaceOptions) {
|
||||
workspaceOptions.getMetrics = /** @type {function():!Object} */ (
|
||||
this.getMetrics_.bind(this));
|
||||
workspaceOptions.setMetrics = this.setMetrics_.bind(this);
|
||||
|
||||
Blockly.HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions);
|
||||
/**
|
||||
* Flyout should be laid out horizontally.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = true;
|
||||
|
||||
this.horizontalLayout = true;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.HorizontalFlyout, Blockly.Flyout);
|
||||
|
||||
@@ -56,8 +50,9 @@ Blockly.utils.object.inherits(Blockly.HorizontalFlyout, Blockly.Flyout);
|
||||
* .viewLeft: Offset of the left edge of visible rectangle from parent,
|
||||
* .contentLeft: Offset of the left-most content from the x=0 coordinate,
|
||||
* .absoluteLeft: Left-edge of view.
|
||||
* @return {Object} Contains size and position metrics of the flyout.
|
||||
* @private
|
||||
* @return {Blockly.utils.Metrics} Contains size and position metrics of the
|
||||
* flyout.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.getMetrics_ = function() {
|
||||
if (!this.isVisible()) {
|
||||
@@ -84,14 +79,16 @@ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() {
|
||||
var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING;
|
||||
|
||||
var metrics = {
|
||||
viewHeight: viewHeight,
|
||||
viewWidth: viewWidth,
|
||||
contentHeight: (optionBox.height + 2 * this.MARGIN) * this.workspace_.scale,
|
||||
contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale,
|
||||
viewTop: -this.workspace_.scrollY,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
contentTop: 0,
|
||||
contentLeft: 0,
|
||||
|
||||
viewHeight: viewHeight,
|
||||
viewWidth: viewWidth,
|
||||
viewTop: -this.workspace_.scrollY,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
|
||||
absoluteTop: absoluteTop,
|
||||
absoluteLeft: absoluteLeft
|
||||
};
|
||||
@@ -100,10 +97,10 @@ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() {
|
||||
|
||||
/**
|
||||
* Sets the translation of the flyout to match the scrollbars.
|
||||
* @param {!Object} xyRatio Contains a y property which is a float
|
||||
* @param {!{x:number,y:number}} xyRatio Contains a y property which is a float
|
||||
* between 0 and 1 specifying the degree of scrolling and a
|
||||
* similar x property.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) {
|
||||
var metrics = this.getMetrics_();
|
||||
@@ -127,7 +124,7 @@ Blockly.HorizontalFlyout.prototype.position = function() {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
|
||||
var targetWorkspaceMetrics = this.targetWorkspace.getMetrics();
|
||||
if (!targetWorkspaceMetrics) {
|
||||
// Hidden components will return null.
|
||||
return;
|
||||
@@ -142,7 +139,7 @@ Blockly.HorizontalFlyout.prototype.position = function() {
|
||||
// X is always 0 since this is a horizontal flyout.
|
||||
var x = 0;
|
||||
// If this flyout is the toolbox flyout.
|
||||
if (this.targetWorkspace_.toolboxPosition == this.toolboxPosition_) {
|
||||
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) {
|
||||
// If there is a toolbox.
|
||||
if (targetWorkspaceMetrics.toolboxHeight) {
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
|
||||
@@ -220,13 +217,13 @@ Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(width,
|
||||
* Scroll the flyout to the top.
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.scrollToStart = function() {
|
||||
this.scrollbar_.set(this.RTL ? Infinity : 0);
|
||||
this.scrollbar.set(this.RTL ? Infinity : 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout.
|
||||
* @param {!Event} e Mouse wheel scroll event.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.wheel_ = function(e) {
|
||||
var scrollDelta = Blockly.utils.getScrollDeltaPixels(e);
|
||||
@@ -238,9 +235,10 @@ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) {
|
||||
var limit = metrics.contentWidth - metrics.viewWidth;
|
||||
pos = Math.min(pos, limit);
|
||||
pos = Math.max(pos, 0);
|
||||
this.scrollbar_.set(pos);
|
||||
// When the flyout moves from a wheel event, hide WidgetDiv.
|
||||
this.scrollbar.set(pos);
|
||||
// When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv.
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
}
|
||||
|
||||
// Don't scroll the page.
|
||||
@@ -253,10 +251,10 @@ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) {
|
||||
* Lay out the blocks in the flyout.
|
||||
* @param {!Array.<!Object>} contents The blocks and buttons to lay out.
|
||||
* @param {!Array.<number>} gaps The visible gaps between blocks.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
this.workspace_.scale = this.targetWorkspace.scale;
|
||||
var margin = this.MARGIN;
|
||||
var cursorX = margin + this.tabWidth_;
|
||||
var cursorY = margin;
|
||||
@@ -350,10 +348,10 @@ Blockly.HorizontalFlyout.prototype.getClientRect = function() {
|
||||
/**
|
||||
* Compute height of flyout. Position mat under each block.
|
||||
* For RTL: Lay out the blocks right-aligned.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.reflowInternal_ = function() {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
this.workspace_.scale = this.targetWorkspace.scale;
|
||||
var flyoutHeight = 0;
|
||||
var blocks = this.workspace_.getTopBlocks(false);
|
||||
for (var i = 0, block; (block = blocks[i]); i++) {
|
||||
|
||||
+24
-28
@@ -21,6 +21,8 @@ goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
goog.require('Blockly.WidgetDiv');
|
||||
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
@@ -30,17 +32,7 @@ goog.require('Blockly.WidgetDiv');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.VerticalFlyout = function(workspaceOptions) {
|
||||
workspaceOptions.getMetrics = /** @type {function():!Object} */ (
|
||||
this.getMetrics_.bind(this));
|
||||
workspaceOptions.setMetrics = this.setMetrics_.bind(this);
|
||||
|
||||
Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
|
||||
/**
|
||||
* Flyout should be laid out vertically.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = false;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.VerticalFlyout, Blockly.Flyout);
|
||||
|
||||
@@ -57,8 +49,9 @@ Blockly.utils.object.inherits(Blockly.VerticalFlyout, Blockly.Flyout);
|
||||
* .viewLeft: Offset of the left edge of visible rectangle from parent,
|
||||
* .contentLeft: Offset of the left-most content from the x=0 coordinate,
|
||||
* .absoluteLeft: Left-edge of view.
|
||||
* @return {Object} Contains size and position metrics of the flyout.
|
||||
* @private
|
||||
* @return {Blockly.utils.Metrics} Contains size and position metrics of the
|
||||
* flyout.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
|
||||
if (!this.isVisible()) {
|
||||
@@ -84,14 +77,16 @@ Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
|
||||
}
|
||||
|
||||
var metrics = {
|
||||
viewHeight: viewHeight,
|
||||
viewWidth: viewWidth,
|
||||
contentHeight: optionBox.height * this.workspace_.scale + 2 * this.MARGIN,
|
||||
contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN,
|
||||
viewTop: -this.workspace_.scrollY + optionBox.y,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
contentTop: optionBox.y,
|
||||
contentLeft: optionBox.x,
|
||||
|
||||
viewHeight: viewHeight,
|
||||
viewWidth: viewWidth,
|
||||
viewTop: -this.workspace_.scrollY + optionBox.y,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
|
||||
absoluteTop: absoluteTop,
|
||||
absoluteLeft: absoluteLeft
|
||||
};
|
||||
@@ -100,10 +95,10 @@ Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
|
||||
|
||||
/**
|
||||
* Sets the translation of the flyout to match the scrollbars.
|
||||
* @param {!Object} xyRatio Contains a y property which is a float
|
||||
* @param {!{x:number,y:number}} xyRatio Contains a y property which is a float
|
||||
* between 0 and 1 specifying the degree of scrolling and a
|
||||
* similar x property.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
|
||||
var metrics = this.getMetrics_();
|
||||
@@ -125,7 +120,7 @@ Blockly.VerticalFlyout.prototype.position = function() {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
|
||||
var targetWorkspaceMetrics = this.targetWorkspace.getMetrics();
|
||||
if (!targetWorkspaceMetrics) {
|
||||
// Hidden components will return null.
|
||||
return;
|
||||
@@ -140,7 +135,7 @@ Blockly.VerticalFlyout.prototype.position = function() {
|
||||
// Y is always 0 since this is a vertical flyout.
|
||||
var y = 0;
|
||||
// If this flyout is the toolbox flyout.
|
||||
if (this.targetWorkspace_.toolboxPosition == this.toolboxPosition_) {
|
||||
if (this.targetWorkspace.toolboxPosition == this.toolboxPosition_) {
|
||||
// If there is a category toolbox.
|
||||
if (targetWorkspaceMetrics.toolboxWidth) {
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
|
||||
@@ -208,13 +203,13 @@ Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
|
||||
* Scroll the flyout to the top.
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.scrollToStart = function() {
|
||||
this.scrollbar_.set(0);
|
||||
this.scrollbar.set(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout.
|
||||
* @param {!Event} e Mouse wheel scroll event.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
|
||||
var scrollDelta = Blockly.utils.getScrollDeltaPixels(e);
|
||||
@@ -225,9 +220,10 @@ Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
|
||||
var limit = metrics.contentHeight - metrics.viewHeight;
|
||||
pos = Math.min(pos, limit);
|
||||
pos = Math.max(pos, 0);
|
||||
this.scrollbar_.set(pos);
|
||||
// When the flyout moves from a wheel event, hide WidgetDiv.
|
||||
this.scrollbar.set(pos);
|
||||
// When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv.
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
}
|
||||
|
||||
// Don't scroll the page.
|
||||
@@ -240,10 +236,10 @@ Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
|
||||
* Lay out the blocks in the flyout.
|
||||
* @param {!Array.<!Object>} contents The blocks and buttons to lay out.
|
||||
* @param {!Array.<number>} gaps The visible gaps between blocks.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
this.workspace_.scale = this.targetWorkspace.scale;
|
||||
var margin = this.MARGIN;
|
||||
var cursorX = this.RTL ? margin : margin + this.tabWidth_;
|
||||
var cursorY = margin;
|
||||
@@ -329,10 +325,10 @@ Blockly.VerticalFlyout.prototype.getClientRect = function() {
|
||||
/**
|
||||
* Compute width of flyout. Position mat under each block.
|
||||
* For RTL: Lay out the blocks and buttons to be right-aligned.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.reflowInternal_ = function() {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
this.workspace_.scale = this.targetWorkspace.scale;
|
||||
var flyoutWidth = 0;
|
||||
var blocks = this.workspace_.getTopBlocks(false);
|
||||
for (var i = 0, block; (block = blocks[i]); i++) {
|
||||
|
||||
@@ -172,6 +172,10 @@ Blockly.Generator.prototype.blockToCode = function(block, opt_thisOnly) {
|
||||
// Skip past this block if it is disabled.
|
||||
return opt_thisOnly ? '' : this.blockToCode(block.getNextBlock());
|
||||
}
|
||||
if (block.isInsertionMarker()) {
|
||||
// Skip past insertion markers.
|
||||
return opt_thisOnly ? '' : this.blockToCode(block.getChildren(false)[0]);
|
||||
}
|
||||
|
||||
var func = this[block.type];
|
||||
if (typeof func != 'function') {
|
||||
|
||||
+14
-2
@@ -314,7 +314,7 @@ Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
|
||||
}
|
||||
if (!this.flyout_.isScrollable() ||
|
||||
this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)) {
|
||||
this.startWorkspace_ = this.flyout_.targetWorkspace_;
|
||||
this.startWorkspace_ = this.flyout_.targetWorkspace;
|
||||
this.startWorkspace_.updateScreenCalculationsIfScrolled();
|
||||
// Start the event group now, so that the same event group is used for block
|
||||
// creation and block dragging.
|
||||
@@ -489,7 +489,7 @@ Blockly.Gesture.prototype.doStart = function(e) {
|
||||
e.shiftKey &&
|
||||
this.targetBlock_.workspace.keyboardAccessibilityMode) {
|
||||
this.creatorWorkspace_.getCursor().setCurNode(
|
||||
Blockly.navigation.getTopNode(this.targetBlock_));
|
||||
Blockly.ASTNode.createTopNode(this.targetBlock_));
|
||||
} else {
|
||||
this.targetBlock_.select();
|
||||
}
|
||||
@@ -656,6 +656,17 @@ Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fires a workspace click event.
|
||||
* @param {!Blockly.WorkspaceSvg} ws The workspace that a user clicks on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.fireWorkspaceClick_ = function(ws) {
|
||||
var clickEvent = new Blockly.Events.Ui(null, 'workspaceClick', null, null);
|
||||
clickEvent.workspaceId = ws.id;
|
||||
Blockly.Events.fire(clickEvent);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mousedown/touchstart event on a flyout.
|
||||
* @param {!Event} e A mouse down or touch start event.
|
||||
@@ -763,6 +774,7 @@ Blockly.Gesture.prototype.doWorkspaceClick_ = function(e) {
|
||||
} else if (Blockly.selected) {
|
||||
Blockly.selected.unselect();
|
||||
}
|
||||
this.fireWorkspaceClick_(ws);
|
||||
};
|
||||
|
||||
/* End functions defining what actions to take to execute clicks on each type
|
||||
|
||||
@@ -22,6 +22,7 @@ goog.require('Blockly.utils.Size');
|
||||
* Class for an icon.
|
||||
* @param {Blockly.BlockSvg} block The block associated with this icon.
|
||||
* @constructor
|
||||
* @abstract
|
||||
*/
|
||||
Blockly.Icon = function(block) {
|
||||
/**
|
||||
@@ -181,3 +182,10 @@ Blockly.Icon.prototype.getCorrectedSize = function() {
|
||||
return new Blockly.utils.Size(
|
||||
Blockly.Icon.prototype.SIZE, Blockly.Icon.prototype.SIZE - 2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the icon.
|
||||
* @param {!Element} group The icon group.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Icon.prototype.drawIcon_;
|
||||
|
||||
+3
-1
@@ -29,6 +29,8 @@ goog.require('Blockly.utils.userAgent');
|
||||
goog.require('Blockly.WorkspaceDragSurfaceSvg');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* Inject a Blockly editor into the specified container element (usually a div).
|
||||
@@ -364,7 +366,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();
|
||||
}
|
||||
}
|
||||
|
||||
+26
-15
@@ -91,35 +91,39 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
|
||||
if (index < 0 || index > this.fieldRow.length) {
|
||||
throw Error('index ' + index + ' out of bounds.');
|
||||
}
|
||||
|
||||
// Falsy field values don't generate a field, unless the field is an empty
|
||||
// string and named.
|
||||
if (!field && !(field == '' && opt_name)) {
|
||||
return index;
|
||||
}
|
||||
|
||||
// Generate a FieldLabel when given a plain text field.
|
||||
if (typeof field == 'string') {
|
||||
field = new Blockly.FieldLabel(/** @type {string} */ (field));
|
||||
}
|
||||
|
||||
field.setSourceBlock(this.sourceBlock_);
|
||||
if (this.sourceBlock_.rendered) {
|
||||
field.init();
|
||||
}
|
||||
field.name = opt_name;
|
||||
field.setVisible(this.isVisible());
|
||||
|
||||
if (field.prefixField) {
|
||||
var fieldDropdown = /** @type {Blockly.FieldDropdown} */ (field);
|
||||
if (fieldDropdown.prefixField) {
|
||||
// Add any prefix.
|
||||
index = this.insertFieldAt(index, field.prefixField);
|
||||
index = this.insertFieldAt(index, fieldDropdown.prefixField);
|
||||
}
|
||||
// Add the field to the field row.
|
||||
this.fieldRow.splice(index, 0, field);
|
||||
++index;
|
||||
if (field.suffixField) {
|
||||
if (fieldDropdown.suffixField) {
|
||||
// Add any suffix.
|
||||
index = this.insertFieldAt(index, field.suffixField);
|
||||
index = this.insertFieldAt(index, fieldDropdown.suffixField);
|
||||
}
|
||||
|
||||
if (this.sourceBlock_.rendered) {
|
||||
this.sourceBlock_ = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
|
||||
this.sourceBlock_.render();
|
||||
// Adding a field will cause the block to change shape.
|
||||
this.sourceBlock_.bumpNeighbours();
|
||||
@@ -130,22 +134,30 @@ Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) {
|
||||
/**
|
||||
* Remove a field from this input.
|
||||
* @param {string} name The name of the field.
|
||||
* @throws {Error} if the field is not present.
|
||||
* @param {boolean=} opt_quiet True to prevent an error if field is not present.
|
||||
* @return {boolean} True if operation succeeds, false if field is not present
|
||||
* and opt_quiet is true.
|
||||
* @throws {Error} if the field is not present and opt_quiet is false.
|
||||
*/
|
||||
Blockly.Input.prototype.removeField = function(name) {
|
||||
Blockly.Input.prototype.removeField = function(name, opt_quiet) {
|
||||
for (var i = 0, field; (field = this.fieldRow[i]); i++) {
|
||||
if (field.name === name) {
|
||||
field.dispose();
|
||||
this.fieldRow.splice(i, 1);
|
||||
if (this.sourceBlock_.rendered) {
|
||||
this.sourceBlock_ = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
|
||||
this.sourceBlock_.render();
|
||||
// Removing a field will cause the block to change shape.
|
||||
this.sourceBlock_.bumpNeighbours();
|
||||
}
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw Error('Field "%s" not found.', name);
|
||||
if (opt_quiet) {
|
||||
return false;
|
||||
} else {
|
||||
throw Error('Field "' + name + '" not found.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -160,7 +172,7 @@ Blockly.Input.prototype.isVisible = function() {
|
||||
* Sets whether this input is visible or not.
|
||||
* Should only be used to collapse/uncollapse a block.
|
||||
* @param {boolean} visible True if visible.
|
||||
* @return {!Array.<!Blockly.Block>} List of blocks to render.
|
||||
* @return {!Array.<!Blockly.BlockSvg>} List of blocks to render.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Input.prototype.setVisible = function(visible) {
|
||||
@@ -173,11 +185,12 @@ Blockly.Input.prototype.setVisible = function(visible) {
|
||||
}
|
||||
this.visible_ = visible;
|
||||
|
||||
var display = visible ? 'block' : 'none';
|
||||
for (var y = 0, field; (field = this.fieldRow[y]); y++) {
|
||||
field.setVisible(visible);
|
||||
}
|
||||
if (this.connection) {
|
||||
this.connection =
|
||||
/** @type {!Blockly.RenderedConnection} */ (this.connection);
|
||||
// Has a connection.
|
||||
if (visible) {
|
||||
renderList = this.connection.startTrackingAll();
|
||||
@@ -186,10 +199,7 @@ Blockly.Input.prototype.setVisible = function(visible) {
|
||||
}
|
||||
var child = this.connection.targetBlock();
|
||||
if (child) {
|
||||
child.getSvgRoot().style.display = display;
|
||||
if (!visible) {
|
||||
child.rendered = false;
|
||||
}
|
||||
child.getSvgRoot().style.display = visible ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
return renderList;
|
||||
@@ -228,6 +238,7 @@ Blockly.Input.prototype.setCheck = function(check) {
|
||||
Blockly.Input.prototype.setAlign = function(align) {
|
||||
this.align = align;
|
||||
if (this.sourceBlock_.rendered) {
|
||||
this.sourceBlock_ = /** @type {!Blockly.BlockSvg} */ (this.sourceBlock_);
|
||||
this.sourceBlock_.render();
|
||||
}
|
||||
return this;
|
||||
|
||||
@@ -160,6 +160,15 @@ Blockly.InsertionMarkerManager.prototype.dispose = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the available connections for the top block. These connections can
|
||||
* change if a block is unplugged and the stack is healed.
|
||||
* @package
|
||||
*/
|
||||
Blockly.InsertionMarkerManager.prototype.updateAvailableConnections = function() {
|
||||
this.availableConnections_ = this.initAvailableConnections_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return whether the block would be deleted if dropped immediately, based on
|
||||
* information from the most recent move event.
|
||||
@@ -253,23 +262,25 @@ Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlo
|
||||
result.domToMutation(oldMutationDom);
|
||||
}
|
||||
}
|
||||
result.setCollapsed(sourceBlock.isCollapsed());
|
||||
result.setInputsInline(sourceBlock.getInputsInline());
|
||||
// Copy visible field values from the other block. These values may impact
|
||||
// the rendered size of the insertion marker. Note that we do not care
|
||||
// about child blocks here.
|
||||
// Copy field values from the other block. These values may impact the
|
||||
// rendered size of the insertion marker. Note that we do not care about
|
||||
// child blocks here.
|
||||
for (var i = 0; i < sourceBlock.inputList.length; i++) {
|
||||
var sourceInput = sourceBlock.inputList[i];
|
||||
if (sourceInput.isVisible()) {
|
||||
var resultInput = result.inputList[i];
|
||||
for (var j = 0; j < sourceInput.fieldRow.length; j++) {
|
||||
var sourceField = sourceInput.fieldRow[j];
|
||||
var resultField = resultInput.fieldRow[j];
|
||||
resultField.setValue(sourceField.getValue());
|
||||
}
|
||||
if (sourceInput.name == Blockly.Block.COLLAPSED_INPUT_NAME) {
|
||||
continue; // Ignore the collapsed input.
|
||||
}
|
||||
var resultInput = result.inputList[i];
|
||||
for (var j = 0; j < sourceInput.fieldRow.length; j++) {
|
||||
var sourceField = sourceInput.fieldRow[j];
|
||||
var resultField = resultInput.fieldRow[j];
|
||||
resultField.setValue(sourceField.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
result.setCollapsed(sourceBlock.isCollapsed());
|
||||
result.setInputsInline(sourceBlock.getInputsInline());
|
||||
|
||||
result.initSvg();
|
||||
result.getSvgRoot().setAttribute('visibility', 'hidden');
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview AST Node and keyboard navigation interfaces.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IASTNodeLocation');
|
||||
goog.provide('Blockly.IASTNodeLocationSvg');
|
||||
goog.provide('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.provide('Blockly.IBlocklyActionable');
|
||||
|
||||
/**
|
||||
* An AST node location interface.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IASTNodeLocation = function() {};
|
||||
|
||||
/**
|
||||
* An AST node location SVG interface.
|
||||
* @interface
|
||||
* @extends {Blockly.IASTNodeLocation}
|
||||
*/
|
||||
Blockly.IASTNodeLocationSvg = function() {};
|
||||
|
||||
/**
|
||||
* Add the marker svg to this node's svg group.
|
||||
* @param {SVGElement} markerSvg The svg root of the marker to be added to the
|
||||
* svg group.
|
||||
*/
|
||||
Blockly.IASTNodeLocationSvg.prototype.setMarkerSvg;
|
||||
|
||||
/**
|
||||
* Add the cursor svg to this node's svg group.
|
||||
* @param {SVGElement} cursorSvg The svg root of the cursor to be added to the
|
||||
* svg group.
|
||||
*/
|
||||
Blockly.IASTNodeLocationSvg.prototype.setCursorSvg;
|
||||
|
||||
/**
|
||||
* An AST node location that has an associated block.
|
||||
* @interface
|
||||
* @extends {Blockly.IASTNodeLocation}
|
||||
*/
|
||||
Blockly.IASTNodeLocationWithBlock = function() {};
|
||||
|
||||
/**
|
||||
* Get the source block associated with this node.
|
||||
* @return {Blockly.Block} The source block.
|
||||
*/
|
||||
Blockly.IASTNodeLocationWithBlock.prototype.getSourceBlock;
|
||||
|
||||
|
||||
/**
|
||||
* An interface for an object that handles Blockly actions when keyboard
|
||||
* navigation is enabled.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IBlocklyActionable = function() {};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
*/
|
||||
Blockly.IBlocklyActionable.prototype.onBlocklyAction;
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for a bounded element.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IBoundedElement');
|
||||
|
||||
goog.requireType('Blockly.utils.Rect');
|
||||
|
||||
|
||||
/**
|
||||
* A bounded element interface.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IBoundedElement = function() {};
|
||||
|
||||
/**
|
||||
* Returns the coordinates of a bounded element describing the dimensions of the
|
||||
* element.
|
||||
* Coordinate system: workspace coordinates.
|
||||
* @return {!Blockly.utils.Rect} Object with coordinates of the bounded element.
|
||||
*/
|
||||
Blockly.IBoundedElement.prototype.getBoundingRectangle;
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for an object that is copyable.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.ICopyable');
|
||||
|
||||
goog.requireType('Blockly.ISelectable');
|
||||
goog.requireType('Blockly.WorkspaceSvg');
|
||||
|
||||
|
||||
/**
|
||||
* @extends {Blockly.ISelectable}
|
||||
* @interface
|
||||
*/
|
||||
Blockly.ICopyable = function() {};
|
||||
|
||||
/**
|
||||
* Encode for copying.
|
||||
* @return {!Blockly.ICopyable.CopyData} Copy metadata.
|
||||
*/
|
||||
Blockly.ICopyable.prototype.toCopyData;
|
||||
|
||||
/**
|
||||
* Copy Metadata.
|
||||
* @typedef {{
|
||||
* xml:!Element,
|
||||
* source:Blockly.WorkspaceSvg,
|
||||
* typeCounts:?Object
|
||||
* }}
|
||||
*/
|
||||
Blockly.ICopyable.CopyData;
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for an object that is deletable.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IDeletable');
|
||||
|
||||
|
||||
/**
|
||||
* The interface for an object that can be deleted.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IDeletable = function() {};
|
||||
|
||||
/**
|
||||
* Get whether this object is deletable or not.
|
||||
* @return {boolean} True if deletable.
|
||||
*/
|
||||
Blockly.IDeletable.prototype.isDeletable;
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for a component that can delete a block that is
|
||||
* dropped on top of it.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IDeleteArea');
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a component that can delete a block that is dropped on top of it.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IDeleteArea = function() {};
|
||||
|
||||
/**
|
||||
* Return the deletion rectangle.
|
||||
* @return {Blockly.utils.Rect} Rectangle in which to delete.
|
||||
*/
|
||||
Blockly.IDeleteArea.prototype.getClientRect;
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for an object that is movable.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IMovable');
|
||||
|
||||
|
||||
/**
|
||||
* The interface for an object that is movable.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IMovable = function() {};
|
||||
|
||||
/**
|
||||
* Get whether this is movable or not.
|
||||
* @return {boolean} True if movable.
|
||||
*/
|
||||
Blockly.IMovable.prototype.isMovable;
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for a Blockly component that can be registered.
|
||||
* (Ex. Toolbox, Fields, Renderers)
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IRegistrable');
|
||||
|
||||
|
||||
/**
|
||||
* The interface for a Blockly component that can be registered.
|
||||
* @interface
|
||||
* */
|
||||
Blockly.IRegistrable = function() {};
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for an object that is selectable.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.ISelectable');
|
||||
|
||||
goog.requireType('Blockly.IDeletable');
|
||||
goog.requireType('Blockly.IMovable');
|
||||
|
||||
|
||||
/**
|
||||
* The interface for an object that is selectable.
|
||||
* @extends {Blockly.IDeletable}
|
||||
* @extends {Blockly.IMovable}
|
||||
* @interface
|
||||
*/
|
||||
Blockly.ISelectable = function() {};
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
Blockly.ISelectable.prototype.id;
|
||||
|
||||
/**
|
||||
* Select this. Highlight it visually.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.ISelectable.prototype.select;
|
||||
|
||||
/**
|
||||
* Unselect this. Unhighlight it visually.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.ISelectable.prototype.unselect;
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for an object that a style can be added to.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IStyleable');
|
||||
|
||||
|
||||
/**
|
||||
* Interface for an object that a style can be added to.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IStyleable = function() {};
|
||||
|
||||
/**
|
||||
* Adds a style on the toolbox. Usually used to change the cursor.
|
||||
* @param {string} style The name of the class to add.
|
||||
*/
|
||||
Blockly.IStyleable.prototype.addStyle;
|
||||
|
||||
/**
|
||||
* Removes a style from the toolbox. Usually used to change the cursor.
|
||||
* @param {string} style The name of the class to remove.
|
||||
*/
|
||||
Blockly.IStyleable.prototype.removeStyle;
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for a toolbox.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IToolbox');
|
||||
|
||||
goog.requireType('Blockly.IRegistrable');
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a toolbox.
|
||||
* @extends {Blockly.IRegistrable}
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IToolbox = function() {};
|
||||
|
||||
/**
|
||||
* Initializes the toolbox.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.init;
|
||||
|
||||
/**
|
||||
* Fill the toolbox with categories and blocks.
|
||||
* @param {Array.<Blockly.utils.toolbox.Toolbox>} toolboxDef Array holding objects
|
||||
* containing information on the contents of the toolbox.
|
||||
*/
|
||||
Blockly.IToolbox.prototype.render;
|
||||
|
||||
/**
|
||||
* Dispose of this toolbox.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.dispose;
|
||||
|
||||
/**
|
||||
* Get the width of the toolbox.
|
||||
* @return {number} The width of the toolbox.
|
||||
*/
|
||||
Blockly.IToolbox.prototype.getWidth;
|
||||
|
||||
/**
|
||||
* Get the height of the toolbox.
|
||||
* @return {number} The width of the toolbox.
|
||||
*/
|
||||
Blockly.IToolbox.prototype.getHeight;
|
||||
|
||||
/**
|
||||
* Get the toolbox flyout.
|
||||
* @return {Blockly.Flyout} The toolbox flyout.
|
||||
*/
|
||||
Blockly.IToolbox.prototype.getFlyout;
|
||||
|
||||
/**
|
||||
* Move the toolbox to the edge.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.position;
|
||||
|
||||
/**
|
||||
* Unhighlight any previously specified option.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.clearSelection;
|
||||
|
||||
/**
|
||||
* Updates the category colours and background colour of selected categories.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.refreshTheme;
|
||||
|
||||
/**
|
||||
* Update the flyout's contents without closing it. Should be used in response
|
||||
* to a change in one of the dynamic categories, such as variables or
|
||||
* procedures.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.refreshSelection;
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the toolbox.
|
||||
* @param {boolean} isVisible True if the toolbox should be visible.
|
||||
*/
|
||||
Blockly.IToolbox.prototype.setVisible;
|
||||
|
||||
/**
|
||||
* Select the first toolbox category if no category is selected.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.IToolbox.prototype.selectFirstCategory;
|
||||
@@ -14,6 +14,9 @@ goog.provide('Blockly.ASTNode');
|
||||
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
|
||||
goog.requireType('Blockly.IASTNodeLocation');
|
||||
goog.requireType('Blockly.IASTNodeLocationWithBlock');
|
||||
|
||||
|
||||
/**
|
||||
* Class for an AST node.
|
||||
@@ -21,9 +24,8 @@ goog.require('Blockly.utils.Coordinate');
|
||||
* creating a node directly.
|
||||
* @param {string} type The type of the location.
|
||||
* Must be in Blockly.ASTNode.types.
|
||||
* @param {!(Blockly.Block|Blockly.Connection|Blockly.Field|Blockly.Workspace)}
|
||||
* location The position in the AST.
|
||||
* @param {!Object=} opt_params Optional dictionary of options.
|
||||
* @param {!Blockly.IASTNodeLocation} location The position in the AST.
|
||||
* @param {!Blockly.ASTNode.Params=} opt_params Optional dictionary of options.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ASTNode = function(type, location, opt_params) {
|
||||
@@ -48,14 +50,28 @@ Blockly.ASTNode = function(type, location, opt_params) {
|
||||
|
||||
/**
|
||||
* The location of the AST node.
|
||||
* @type {!(Blockly.Block|Blockly.Connection|Blockly.Field|Blockly.Workspace)}
|
||||
* @type {!Blockly.IASTNodeLocation}
|
||||
* @private
|
||||
*/
|
||||
this.location_ = location;
|
||||
|
||||
/**
|
||||
* The coordinate on the workspace.
|
||||
* @type {Blockly.utils.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.wsCoordinate_ = null;
|
||||
|
||||
this.processParams_(opt_params || null);
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* wsCoordinate: Blockly.utils.Coordinate
|
||||
* }}
|
||||
*/
|
||||
Blockly.ASTNode.Params;
|
||||
|
||||
/**
|
||||
* Object holding different types for an AST node.
|
||||
* @enum {string}
|
||||
@@ -199,9 +215,27 @@ Blockly.ASTNode.createWorkspaceNode = function(workspace, wsCoordinate) {
|
||||
Blockly.ASTNode.types.WORKSPACE, workspace, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an AST node for the top position on a block.
|
||||
* This is either an output connection, previous connection, or block.
|
||||
* @param {!Blockly.Block} block The block to find the top most AST node on.
|
||||
* @return {Blockly.ASTNode} The AST node holding the top most position on the
|
||||
* block.
|
||||
*/
|
||||
Blockly.ASTNode.createTopNode = function(block) {
|
||||
var astNode;
|
||||
var topConnection = block.previousConnection || block.outputConnection;
|
||||
if (topConnection) {
|
||||
astNode = Blockly.ASTNode.createConnectionNode(topConnection);
|
||||
} else {
|
||||
astNode = Blockly.ASTNode.createBlockNode(block);
|
||||
}
|
||||
return astNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the optional parameters.
|
||||
* @param {Object} params The user specified parameters.
|
||||
* @param {?Blockly.ASTNode.Params} params The user specified parameters.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ASTNode.prototype.processParams_ = function(params) {
|
||||
@@ -217,8 +251,8 @@ Blockly.ASTNode.prototype.processParams_ = function(params) {
|
||||
* Gets the value pointed to by this node.
|
||||
* It is the callers responsibility to check the node type to figure out what
|
||||
* type of object they get back from this.
|
||||
* @return {!(Blockly.Field|Blockly.Connection|Blockly.Block|Blockly.Workspace)}
|
||||
* The current field, connection, workspace, or block the cursor is on.
|
||||
* @return {!Blockly.IASTNodeLocation} The current field, connection, workspace, or
|
||||
* block the cursor is on.
|
||||
*/
|
||||
Blockly.ASTNode.prototype.getLocation = function() {
|
||||
return this.location_;
|
||||
@@ -261,7 +295,8 @@ Blockly.ASTNode.prototype.isConnection = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.ASTNode.prototype.findNextForInput_ = function() {
|
||||
var parentInput = this.location_.getParentInput();
|
||||
var location = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
var parentInput = location.getParentInput();
|
||||
var block = parentInput.getSourceBlock();
|
||||
var curIdx = block.inputList.indexOf(parentInput);
|
||||
for (var i = curIdx + 1, input; (input = block.inputList[i]); i++) {
|
||||
@@ -317,11 +352,12 @@ Blockly.ASTNode.prototype.findNextForField_ = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.ASTNode.prototype.findPrevForInput_ = function() {
|
||||
var location = this.location_.getParentInput();
|
||||
var block = location.getSourceBlock();
|
||||
var curIdx = block.inputList.indexOf(location);
|
||||
var location = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
var parentInput = location.getParentInput();
|
||||
var block = parentInput.getSourceBlock();
|
||||
var curIdx = block.inputList.indexOf(parentInput);
|
||||
for (var i = curIdx, input; (input = block.inputList[i]); i--) {
|
||||
if (input.connection && input !== location) {
|
||||
if (input.connection && input !== parentInput) {
|
||||
return Blockly.ASTNode.createInputNode(input);
|
||||
}
|
||||
var fieldRow = input.fieldRow;
|
||||
@@ -376,7 +412,8 @@ Blockly.ASTNode.prototype.findPrevForField_ = function() {
|
||||
Blockly.ASTNode.prototype.navigateBetweenStacks_ = function(forward) {
|
||||
var curLocation = this.getLocation();
|
||||
if (!(curLocation instanceof Blockly.Block)) {
|
||||
curLocation = curLocation.getSourceBlock();
|
||||
curLocation = /** @type {!Blockly.IASTNodeLocationWithBlock} */ (
|
||||
curLocation).getSourceBlock();
|
||||
}
|
||||
if (!curLocation || !curLocation.workspace) {
|
||||
return null;
|
||||
@@ -481,7 +518,8 @@ Blockly.ASTNode.prototype.getSourceBlock = function() {
|
||||
} else if (this.getType() === Blockly.ASTNode.types.WORKSPACE) {
|
||||
return null;
|
||||
} else {
|
||||
return this.getLocation().getSourceBlock();
|
||||
return /** @type {Blockly.IASTNodeLocationWithBlock} */ (
|
||||
this.getLocation()).getSourceBlock();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -496,7 +534,8 @@ Blockly.ASTNode.prototype.next = function() {
|
||||
return this.navigateBetweenStacks_(true);
|
||||
|
||||
case Blockly.ASTNode.types.OUTPUT:
|
||||
return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
|
||||
|
||||
case Blockly.ASTNode.types.FIELD:
|
||||
return this.findNextForField_();
|
||||
@@ -505,14 +544,17 @@ Blockly.ASTNode.prototype.next = function() {
|
||||
return this.findNextForInput_();
|
||||
|
||||
case Blockly.ASTNode.types.BLOCK:
|
||||
var nextConnection = this.location_.nextConnection;
|
||||
var block = /** @type {!Blockly.Block} */ (this.location_);
|
||||
var nextConnection = block.nextConnection;
|
||||
return Blockly.ASTNode.createConnectionNode(nextConnection);
|
||||
|
||||
case Blockly.ASTNode.types.PREVIOUS:
|
||||
return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
|
||||
|
||||
case Blockly.ASTNode.types.NEXT:
|
||||
var targetConnection = this.location_.targetConnection;
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
var targetConnection = connection.targetConnection;
|
||||
return Blockly.ASTNode.createConnectionNode(targetConnection);
|
||||
}
|
||||
|
||||
@@ -528,7 +570,8 @@ Blockly.ASTNode.prototype.next = function() {
|
||||
Blockly.ASTNode.prototype.in = function() {
|
||||
switch (this.type_) {
|
||||
case Blockly.ASTNode.types.WORKSPACE:
|
||||
var topBlocks = this.location_.getTopBlocks(true);
|
||||
var workspace = /** @type {!Blockly.Workspace} */ (this.location_);
|
||||
var topBlocks = workspace.getTopBlocks(true);
|
||||
if (topBlocks.length > 0) {
|
||||
return Blockly.ASTNode.createStackNode(topBlocks[0]);
|
||||
}
|
||||
@@ -543,7 +586,8 @@ Blockly.ASTNode.prototype.in = function() {
|
||||
return this.findFirstFieldOrInput_(block);
|
||||
|
||||
case Blockly.ASTNode.types.INPUT:
|
||||
var targetConnection = this.location_.targetConnection;
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
var targetConnection = connection.targetConnection;
|
||||
return Blockly.ASTNode.createConnectionNode(targetConnection);
|
||||
}
|
||||
|
||||
@@ -571,19 +615,21 @@ Blockly.ASTNode.prototype.prev = function() {
|
||||
return this.findPrevForInput_();
|
||||
|
||||
case Blockly.ASTNode.types.BLOCK:
|
||||
var block = this.location_;
|
||||
var block = /** @type {!Blockly.Block} */ (this.location_);
|
||||
var topConnection = block.previousConnection || block.outputConnection;
|
||||
return Blockly.ASTNode.createConnectionNode(topConnection);
|
||||
|
||||
case Blockly.ASTNode.types.PREVIOUS:
|
||||
var targetConnection = this.location_.targetConnection;
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
var targetConnection = connection.targetConnection;
|
||||
if (targetConnection && !targetConnection.getParentInput()) {
|
||||
return Blockly.ASTNode.createConnectionNode(targetConnection);
|
||||
}
|
||||
break;
|
||||
|
||||
case Blockly.ASTNode.types.NEXT:
|
||||
return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -598,35 +644,40 @@ Blockly.ASTNode.prototype.prev = function() {
|
||||
Blockly.ASTNode.prototype.out = function() {
|
||||
switch (this.type_) {
|
||||
case Blockly.ASTNode.types.STACK:
|
||||
var blockPos = this.location_.getRelativeToSurfaceXY();
|
||||
var block = /** @type {!Blockly.Block} */ (this.location_);
|
||||
var blockPos = block.getRelativeToSurfaceXY();
|
||||
// TODO: Make sure this is in the bounds of the workspace.
|
||||
var wsCoordinate = new Blockly.utils.Coordinate(
|
||||
blockPos.x, blockPos.y + Blockly.ASTNode.DEFAULT_OFFSET_Y);
|
||||
return Blockly.ASTNode.createWorkspaceNode(
|
||||
this.location_.workspace, wsCoordinate);
|
||||
return Blockly.ASTNode.createWorkspaceNode(block.workspace, wsCoordinate);
|
||||
|
||||
case Blockly.ASTNode.types.OUTPUT:
|
||||
var target = this.location_.targetConnection;
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
var target = connection.targetConnection;
|
||||
if (target) {
|
||||
return Blockly.ASTNode.createConnectionNode(target);
|
||||
}
|
||||
return Blockly.ASTNode.createStackNode(this.location_.getSourceBlock());
|
||||
return Blockly.ASTNode.createStackNode(connection.getSourceBlock());
|
||||
|
||||
case Blockly.ASTNode.types.FIELD:
|
||||
return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());
|
||||
var field = /** @type {!Blockly.Field} */ (this.location_);
|
||||
return Blockly.ASTNode.createBlockNode(field.getSourceBlock());
|
||||
|
||||
case Blockly.ASTNode.types.INPUT:
|
||||
return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock());
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
return Blockly.ASTNode.createBlockNode(connection.getSourceBlock());
|
||||
|
||||
case Blockly.ASTNode.types.BLOCK:
|
||||
var block = /** @type {!Blockly.Block} */ (this.location_);
|
||||
return this.getOutAstNodeForBlock_(block);
|
||||
|
||||
case Blockly.ASTNode.types.PREVIOUS:
|
||||
return this.getOutAstNodeForBlock_(this.location_.getSourceBlock());
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
|
||||
|
||||
case Blockly.ASTNode.types.NEXT:
|
||||
return this.getOutAstNodeForBlock_(this.location_.getSourceBlock());
|
||||
var connection = /** @type {!Blockly.Connection} */ (this.location_);
|
||||
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -71,7 +71,7 @@ Blockly.BasicCursor.prototype.prev = function() {
|
||||
return null;
|
||||
}
|
||||
var newNode = this.getPreviousNode_(curNode, this.validNode_);
|
||||
|
||||
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,15 @@ goog.require('Blockly.Marker');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a cursor.
|
||||
* A cursor controls how a user navigates the Blockly AST.
|
||||
* @constructor
|
||||
* @extends {Blockly.Marker}
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
*/
|
||||
Blockly.Cursor = function() {
|
||||
Blockly.Cursor.superClass_.constructor.call(this);
|
||||
@@ -144,7 +147,8 @@ Blockly.Cursor.prototype.onBlocklyAction = function(action) {
|
||||
// If we are on a field give it the option to handle the action
|
||||
if (this.getCurNode() &&
|
||||
this.getCurNode().getType() === Blockly.ASTNode.types.FIELD &&
|
||||
this.getCurNode().getLocation().onBlocklyAction(action)) {
|
||||
(/** @type {!Blockly.Field} */ (this.getCurNode().getLocation()))
|
||||
.onBlocklyAction(action)) {
|
||||
return true;
|
||||
}
|
||||
switch (action.name) {
|
||||
|
||||
@@ -101,7 +101,7 @@ Blockly.user.keyMap.getKeyByAction = function(action) {
|
||||
|
||||
/**
|
||||
* Serialize the key event.
|
||||
* @param {!Event} e A key up event holding the key code.
|
||||
* @param {!KeyboardEvent} e A key up event holding the key code.
|
||||
* @return {string} A string containing the serialized key event.
|
||||
* @package
|
||||
*/
|
||||
|
||||
@@ -23,7 +23,6 @@ goog.require('Blockly.navigation');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Marker = function() {
|
||||
|
||||
/**
|
||||
* The colour of the marker.
|
||||
* @type {?string}
|
||||
@@ -119,4 +118,3 @@ Blockly.Marker.prototype.dispose = function() {
|
||||
this.getDrawer().dispose();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.user.keyMap');
|
||||
|
||||
|
||||
/**
|
||||
* A function to call to give feedback to the user about logs, warnings, and
|
||||
* errors. You can override this to customize feedback (e.g. warning sounds,
|
||||
@@ -102,10 +101,19 @@ Blockly.navigation.MARKER_NAME = 'local_marker_1';
|
||||
|
||||
/**
|
||||
* Get the local marker.
|
||||
* @return {!Blockly.Marker} The local marker for the main workspace.
|
||||
* @return {Blockly.Marker} The local marker for the main workspace.
|
||||
*/
|
||||
Blockly.navigation.getMarker = function() {
|
||||
return Blockly.getMainWorkspace().getMarker(Blockly.navigation.MARKER_NAME);
|
||||
return Blockly.navigation.getNavigationWorkspace()
|
||||
.getMarker(Blockly.navigation.MARKER_NAME);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the workspace that is being navigated.
|
||||
* @return {!Blockly.WorkspaceSvg} The workspace being navigated.
|
||||
*/
|
||||
Blockly.navigation.getNavigationWorkspace = function() {
|
||||
return /** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -114,8 +122,7 @@ Blockly.navigation.getMarker = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.focusToolbox_ = function() {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var toolbox = Blockly.navigation.getNavigationWorkspace().getToolbox();
|
||||
if (toolbox) {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
Blockly.navigation.resetFlyout_(false /* shouldHide */);
|
||||
@@ -134,9 +141,9 @@ Blockly.navigation.focusToolbox_ = function() {
|
||||
Blockly.navigation.focusFlyout_ = function() {
|
||||
var topBlock = null;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_FLYOUT;
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout();
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
|
||||
if (!Blockly.navigation.getMarker().getCurNode()) {
|
||||
Blockly.navigation.markAtCursor_();
|
||||
@@ -159,7 +166,7 @@ Blockly.navigation.focusFlyout_ = function() {
|
||||
*/
|
||||
Blockly.navigation.focusWorkspace_ = function() {
|
||||
Blockly.hideChaff();
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var cursor = workspace.getCursor();
|
||||
var reset = !!workspace.getToolbox();
|
||||
var topBlocks = workspace.getTopBlocks(true);
|
||||
@@ -167,7 +174,7 @@ Blockly.navigation.focusWorkspace_ = function() {
|
||||
Blockly.navigation.resetFlyout_(reset);
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
if (topBlocks.length > 0) {
|
||||
cursor.setCurNode(Blockly.navigation.getTopNode(topBlocks[0]));
|
||||
cursor.setCurNode(Blockly.ASTNode.createTopNode(topBlocks[0]));
|
||||
} else {
|
||||
// TODO: Find the center of the visible workspace.
|
||||
var wsCoord = new Blockly.utils.Coordinate(100, 100);
|
||||
@@ -186,14 +193,14 @@ Blockly.navigation.focusWorkspace_ = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.getFlyoutCursor_ = function() {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var cursor = null;
|
||||
if (workspace.rendered) {
|
||||
var toolbox = workspace.getToolbox();
|
||||
var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout();
|
||||
cursor = flyout ? flyout.workspace_.getCursor() : null;
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
cursor = flyout ? flyout.getWorkspace().getCursor() : null;
|
||||
}
|
||||
return cursor;
|
||||
return /** @type {Blockly.FlyoutCursor} */ (cursor);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -202,7 +209,7 @@ Blockly.navigation.getFlyoutCursor_ = function() {
|
||||
* it on the workspace.
|
||||
*/
|
||||
Blockly.navigation.insertFromFlyout = function() {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var flyout = workspace.getFlyout();
|
||||
if (!flyout || !flyout.isVisible()) {
|
||||
Blockly.navigation.warn_('Trying to insert from the flyout when the flyout does not ' +
|
||||
@@ -210,7 +217,8 @@ Blockly.navigation.insertFromFlyout = function() {
|
||||
return;
|
||||
}
|
||||
|
||||
var curBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
var curBlock = /** @type {!Blockly.BlockSvg} */ (
|
||||
Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation());
|
||||
if (!curBlock.isEnabled()) {
|
||||
Blockly.navigation.warn_('Can\'t insert a disabled block.');
|
||||
return;
|
||||
@@ -230,7 +238,7 @@ Blockly.navigation.insertFromFlyout = function() {
|
||||
}
|
||||
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
workspace.getCursor().setCurNode(Blockly.navigation.getTopNode(newBlock));
|
||||
workspace.getCursor().setCurNode(Blockly.ASTNode.createTopNode(newBlock));
|
||||
Blockly.navigation.removeMark_();
|
||||
};
|
||||
|
||||
@@ -243,7 +251,7 @@ Blockly.navigation.resetFlyout_ = function(shouldHide) {
|
||||
if (Blockly.navigation.getFlyoutCursor_()) {
|
||||
Blockly.navigation.getFlyoutCursor_().hide();
|
||||
if (shouldHide) {
|
||||
Blockly.getMainWorkspace().getFlyout().hide();
|
||||
Blockly.navigation.getNavigationWorkspace().getFlyout().hide();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -260,7 +268,8 @@ Blockly.navigation.resetFlyout_ = function(shouldHide) {
|
||||
*/
|
||||
Blockly.navigation.modifyWarn_ = function() {
|
||||
var markerNode = Blockly.navigation.getMarker().getCurNode();
|
||||
var cursorNode = Blockly.getMainWorkspace().getCursor().getCurNode();
|
||||
var cursorNode = Blockly.navigation.getNavigationWorkspace()
|
||||
.getCursor().getCurNode();
|
||||
|
||||
if (!markerNode) {
|
||||
Blockly.navigation.warn_('Cannot insert with no marked node.');
|
||||
@@ -300,7 +309,7 @@ Blockly.navigation.modifyWarn_ = function() {
|
||||
/**
|
||||
* Disconnect the block from its parent and move to the position of the
|
||||
* workspace node.
|
||||
* @param {Blockly.Block} block The block to be moved to the workspace.
|
||||
* @param {Blockly.BlockSvg} block The block to be moved to the workspace.
|
||||
* @param {!Blockly.ASTNode} wsNode The workspace node holding the position the
|
||||
* block will be moved to.
|
||||
* @return {boolean} True if the block can be moved to the workspace,
|
||||
@@ -331,7 +340,8 @@ Blockly.navigation.moveBlockToWorkspace_ = function(block, wsNode) {
|
||||
*/
|
||||
Blockly.navigation.modify_ = function() {
|
||||
var markerNode = Blockly.navigation.getMarker().getCurNode();
|
||||
var cursorNode = Blockly.getMainWorkspace().getCursor().getCurNode();
|
||||
var cursorNode = Blockly.navigation.getNavigationWorkspace()
|
||||
.getCursor().getCurNode();
|
||||
if (!Blockly.navigation.modifyWarn_()) {
|
||||
return false;
|
||||
}
|
||||
@@ -343,18 +353,19 @@ Blockly.navigation.modify_ = function() {
|
||||
var markerLoc = markerNode.getLocation();
|
||||
|
||||
if (markerNode.isConnection() && cursorNode.isConnection()) {
|
||||
cursorLoc = /** @type {!Blockly.Connection} */ (cursorLoc);
|
||||
markerLoc = /** @type {!Blockly.Connection} */ (markerLoc);
|
||||
cursorLoc = /** @type {!Blockly.RenderedConnection} */ (cursorLoc);
|
||||
markerLoc = /** @type {!Blockly.RenderedConnection} */ (markerLoc);
|
||||
return Blockly.navigation.connect_(cursorLoc, markerLoc);
|
||||
} else if (markerNode.isConnection() &&
|
||||
(cursorType == Blockly.ASTNode.types.BLOCK ||
|
||||
cursorType == Blockly.ASTNode.types.STACK)) {
|
||||
cursorLoc = /** @type {!Blockly.Block} */ (cursorLoc);
|
||||
markerLoc = /** @type {!Blockly.Connection} */ (markerLoc);
|
||||
cursorLoc = /** @type {!Blockly.BlockSvg} */ (cursorLoc);
|
||||
markerLoc = /** @type {!Blockly.RenderedConnection} */ (markerLoc);
|
||||
return Blockly.navigation.insertBlock(cursorLoc, markerLoc);
|
||||
} else if (markerType == Blockly.ASTNode.types.WORKSPACE) {
|
||||
var block = cursorNode ? cursorNode.getSourceBlock() : null;
|
||||
return Blockly.navigation.moveBlockToWorkspace_(block, markerNode);
|
||||
return Blockly.navigation.moveBlockToWorkspace_(
|
||||
/** @type {Blockly.BlockSvg} */ (block), markerNode);
|
||||
}
|
||||
Blockly.navigation.warn_('Unexpected state in Blockly.navigation.modify_.');
|
||||
return false;
|
||||
@@ -363,9 +374,10 @@ Blockly.navigation.modify_ = function() {
|
||||
/**
|
||||
* If one of the connections source blocks is a child of the other, disconnect
|
||||
* the child.
|
||||
* @param {!Blockly.Connection} movingConnection The connection that is being
|
||||
* moved.
|
||||
* @param {!Blockly.Connection} destConnection The connection to be moved to.
|
||||
* @param {!Blockly.RenderedConnection} movingConnection The connection that is
|
||||
* being moved.
|
||||
* @param {!Blockly.RenderedConnection} destConnection The connection to be
|
||||
* moved to.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.disconnectChild_ = function(movingConnection, destConnection) {
|
||||
@@ -384,9 +396,10 @@ Blockly.navigation.disconnectChild_ = function(movingConnection, destConnection)
|
||||
/**
|
||||
* If the two blocks are compatible move the moving connection to the target
|
||||
* connection and connect them.
|
||||
* @param {Blockly.Connection} movingConnection The connection that is being
|
||||
* moved.
|
||||
* @param {Blockly.Connection} destConnection The connection to be moved to.
|
||||
* @param {Blockly.RenderedConnection} movingConnection The connection that is
|
||||
* being moved.
|
||||
* @param {Blockly.RenderedConnection} destConnection The connection to be moved
|
||||
* to.
|
||||
* @return {boolean} True if the connections were connected, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
@@ -414,8 +427,10 @@ Blockly.navigation.moveAndConnect_ = function(movingConnection, destConnection)
|
||||
/**
|
||||
* If the given connection is superior find the inferior connection on the
|
||||
* source block.
|
||||
* @param {Blockly.Connection} connection The connection trying to be connected.
|
||||
* @return {Blockly.Connection} The inferior connection or null if none exists.
|
||||
* @param {Blockly.RenderedConnection} connection The connection trying to be
|
||||
* connected.
|
||||
* @return {Blockly.RenderedConnection} The inferior connection or null if none
|
||||
* exists.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.getInferiorConnection_ = function(connection) {
|
||||
@@ -434,8 +449,10 @@ Blockly.navigation.getInferiorConnection_ = function(connection) {
|
||||
/**
|
||||
* If the given connection is inferior tries to find a superior connection to
|
||||
* connect to.
|
||||
* @param {Blockly.Connection} connection The connection trying to be connected.
|
||||
* @return {Blockly.Connection} The superior connection or null if none exists.
|
||||
* @param {Blockly.RenderedConnection} connection The connection trying to be
|
||||
* connected.
|
||||
* @return {Blockly.RenderedConnection} The superior connection or null if none
|
||||
* exists.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.getSuperiorConnection_ = function(connection) {
|
||||
@@ -453,9 +470,10 @@ Blockly.navigation.getSuperiorConnection_ = function(connection) {
|
||||
* If the given connections are not compatible try finding compatible connections
|
||||
* on the source blocks of the given connections.
|
||||
*
|
||||
* @param {Blockly.Connection} movingConnection The connection that is being
|
||||
* moved.
|
||||
* @param {Blockly.Connection} destConnection The connection to be moved to.
|
||||
* @param {Blockly.RenderedConnection} movingConnection The connection that is
|
||||
* being moved.
|
||||
* @param {Blockly.RenderedConnection} destConnection The connection to be moved
|
||||
* to.
|
||||
* @return {boolean} True if the two connections or their target connections
|
||||
* were connected, false otherwise.
|
||||
* @private
|
||||
@@ -495,8 +513,9 @@ Blockly.navigation.connect_ = function(movingConnection, destConnection) {
|
||||
/**
|
||||
* Tries to connect the given block to the destination connection, making an
|
||||
* intelligent guess about which connection to use to on the moving block.
|
||||
* @param {!Blockly.Block} block The block to move.
|
||||
* @param {!Blockly.Connection} destConnection The connection to connect to.
|
||||
* @param {!Blockly.BlockSvg} block The block to move.
|
||||
* @param {!Blockly.RenderedConnection} destConnection The connection to connect
|
||||
* to.
|
||||
* @return {boolean} Whether the connection was successful.
|
||||
*/
|
||||
Blockly.navigation.insertBlock = function(block, destConnection) {
|
||||
@@ -518,7 +537,8 @@ Blockly.navigation.insertBlock = function(block, destConnection) {
|
||||
break;
|
||||
case Blockly.OUTPUT_VALUE:
|
||||
for (var i = 0; i < block.inputList.length; i++) {
|
||||
var inputConnection = block.inputList[i].connection;
|
||||
var inputConnection = /** @type {Blockly.RenderedConnection} */ (
|
||||
block.inputList[i].connection);
|
||||
if (inputConnection && inputConnection.type === Blockly.INPUT_VALUE &&
|
||||
Blockly.navigation.connect_(inputConnection, destConnection)) {
|
||||
return true;
|
||||
@@ -543,13 +563,14 @@ Blockly.navigation.insertBlock = function(block, destConnection) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.disconnectBlocks_ = function() {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var curNode = workspace.getCursor().getCurNode();
|
||||
if (!curNode.isConnection()) {
|
||||
Blockly.navigation.log_('Cannot disconnect blocks when the cursor is not on a connection');
|
||||
return;
|
||||
}
|
||||
var curConnection = /** @type {!Blockly.Connection} */ (curNode.getLocation());
|
||||
var curConnection =
|
||||
/** @type {!Blockly.RenderedConnection} */ (curNode.getLocation());
|
||||
if (!curConnection.isConnected()) {
|
||||
Blockly.navigation.log_('Cannot disconnect unconnected connection');
|
||||
return;
|
||||
@@ -584,7 +605,7 @@ Blockly.navigation.disconnectBlocks_ = function() {
|
||||
*/
|
||||
Blockly.navigation.markAtCursor_ = function() {
|
||||
Blockly.navigation.getMarker().setCurNode(
|
||||
Blockly.getMainWorkspace().getCursor().getCurNode());
|
||||
Blockly.navigation.getNavigationWorkspace().getCursor().getCurNode());
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -606,31 +627,12 @@ Blockly.navigation.setState = function(newState) {
|
||||
Blockly.navigation.currentState_ = newState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the top node on a block.
|
||||
* This is either the previous connection, output connection or the block.
|
||||
* @param {!Blockly.Block} block The block to find the top most AST node on.
|
||||
* @return {Blockly.ASTNode} The AST node holding the top most node on the
|
||||
* block.
|
||||
* @package
|
||||
*/
|
||||
Blockly.navigation.getTopNode = function(block) {
|
||||
var astNode;
|
||||
var topConnection = block.previousConnection || block.outputConnection;
|
||||
if (topConnection) {
|
||||
astNode = Blockly.ASTNode.createConnectionNode(topConnection);
|
||||
} else {
|
||||
astNode = Blockly.ASTNode.createBlockNode(block);
|
||||
}
|
||||
return astNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Before a block is deleted move the cursor to the appropriate position.
|
||||
* @param {!Blockly.Block} deletedBlock The block that is being deleted.
|
||||
* @param {!Blockly.BlockSvg} deletedBlock The block that is being deleted.
|
||||
*/
|
||||
Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
if (!workspace) {
|
||||
return;
|
||||
}
|
||||
@@ -664,11 +666,11 @@ Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) {
|
||||
/**
|
||||
* When a block that the cursor is on is mutated move the cursor to the block
|
||||
* level.
|
||||
* @param {!Blockly.Block} mutatedBlock The block that is being mutated.
|
||||
* @param {!Blockly.BlockSvg} mutatedBlock The block that is being mutated.
|
||||
* @package
|
||||
*/
|
||||
Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) {
|
||||
var cursor = Blockly.getMainWorkspace().getCursor();
|
||||
var cursor = Blockly.navigation.getNavigationWorkspace().getCursor();
|
||||
if (cursor) {
|
||||
var curNode = cursor.getCurNode();
|
||||
var block = curNode ? curNode.getSourceBlock() : null;
|
||||
@@ -683,8 +685,9 @@ Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) {
|
||||
* Enable accessibility mode.
|
||||
*/
|
||||
Blockly.navigation.enableKeyboardAccessibility = function() {
|
||||
if (!Blockly.getMainWorkspace().keyboardAccessibilityMode) {
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
if (!workspace.keyboardAccessibilityMode) {
|
||||
workspace.keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
}
|
||||
};
|
||||
@@ -693,9 +696,9 @@ Blockly.navigation.enableKeyboardAccessibility = function() {
|
||||
* Disable accessibility mode.
|
||||
*/
|
||||
Blockly.navigation.disableKeyboardAccessibility = function() {
|
||||
if (Blockly.getMainWorkspace().keyboardAccessibilityMode) {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = false;
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
if (workspace.keyboardAccessibilityMode) {
|
||||
workspace.keyboardAccessibilityMode = false;
|
||||
workspace.getCursor().hide();
|
||||
Blockly.navigation.getMarker().hide();
|
||||
if (Blockly.navigation.getFlyoutCursor_()) {
|
||||
@@ -752,7 +755,7 @@ Blockly.navigation.error_ = function(msg) {
|
||||
|
||||
/**
|
||||
* Handler for all the keyboard navigation events.
|
||||
* @param {!Event} e The keyboard event.
|
||||
* @param {!KeyboardEvent} e The keyboard event.
|
||||
* @return {boolean} True if the key was handled false otherwise.
|
||||
*/
|
||||
Blockly.navigation.onKeyPress = function(e) {
|
||||
@@ -772,10 +775,11 @@ Blockly.navigation.onKeyPress = function(e) {
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
*/
|
||||
Blockly.navigation.onBlocklyAction = function(action) {
|
||||
var readOnly = Blockly.getMainWorkspace().options.readOnly;
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var readOnly = workspace.options.readOnly;
|
||||
var actionHandled = false;
|
||||
|
||||
if (Blockly.getMainWorkspace().keyboardAccessibilityMode) {
|
||||
if (workspace.keyboardAccessibilityMode) {
|
||||
if (!readOnly) {
|
||||
actionHandled = Blockly.navigation.handleActions_(action);
|
||||
// If in readonly mode only handle valid actions.
|
||||
@@ -818,9 +822,9 @@ Blockly.navigation.handleActions_ = function(action) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.flyoutOnAction_ = function(action) {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout();
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
|
||||
if (flyout && flyout.onBlocklyAction(action)) {
|
||||
return true;
|
||||
@@ -848,9 +852,10 @@ Blockly.navigation.flyoutOnAction_ = function(action) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.toolboxOnAction_ = function(action) {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var handled = toolbox ? toolbox.onBlocklyAction(action) : false;
|
||||
var handled = toolbox && typeof toolbox.onBlocklyAction == 'function' ?
|
||||
toolbox.onBlocklyAction(action) : false;
|
||||
|
||||
if (handled) {
|
||||
return true;
|
||||
@@ -881,8 +886,9 @@ Blockly.navigation.toolboxOnAction_ = function(action) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) {
|
||||
var cursor = Blockly.getMainWorkspace().getCursor();
|
||||
var curNode = Blockly.getMainWorkspace().getCursor().getCurNode();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var cursor = workspace.getCursor();
|
||||
var curNode = workspace.getCursor().getCurNode();
|
||||
|
||||
if (curNode.getType() !== Blockly.ASTNode.types.WORKSPACE) {
|
||||
return false;
|
||||
@@ -893,7 +899,7 @@ Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) {
|
||||
var newY = yDirection * Blockly.navigation.WS_MOVE_DISTANCE + wsCoord.y;
|
||||
|
||||
cursor.setCurNode(Blockly.ASTNode.createWorkspaceNode(
|
||||
Blockly.getMainWorkspace(), new Blockly.utils.Coordinate(newX, newY)));
|
||||
workspace, new Blockly.utils.Coordinate(newX, newY)));
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -904,7 +910,8 @@ Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.workspaceOnAction_ = function(action) {
|
||||
if (Blockly.getMainWorkspace().getCursor().onBlocklyAction(action)) {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
if (workspace.getCursor().onBlocklyAction(action)) {
|
||||
return true;
|
||||
}
|
||||
switch (action.name) {
|
||||
@@ -935,11 +942,11 @@ Blockly.navigation.workspaceOnAction_ = function(action) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.handleEnterForWS_ = function() {
|
||||
var cursor = Blockly.getMainWorkspace().getCursor();
|
||||
var cursor = Blockly.navigation.getNavigationWorkspace().getCursor();
|
||||
var curNode = cursor.getCurNode();
|
||||
var nodeType = curNode.getType();
|
||||
if (nodeType == Blockly.ASTNode.types.FIELD) {
|
||||
curNode.getLocation().showEditor();
|
||||
(/** @type {!Blockly.Field} */(curNode.getLocation())).showEditor();
|
||||
} else if (curNode.isConnection() ||
|
||||
nodeType == Blockly.ASTNode.types.WORKSPACE) {
|
||||
Blockly.navigation.markAtCursor_();
|
||||
|
||||
@@ -38,10 +38,9 @@ Blockly.TabNavigateCursor.prototype.validNode_ = function(node) {
|
||||
var isValid = false;
|
||||
var type = node && node.getType();
|
||||
if (node) {
|
||||
var location = node.getLocation();
|
||||
var location = /** @type {Blockly.Field} */ (node.getLocation());
|
||||
if (type == Blockly.ASTNode.types.FIELD &&
|
||||
location && location.isTabNavigable() &&
|
||||
(/** @type {!Blockly.Field} */ (location)).isClickable()) {
|
||||
location && location.isTabNavigable() && location.isClickable()) {
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Blockly menu similar to Closure's goog.ui.Menu
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Menu');
|
||||
|
||||
goog.require('Blockly.utils.aria');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.KeyCodes');
|
||||
goog.require('Blockly.utils.style');
|
||||
|
||||
|
||||
/**
|
||||
* A basic menu class.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Menu = function() {
|
||||
/**
|
||||
* Array of menu items.
|
||||
* (Nulls are never in the array, but typing the array as nullable prevents
|
||||
* the compiler from objecting to .indexOf(null))
|
||||
* @type {!Array.<Blockly.MenuItem>}
|
||||
* @private
|
||||
*/
|
||||
this.menuItems_ = [];
|
||||
|
||||
/**
|
||||
* Coordinates of the mousedown event that caused this menu to open. Used to
|
||||
* prevent the consequent mouseup event due to a simple click from activating
|
||||
* a menu item immediately.
|
||||
* @type {?Blockly.utils.Coordinate}
|
||||
* @package
|
||||
*/
|
||||
this.openingCoords = null;
|
||||
|
||||
/**
|
||||
* This is the element that we will listen to the real focus events on.
|
||||
* A value of null means no menu item is highlighted.
|
||||
* @type {Blockly.MenuItem}
|
||||
* @private
|
||||
*/
|
||||
this.highlightedItem_ = null;
|
||||
|
||||
/**
|
||||
* Mouse over event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.mouseOverHandler_ = null;
|
||||
|
||||
/**
|
||||
* Click event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.clickHandler_ = null;
|
||||
|
||||
/**
|
||||
* Mouse enter event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.mouseEnterHandler_ = null;
|
||||
|
||||
/**
|
||||
* Mouse leave event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.mouseLeaveHandler_ = null;
|
||||
|
||||
/**
|
||||
* Key down event data.
|
||||
* @type {?Blockly.EventData}
|
||||
* @private
|
||||
*/
|
||||
this.onKeyDownHandler_ = null;
|
||||
|
||||
/**
|
||||
* The menu's root DOM element.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = null;
|
||||
|
||||
/**
|
||||
* ARIA name for this menu.
|
||||
* @type {?Blockly.utils.aria.Role}
|
||||
* @private
|
||||
*/
|
||||
this.roleName_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add a new menu item to the bottom of this menu.
|
||||
* @param {!Blockly.MenuItem} menuItem Menu item to append.
|
||||
*/
|
||||
Blockly.Menu.prototype.addChild = function(menuItem) {
|
||||
this.menuItems_.push(menuItem);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the menu DOM.
|
||||
* @param {!Element} container Element upon which to append this menu.
|
||||
*/
|
||||
Blockly.Menu.prototype.render = function(container) {
|
||||
var element = /** @type {!HTMLDivElement} */ (document.createElement('div'));
|
||||
// goog-menu is deprecated, use blocklyMenu. May 2020.
|
||||
element.className = 'blocklyMenu goog-menu blocklyNonSelectable';
|
||||
element.tabIndex = 0;
|
||||
if (this.roleName_) {
|
||||
Blockly.utils.aria.setRole(element, this.roleName_);
|
||||
}
|
||||
this.element_ = element;
|
||||
|
||||
// Add menu items.
|
||||
for (var i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
|
||||
element.appendChild(menuItem.createDom());
|
||||
}
|
||||
|
||||
// Add event handlers.
|
||||
this.mouseOverHandler_ = Blockly.bindEventWithChecks_(element,
|
||||
'mouseover', this, this.handleMouseOver_, true);
|
||||
this.clickHandler_ = Blockly.bindEventWithChecks_(element,
|
||||
'click', this, this.handleClick_, true);
|
||||
this.mouseEnterHandler_ = Blockly.bindEventWithChecks_(element,
|
||||
'mouseenter', this, this.handleMouseEnter_, true);
|
||||
this.mouseLeaveHandler_ = Blockly.bindEventWithChecks_(element,
|
||||
'mouseleave', this, this.handleMouseLeave_, true);
|
||||
this.onKeyDownHandler_ = Blockly.bindEventWithChecks_(element,
|
||||
'keydown', this, this.handleKeyEvent_);
|
||||
|
||||
container.appendChild(element);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the menu's element.
|
||||
* @return {Element} The DOM element.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.getElement = function() {
|
||||
return this.element_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Focus the menu element.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.focus = function() {
|
||||
var el = this.getElement();
|
||||
if (el) {
|
||||
el.focus({preventScroll:true});
|
||||
Blockly.utils.dom.addClass(el, 'blocklyFocused');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Blur the menu element.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.blur_ = function() {
|
||||
var el = this.getElement();
|
||||
if (el) {
|
||||
el.blur();
|
||||
Blockly.utils.dom.removeClass(el, 'blocklyFocused');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the menu accessibility role.
|
||||
* @param {!Blockly.utils.aria.Role} roleName role name.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.setRole = function(roleName) {
|
||||
this.roleName_ = roleName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of this menu.
|
||||
*/
|
||||
Blockly.Menu.prototype.dispose = function() {
|
||||
// Remove event handlers.
|
||||
if (this.mouseOverHandler_) {
|
||||
Blockly.unbindEvent_(this.mouseOverHandler_);
|
||||
this.mouseOverHandler_ = null;
|
||||
}
|
||||
if (this.clickHandler_) {
|
||||
Blockly.unbindEvent_(this.clickHandler_);
|
||||
this.clickHandler_ = null;
|
||||
}
|
||||
if (this.mouseEnterHandler_) {
|
||||
Blockly.unbindEvent_(this.mouseEnterHandler_);
|
||||
this.mouseEnterHandler_ = null;
|
||||
}
|
||||
if (this.mouseLeaveHandler_) {
|
||||
Blockly.unbindEvent_(this.mouseLeaveHandler_);
|
||||
this.mouseLeaveHandler_ = null;
|
||||
}
|
||||
if (this.onKeyDownHandler_) {
|
||||
Blockly.unbindEvent_(this.onKeyDownHandler_);
|
||||
this.onKeyDownHandler_ = null;
|
||||
}
|
||||
|
||||
// Remove menu items.
|
||||
for (var i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
|
||||
menuItem.dispose();
|
||||
}
|
||||
this.element_ = null;
|
||||
};
|
||||
|
||||
// Child component management.
|
||||
|
||||
/**
|
||||
* Returns the child menu item that owns the given DOM element,
|
||||
* or null if no such menu item is found.
|
||||
* @param {Element} elem DOM element whose owner is to be returned.
|
||||
* @return {?Blockly.MenuItem} Menu item for which the DOM element belongs to.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.getMenuItem_ = function(elem) {
|
||||
var menuElem = this.getElement();
|
||||
// Node might be the menu border (resulting in no associated menu item), or
|
||||
// a menu item's div, or some element within the menu item.
|
||||
// Walk up parents until one meets either the menu's root element, or
|
||||
// a menu item's div.
|
||||
while (elem && elem != menuElem) {
|
||||
if (Blockly.utils.dom.hasClass(elem, 'blocklyMenuItem')) {
|
||||
// Having found a menu item's div, locate that menu item in this menu.
|
||||
for (var i = 0, menuItem; (menuItem = this.menuItems_[i]); i++) {
|
||||
if (menuItem.getElement() == elem) {
|
||||
return menuItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
elem = elem.parentElement;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Highlight management.
|
||||
|
||||
/**
|
||||
* Highlights the given menu item, or clears highlighting if null.
|
||||
* @param {Blockly.MenuItem} item Item to highlight, or null.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.setHighlighted = function(item) {
|
||||
var currentHighlighted = this.highlightedItem_;
|
||||
if (currentHighlighted) {
|
||||
currentHighlighted.setHighlighted(false);
|
||||
this.highlightedItem_ = null;
|
||||
}
|
||||
if (item) {
|
||||
item.setHighlighted(true);
|
||||
this.highlightedItem_ = item;
|
||||
// Bring the highlighted item into view. This has no effect if the menu is
|
||||
// not scrollable.
|
||||
var el = /** @type {!Element} */ (this.getElement());
|
||||
Blockly.utils.style.scrollIntoContainerView(
|
||||
/** @type {!Element} */ (item.getElement()), el);
|
||||
|
||||
Blockly.utils.aria.setState(el, Blockly.utils.aria.State.ACTIVEDESCENDANT,
|
||||
item.getId());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the next highlightable item (or the first if nothing is currently
|
||||
* highlighted).
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightNext = function() {
|
||||
var index = this.menuItems_.indexOf(this.highlightedItem_);
|
||||
this.highlightHelper_(index, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the previous highlightable item (or the last if nothing is
|
||||
* currently highlighted).
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightPrevious = function() {
|
||||
var index = this.menuItems_.indexOf(this.highlightedItem_);
|
||||
this.highlightHelper_(index < 0 ? this.menuItems_.length : index, -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the first highlightable item.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightFirst_ = function() {
|
||||
this.highlightHelper_(-1, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights the last highlightable item.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightLast_ = function() {
|
||||
this.highlightHelper_(this.menuItems_.length, -1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function that manages the details of moving the highlight among
|
||||
* child menuitems in response to keyboard events.
|
||||
* @param {number} startIndex Start index.
|
||||
* @param {number} delta Step direction: 1 to go down, -1 to go up.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.highlightHelper_ = function(startIndex, delta) {
|
||||
var index = startIndex + delta;
|
||||
var menuItem;
|
||||
while ((menuItem = this.menuItems_[index])) {
|
||||
if (menuItem.isEnabled()) {
|
||||
this.setHighlighted(menuItem);
|
||||
break;
|
||||
}
|
||||
index += delta;
|
||||
}
|
||||
};
|
||||
|
||||
// Mouse events.
|
||||
|
||||
/**
|
||||
* Handles mouseover events. Highlight menuitems as the user hovers over them.
|
||||
* @param {!Event} e Mouse event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleMouseOver_ = function(e) {
|
||||
var menuItem = this.getMenuItem_(/** @type {Element} */ (e.target));
|
||||
|
||||
if (menuItem) {
|
||||
if (menuItem.isEnabled()) {
|
||||
if (this.highlightedItem_ != menuItem) {
|
||||
this.setHighlighted(menuItem);
|
||||
}
|
||||
} else {
|
||||
this.setHighlighted(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles click events. Pass the event onto the child menuitem to handle.
|
||||
* @param {!Event} e Click event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleClick_ = function(e) {
|
||||
var oldCoords = this.openingCoords;
|
||||
// Clear out the saved opening coords immediately so they're not used twice.
|
||||
this.openingCoords = null;
|
||||
if (oldCoords && typeof e.clientX == 'number') {
|
||||
var newCoords = new Blockly.utils.Coordinate(e.clientX, e.clientY);
|
||||
if (Blockly.utils.Coordinate.distance(oldCoords, newCoords) < 1) {
|
||||
// This menu was opened by a mousedown and we're handling the consequent
|
||||
// click event. The coords haven't changed, meaning this was the same
|
||||
// opening event. Don't do the usual behavior because the menu just popped
|
||||
// up under the mouse and the user didn't mean to activate this item.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var menuItem = this.getMenuItem_(/** @type {Element} */ (e.target));
|
||||
if (menuItem) {
|
||||
menuItem.performAction();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles mouse enter events. Focus the element.
|
||||
* @param {Event} _e Mouse event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleMouseEnter_ = function(_e) {
|
||||
this.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles mouse leave events. Blur and clear highlight.
|
||||
* @param {Event} _e Mouse event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleMouseLeave_ = function(_e) {
|
||||
if (this.getElement()) {
|
||||
this.blur_();
|
||||
this.setHighlighted(null);
|
||||
}
|
||||
};
|
||||
|
||||
// Keyboard events.
|
||||
|
||||
/**
|
||||
* Attempts to handle a keyboard event, if the menu item is enabled, by calling
|
||||
* {@link handleKeyEventInternal_}.
|
||||
* @param {!Event} e Key event to handle.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Menu.prototype.handleKeyEvent_ = function(e) {
|
||||
if (!this.menuItems_.length) {
|
||||
// Empty menu.
|
||||
return;
|
||||
}
|
||||
if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) {
|
||||
// Do not handle the key event if any modifier key is pressed.
|
||||
return;
|
||||
}
|
||||
|
||||
var highlighted = this.highlightedItem_;
|
||||
switch (e.keyCode) {
|
||||
case Blockly.utils.KeyCodes.ENTER:
|
||||
case Blockly.utils.KeyCodes.SPACE:
|
||||
if (highlighted) {
|
||||
highlighted.performAction();
|
||||
}
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.UP:
|
||||
this.highlightPrevious();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.DOWN:
|
||||
this.highlightNext();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.PAGE_UP:
|
||||
case Blockly.utils.KeyCodes.HOME:
|
||||
this.highlightFirst_();
|
||||
break;
|
||||
|
||||
case Blockly.utils.KeyCodes.PAGE_DOWN:
|
||||
case Blockly.utils.KeyCodes.END:
|
||||
this.highlightLast_();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Not a key the menu is interested in.
|
||||
return;
|
||||
}
|
||||
// The menu used this key, don't let it have secondary effects.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size of a rendered menu.
|
||||
* @return {!Blockly.utils.Size} Object with width and height properties.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Menu.prototype.getSize = function() {
|
||||
var menuDom = this.getElement();
|
||||
var menuSize = Blockly.utils.style.getSize(/** @type {!Element} */ (menuDom));
|
||||
// Recalculate height for the total content, not only box height.
|
||||
menuSize.height = menuDom.scrollHeight;
|
||||
return menuSize;
|
||||
};
|
||||
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Blockly menu item similar to Closure's goog.ui.MenuItem
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.MenuItem');
|
||||
|
||||
goog.require('Blockly.utils.aria');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.IdGenerator');
|
||||
|
||||
|
||||
/**
|
||||
* Class representing an item in a menu.
|
||||
*
|
||||
* @param {string} content Text caption to display as the content of
|
||||
* the item.
|
||||
* @param {string=} opt_value Data/model associated with the menu item.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.MenuItem = function(content, opt_value) {
|
||||
/**
|
||||
* Human-readable text of this menu item.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.content_ = content;
|
||||
|
||||
/**
|
||||
* Machine-readable value of this menu item.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.value_ = opt_value;
|
||||
|
||||
/**
|
||||
* Is the menu item clickable, as opposed to greyed-out.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.enabled_ = true;
|
||||
|
||||
/**
|
||||
* The DOM element for the menu item.
|
||||
* @type {?Element}
|
||||
* @private
|
||||
*/
|
||||
this.element_ = null;
|
||||
|
||||
/**
|
||||
* Whether the menu item is rendered right-to-left.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.rightToLeft_ = false;
|
||||
|
||||
/**
|
||||
* ARIA name for this menu.
|
||||
* @type {?Blockly.utils.aria.Role}
|
||||
* @private
|
||||
*/
|
||||
this.roleName_ = null;
|
||||
|
||||
/**
|
||||
* Is this menu item checkable.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.checkable_ = false;
|
||||
|
||||
/**
|
||||
* Is this menu item currently checked.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.checked_ = false;
|
||||
|
||||
/**
|
||||
* Is this menu item currently highlighted.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.highlight_ = false;
|
||||
|
||||
/**
|
||||
* Bound function to call when this menu item is clicked.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
this.actionHandler_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the menuitem's DOM.
|
||||
* @return {!Element} Completed DOM.
|
||||
*/
|
||||
Blockly.MenuItem.prototype.createDom = function() {
|
||||
var element = document.createElement('div');
|
||||
element.id = Blockly.utils.IdGenerator.getNextUniqueId();
|
||||
this.element_ = element;
|
||||
|
||||
// Set class and style
|
||||
// goog-menuitem* is deprecated, use blocklyMenuItem*. May 2020.
|
||||
element.className = 'blocklyMenuItem goog-menuitem ' +
|
||||
(this.enabled_ ? '' : 'blocklyMenuItemDisabled goog-menuitem-disabled ') +
|
||||
(this.checked_ ? 'blocklyMenuItemSelected goog-option-selected ' : '') +
|
||||
(this.highlight_ ?
|
||||
'blocklyMenuItemHighlight goog-menuitem-highlight ' : '') +
|
||||
(this.rightToLeft_ ? 'blocklyMenuItemRtl goog-menuitem-rtl ' : '');
|
||||
|
||||
var content = document.createElement('div');
|
||||
content.className = 'blocklyMenuItemContent goog-menuitem-content';
|
||||
// Add a checkbox for checkable menu items.
|
||||
if (this.checkable_) {
|
||||
var checkbox = document.createElement('div');
|
||||
checkbox.className = 'blocklyMenuItemCheckbox goog-menuitem-checkbox';
|
||||
content.appendChild(checkbox);
|
||||
}
|
||||
|
||||
content.appendChild(document.createTextNode(this.content_));
|
||||
element.appendChild(content);
|
||||
|
||||
// Initialize ARIA role and state.
|
||||
if (this.roleName_) {
|
||||
Blockly.utils.aria.setRole(element, this.roleName_);
|
||||
}
|
||||
Blockly.utils.aria.setState(element, Blockly.utils.aria.State.SELECTED,
|
||||
(this.checkable_ && this.checked_) || false);
|
||||
Blockly.utils.aria.setState(element, Blockly.utils.aria.State.DISABLED,
|
||||
!this.enabled_);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of this menu item.
|
||||
*/
|
||||
Blockly.MenuItem.prototype.dispose = function() {
|
||||
this.element_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the menu item's element.
|
||||
* @return {Element} The DOM element.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getElement = function() {
|
||||
return this.element_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the unique ID for this menu item.
|
||||
* @return {string} Unique component ID.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getId = function() {
|
||||
return this.element_.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the value associated with the menu item.
|
||||
* @return {*} value Value associated with the menu item.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.getValue = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set menu item's rendering direction.
|
||||
* @param {boolean} rtl True if RTL, false if LTR.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setRightToLeft = function(rtl) {
|
||||
this.rightToLeft_ = rtl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the menu item's accessibility role.
|
||||
* @param {!Blockly.utils.aria.Role} roleName Role name.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setRole = function(roleName) {
|
||||
this.roleName_ = roleName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the menu item to be checkable or not. Set to true for menu items
|
||||
* that represent checkable options.
|
||||
* @param {boolean} checkable Whether the menu item is checkable.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setCheckable = function(checkable) {
|
||||
this.checkable_ = checkable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks or unchecks the component.
|
||||
* @param {boolean} checked Whether to check or uncheck the component.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setChecked = function(checked) {
|
||||
this.checked_ = checked;
|
||||
};
|
||||
|
||||
/**
|
||||
* Highlights or unhighlights the component.
|
||||
* @param {boolean} highlight Whether to highlight or unhighlight the component.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setHighlighted = function(highlight) {
|
||||
this.highlight_ = highlight;
|
||||
|
||||
var el = this.getElement();
|
||||
if (el && this.isEnabled()) {
|
||||
// goog-menuitem-highlight is deprecated, use blocklyMenuItemHighlight.
|
||||
// May 2020.
|
||||
var name = 'blocklyMenuItemHighlight';
|
||||
var nameDep = 'goog-menuitem-highlight';
|
||||
if (highlight) {
|
||||
Blockly.utils.dom.addClass(el, name);
|
||||
Blockly.utils.dom.addClass(el, nameDep);
|
||||
} else {
|
||||
Blockly.utils.dom.removeClass(el, name);
|
||||
Blockly.utils.dom.removeClass(el, nameDep);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the menu item is enabled, false otherwise.
|
||||
* @return {boolean} Whether the menu item is enabled.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.isEnabled = function() {
|
||||
return this.enabled_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables or disables the menu item.
|
||||
* @param {boolean} enabled Whether to enable or disable the menu item.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.setEnabled = function(enabled) {
|
||||
this.enabled_ = enabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performs the appropriate action when the menu item is activated
|
||||
* by the user.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.performAction = function() {
|
||||
if (this.isEnabled() && this.actionHandler_) {
|
||||
this.actionHandler_(this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the handler that's called when the menu item is activated by the user.
|
||||
* `obj` will be used as the 'this' object in the function when called.
|
||||
* @param {function(!Blockly.MenuItem)} fn The handler.
|
||||
* @param {!Object} obj Used as the 'this' object in fn when called.
|
||||
* @package
|
||||
*/
|
||||
Blockly.MenuItem.prototype.onAction = function(fn, obj) {
|
||||
this.actionHandler_ = fn.bind(obj);
|
||||
};
|
||||
+33
-20
@@ -27,6 +27,8 @@ goog.require('Blockly.utils.xml');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a mutator dialog.
|
||||
@@ -74,7 +76,7 @@ Blockly.Mutator.prototype.getWorkspace = function() {
|
||||
/**
|
||||
* Draw the mutator icon.
|
||||
* @param {!Element} group The icon group.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Mutator.prototype.drawIcon_ = function(group) {
|
||||
// Square with rounded corners.
|
||||
@@ -165,8 +167,12 @@ Blockly.Mutator.prototype.createEditor_ = function() {
|
||||
}));
|
||||
workspaceOptions.toolboxPosition = this.block_.RTL ? Blockly.TOOLBOX_AT_RIGHT :
|
||||
Blockly.TOOLBOX_AT_LEFT;
|
||||
workspaceOptions.languageTree = quarkXml;
|
||||
workspaceOptions.getMetrics = this.getFlyoutMetrics_.bind(this);
|
||||
var hasFlyout = !!quarkXml;
|
||||
if (hasFlyout) {
|
||||
workspaceOptions.languageTree =
|
||||
Blockly.utils.toolbox.convertToolboxToJSON(quarkXml);
|
||||
workspaceOptions.getMetrics = this.getFlyoutMetrics_.bind(this);
|
||||
}
|
||||
this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions);
|
||||
this.workspace_.isMutator = true;
|
||||
this.workspace_.addChangeListener(Blockly.Events.disableOrphans);
|
||||
@@ -175,13 +181,15 @@ Blockly.Mutator.prototype.createEditor_ = function() {
|
||||
// a top level svg. Instead of handling scale themselves, mutators
|
||||
// inherit scale from the parent workspace.
|
||||
// To fix this, scale needs to be applied at a different level in the dom.
|
||||
var flyoutSvg = this.workspace_.addFlyout('g');
|
||||
var flyoutSvg = hasFlyout ? this.workspace_.addFlyout('g') : null;
|
||||
var background = this.workspace_.createDom('blocklyMutatorBackground');
|
||||
|
||||
// Insert the flyout after the <rect> but before the block canvas so that
|
||||
// the flyout is underneath in z-order. This makes blocks layering during
|
||||
// dragging work properly.
|
||||
background.insertBefore(flyoutSvg, this.workspace_.svgBlockCanvas_);
|
||||
if (flyoutSvg) {
|
||||
// Insert the flyout after the <rect> but before the block canvas so that
|
||||
// the flyout is underneath in z-order. This makes blocks layering during
|
||||
// dragging work properly.
|
||||
background.insertBefore(flyoutSvg, this.workspace_.svgBlockCanvas_);
|
||||
}
|
||||
this.svgDialog_.appendChild(background);
|
||||
|
||||
return this.svgDialog_;
|
||||
@@ -286,7 +294,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_);
|
||||
@@ -378,7 +386,8 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
|
||||
block.initSvg();
|
||||
block.render();
|
||||
|
||||
if (Blockly.getMainWorkspace().keyboardAccessibilityMode) {
|
||||
if ((/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace()))
|
||||
.keyboardAccessibilityMode) {
|
||||
Blockly.navigation.moveCursorOnBlockMutation(block);
|
||||
}
|
||||
var newMutationDom = block.mutationToDom();
|
||||
@@ -386,13 +395,6 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
|
||||
if (oldMutation != newMutation) {
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
block, 'mutation', null, oldMutation, newMutation));
|
||||
// Ensure that any bump is part of this mutation's event group.
|
||||
var group = Blockly.Events.getGroup();
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
block.bumpNeighbours();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY);
|
||||
}
|
||||
|
||||
// Don't update the bubble until the drag has ended, to avoid moving blocks
|
||||
@@ -411,15 +413,26 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
|
||||
* .viewWidth: Width of the visible rectangle,
|
||||
* .absoluteTop: Top-edge of view.
|
||||
* .absoluteLeft: Left-edge of view.
|
||||
* @return {!Object} Contains size and position metrics of mutator dialog's
|
||||
* workspace.
|
||||
* @return {!Blockly.utils.Metrics} Contains size and position metrics of
|
||||
* mutator dialog's workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Mutator.prototype.getFlyoutMetrics_ = function() {
|
||||
// The mutator workspace only uses a subset of Blockly.utils.Metrics
|
||||
// properties as features such as scroll and zoom are unsupported.
|
||||
var unsupported = 0;
|
||||
return {
|
||||
contentHeight: unsupported,
|
||||
contentWidth: unsupported,
|
||||
contentTop: unsupported,
|
||||
contentLeft: unsupported,
|
||||
|
||||
viewHeight: this.workspaceHeight_,
|
||||
viewWidth: this.workspaceWidth_ - this.workspace_.getFlyout().getWidth(),
|
||||
absoluteTop: 0,
|
||||
viewTop: unsupported,
|
||||
viewLeft: unsupported,
|
||||
|
||||
absoluteTop: unsupported,
|
||||
absoluteLeft: this.workspace_.RTL ? 0 :
|
||||
this.workspace_.getFlyout().getWidth()
|
||||
};
|
||||
|
||||
+57
-14
@@ -14,7 +14,11 @@ goog.provide('Blockly.Options');
|
||||
|
||||
goog.require('Blockly.Theme');
|
||||
goog.require('Blockly.Themes.Classic');
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.user.keyMap');
|
||||
goog.require('Blockly.utils.IdGenerator');
|
||||
goog.require('Blockly.utils.Metrics');
|
||||
goog.require('Blockly.utils.toolbox');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
@@ -29,7 +33,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 +41,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;
|
||||
@@ -106,39 +112,65 @@ Blockly.Options = function(options) {
|
||||
|
||||
var renderer = options['renderer'] || 'geras';
|
||||
|
||||
var plugins = options['plugins'] || {};
|
||||
|
||||
/** @type {boolean} */
|
||||
this.RTL = rtl;
|
||||
/** @type {boolean} */
|
||||
this.oneBasedIndex = oneBasedIndex;
|
||||
/** @type {boolean} */
|
||||
this.collapse = hasCollapse;
|
||||
/** @type {boolean} */
|
||||
this.comments = hasComments;
|
||||
/** @type {boolean} */
|
||||
this.disable = hasDisable;
|
||||
/** @type {boolean} */
|
||||
this.readOnly = readOnly;
|
||||
/** @type {number} */
|
||||
this.maxBlocks = options['maxBlocks'] || Infinity;
|
||||
/** @type {?Object.<string, number>} */
|
||||
this.maxInstances = options['maxInstances'];
|
||||
/** @type {string} */
|
||||
this.pathToMedia = pathToMedia;
|
||||
/** @type {boolean} */
|
||||
this.hasCategories = hasCategories;
|
||||
/** @type {!Object} */
|
||||
this.moveOptions = Blockly.Options.parseMoveOptions(options, hasCategories);
|
||||
/** @deprecated January 2019 */
|
||||
this.hasScrollbars = this.moveOptions.scrollbars;
|
||||
/** @type {boolean} */
|
||||
this.hasTrashcan = hasTrashcan;
|
||||
/** @type {number} */
|
||||
this.maxTrashcanContents = maxTrashcanContents;
|
||||
/** @type {boolean} */
|
||||
this.hasSounds = hasSounds;
|
||||
/** @type {boolean} */
|
||||
this.hasCss = hasCss;
|
||||
/** @type {boolean} */
|
||||
this.horizontalLayout = horizontalLayout;
|
||||
this.languageTree = languageTree;
|
||||
/** @type {Array.<Blockly.utils.toolbox.Toolbox>} */
|
||||
this.languageTree = toolboxContents;
|
||||
/** @type {!Object} */
|
||||
this.gridOptions = Blockly.Options.parseGridOptions_(options);
|
||||
/** @type {!Object} */
|
||||
this.zoomOptions = Blockly.Options.parseZoomOptions_(options);
|
||||
/** @type {number} */
|
||||
this.toolboxPosition = toolboxPosition;
|
||||
/** @type {!Blockly.Theme} */
|
||||
this.theme = Blockly.Options.parseThemeOptions_(options);
|
||||
/** @type {!Object<string,Blockly.Action>} */
|
||||
this.keyMap = keyMap;
|
||||
/** @type {string} */
|
||||
this.renderer = renderer;
|
||||
/** @type {?Object} */
|
||||
this.rendererOverrides = options['rendererOverrides'];
|
||||
|
||||
/**
|
||||
* The SVG element for the grid pattern.
|
||||
* Created during injection.
|
||||
* @type {!SVGElement}
|
||||
* @type {SVGElement}
|
||||
*/
|
||||
this.gridPattern = undefined;
|
||||
this.gridPattern = null;
|
||||
|
||||
/**
|
||||
* The parent of the current workspace, or null if there is no parent
|
||||
@@ -146,6 +178,12 @@ Blockly.Options = function(options) {
|
||||
* @type {Blockly.Workspace}
|
||||
*/
|
||||
this.parentWorkspace = options['parentWorkspace'];
|
||||
|
||||
/**
|
||||
* Map of plugin type to name of registered plugin or plugin class.
|
||||
* @type {!Object.<string, (function(new:?, ...?)|string)>}
|
||||
*/
|
||||
this.plugins = plugins;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -158,15 +196,15 @@ Blockly.BlocklyOptions = function() {};
|
||||
|
||||
/**
|
||||
* If set, sets the translation of the workspace to match the scrollbars.
|
||||
* @param {!Object} xyRatio Contains an x and/or y property which is a float
|
||||
* between 0 and 1 specifying the degree of scrolling.
|
||||
* @param {!{x:number,y:number}} xyRatio Contains an x and/or y property which
|
||||
* is a float between 0 and 1 specifying the degree of scrolling.
|
||||
* @return {void}
|
||||
*/
|
||||
Blockly.Options.prototype.setMetrics;
|
||||
|
||||
/**
|
||||
* Return an object with the metrics required to size the workspace.
|
||||
* @return {!Object} Contains size and position metrics.
|
||||
* @return {!Blockly.utils.Metrics} Contains size and position metrics.
|
||||
*/
|
||||
Blockly.Options.prototype.getMetrics;
|
||||
|
||||
@@ -280,15 +318,20 @@ Blockly.Options.parseGridOptions_ = function(options) {
|
||||
*/
|
||||
Blockly.Options.parseThemeOptions_ = function(options) {
|
||||
var theme = options['theme'] || Blockly.Themes.Classic;
|
||||
if (theme instanceof Blockly.Theme) {
|
||||
if (typeof theme == 'string') {
|
||||
return /** @type {!Blockly.Theme} */ (
|
||||
Blockly.registry.getObject(Blockly.registry.Type.THEME, theme));
|
||||
} else if (theme instanceof Blockly.Theme) {
|
||||
return /** @type {!Blockly.Theme} */ (theme);
|
||||
}
|
||||
return Blockly.Theme.defineTheme(theme.name || 'builtin', theme);
|
||||
return Blockly.Theme.defineTheme(theme.name ||
|
||||
('builtin' + Blockly.utils.IdGenerator.getNextUniqueId()), theme);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
+7
-17
@@ -60,23 +60,13 @@ Blockly.Procedures.ProcedureBlock;
|
||||
* list, and return value boolean.
|
||||
*/
|
||||
Blockly.Procedures.allProcedures = function(root) {
|
||||
var blocks = root.getAllBlocks(false);
|
||||
var proceduresReturn = [];
|
||||
var proceduresNoReturn = [];
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i].getProcedureDef) {
|
||||
var procedureBlock = /** @type {!Blockly.Procedures.ProcedureBlock} */ (
|
||||
blocks[i]);
|
||||
var tuple = procedureBlock.getProcedureDef();
|
||||
if (tuple) {
|
||||
if (tuple[2]) {
|
||||
proceduresReturn.push(tuple);
|
||||
} else {
|
||||
proceduresNoReturn.push(tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var proceduresNoReturn = root.getBlocksByType('procedures_defnoreturn', false)
|
||||
.map(function(block) {
|
||||
return /** @type {!Blockly.Procedures.ProcedureBlock} */ (block).getProcedureDef();
|
||||
});
|
||||
var proceduresReturn = root.getBlocksByType('procedures_defreturn', false).map(function(block) {
|
||||
return /** @type {!Blockly.Procedures.ProcedureBlock} */ (block).getProcedureDef();
|
||||
});
|
||||
proceduresNoReturn.sort(Blockly.Procedures.procTupleComparator_);
|
||||
proceduresReturn.sort(Blockly.Procedures.procTupleComparator_);
|
||||
return [proceduresNoReturn, proceduresReturn];
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview This file is a universal registry that provides generic methods
|
||||
* for registering and unregistering different types of classes.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.registry');
|
||||
|
||||
goog.requireType('Blockly.blockRendering.Renderer');
|
||||
goog.requireType('Blockly.Field');
|
||||
goog.requireType('Blockly.IToolbox');
|
||||
goog.requireType('Blockly.Theme');
|
||||
goog.requireType('Blockly.utils.toolbox');
|
||||
|
||||
|
||||
/**
|
||||
* A map of maps. With the keys being the type and name of the class we are
|
||||
* registering and the value being the constructor function.
|
||||
* e.g. {'field': {'field_angle': Blockly.FieldAngle}}
|
||||
*
|
||||
* @type {Object<string, Object<string, function(new:?)>>}
|
||||
*/
|
||||
Blockly.registry.typeMap_ = {};
|
||||
|
||||
/**
|
||||
* The string used to register the default class for a type of plugin.
|
||||
* @type {string}
|
||||
*/
|
||||
Blockly.registry.DEFAULT = 'default';
|
||||
|
||||
/**
|
||||
* A name with the type of the element stored in the generic.
|
||||
* @param {string} name The name of the registry type.
|
||||
* @constructor
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.Type = function(name) {
|
||||
/**
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.name_ = name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the name of the type.
|
||||
* @return {string} The name.
|
||||
* @override
|
||||
*/
|
||||
Blockly.registry.Type.prototype.toString = function() {
|
||||
return this.name_;
|
||||
};
|
||||
|
||||
/** @type {!Blockly.registry.Type<Blockly.blockRendering.Renderer>} */
|
||||
Blockly.registry.Type.RENDERER = new Blockly.registry.Type('renderer');
|
||||
|
||||
/** @type {!Blockly.registry.Type<Blockly.Field>} */
|
||||
Blockly.registry.Type.FIELD = new Blockly.registry.Type('field');
|
||||
|
||||
/** @type {!Blockly.registry.Type<Blockly.IToolbox>} */
|
||||
Blockly.registry.Type.TOOLBOX = new Blockly.registry.Type('toolbox');
|
||||
|
||||
/** @type {!Blockly.registry.Type<Blockly.Theme>} */
|
||||
Blockly.registry.Type.THEME = new Blockly.registry.Type('theme');
|
||||
|
||||
/**
|
||||
* Registers a class based on a type and name.
|
||||
* @param {string|Blockly.registry.Type<T>} type The type of the plugin.
|
||||
* (e.g. Field, Renderer)
|
||||
* @param {string} name The plugin's name. (Ex. field_angle, geras)
|
||||
* @param {?function(new:T, ...?)|Object} registryItem The class or object to
|
||||
* register.
|
||||
* @throws {Error} if the type or name is empty, a name with the given type has
|
||||
* already been registered, or if the given class or object is not valid for it's type.
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.register = function(type, name, registryItem) {
|
||||
if ((!(type instanceof Blockly.registry.Type) && typeof type != 'string') || String(type).trim() == '') {
|
||||
throw Error('Invalid type "' + type + '". The type must be a' +
|
||||
' non-empty string or a Blockly.registry.Type.');
|
||||
}
|
||||
type = String(type).toLowerCase();
|
||||
|
||||
if ((typeof name != 'string') || (name.trim() == '')) {
|
||||
throw Error('Invalid name "' + name + '". The name must be a' +
|
||||
' non-empty string.');
|
||||
}
|
||||
name = name.toLowerCase();
|
||||
if (!registryItem) {
|
||||
throw Error('Can not register a null value');
|
||||
}
|
||||
var typeRegistry = Blockly.registry.typeMap_[type];
|
||||
// If the type registry has not been created, create it.
|
||||
if (!typeRegistry) {
|
||||
typeRegistry = Blockly.registry.typeMap_[type] = {};
|
||||
}
|
||||
|
||||
// Validate that the given class has all the required properties.
|
||||
Blockly.registry.validate_(type, registryItem);
|
||||
|
||||
// If the name already exists throw an error.
|
||||
if (typeRegistry[name]) {
|
||||
throw Error('Name "' + name + '" with type "' + type + '" already registered.');
|
||||
}
|
||||
typeRegistry[name] = registryItem;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the given registry item for properties that are required based on the
|
||||
* type.
|
||||
* @param {string} type The type of the plugin. (e.g. Field, Renderer)
|
||||
* @param {Function|Object} registryItem A class or object that we are checking
|
||||
* for the required properties.
|
||||
* @private
|
||||
*/
|
||||
Blockly.registry.validate_ = function(type, registryItem) {
|
||||
switch (type) {
|
||||
case String(Blockly.registry.Type.FIELD):
|
||||
if (typeof registryItem.fromJson != 'function') {
|
||||
throw Error('Type "' + type + '" must have a fromJson function');
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregisters the registry item with the given type and name.
|
||||
* @param {string|Blockly.registry.Type<T>} type The type of the plugin.
|
||||
* (e.g. Field, Renderer)
|
||||
* @param {string} name The plugin's name. (Ex. field_angle, geras)
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.unregister = function(type, name) {
|
||||
type = String(type).toLowerCase();
|
||||
name = name.toLowerCase();
|
||||
var typeRegistry = Blockly.registry.typeMap_[type];
|
||||
if (!typeRegistry) {
|
||||
console.warn('No type "' + type + '" found');
|
||||
return;
|
||||
}
|
||||
if (!typeRegistry[name]) {
|
||||
console.warn('No name "' + name + '" with type "' + type + '" found');
|
||||
return;
|
||||
}
|
||||
delete Blockly.registry.typeMap_[type][name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the registry item for the given name and type. This can be either a
|
||||
* class or an object.l
|
||||
* @param {string|Blockly.registry.Type<T>} type The type of the plugin.
|
||||
* (e.g. Field, Renderer)
|
||||
* @param {string} name The plugin's name. (Ex. field_angle, geras)
|
||||
* @return {?function(new:T, ...?)|Object} The class or object with the given
|
||||
* name and type or null if none exists.
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.getItem_ = function(type, name) {
|
||||
type = String(type).toLowerCase();
|
||||
name = name.toLowerCase();
|
||||
var typeRegistry = Blockly.registry.typeMap_[type];
|
||||
if (!typeRegistry) {
|
||||
console.warn('No type "' + type + '" found');
|
||||
return null;
|
||||
}
|
||||
if (!typeRegistry[name]) {
|
||||
console.warn('No name "' + name + '" with type "' + type + '" found');
|
||||
return null;
|
||||
}
|
||||
return typeRegistry[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the class for the given name and type.
|
||||
* @param {string|Blockly.registry.Type<T>} type The type of the plugin.
|
||||
* (e.g. Field, Renderer)
|
||||
* @param {string} name The plugin's name. (Ex. field_angle, geras)
|
||||
* @return {?function(new:T, ...?)} The class with the given name and type or
|
||||
* null if none exists.
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.getClass = function(type, name) {
|
||||
return /** @type {?function(new:T, ...?)} */ (Blockly.registry.getItem_(type, name));
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the object for the given name and type.
|
||||
* @param {string|Blockly.registry.Type<T>} type The type of the plugin.
|
||||
* (e.g. Category)
|
||||
* @param {string} name The plugin's name. (Ex. logic_category)
|
||||
* @returns {T} The object with the given name and type or null if none exists.
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.getObject = function(type, name) {
|
||||
return /** @type {T} */ (Blockly.registry.getItem_(type, name));
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the class from Blockly options for the given type.
|
||||
* This is used for plugins that override a built in feature. (e.g. Toolbox)
|
||||
* @param {Blockly.registry.Type<T>} type The type of the plugin.
|
||||
* @param {!Blockly.Options} options The option object to check for the given
|
||||
* plugin.
|
||||
* @return {?function(new:T, ...?)} The class for the plugin.
|
||||
* @template T
|
||||
*/
|
||||
Blockly.registry.getClassFromOptions = function(type, options) {
|
||||
var typeName = type.toString();
|
||||
var plugin = options.plugins[typeName] || Blockly.registry.DEFAULT;
|
||||
|
||||
// If the user passed in a plugin class instead of a registered plugin name.
|
||||
if (typeof plugin == 'function') {
|
||||
return plugin;
|
||||
}
|
||||
return Blockly.registry.getClass(type, plugin);
|
||||
};
|
||||
@@ -59,6 +59,12 @@ Blockly.RenderedConnection = function(source, type) {
|
||||
* @private
|
||||
*/
|
||||
this.trackedState_ = Blockly.RenderedConnection.TrackedState.WILL_TRACK;
|
||||
|
||||
/**
|
||||
* Connection this connection connects to. Null if not connected.
|
||||
* @type {Blockly.RenderedConnection}
|
||||
*/
|
||||
this.targetConnection = null;
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.RenderedConnection, Blockly.Connection);
|
||||
|
||||
@@ -454,6 +460,8 @@ Blockly.RenderedConnection.prototype.disconnectInternal_ = function(parentBlock,
|
||||
if (childBlock.rendered) {
|
||||
childBlock.updateDisabled();
|
||||
childBlock.render();
|
||||
// Reset visibility, since the child is now a top block.
|
||||
childBlock.getSvgRoot().style.display = 'block';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -504,14 +512,16 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
|
||||
var parentConnection = this;
|
||||
var parentBlock = parentConnection.getSourceBlock();
|
||||
var childBlock = childConnection.getSourceBlock();
|
||||
var parentRendered = parentBlock.rendered;
|
||||
var childRendered = childBlock.rendered;
|
||||
|
||||
if (parentBlock.rendered) {
|
||||
if (parentRendered) {
|
||||
parentBlock.updateDisabled();
|
||||
}
|
||||
if (childBlock.rendered) {
|
||||
if (childRendered) {
|
||||
childBlock.updateDisabled();
|
||||
}
|
||||
if (parentBlock.rendered && childBlock.rendered) {
|
||||
if (parentRendered && childRendered) {
|
||||
if (parentConnection.type == Blockly.NEXT_STATEMENT ||
|
||||
parentConnection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Child block may need to square off its corners if it is in a stack.
|
||||
@@ -523,6 +533,13 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
|
||||
parentBlock.render();
|
||||
}
|
||||
}
|
||||
|
||||
// The input the child block is connected to (if any).
|
||||
var parentInput = parentBlock.getInputWithBlock(childBlock);
|
||||
if (parentInput) {
|
||||
var visible = parentInput.isVisible();
|
||||
childBlock.getSvgRoot().style.display = visible ? 'block' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,16 +16,10 @@
|
||||
*/
|
||||
goog.provide('Blockly.blockRendering');
|
||||
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
|
||||
/**
|
||||
* The set of all registered renderers, keyed by their name.
|
||||
* @type {!Object<string, !Function>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.blockRendering.rendererMap_ = {};
|
||||
|
||||
/**
|
||||
* Whether or not the debugger is turned on.
|
||||
* @type {boolean}
|
||||
@@ -41,10 +35,8 @@ Blockly.blockRendering.useDebugger = false;
|
||||
* @throws {Error} if a renderer with the same name has already been registered.
|
||||
*/
|
||||
Blockly.blockRendering.register = function(name, rendererClass) {
|
||||
if (Blockly.blockRendering.rendererMap_[name]) {
|
||||
throw Error('Renderer has already been registered.');
|
||||
}
|
||||
Blockly.blockRendering.rendererMap_[name] = rendererClass;
|
||||
Blockly.registry.register(Blockly.registry.Type.RENDERER, name,
|
||||
rendererClass);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -52,14 +44,8 @@ Blockly.blockRendering.register = function(name, rendererClass) {
|
||||
* @param {string} name The name of the renderer.
|
||||
*/
|
||||
Blockly.blockRendering.unregister = function(name) {
|
||||
if (Blockly.blockRendering.rendererMap_[name]) {
|
||||
delete Blockly.blockRendering.rendererMap_[name];
|
||||
} else {
|
||||
console.warn('No renderer mapping for name "' + name +
|
||||
'" found to unregister');
|
||||
}
|
||||
Blockly.registry.unregister(Blockly.registry.Type.RENDERER, name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Turn on the blocks debugger.
|
||||
* @package
|
||||
@@ -85,12 +71,11 @@ Blockly.blockRendering.stopDebugger = function() {
|
||||
* Already initialized.
|
||||
* @package
|
||||
*/
|
||||
|
||||
Blockly.blockRendering.init = function(name, theme, opt_rendererOverrides) {
|
||||
if (!Blockly.blockRendering.rendererMap_[name]) {
|
||||
throw Error('Renderer not registered: ', name);
|
||||
}
|
||||
var renderer = (/** @type {!Blockly.blockRendering.Renderer} */ (
|
||||
new Blockly.blockRendering.rendererMap_[name](name)));
|
||||
var rendererClass = Blockly.registry.getClass(
|
||||
Blockly.registry.Type.RENDERER, name);
|
||||
var renderer = new rendererClass(name);
|
||||
renderer.init(theme, opt_rendererOverrides);
|
||||
return renderer;
|
||||
};
|
||||
|
||||
@@ -256,7 +256,7 @@ Blockly.blockRendering.ConstantProvider = function() {
|
||||
* @type {number}
|
||||
*/
|
||||
this.FIELD_TEXT_HEIGHT = -1; // Dynamically set
|
||||
|
||||
|
||||
/**
|
||||
* Text baseline. This constant is dynamically set in ``setFontConstants_``
|
||||
* to be the baseline of the text based on the font used.
|
||||
@@ -1168,9 +1168,8 @@ Blockly.blockRendering.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
// Text.
|
||||
selector + ' .blocklyText, ',
|
||||
selector + ' .blocklyFlyoutLabelText {',
|
||||
'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';',
|
||||
'font-size: ' + this.FIELD_TEXT_FONTSIZE + 'pt;',
|
||||
'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';',
|
||||
'font: ' + this.FIELD_TEXT_FONTWEIGHT + ' ' +
|
||||
this.FIELD_TEXT_FONTSIZE + 'pt ' + this.FIELD_TEXT_FONTFAMILY + ';',
|
||||
'}',
|
||||
|
||||
// Fields.
|
||||
@@ -1233,7 +1232,7 @@ Blockly.blockRendering.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
// Insertion marker.
|
||||
selector + ' .blocklyInsertionMarker>.blocklyPath {',
|
||||
'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';',
|
||||
'stroke: none',
|
||||
'stroke: none;',
|
||||
'}',
|
||||
/* eslint-enable indent */
|
||||
];
|
||||
|
||||
@@ -27,6 +27,38 @@ goog.requireType('Blockly.Theme');
|
||||
*/
|
||||
Blockly.blockRendering.IPathObject = function(_root, _constants) {};
|
||||
|
||||
/**
|
||||
* The primary path of the block.
|
||||
* @type {!SVGElement}
|
||||
*/
|
||||
Blockly.blockRendering.IPathObject.prototype.svgPath;
|
||||
|
||||
/**
|
||||
* The renderer's constant provider.
|
||||
* @type {!Blockly.blockRendering.ConstantProvider}
|
||||
*/
|
||||
Blockly.blockRendering.IPathObject.prototype.constants;
|
||||
|
||||
/**
|
||||
* The primary path of the block.
|
||||
* @type {!Blockly.Theme.BlockStyle}
|
||||
*/
|
||||
Blockly.blockRendering.IPathObject.prototype.style;
|
||||
|
||||
/**
|
||||
* Holds the cursors svg element when the cursor is attached to the block.
|
||||
* This is null if there is no cursor on the block.
|
||||
* @type {SVGElement}
|
||||
*/
|
||||
Blockly.blockRendering.IPathObject.prototype.cursorSvg;
|
||||
|
||||
/**
|
||||
* Holds the markers svg element when the marker is attached to the block.
|
||||
* This is null if there is no marker on the block.
|
||||
* @type {SVGElement}
|
||||
*/
|
||||
Blockly.blockRendering.IPathObject.prototype.markerSvg;
|
||||
|
||||
/**
|
||||
* Set the path generated by the renderer onto the respective SVG element.
|
||||
* @param {string} pathString The path.
|
||||
|
||||
@@ -581,7 +581,7 @@ Blockly.blockRendering.RenderInfo.prototype.addAlignmentPadding_ = function(row,
|
||||
if (row.hasExternalInput || row.hasStatement) {
|
||||
row.widthWithConnectedBlocks += missingSpace;
|
||||
}
|
||||
|
||||
|
||||
// Decide where the extra padding goes.
|
||||
if (row.align == Blockly.ALIGN_LEFT) {
|
||||
// Add padding to the end of the row.
|
||||
|
||||
+142
-101
@@ -42,7 +42,7 @@ Blockly.blockRendering.MarkerSvg = function(workspace, constants, marker) {
|
||||
/**
|
||||
* The workspace, field, or block that the marker SVG element should be
|
||||
* attached to.
|
||||
* @type {Blockly.WorkspaceSvg|Blockly.Field|Blockly.BlockSvg}
|
||||
* @type {Blockly.IASTNodeLocationSvg}
|
||||
* @private
|
||||
*/
|
||||
this.parent_ = null;
|
||||
@@ -85,8 +85,7 @@ Blockly.blockRendering.MarkerSvg.MARKER_CLASS = 'blocklyMarker';
|
||||
/**
|
||||
* What we multiply the height by to get the height of the marker.
|
||||
* Only used for the block and block connections.
|
||||
* @type {number}
|
||||
* @const
|
||||
* @const {number}
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.HEIGHT_MULTIPLIER = 3 / 4;
|
||||
|
||||
@@ -98,6 +97,14 @@ Blockly.blockRendering.MarkerSvg.prototype.getSvgRoot = function() {
|
||||
return this.svgGroup_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the marker.
|
||||
* @return {!Blockly.Marker} The marker to draw for.
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.getMarker = function() {
|
||||
return this.marker_;
|
||||
};
|
||||
|
||||
/**
|
||||
* True if the marker should be drawn as a cursor, false otherwise.
|
||||
* A cursor is drawn as a flashing line. A marker is drawn as a solid line.
|
||||
@@ -123,15 +130,13 @@ Blockly.blockRendering.MarkerSvg.prototype.createDom = function() {
|
||||
}, null);
|
||||
|
||||
this.createDomInternal_();
|
||||
this.applyColour_();
|
||||
return this.svgGroup_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches the SVG root of the marker to the SVG group of the parent.
|
||||
* @param {!Blockly.WorkspaceSvg|!Blockly.Field|!Blockly.BlockSvg} newParent
|
||||
* The workspace, field, or block that the marker SVG element should be
|
||||
* attached to.
|
||||
* @param {!Blockly.IASTNodeLocationSvg} newParent The workspace, field, or
|
||||
* block that the marker SVG element should be attached to.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.setParent_ = function(newParent) {
|
||||
@@ -149,6 +154,63 @@ Blockly.blockRendering.MarkerSvg.prototype.setParent_ = function(newParent) {
|
||||
this.parent_ = newParent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the marker.
|
||||
* @param {Blockly.ASTNode} oldNode The previous node the marker was on or null.
|
||||
* @param {Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.draw = function(oldNode, curNode) {
|
||||
if (!curNode) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this.constants_ = this.workspace_.getRenderer().getConstants();
|
||||
|
||||
var defaultColour = this.isCursor() ? this.constants_.CURSOR_COLOUR :
|
||||
this.constants_.MARKER_COLOUR;
|
||||
this.colour_ = this.marker_.colour || defaultColour;
|
||||
this.applyColour_(curNode);
|
||||
|
||||
this.showAtLocation_(curNode);
|
||||
|
||||
this.fireMarkerEvent_(oldNode, curNode);
|
||||
|
||||
// Ensures the marker will be visible immediately after the move.
|
||||
var animate = this.currentMarkerSvg.childNodes[0];
|
||||
if (animate !== undefined) {
|
||||
animate.beginElement && animate.beginElement();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Update the marker's visible state based on the type of curNode..
|
||||
* @param {!Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showAtLocation_ = function(curNode) {
|
||||
var curNodeAsConnection =
|
||||
/** @type {!Blockly.Connection} */ (curNode.getLocation());
|
||||
if (curNode.getType() == Blockly.ASTNode.types.BLOCK) {
|
||||
this.showWithBlock_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.OUTPUT) {
|
||||
this.showWithOutput_(curNode);
|
||||
} else if (curNodeAsConnection.type == Blockly.INPUT_VALUE) {
|
||||
this.showWithInput_(curNode);
|
||||
} else if (curNodeAsConnection.type == Blockly.NEXT_STATEMENT) {
|
||||
this.showWithNext_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.PREVIOUS) {
|
||||
this.showWithPrevious_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.FIELD) {
|
||||
this.showWithField_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.WORKSPACE) {
|
||||
this.showWithCoordinates_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.STACK) {
|
||||
this.showWithStack_(curNode);
|
||||
}
|
||||
};
|
||||
|
||||
/**************************
|
||||
* Display
|
||||
**************************/
|
||||
@@ -156,13 +218,12 @@ Blockly.blockRendering.MarkerSvg.prototype.setParent_ = function(newParent) {
|
||||
/**
|
||||
* Show the marker as a combination of the previous connection and block,
|
||||
* the output connection and block, or just the block.
|
||||
* @param {Blockly.BlockSvg} block The block the marker is currently on.
|
||||
* @protected
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @private
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithBlockPrevOutput_ = function(block) {
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithBlockPrevOutput_ = function(
|
||||
curNode) {
|
||||
var block = /** @type {!Blockly.BlockSvg} */ (curNode.getSourceBlock());
|
||||
var width = block.width;
|
||||
var height = block.height;
|
||||
var markerHeight = height * Blockly.blockRendering.MarkerSvg.HEIGHT_MULTIPLIER;
|
||||
@@ -177,18 +238,46 @@ Blockly.blockRendering.MarkerSvg.prototype.showWithBlockPrevOutput_ = function(b
|
||||
} else {
|
||||
this.positionBlock_(width, markerOffset, markerHeight);
|
||||
}
|
||||
|
||||
this.setParent_(block);
|
||||
this.showCurrent_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the visual representation of a workspace coordinate.
|
||||
* This is a horizontal line.
|
||||
* @param {!Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* Position and display the marker for a block.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithCoordinates_ = function(curNode) {
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithBlock_ = function(curNode) {
|
||||
this.showWithBlockPrevOutput_(curNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Position and display the marker for a previous connection.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithPrevious_ = function(
|
||||
curNode) {
|
||||
this.showWithBlockPrevOutput_(curNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Position and display the marker for an output connection.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithOutput_ = function(curNode) {
|
||||
this.showWithBlockPrevOutput_(curNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Position and display the marker for a workspace coordinate.
|
||||
* This is a horizontal line.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithCoordinates_ = function(
|
||||
curNode) {
|
||||
var wsCoordinate = curNode.getWsCoordinate();
|
||||
var x = wsCoordinate.x;
|
||||
var y = wsCoordinate.y;
|
||||
@@ -203,9 +292,9 @@ Blockly.blockRendering.MarkerSvg.prototype.showWithCoordinates_ = function(curNo
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the visual representation of a field.
|
||||
* Position and display the marker for a field.
|
||||
* This is a box around the field.
|
||||
* @param {!Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithField_ = function(curNode) {
|
||||
@@ -219,9 +308,9 @@ Blockly.blockRendering.MarkerSvg.prototype.showWithField_ = function(curNode) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the visual representation of an input.
|
||||
* Position and display the marker for an input.
|
||||
* This is a puzzle piece.
|
||||
* @param {!Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithInput_ = function(curNode) {
|
||||
@@ -236,14 +325,16 @@ Blockly.blockRendering.MarkerSvg.prototype.showWithInput_ = function(curNode) {
|
||||
|
||||
|
||||
/**
|
||||
* Show the visual representation of a next connection.
|
||||
* Position and display the marker for a next connection.
|
||||
* This is a horizontal line.
|
||||
* @param {!Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithNext_ = function(curNode) {
|
||||
var connection = curNode.getLocation();
|
||||
var targetBlock = /** @type {Blockly.BlockSvg} */ (connection.getSourceBlock());
|
||||
var connection =
|
||||
/** @type {!Blockly.RenderedConnection} */ (curNode.getLocation());
|
||||
var targetBlock =
|
||||
/** @type {Blockly.BlockSvg} */ (connection.getSourceBlock());
|
||||
var x = 0;
|
||||
var y = connection.getOffsetInBlock().y;
|
||||
var width = targetBlock.getHeightWidth().width;
|
||||
@@ -256,9 +347,9 @@ Blockly.blockRendering.MarkerSvg.prototype.showWithNext_ = function(curNode) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the visual representation of a stack.
|
||||
* Position and display the marker for a stack.
|
||||
* This is a box with extra padding around the entire stack of blocks.
|
||||
* @param {!Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showWithStack_ = function(curNode) {
|
||||
@@ -305,7 +396,7 @@ Blockly.blockRendering.MarkerSvg.prototype.showCurrent_ = function() {
|
||||
* @param {number} width The width of the block.
|
||||
* @param {number} markerOffset The extra padding for around the block.
|
||||
* @param {number} markerHeight The height of the marker.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionBlock_ = function(
|
||||
width, markerOffset, markerHeight) {
|
||||
@@ -323,10 +414,12 @@ Blockly.blockRendering.MarkerSvg.prototype.positionBlock_ = function(
|
||||
/**
|
||||
* Position the marker for an input connection.
|
||||
* Displays a filled in puzzle piece.
|
||||
* @param {!Blockly.RenderedConnection} connection The connection to position marker around.
|
||||
* @private
|
||||
* @param {!Blockly.RenderedConnection} connection The connection to position
|
||||
* marker around.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionInput_ = function(connection) {
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionInput_ = function(
|
||||
connection) {
|
||||
var x = connection.getOffsetInBlock().x;
|
||||
var y = connection.getOffsetInBlock().y;
|
||||
|
||||
@@ -335,7 +428,8 @@ Blockly.blockRendering.MarkerSvg.prototype.positionInput_ = function(connection)
|
||||
|
||||
this.markerInput_.setAttribute('d', path);
|
||||
this.markerInput_.setAttribute('transform',
|
||||
'translate(' + x + ',' + y + ')' + (this.workspace_.RTL ? ' scale(-1 1)' : ''));
|
||||
'translate(' + x + ',' + y + ')' +
|
||||
(this.workspace_.RTL ? ' scale(-1 1)' : ''));
|
||||
this.currentMarkerSvg = this.markerInput_;
|
||||
};
|
||||
|
||||
@@ -347,7 +441,8 @@ Blockly.blockRendering.MarkerSvg.prototype.positionInput_ = function(connection)
|
||||
* @param {number} width The new width, in workspace units.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionLine_ = function(x, y, width) {
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionLine_ = function(
|
||||
x, y, width) {
|
||||
this.markerSvgLine_.setAttribute('x', x);
|
||||
this.markerSvgLine_.setAttribute('y', y);
|
||||
this.markerSvgLine_.setAttribute('width', width);
|
||||
@@ -360,7 +455,7 @@ Blockly.blockRendering.MarkerSvg.prototype.positionLine_ = function(x, y, width)
|
||||
* @param {number} width The width of the block.
|
||||
* @param {number} height The height of the block.
|
||||
* @param {!Object} connectionShape The shape object for the connection.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionOutput_ = function(
|
||||
width, height, connectionShape) {
|
||||
@@ -387,7 +482,7 @@ Blockly.blockRendering.MarkerSvg.prototype.positionOutput_ = function(
|
||||
* @param {number} markerOffset The offset of the marker from around the block.
|
||||
* @param {number} markerHeight The height of the marker.
|
||||
* @param {!Object} connectionShape The shape object for the connection.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionPrevious_ = function(
|
||||
width, markerOffset, markerHeight, connectionShape) {
|
||||
@@ -415,7 +510,8 @@ Blockly.blockRendering.MarkerSvg.prototype.positionPrevious_ = function(
|
||||
* @param {number} height The new height, in workspace units.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionRect_ = function(x, y, width, height) {
|
||||
Blockly.blockRendering.MarkerSvg.prototype.positionRect_ = function(
|
||||
x, y, width, height) {
|
||||
this.markerSvgRect_.setAttribute('x', x);
|
||||
this.markerSvgRect_.setAttribute('y', y);
|
||||
this.markerSvgRect_.setAttribute('width', width);
|
||||
@@ -434,7 +530,6 @@ Blockly.blockRendering.MarkerSvg.prototype.flipRtl_ = function(markerSvg) {
|
||||
|
||||
/**
|
||||
* Hide the marker.
|
||||
* @package
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.hide = function() {
|
||||
this.markerSvgLine_.style.display = 'none';
|
||||
@@ -443,64 +538,6 @@ Blockly.blockRendering.MarkerSvg.prototype.hide = function() {
|
||||
this.markerBlock_.style.display = 'none';
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the marker.
|
||||
* @param {Blockly.ASTNode} oldNode The previous node the marker was on or null.
|
||||
* @param {Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @package
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.draw = function(oldNode, curNode) {
|
||||
if (!curNode) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this.constants_ = this.workspace_.getRenderer().getConstants();
|
||||
|
||||
var defaultColour = this.isCursor() ? this.constants_.CURSOR_COLOUR :
|
||||
this.constants_.MARKER_COLOUR;
|
||||
this.colour_ = this.marker_.colour || defaultColour;
|
||||
this.applyColour_();
|
||||
|
||||
this.showAtLocation_(curNode);
|
||||
|
||||
this.firemarkerEvent_(oldNode, curNode);
|
||||
|
||||
// Ensures the marker will be visible immediately after the move.
|
||||
var animate = this.currentMarkerSvg.childNodes[0];
|
||||
if (animate !== undefined) {
|
||||
animate.beginElement && animate.beginElement();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Update the marker's visible state based on the type of curNode..
|
||||
* @param {Blockly.ASTNode} curNode The node that we want to draw the marker for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.showAtLocation_ = function(curNode) {
|
||||
if (curNode.getType() == Blockly.ASTNode.types.BLOCK) {
|
||||
var block = /** @type {Blockly.BlockSvg} */ (curNode.getLocation());
|
||||
this.showWithBlockPrevOutput_(block);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.OUTPUT) {
|
||||
var outputBlock = /** @type {Blockly.BlockSvg} */ (curNode.getLocation().getSourceBlock());
|
||||
this.showWithBlockPrevOutput_(outputBlock);
|
||||
} else if (curNode.getLocation().type == Blockly.INPUT_VALUE) {
|
||||
this.showWithInput_(curNode);
|
||||
} else if (curNode.getLocation().type == Blockly.NEXT_STATEMENT) {
|
||||
this.showWithNext_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.PREVIOUS) {
|
||||
var previousBlock = /** @type {Blockly.BlockSvg} */ (curNode.getLocation().getSourceBlock());
|
||||
this.showWithBlockPrevOutput_(previousBlock);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.FIELD) {
|
||||
this.showWithField_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.WORKSPACE) {
|
||||
this.showWithCoordinates_(curNode);
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.STACK) {
|
||||
this.showWithStack_(curNode);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire event for the marker or marker.
|
||||
@@ -508,12 +545,14 @@ Blockly.blockRendering.MarkerSvg.prototype.showAtLocation_ = function(curNode) {
|
||||
* @param {!Blockly.ASTNode} curNode The new node the marker is currently on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.firemarkerEvent_ = function(oldNode, curNode) {
|
||||
Blockly.blockRendering.MarkerSvg.prototype.fireMarkerEvent_ = function(
|
||||
oldNode, curNode) {
|
||||
var curBlock = curNode.getSourceBlock();
|
||||
var eventType = this.isCursor() ? 'cursorMove' : 'markerMove';
|
||||
var event = new Blockly.Events.Ui(curBlock, eventType, oldNode, curNode);
|
||||
if (curNode.getType() == Blockly.ASTNode.types.WORKSPACE) {
|
||||
event.workspaceId = curNode.getLocation().id;
|
||||
event.workspaceId =
|
||||
(/** @type {!Blockly.Workspace} */ (curNode.getLocation())).id;
|
||||
}
|
||||
Blockly.Events.fire(event);
|
||||
};
|
||||
@@ -555,7 +594,8 @@ Blockly.blockRendering.MarkerSvg.prototype.createDomInternal_ = function() {
|
||||
'height': this.constants_.WS_CURSOR_HEIGHT
|
||||
}, this.svgGroup_);
|
||||
|
||||
// A horizontal line used to represent a workspace coordinate or next connection.
|
||||
// A horizontal line used to represent a workspace coordinate or next
|
||||
// connection.
|
||||
this.markerSvgLine_ = Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'width': this.constants_.CURSOR_WS_WIDTH,
|
||||
@@ -609,9 +649,11 @@ Blockly.blockRendering.MarkerSvg.prototype.createDomInternal_ = function() {
|
||||
|
||||
/**
|
||||
* Apply the marker's colour.
|
||||
* @param {!Blockly.ASTNode} _curNode The node that we want to draw the marker
|
||||
* for.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.applyColour_ = function() {
|
||||
Blockly.blockRendering.MarkerSvg.prototype.applyColour_ = function(_curNode) {
|
||||
this.markerSvgLine_.setAttribute('fill', this.colour_);
|
||||
this.markerSvgRect_.setAttribute('stroke', this.colour_);
|
||||
this.markerInput_.setAttribute('fill', this.colour_);
|
||||
@@ -627,7 +669,6 @@ Blockly.blockRendering.MarkerSvg.prototype.applyColour_ = function() {
|
||||
|
||||
/**
|
||||
* Dispose of this marker.
|
||||
* @package
|
||||
*/
|
||||
Blockly.blockRendering.MarkerSvg.prototype.dispose = function() {
|
||||
if (this.svgGroup_) {
|
||||
|
||||
@@ -43,7 +43,7 @@ Blockly.blockRendering.PathObject = function(root, style, constants) {
|
||||
|
||||
/**
|
||||
* The primary path of the block.
|
||||
* @type {SVGElement}
|
||||
* @type {!SVGElement}
|
||||
* @package
|
||||
*/
|
||||
this.svgPath = Blockly.utils.dom.createSvgElement('path',
|
||||
|
||||
@@ -21,6 +21,7 @@ goog.require('Blockly.blockRendering.RenderInfo');
|
||||
goog.require('Blockly.InsertionMarkerManager');
|
||||
|
||||
goog.requireType('Blockly.blockRendering.Debug');
|
||||
goog.requireType('Blockly.IRegistrable');
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,6 +29,7 @@ goog.requireType('Blockly.blockRendering.Debug');
|
||||
* @param {string} name The renderer name.
|
||||
* @package
|
||||
* @constructor
|
||||
* @implements {Blockly.IRegistrable}
|
||||
*/
|
||||
Blockly.blockRendering.Renderer = function(name) {
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Blockly.geras.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
selector + ' .blocklyInsertionMarker>.blocklyPathLight,',
|
||||
selector + ' .blocklyInsertionMarker>.blocklyPathDark {',
|
||||
'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';',
|
||||
'stroke: none',
|
||||
'stroke: none;',
|
||||
'}',
|
||||
/* eslint-enable indent */
|
||||
]);
|
||||
|
||||
@@ -54,7 +54,7 @@ Blockly.geras.PathObject = function(root, style, constants) {
|
||||
|
||||
/**
|
||||
* The primary path of the block.
|
||||
* @type {SVGElement}
|
||||
* @type {!SVGElement}
|
||||
* @package
|
||||
*/
|
||||
this.svgPath = Blockly.utils.dom.createSvgElement('path',
|
||||
@@ -122,7 +122,7 @@ Blockly.geras.PathObject.prototype.applyColour = function(block) {
|
||||
this.svgPathDark.setAttribute('fill', this.colourDark);
|
||||
|
||||
Blockly.geras.PathObject.superClass_.applyColour.call(this, block);
|
||||
|
||||
|
||||
this.svgPath.setAttribute('stroke', 'none');
|
||||
};
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ Blockly.zelos.ConstantProvider = function() {
|
||||
* @override
|
||||
*/
|
||||
this.NOTCH_OFFSET_LEFT = 3 * this.GRID_UNIT;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -259,7 +259,7 @@ Blockly.zelos.ConstantProvider = function() {
|
||||
* @override
|
||||
*/
|
||||
this.FIELD_BORDER_RECT_X_PADDING = 2 * this.GRID_UNIT;
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -905,13 +905,12 @@ Blockly.zelos.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
return [
|
||||
/* eslint-disable indent */
|
||||
// Text.
|
||||
selector + ' .blocklyText, ',
|
||||
selector + ' .blocklyText,',
|
||||
selector + ' .blocklyFlyoutLabelText {',
|
||||
'font-family: ' + this.FIELD_TEXT_FONTFAMILY + ';',
|
||||
'font-size: ' + this.FIELD_TEXT_FONTSIZE + 'pt;',
|
||||
'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';',
|
||||
'font: ' + this.FIELD_TEXT_FONTWEIGHT + ' ' +
|
||||
this.FIELD_TEXT_FONTSIZE + 'pt ' + this.FIELD_TEXT_FONTFAMILY + ';',
|
||||
'}',
|
||||
|
||||
|
||||
// Fields.
|
||||
selector + ' .blocklyText {',
|
||||
'fill: #fff;',
|
||||
@@ -926,7 +925,7 @@ Blockly.zelos.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
selector + ' .blocklyEditableText>g>text {',
|
||||
'fill: #575E75;',
|
||||
'}',
|
||||
|
||||
|
||||
// Flyout labels.
|
||||
selector + ' .blocklyFlyoutLabelText {',
|
||||
'fill: #575E75;',
|
||||
@@ -939,7 +938,7 @@ Blockly.zelos.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
|
||||
// Editable field hover.
|
||||
selector + ' .blocklyDraggable:not(.blocklyDisabled)',
|
||||
' .blocklyEditableText:not(.editing):hover>rect ,',
|
||||
' .blocklyEditableText:not(.editing):hover>rect,',
|
||||
selector + ' .blocklyDraggable:not(.blocklyDisabled)',
|
||||
' .blocklyEditableText:not(.editing):hover>.blocklyPath {',
|
||||
'stroke: #fff;',
|
||||
@@ -952,7 +951,7 @@ Blockly.zelos.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
'font-weight: ' + this.FIELD_TEXT_FONTWEIGHT + ';',
|
||||
'color: #575E75;',
|
||||
'}',
|
||||
|
||||
|
||||
// Dropdown field.
|
||||
selector + ' .blocklyDropdownText {',
|
||||
'fill: #fff !important;',
|
||||
@@ -979,7 +978,7 @@ Blockly.zelos.ConstantProvider.prototype.getCSS_ = function(selector) {
|
||||
// Insertion marker.
|
||||
selector + ' .blocklyInsertionMarker>.blocklyPath {',
|
||||
'fill-opacity: ' + this.INSERTION_MARKER_OPACITY + ';',
|
||||
'stroke: none',
|
||||
'stroke: none;',
|
||||
'}',
|
||||
/* eslint-enable indent */
|
||||
];
|
||||
|
||||
@@ -32,11 +32,13 @@ Blockly.utils.object.inherits(Blockly.zelos.MarkerSvg,
|
||||
Blockly.blockRendering.MarkerSvg);
|
||||
|
||||
/**
|
||||
* @override
|
||||
* Position and display the marker for an input or an output connection.
|
||||
* @param {!Blockly.ASTNode} curNode The node to draw the marker for.
|
||||
* @private
|
||||
*/
|
||||
Blockly.zelos.MarkerSvg.prototype.showWithInput_ = function(curNode) {
|
||||
Blockly.zelos.MarkerSvg.prototype.showWithInputOutput_ = function(curNode) {
|
||||
var block = /** @type {!Blockly.BlockSvg} */ (curNode.getSourceBlock());
|
||||
var connection = curNode.getLocation();
|
||||
var connection = /** @type {!Blockly.Connection} */ (curNode.getLocation());
|
||||
var offsetInBlock = connection.getOffsetInBlock();
|
||||
|
||||
this.positionCircle_(offsetInBlock.x, offsetInBlock.y);
|
||||
@@ -44,6 +46,20 @@ Blockly.zelos.MarkerSvg.prototype.showWithInput_ = function(curNode) {
|
||||
this.showCurrent_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.MarkerSvg.prototype.showWithOutput_ = function(curNode) {
|
||||
this.showWithInputOutput_(curNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.MarkerSvg.prototype.showWithInput_ = function(curNode) {
|
||||
this.showWithInputOutput_(curNode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a rectangle around the block.
|
||||
* @param {!Blockly.ASTNode} curNode The current node of the marker.
|
||||
@@ -72,25 +88,6 @@ Blockly.zelos.MarkerSvg.prototype.positionCircle_ = function(x, y) {
|
||||
this.currentMarkerSvg = this.markerCircle_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.MarkerSvg.prototype.showAtLocation_ = function(curNode) {
|
||||
var handled = false;
|
||||
if (curNode.getType() == Blockly.ASTNode.types.OUTPUT) {
|
||||
// Inputs and outputs are drawn the same.
|
||||
this.showWithInput_(curNode);
|
||||
handled = true;
|
||||
} else if (curNode.getType() == Blockly.ASTNode.types.BLOCK) {
|
||||
this.showWithBlock_(curNode);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
Blockly.zelos.MarkerSvg.superClass_.showAtLocation_.call(this, curNode);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -134,8 +131,8 @@ Blockly.zelos.MarkerSvg.prototype.createDomInternal_ = function() {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
Blockly.zelos.MarkerSvg.prototype.applyColour_ = function() {
|
||||
Blockly.zelos.MarkerSvg.superClass_.applyColour_.call(this);
|
||||
Blockly.zelos.MarkerSvg.prototype.applyColour_ = function(curNode) {
|
||||
Blockly.zelos.MarkerSvg.superClass_.applyColour_.call(this, curNode);
|
||||
|
||||
this.markerCircle_.setAttribute('fill', this.colour_);
|
||||
this.markerCircle_.setAttribute('stroke', this.colour_);
|
||||
|
||||
@@ -63,13 +63,6 @@ goog.require('Blockly.FieldMultilineInput');
|
||||
goog.require('Blockly.FieldNumber');
|
||||
goog.require('Blockly.FieldVariable');
|
||||
|
||||
// If you'd like to include the date field in your build, you will also need to
|
||||
// include the Closure library as a build dependency. You can do so by running:
|
||||
// gulp build-compressed --closure-library
|
||||
// Be sure to also include "google-closure-library" to your list of
|
||||
// devDependencies.
|
||||
// goog.require('Blockly.FieldDate');
|
||||
|
||||
|
||||
// Blockly Renderers.
|
||||
// At least one renderer is mandatory. Geras is the default one.
|
||||
|
||||
+62
-55
@@ -17,6 +17,7 @@ goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
@@ -26,7 +27,7 @@ goog.require('Blockly.utils.dom');
|
||||
|
||||
/**
|
||||
* Class for a pair of scrollbars. Horizontal and vertical.
|
||||
* @param {!Blockly.Workspace} workspace Workspace to bind the scrollbars to.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace Workspace to bind the scrollbars to.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ScrollbarPair = function(workspace) {
|
||||
@@ -44,14 +45,14 @@ Blockly.ScrollbarPair = function(workspace) {
|
||||
},
|
||||
null);
|
||||
Blockly.utils.dom.insertAfter(this.corner_, workspace.getBubbleCanvas());
|
||||
};
|
||||
|
||||
/**
|
||||
* Previously recorded metrics from the workspace.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
Blockly.ScrollbarPair.prototype.oldHostMetrics_ = null;
|
||||
/**
|
||||
* Previously recorded metrics from the workspace.
|
||||
* @type {?Blockly.utils.Metrics}
|
||||
* @private
|
||||
*/
|
||||
this.oldHostMetrics_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of this pair of scrollbars.
|
||||
@@ -117,12 +118,12 @@ Blockly.ScrollbarPair.prototype.resize = function() {
|
||||
if (!this.oldHostMetrics_ ||
|
||||
this.oldHostMetrics_.viewWidth != hostMetrics.viewWidth ||
|
||||
this.oldHostMetrics_.absoluteLeft != hostMetrics.absoluteLeft) {
|
||||
this.corner_.setAttribute('x', this.vScroll.position_.x);
|
||||
this.corner_.setAttribute('x', this.vScroll.position.x);
|
||||
}
|
||||
if (!this.oldHostMetrics_ ||
|
||||
this.oldHostMetrics_.viewHeight != hostMetrics.viewHeight ||
|
||||
this.oldHostMetrics_.absoluteTop != hostMetrics.absoluteTop) {
|
||||
this.corner_.setAttribute('y', this.hScroll.position_.y);
|
||||
this.corner_.setAttribute('y', this.hScroll.position.y);
|
||||
}
|
||||
|
||||
// Cache the current metrics to potentially short-cut the next resize event.
|
||||
@@ -144,8 +145,8 @@ Blockly.ScrollbarPair.prototype.set = function(x, y) {
|
||||
// Combining them speeds up rendering.
|
||||
var xyRatio = {};
|
||||
|
||||
var hHandlePosition = x * this.hScroll.ratio_;
|
||||
var vHandlePosition = y * this.vScroll.ratio_;
|
||||
var hHandlePosition = x * this.hScroll.ratio;
|
||||
var vHandlePosition = y * this.vScroll.ratio;
|
||||
|
||||
var hBarLength = this.hScroll.scrollViewSize_;
|
||||
var vBarLength = this.vScroll.scrollViewSize_;
|
||||
@@ -179,7 +180,7 @@ Blockly.ScrollbarPair.prototype.getRatio_ = function(handlePosition, viewSize) {
|
||||
* Class for a pure SVG scrollbar.
|
||||
* This technique offers a scrollbar that is guaranteed to work, but may not
|
||||
* look or behave like the system's scrollbars.
|
||||
* @param {!Blockly.Workspace} workspace Workspace to bind the scrollbar to.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace Workspace to bind the scrollbar to.
|
||||
* @param {boolean} horizontal True if horizontal, false if vertical.
|
||||
* @param {boolean=} opt_pair True if scrollbar is part of a horiz/vert pair.
|
||||
* @param {string=} opt_class A class to be applied to this scrollbar.
|
||||
@@ -191,6 +192,12 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) {
|
||||
this.horizontal_ = horizontal;
|
||||
this.oldHostMetrics_ = null;
|
||||
|
||||
/**
|
||||
* @type {?number}
|
||||
* @package
|
||||
*/
|
||||
this.ratio = null;
|
||||
|
||||
this.createDom_(opt_class);
|
||||
|
||||
/**
|
||||
@@ -198,9 +205,9 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) {
|
||||
* to the scrollbar's origin. This is usually relative to the injection div
|
||||
* origin.
|
||||
* @type {Blockly.utils.Coordinate}
|
||||
* @private
|
||||
* @package
|
||||
*/
|
||||
this.position_ = new Blockly.utils.Coordinate(0, 0);
|
||||
this.position = new Blockly.utils.Coordinate(0, 0);
|
||||
|
||||
// Store the thickness in a temp variable for readability.
|
||||
var scrollbarThickness = Blockly.Scrollbar.scrollbarThickness;
|
||||
@@ -295,10 +302,10 @@ if (Blockly.Touch.TOUCH_ENABLED) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} first An object containing computed measurements of a
|
||||
* workspace.
|
||||
* @param {Object} second Another object containing computed measurements of a
|
||||
* workspace.
|
||||
* @param {Blockly.utils.Metrics} first An object containing computed
|
||||
* measurements of a workspace.
|
||||
* @param {Blockly.utils.Metrics} second Another object containing computed
|
||||
* measurements of a workspace.
|
||||
* @return {boolean} Whether the two sets of metrics are equivalent.
|
||||
* @private
|
||||
*/
|
||||
@@ -392,23 +399,23 @@ Blockly.ScrollbarPair.prototype.setContainerVisible = function(visible) {
|
||||
* scrollbar's origin. This sets the scrollbar's location within the workspace.
|
||||
* @param {number} x The new x coordinate.
|
||||
* @param {number} y The new y coordinate.
|
||||
* @private
|
||||
* @package
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.setPosition_ = function(x, y) {
|
||||
this.position_.x = x;
|
||||
this.position_.y = y;
|
||||
Blockly.Scrollbar.prototype.setPosition = function(x, y) {
|
||||
this.position.x = x;
|
||||
this.position.y = y;
|
||||
|
||||
var tempX = this.position_.x + this.origin_.x;
|
||||
var tempY = this.position_.y + this.origin_.y;
|
||||
var tempX = this.position.x + this.origin_.x;
|
||||
var tempY = this.position.y + this.origin_.y;
|
||||
var transform = 'translate(' + tempX + 'px,' + tempY + 'px)';
|
||||
Blockly.utils.dom.setCssTransform(this.outerSvg_, transform);
|
||||
};
|
||||
|
||||
/**
|
||||
* Recalculate the scrollbar's location and its length.
|
||||
* @param {Object=} opt_metrics A data structure of from the describing all the
|
||||
* required dimensions. If not provided, it will be fetched from the host
|
||||
* object.
|
||||
* @param {Blockly.utils.Metrics=} opt_metrics A data structure of from the
|
||||
* describing all the required dimensions. If not provided, it will be
|
||||
* fetched from the host object.
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resize = function(opt_metrics) {
|
||||
// Determine the location, height and width of the host element.
|
||||
@@ -450,8 +457,8 @@ Blockly.Scrollbar.prototype.resize = function(opt_metrics) {
|
||||
|
||||
/**
|
||||
* Recalculate a horizontal scrollbar's location and length.
|
||||
* @param {!Object} hostMetrics A data structure describing all the
|
||||
* required dimensions, possibly fetched from the host object.
|
||||
* @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all
|
||||
* the required dimensions, possibly fetched from the host object.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) {
|
||||
@@ -463,8 +470,8 @@ Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) {
|
||||
/**
|
||||
* Recalculate a horizontal scrollbar's location on the screen and path length.
|
||||
* This should be called when the layout or size of the window has changed.
|
||||
* @param {!Object} hostMetrics A data structure describing all the
|
||||
* required dimensions, possibly fetched from the host object.
|
||||
* @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all
|
||||
* the required dimensions, possibly fetched from the host object.
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) {
|
||||
var viewSize = hostMetrics.viewWidth - 1;
|
||||
@@ -482,7 +489,7 @@ Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) {
|
||||
// Horizontal toolbar should always be just above the bottom of the workspace.
|
||||
var yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight -
|
||||
Blockly.Scrollbar.scrollbarThickness - 0.5;
|
||||
this.setPosition_(xCoordinate, yCoordinate);
|
||||
this.setPosition(xCoordinate, yCoordinate);
|
||||
|
||||
// If the view has been resized, a content resize will also be necessary. The
|
||||
// reverse is not true.
|
||||
@@ -492,8 +499,8 @@ Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) {
|
||||
/**
|
||||
* Recalculate a horizontal scrollbar's location within its path and length.
|
||||
* This should be called when the contents of the workspace have changed.
|
||||
* @param {!Object} hostMetrics A data structure describing all the
|
||||
* required dimensions, possibly fetched from the host object.
|
||||
* @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all
|
||||
* the required dimensions, possibly fetched from the host object.
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resizeContentHorizontal = function(hostMetrics) {
|
||||
if (!this.pair_) {
|
||||
@@ -503,24 +510,24 @@ Blockly.Scrollbar.prototype.resizeContentHorizontal = function(hostMetrics) {
|
||||
this.setVisible(this.scrollViewSize_ < hostMetrics.contentWidth);
|
||||
}
|
||||
|
||||
this.ratio_ = this.scrollViewSize_ / hostMetrics.contentWidth;
|
||||
if (this.ratio_ == -Infinity || this.ratio_ == Infinity ||
|
||||
isNaN(this.ratio_)) {
|
||||
this.ratio_ = 0;
|
||||
this.ratio = this.scrollViewSize_ / hostMetrics.contentWidth;
|
||||
if (this.ratio == -Infinity || this.ratio == Infinity ||
|
||||
isNaN(this.ratio)) {
|
||||
this.ratio = 0;
|
||||
}
|
||||
|
||||
var handleLength = hostMetrics.viewWidth * this.ratio_;
|
||||
var handleLength = hostMetrics.viewWidth * this.ratio;
|
||||
this.setHandleLength_(Math.max(0, handleLength));
|
||||
|
||||
var handlePosition = (hostMetrics.viewLeft - hostMetrics.contentLeft) *
|
||||
this.ratio_;
|
||||
this.ratio;
|
||||
this.setHandlePosition(this.constrainHandle_(handlePosition));
|
||||
};
|
||||
|
||||
/**
|
||||
* Recalculate a vertical scrollbar's location and length.
|
||||
* @param {!Object} hostMetrics A data structure describing all the
|
||||
* required dimensions, possibly fetched from the host object.
|
||||
* @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all
|
||||
* the required dimensions, possibly fetched from the host object.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) {
|
||||
@@ -532,8 +539,8 @@ Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) {
|
||||
/**
|
||||
* Recalculate a vertical scrollbar's location on the screen and path length.
|
||||
* This should be called when the layout or size of the window has changed.
|
||||
* @param {!Object} hostMetrics A data structure describing all the
|
||||
* required dimensions, possibly fetched from the host object.
|
||||
* @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all
|
||||
* the required dimensions, possibly fetched from the host object.
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) {
|
||||
var viewSize = hostMetrics.viewHeight - 1;
|
||||
@@ -549,7 +556,7 @@ Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) {
|
||||
Blockly.Scrollbar.scrollbarThickness - 1;
|
||||
}
|
||||
var yCoordinate = hostMetrics.absoluteTop + 0.5;
|
||||
this.setPosition_(xCoordinate, yCoordinate);
|
||||
this.setPosition(xCoordinate, yCoordinate);
|
||||
|
||||
// If the view has been resized, a content resize will also be necessary. The
|
||||
// reverse is not true.
|
||||
@@ -559,8 +566,8 @@ Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) {
|
||||
/**
|
||||
* Recalculate a vertical scrollbar's location within its path and length.
|
||||
* This should be called when the contents of the workspace have changed.
|
||||
* @param {!Object} hostMetrics A data structure describing all the
|
||||
* required dimensions, possibly fetched from the host object.
|
||||
* @param {!Blockly.utils.Metrics} hostMetrics A data structure describing all
|
||||
* the required dimensions, possibly fetched from the host object.
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.resizeContentVertical = function(hostMetrics) {
|
||||
if (!this.pair_) {
|
||||
@@ -568,17 +575,17 @@ Blockly.Scrollbar.prototype.resizeContentVertical = function(hostMetrics) {
|
||||
this.setVisible(this.scrollViewSize_ < hostMetrics.contentHeight);
|
||||
}
|
||||
|
||||
this.ratio_ = this.scrollViewSize_ / hostMetrics.contentHeight;
|
||||
if (this.ratio_ == -Infinity || this.ratio_ == Infinity ||
|
||||
isNaN(this.ratio_)) {
|
||||
this.ratio_ = 0;
|
||||
this.ratio = this.scrollViewSize_ / hostMetrics.contentHeight;
|
||||
if (this.ratio == -Infinity || this.ratio == Infinity ||
|
||||
isNaN(this.ratio)) {
|
||||
this.ratio = 0;
|
||||
}
|
||||
|
||||
var handleLength = hostMetrics.viewHeight * this.ratio_;
|
||||
var handleLength = hostMetrics.viewHeight * this.ratio;
|
||||
this.setHandleLength_(Math.max(0, handleLength));
|
||||
|
||||
var handlePosition = (hostMetrics.viewTop - hostMetrics.contentTop) *
|
||||
this.ratio_;
|
||||
this.ratio;
|
||||
this.setHandlePosition(this.constrainHandle_(handlePosition));
|
||||
};
|
||||
|
||||
@@ -844,7 +851,7 @@ Blockly.Scrollbar.prototype.onScroll_ = function() {
|
||||
* scrollbar handle.
|
||||
*/
|
||||
Blockly.Scrollbar.prototype.set = function(value) {
|
||||
this.setHandlePosition(this.constrainHandle_(value * this.ratio_));
|
||||
this.setHandlePosition(this.constrainHandle_(value * this.ratio));
|
||||
this.onScroll_();
|
||||
};
|
||||
|
||||
|
||||
+33
-23
@@ -11,6 +11,7 @@
|
||||
|
||||
goog.provide('Blockly.Theme');
|
||||
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.colour');
|
||||
goog.require('Blockly.utils.object');
|
||||
@@ -73,6 +74,9 @@ Blockly.Theme = function(name, opt_blockStyles, opt_categoryStyles,
|
||||
* @package
|
||||
*/
|
||||
this.startHats = null;
|
||||
|
||||
// Register the theme by name.
|
||||
Blockly.registry.register(Blockly.registry.Type.THEME, name, this);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -97,22 +101,22 @@ Blockly.Theme.CategoryStyle;
|
||||
/**
|
||||
* A component style.
|
||||
* @typedef {{
|
||||
* workspaceBackgroundColour:string?,
|
||||
* toolboxBackgroundColour:string?,
|
||||
* toolboxForegroundColour:string?,
|
||||
* flyoutBackgroundColour:string?,
|
||||
* flyoutForegroundColour:string?,
|
||||
* flyoutOpacity:number?,
|
||||
* scrollbarColour:string?,
|
||||
* scrollbarOpacity:number?,
|
||||
* insertionMarkerColour:string?,
|
||||
* insertionMarkerOpacity:number?,
|
||||
* markerColour:string?,
|
||||
* cursorColour:string?,
|
||||
* selectedGlowColour:string?,
|
||||
* selectedGlowOpacity:number?,
|
||||
* replacementGlowColour:string?,
|
||||
* replacementGlowOpacity:number?
|
||||
* workspaceBackgroundColour:?string,
|
||||
* toolboxBackgroundColour:?string,
|
||||
* toolboxForegroundColour:?string,
|
||||
* flyoutBackgroundColour:?string,
|
||||
* flyoutForegroundColour:?string,
|
||||
* flyoutOpacity:?number,
|
||||
* scrollbarColour:?string,
|
||||
* scrollbarOpacity:?number,
|
||||
* insertionMarkerColour:?string,
|
||||
* insertionMarkerOpacity:?number,
|
||||
* markerColour:?string,
|
||||
* cursorColour:?string,
|
||||
* selectedGlowColour:?string,
|
||||
* selectedGlowOpacity:?number,
|
||||
* replacementGlowColour:?string,
|
||||
* replacementGlowOpacity:?number
|
||||
* }}
|
||||
*/
|
||||
Blockly.Theme.ComponentStyle;
|
||||
@@ -120,9 +124,9 @@ Blockly.Theme.ComponentStyle;
|
||||
/**
|
||||
* A font style.
|
||||
* @typedef {{
|
||||
* family:string?,
|
||||
* weight:string?,
|
||||
* size:number?
|
||||
* family:?string,
|
||||
* weight:?string,
|
||||
* size:?number
|
||||
* }}
|
||||
*/
|
||||
Blockly.Theme.FontStyle;
|
||||
@@ -206,11 +210,16 @@ Blockly.Theme.prototype.setStartHats = function(startHats) {
|
||||
Blockly.Theme.defineTheme = function(name, themeObj) {
|
||||
var theme = new Blockly.Theme(name);
|
||||
var base = themeObj['base'];
|
||||
if (base && base instanceof Blockly.Theme) {
|
||||
Blockly.utils.object.deepMerge(theme, base);
|
||||
theme.name = name;
|
||||
if (base) {
|
||||
if (typeof base == "string") {
|
||||
base = Blockly.registry.getObject(Blockly.registry.Type.THEME, base);
|
||||
}
|
||||
if (base instanceof Blockly.Theme) {
|
||||
Blockly.utils.object.deepMerge(theme, base);
|
||||
theme.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Blockly.utils.object.deepMerge(theme.blockStyles,
|
||||
themeObj['blockStyles']);
|
||||
Blockly.utils.object.deepMerge(theme.categoryStyles,
|
||||
@@ -222,5 +231,6 @@ Blockly.Theme.defineTheme = function(name, themeObj) {
|
||||
if (themeObj['startHats'] != null) {
|
||||
theme.startHats = themeObj['startHats'];
|
||||
}
|
||||
|
||||
return theme;
|
||||
};
|
||||
|
||||
+207
-129
@@ -16,6 +16,7 @@ goog.require('Blockly.Css');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.Ui');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.registry');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.tree.TreeControl');
|
||||
goog.require('Blockly.tree.TreeNode');
|
||||
@@ -25,6 +26,12 @@ 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');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
goog.requireType('Blockly.IStyleable');
|
||||
goog.requireType('Blockly.IToolbox');
|
||||
|
||||
|
||||
/**
|
||||
@@ -33,6 +40,10 @@ goog.require('Blockly.utils.Rect');
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to create new
|
||||
* blocks.
|
||||
* @constructor
|
||||
* @implements {Blockly.IBlocklyActionable}
|
||||
* @implements {Blockly.IDeleteArea}
|
||||
* @implements {Blockly.IStyleable}
|
||||
* @implements {Blockly.IToolbox}
|
||||
*/
|
||||
Blockly.Toolbox = function(workspace) {
|
||||
/**
|
||||
@@ -106,34 +117,27 @@ Blockly.Toolbox = function(workspace) {
|
||||
* @private
|
||||
*/
|
||||
this.flyout_ = null;
|
||||
|
||||
/**
|
||||
* Width of the toolbox, which changes only in vertical layout.
|
||||
* @type {number}
|
||||
*/
|
||||
this.width = 0;
|
||||
|
||||
/**
|
||||
* Height of the toolbox, which changes only in horizontal layout.
|
||||
* @type {number}
|
||||
*/
|
||||
this.height = 0;
|
||||
|
||||
/**
|
||||
* The TreeNode most recently selected.
|
||||
* @type {Blockly.tree.BaseNode}
|
||||
* @private
|
||||
*/
|
||||
this.lastCategory_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Width of the toolbox, which changes only in vertical layout.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.Toolbox.prototype.width = 0;
|
||||
|
||||
/**
|
||||
* Height of the toolbox, which changes only in horizontal layout.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.Toolbox.prototype.height = 0;
|
||||
|
||||
/**
|
||||
* The SVG group currently selected.
|
||||
* @type {SVGGElement}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Toolbox.prototype.selectedOption_ = null;
|
||||
|
||||
/**
|
||||
* The tree node most recently selected.
|
||||
* @type {Blockly.tree.BaseNode}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Toolbox.prototype.lastCategory_ = null;
|
||||
|
||||
/**
|
||||
* Initializes the toolbox.
|
||||
* @throws {Error} If missing a require for both `Blockly.HorizontalFlyout` and
|
||||
@@ -179,7 +183,7 @@ Blockly.Toolbox.prototype.init = function() {
|
||||
'rendererOverrides': workspace.options.rendererOverrides
|
||||
}));
|
||||
workspaceOptions.toolboxPosition = workspace.options.toolboxPosition;
|
||||
|
||||
|
||||
if (workspace.horizontalLayout) {
|
||||
if (!Blockly.HorizontalFlyout) {
|
||||
throw Error('Missing require for Blockly.HorizontalFlyout');
|
||||
@@ -195,23 +199,23 @@ Blockly.Toolbox.prototype.init = function() {
|
||||
throw Error('One of Blockly.VerticalFlyout or Blockly.Horizontal must be' +
|
||||
'required.');
|
||||
}
|
||||
|
||||
|
||||
// Insert the flyout after the workspace.
|
||||
Blockly.utils.dom.insertAfter(this.flyout_.createDom('svg'), svg);
|
||||
this.flyout_.init(workspace);
|
||||
|
||||
this.config_['cleardotPath'] = workspace.options.pathToMedia + '1x1.gif';
|
||||
this.config_['cssCollapsedFolderIcon'] =
|
||||
'blocklyTreeIconClosed' + (workspace.RTL ? 'Rtl' : 'Ltr');
|
||||
this.renderTree(workspace.options.languageTree);
|
||||
this.render(workspace.options.languageTree);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill the toolbox with categories and blocks.
|
||||
* @param {Node} languageTree DOM tree of blocks.
|
||||
* @param {Array.<Blockly.utils.toolbox.Toolbox>} toolboxDef Array holding objects
|
||||
* containing information on the contents of the toolbox.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Toolbox.prototype.renderTree = function(languageTree) {
|
||||
Blockly.Toolbox.prototype.render = function(toolboxDef) {
|
||||
if (this.tree_) {
|
||||
this.tree_.dispose(); // Delete any existing content.
|
||||
this.lastCategory_ = null;
|
||||
@@ -223,13 +227,12 @@ Blockly.Toolbox.prototype.renderTree = function(languageTree) {
|
||||
tree.onBeforeSelected(this.handleBeforeTreeSelected_);
|
||||
tree.onAfterSelected(this.handleAfterTreeSelected_);
|
||||
var openNode = null;
|
||||
if (languageTree) {
|
||||
this.tree_.blocks = [];
|
||||
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_.blocks.length) {
|
||||
if (this.tree_.contents.length) {
|
||||
throw Error('Toolbox cannot have both blocks and categories ' +
|
||||
'in the root level.');
|
||||
}
|
||||
@@ -252,6 +255,156 @@ Blockly.Toolbox.prototype.renderTree = function(languageTree) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the toolbox tree.
|
||||
* @param {Array.<Blockly.utils.toolbox.Toolbox>} 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.
|
||||
// <sep></sep>
|
||||
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.
|
||||
@@ -283,8 +436,8 @@ Blockly.Toolbox.prototype.handleBeforeTreeSelected_ = function(node) {
|
||||
*/
|
||||
Blockly.Toolbox.prototype.handleAfterTreeSelected_ = function(
|
||||
oldNode, newNode) {
|
||||
if (newNode && newNode.blocks && newNode.blocks.length) {
|
||||
this.flyout_.show(newNode.blocks);
|
||||
if (newNode && newNode.contents && newNode.contents.length) {
|
||||
this.flyout_.show(newNode.contents);
|
||||
// Scroll the flyout to the top if the category has changed.
|
||||
if (this.lastCategory_ != newNode) {
|
||||
this.flyout_.scrollToStart();
|
||||
@@ -358,6 +511,14 @@ Blockly.Toolbox.prototype.dispose = function() {
|
||||
this.lastCategory_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the visibility of the toolbox.
|
||||
* @param {boolean} isVisible True if toolbox should be visible.
|
||||
*/
|
||||
Blockly.Toolbox.prototype.setVisible = function(isVisible) {
|
||||
this.HtmlDiv.style.display = isVisible ? 'block' : 'none';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width of the toolbox.
|
||||
* @return {number} The width of the toolbox.
|
||||
@@ -414,91 +575,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.blocks = [];
|
||||
treeOut.add(childOut);
|
||||
var custom = childIn.getAttribute('custom');
|
||||
if (custom) {
|
||||
// Variables and procedures are special dynamic categories.
|
||||
childOut.blocks = 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.blocks.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.
|
||||
// <sep></sep>
|
||||
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.blocks.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,
|
||||
@@ -581,7 +657,7 @@ Blockly.Toolbox.prototype.updateColourFromTheme_ = function(opt_tree) {
|
||||
* Updates the category colours and background colour of selected categories.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Toolbox.prototype.updateColourFromTheme = function() {
|
||||
Blockly.Toolbox.prototype.refreshTheme = function() {
|
||||
var tree = this.tree_;
|
||||
if (tree) {
|
||||
this.updateColourFromTheme_(tree);
|
||||
@@ -697,8 +773,8 @@ Blockly.Toolbox.prototype.getClientRect = function() {
|
||||
*/
|
||||
Blockly.Toolbox.prototype.refreshSelection = function() {
|
||||
var selectedItem = this.tree_.getSelectedItem();
|
||||
if (selectedItem && selectedItem.blocks) {
|
||||
this.flyout_.show(selectedItem.blocks);
|
||||
if (selectedItem && selectedItem.contents) {
|
||||
this.flyout_.show(selectedItem.contents);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -835,8 +911,7 @@ Blockly.Css.register([
|
||||
|
||||
'.blocklyTreeLabel {',
|
||||
'cursor: default;',
|
||||
'font-family: sans-serif;',
|
||||
'font-size: 16px;',
|
||||
'font: 16px sans-serif;',
|
||||
'padding: 0 3px;',
|
||||
'vertical-align: middle;',
|
||||
'}',
|
||||
@@ -850,3 +925,6 @@ Blockly.Css.register([
|
||||
'}'
|
||||
/* eslint-enable indent */
|
||||
]);
|
||||
|
||||
Blockly.registry.register(Blockly.registry.Type.TOOLBOX,
|
||||
Blockly.registry.DEFAULT, Blockly.Toolbox);
|
||||
|
||||
@@ -17,11 +17,14 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a trash can.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace to sit in.
|
||||
* @constructor
|
||||
* @implements {Blockly.IDeleteArea}
|
||||
*/
|
||||
Blockly.Trashcan = function(workspace) {
|
||||
/**
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Utility methods for working with the Closure menu
|
||||
* (goog.ui.menu).
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @name Blockly.utils.uiMenu
|
||||
* @namespace
|
||||
*/
|
||||
goog.provide('Blockly.utils.uiMenu');
|
||||
|
||||
goog.require('Blockly.utils.style');
|
||||
|
||||
|
||||
/**
|
||||
* Get the size of a rendered goog.ui.Menu.
|
||||
* @param {!Blockly.Menu} menu The menu to measure.
|
||||
* @return {!Blockly.utils.Size} Object with width and height properties.
|
||||
* @package
|
||||
*/
|
||||
Blockly.utils.uiMenu.getSize = function(menu) {
|
||||
var menuDom = menu.getElement();
|
||||
var menuSize = Blockly.utils.style.getSize(/** @type {!Element} */ (menuDom));
|
||||
// Recalculate height for the total content, not only box height.
|
||||
menuSize.height = menuDom.scrollHeight;
|
||||
return menuSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adjust the bounding boxes used to position the widget div to deal with RTL
|
||||
* goog.ui.Menu positioning. In RTL mode the menu renders down and to the left
|
||||
* of its start point, instead of down and to the right. Adjusting all of the
|
||||
* bounding boxes accordingly allows us to use the same code for all widgets.
|
||||
* This function in-place modifies the provided bounding boxes.
|
||||
* @param {!Object} viewportBBox The bounding rectangle of the current viewport,
|
||||
* in window coordinates.
|
||||
* @param {!Object} anchorBBox The bounding rectangle of the anchor, in window
|
||||
* coordinates.
|
||||
* @param {!Blockly.utils.Size} menuSize The size of the menu that is inside the
|
||||
* widget div, in window coordinates.
|
||||
* @package
|
||||
*/
|
||||
Blockly.utils.uiMenu.adjustBBoxesForRTL = function(viewportBBox, anchorBBox,
|
||||
menuSize) {
|
||||
anchorBBox.left += menuSize.width;
|
||||
anchorBBox.right += menuSize.width;
|
||||
viewportBBox.left += menuSize.width;
|
||||
viewportBBox.right += menuSize.width;
|
||||
};
|
||||
+9
-8
@@ -23,6 +23,7 @@ goog.require('Blockly.constants');
|
||||
goog.require('Blockly.utils.colour');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.utils.global');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.string');
|
||||
goog.require('Blockly.utils.style');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
@@ -496,19 +497,19 @@ Blockly.utils.runAfterPageLoad = function(fn) {
|
||||
/**
|
||||
* Get the position of the current viewport in window coordinates. This takes
|
||||
* scroll into account.
|
||||
* @return {!Object} An object containing window width, height, and scroll
|
||||
* position in window coordinates.
|
||||
* @return {!Blockly.utils.Rect} An object containing window width, height, and
|
||||
* scroll position in window coordinates.
|
||||
* @package
|
||||
*/
|
||||
Blockly.utils.getViewportBBox = function() {
|
||||
// Pixels, in window coordinates.
|
||||
var scrollOffset = Blockly.utils.style.getViewportPageOffset();
|
||||
return {
|
||||
right: document.documentElement.clientWidth + scrollOffset.x,
|
||||
bottom: document.documentElement.clientHeight + scrollOffset.y,
|
||||
top: scrollOffset.y,
|
||||
left: scrollOffset.x
|
||||
};
|
||||
return new Blockly.utils.Rect(
|
||||
scrollOffset.y,
|
||||
document.documentElement.clientHeight + scrollOffset.y,
|
||||
scrollOffset.x,
|
||||
document.documentElement.clientWidth + scrollOffset.x
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,6 +86,9 @@ Blockly.utils.aria.State = {
|
||||
// Value: integer.
|
||||
COLCOUNT: 'colcount',
|
||||
|
||||
// ARIA state for a disabled item. Value: one of {true, false}.
|
||||
DISABLED: 'disabled',
|
||||
|
||||
// ARIA state for setting whether the element like a tree node is expanded.
|
||||
// Value: one of {true, false, undefined}.
|
||||
EXPANDED: 'expanded',
|
||||
|
||||
+6
-6
@@ -44,7 +44,7 @@ Blockly.utils.dom.XLINK_NS = 'http://www.w3.org/1999/xlink';
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
|
||||
* @enum {number}
|
||||
*/
|
||||
Blockly.utils.dom.Node = {
|
||||
Blockly.utils.dom.NodeType = {
|
||||
ELEMENT_NODE: 1,
|
||||
TEXT_NODE: 3,
|
||||
COMMENT_NODE: 8,
|
||||
@@ -76,10 +76,10 @@ Blockly.utils.dom.canvasContext_ = null;
|
||||
* Helper method for creating SVG elements.
|
||||
* @param {string} name Element's tag name.
|
||||
* @param {!Object} attrs Dictionary of attribute names and values.
|
||||
* @param {Element} parent Optional parent on which to append the element.
|
||||
* @param {Element=} opt_parent Optional parent on which to append the element.
|
||||
* @return {!SVGElement} Newly created SVG element.
|
||||
*/
|
||||
Blockly.utils.dom.createSvgElement = function(name, attrs, parent) {
|
||||
Blockly.utils.dom.createSvgElement = function(name, attrs, opt_parent) {
|
||||
var e = /** @type {!SVGElement} */
|
||||
(document.createElementNS(Blockly.utils.dom.SVG_NS, name));
|
||||
for (var key in attrs) {
|
||||
@@ -91,8 +91,8 @@ Blockly.utils.dom.createSvgElement = function(name, attrs, parent) {
|
||||
if (document.body.runtimeStyle) { // Indicates presence of IE-only attr.
|
||||
e.runtimeStyle = e.currentStyle = e.style;
|
||||
}
|
||||
if (parent) {
|
||||
parent.appendChild(e);
|
||||
if (opt_parent) {
|
||||
opt_parent.appendChild(e);
|
||||
}
|
||||
return e;
|
||||
};
|
||||
@@ -192,7 +192,7 @@ Blockly.utils.dom.insertAfter = function(newNode, refNode) {
|
||||
*/
|
||||
Blockly.utils.dom.containsNode = function(parent, descendant) {
|
||||
return !!(parent.compareDocumentPosition(descendant) &
|
||||
Blockly.utils.dom.Node.DOCUMENT_POSITION_CONTAINED_BY);
|
||||
Blockly.utils.dom.NodeType.DOCUMENT_POSITION_CONTAINED_BY);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,5 +29,5 @@ Blockly.utils.IdGenerator.nextId_ = 0;
|
||||
* @return {string} The next unique identifier.
|
||||
*/
|
||||
Blockly.utils.IdGenerator.getNextUniqueId = function() {
|
||||
return 'blockly:' + (Blockly.utils.IdGenerator.nextId_++).toString(36);
|
||||
return 'blockly-' + (Blockly.utils.IdGenerator.nextId_++).toString(36);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Workspace metrics definitions.
|
||||
* @author samelh@google.com (Sam El-Husseini)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
/**
|
||||
* @record
|
||||
*/
|
||||
Blockly.utils.Metrics = function() {};
|
||||
|
||||
/**
|
||||
* Height of the visible portion of the workspace.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.viewHeight;
|
||||
|
||||
/**
|
||||
* Width of the visible portion of the workspace.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.viewWidth;
|
||||
|
||||
/**
|
||||
* Height of the content.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.contentHeight;
|
||||
|
||||
/**
|
||||
* Width of the content.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.contentWidth;
|
||||
|
||||
/**
|
||||
* Top-edge of the visible portion of the workspace, relative to the workspace
|
||||
* origin.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.viewTop;
|
||||
|
||||
/**
|
||||
* Left-edge of the visible portion of the workspace, relative to the workspace
|
||||
* origin.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.viewLeft;
|
||||
|
||||
/**
|
||||
* Top-edge of the content, relative to the workspace origin.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.contentTop;
|
||||
|
||||
/**
|
||||
* Left-edge of the content relative to the workspace origin.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.contentLeft;
|
||||
|
||||
/**
|
||||
* Top-edge of the visible portion of the workspace, relative to the blocklyDiv.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.absoluteTop;
|
||||
|
||||
/**
|
||||
* Left-edge of the visible portion of the workspace, relative to the
|
||||
* blocklyDiv.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.absoluteLeft;
|
||||
|
||||
/**
|
||||
* Height of the Blockly div (the view + the toolbox, simple of otherwise).
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.svgHeight;
|
||||
|
||||
/**
|
||||
* Width of the Blockly div (the view + the toolbox, simple or otherwise).
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.svgWidth;
|
||||
|
||||
/**
|
||||
* Width of the toolbox, if it exists. Otherwise zero.
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.toolboxWidth;
|
||||
|
||||
/**
|
||||
* Height of the toolbox, if it exists. Otherwise zero.
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.toolboxHeight;
|
||||
|
||||
/**
|
||||
* Top, bottom, left or right. Use TOOLBOX_AT constants to compare.
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.toolboxPosition;
|
||||
|
||||
/**
|
||||
* Width of the flyout if it is always open. Otherwise zero.
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.flyoutWidth;
|
||||
|
||||
/**
|
||||
* Height of the flyout if it is always open. Otherwise zero.
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
Blockly.utils.Metrics.prototype.flyoutHeight;
|
||||
@@ -45,7 +45,7 @@ Blockly.utils.object.mixin = function(target, source) {
|
||||
*/
|
||||
Blockly.utils.object.deepMerge = function(target, source) {
|
||||
for (var x in source) {
|
||||
if (typeof source[x] === 'object') {
|
||||
if (source[x] != null && typeof source[x] === 'object') {
|
||||
target[x] = Blockly.utils.object.deepMerge(
|
||||
target[x] || Object.create(null), source[x]);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @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),
|
||||
* type: ?string,
|
||||
* gap: (?string|?number),
|
||||
* disabled: (?string|?boolean)
|
||||
* }}
|
||||
*/
|
||||
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.Toolbox>
|
||||
* }}
|
||||
*/
|
||||
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.<Blockly.utils.toolbox.Toolbox>|
|
||||
* Array.<Node>}
|
||||
*/
|
||||
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.<Blockly.utils.toolbox.Toolbox>} 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)) {
|
||||
if (Blockly.utils.toolbox.hasCategories(toolboxDef)) {
|
||||
// TODO: Remove after #3985 has been looked into.
|
||||
console.warn('Due to some performance issues, defining a toolbox using' +
|
||||
'JSON is not ready yet. Please define your toolbox using xml.');
|
||||
}
|
||||
return /** @type {!Array.<Blockly.utils.toolbox.Toolbox>} */ (toolboxDef);
|
||||
}
|
||||
|
||||
return Blockly.utils.toolbox.toolboxXmlToJson_(toolboxDef);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the xml for a toolbox to JSON.
|
||||
* @param {!NodeList|!Node|!Array.<Node>} toolboxDef The
|
||||
* definition of the toolbox in one of its many forms.
|
||||
* @return {!Array.<Blockly.utils.toolbox.Toolbox>} 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 = {};
|
||||
var tagName = child.tagName.toUpperCase();
|
||||
obj['kind'] = tagName;
|
||||
|
||||
// Store the xml for a block
|
||||
if (tagName == 'BLOCK') {
|
||||
obj['blockxml'] = child;
|
||||
} else if (tagName == 'CATEGORY') {
|
||||
// Get the contents of a 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.<Blockly.utils.toolbox.Toolbox>} 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);
|
||||
}
|
||||
};
|
||||
@@ -20,6 +20,45 @@ goog.provide('Blockly.utils.userAgent');
|
||||
|
||||
goog.require('Blockly.utils.global');
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.IE;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.EDGE;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.JAVA_FX;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.CHROME;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.WEBKIT;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.GECKO;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.ANDROID;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.IPAD;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.IPOD;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.IPHONE;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.MAC;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.TABLET;
|
||||
|
||||
/** @const {boolean} */
|
||||
Blockly.utils.userAgent.MOBILE;
|
||||
|
||||
(function(raw) {
|
||||
Blockly.utils.userAgent.raw = raw;
|
||||
var rawUpper = Blockly.utils.userAgent.raw.toUpperCase();
|
||||
|
||||
@@ -378,7 +378,7 @@ Blockly.VariableMap.prototype.getAllVariables = function() {
|
||||
|
||||
/**
|
||||
* Returns all of the variable names of all types.
|
||||
* @return {!Array<string>} All of the variable names of all types.
|
||||
* @return {!Array.<string>} All of the variable names of all types.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.getAllVariableNames = function() {
|
||||
var allNames = [];
|
||||
|
||||
+6
-5
@@ -217,7 +217,7 @@ Blockly.Variables.generateUniqueName = function(workspace) {
|
||||
* will try to generate single letter names in the range a -> z (skip l). It
|
||||
* will start with the character passed to startChar.
|
||||
* @param {string} startChar The character to start the search at.
|
||||
* @param {!Array<string>} usedNames A list of all of the used names.
|
||||
* @param {!Array.<string>} usedNames A list of all of the used names.
|
||||
* @return {string} A unique name that is not present in the usedNames array.
|
||||
*/
|
||||
Blockly.Variables.generateUniqueNameFromOptions = function(startChar, usedNames) {
|
||||
@@ -279,7 +279,7 @@ Blockly.Variables.createVariableButtonHandler = function(
|
||||
function(text) {
|
||||
if (text) {
|
||||
var existing =
|
||||
Blockly.Variables.nameUsedWithAnyType_(text, workspace);
|
||||
Blockly.Variables.nameUsedWithAnyType(text, workspace);
|
||||
if (existing) {
|
||||
if (existing.type == type) {
|
||||
var msg = Blockly.Msg['VARIABLE_ALREADY_EXISTS'].replace(
|
||||
@@ -327,7 +327,9 @@ Blockly.Variables.createVariable =
|
||||
Blockly.Variables.createVariableButtonHandler;
|
||||
|
||||
/**
|
||||
* Rename a variable with the given workspace, variableType, and oldName.
|
||||
* Opens a prompt that allows the user to enter a new name for a variable.
|
||||
* Triggers a rename if the new name is valid. Or re-prompts if there is a
|
||||
* collision.
|
||||
* @param {!Blockly.Workspace} workspace The workspace on which to rename the
|
||||
* variable.
|
||||
* @param {Blockly.VariableModel} variable Variable to rename.
|
||||
@@ -424,9 +426,8 @@ Blockly.Variables.nameUsedWithOtherType_ = function(name, type, workspace) {
|
||||
* variable.
|
||||
* @return {Blockly.VariableModel} The variable with the given name,
|
||||
* or null if none was found.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Variables.nameUsedWithAnyType_ = function(name, workspace) {
|
||||
Blockly.Variables.nameUsedWithAnyType = function(name, workspace) {
|
||||
var allVariables = workspace.getVariableMap().getAllVariables();
|
||||
|
||||
name = name.toLowerCase();
|
||||
|
||||
+1
-11
@@ -42,7 +42,7 @@ Blockly.Warning.prototype.collapseHidden = false;
|
||||
/**
|
||||
* Draw the warning icon.
|
||||
* @param {!Element} group The icon group.
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Warning.prototype.drawIcon_ = function(group) {
|
||||
// Triangle with rounded corners.
|
||||
@@ -154,16 +154,6 @@ Blockly.Warning.prototype.disposeBubble = function() {
|
||||
this.paragraphElement_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bring the warning to the top of the stack when clicked on.
|
||||
* @param {!Event} _e Mouse up event.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Blockly.Warning.prototype.bodyFocus_ = function(_e) {
|
||||
this.bubble_.promote();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set this warning's text.
|
||||
* @param {string} text Warning text (or '' to delete). This supports
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user