mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
Keyboard shortcuts (#4421)
* Adds shortcut registry and removes action and key map (#4398) * Adds Shortcut tests and refactored navigation tests (#4412) * Adds shortcut items (#4408) * Add shortcuts for navigation (#4409) * Add final keyboard shortcut cleanup (#4413)
This commit is contained in:
@@ -21,24 +21,23 @@ this.BLOCKLY_DIR = (function(root) {
|
||||
|
||||
this.BLOCKLY_BOOT = function(root) {
|
||||
// Execute after Closure has loaded.
|
||||
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.Tooltip', 'Blockly.Workspace', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.deprecation', 'Blockly.utils.object', 'Blockly.utils.string'], {'lang': 'es5'});
|
||||
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.Tooltip', 'Blockly.Workspace', 'Blockly.constants', 'Blockly.fieldRegistry', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.deprecation', 'Blockly.utils.object', 'Blockly.utils.string'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/block_animations.js', ['Blockly.blockAnimations'], ['Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/block_drag_surface.js', ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', '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_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.constants', '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.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.object', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', '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.deprecation', '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.Size', 'Blockly.utils.colour'], {});
|
||||
goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Events.Ui', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.blockAnimations', 'Blockly.blockRendering.IPathObject', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Procedures', 'Blockly.ShortcutRegistry', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.constants', 'Blockly.inject', '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.Svg', '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/bubble_dragger.js', ['Blockly.BubbleDragger'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.CommentMove', 'Blockly.constants', '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.Svg', 'Blockly.utils.deprecation', '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/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml', 'Blockly.utils.deprecation'], {});
|
||||
goog.addDependency('../../core/connection_checker.js', ['Blockly.ConnectionChecker'], ['Blockly.registry'], {});
|
||||
goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.RenderedConnection'], {});
|
||||
goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml', 'Blockly.constants', 'Blockly.utils.deprecation'], {});
|
||||
goog.addDependency('../../core/connection_checker.js', ['Blockly.ConnectionChecker'], ['Blockly.constants', 'Blockly.registry'], {});
|
||||
goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.RenderedConnection', 'Blockly.constants'], {});
|
||||
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.Rect', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/contextmenu_items.js', ['Blockly.ContextMenuItems'], [], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/contextmenu.js', ['Blockly.ContextMenu'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.Msg', 'Blockly.Xml', 'Blockly.constants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/contextmenu_items.js', ['Blockly.ContextMenuItems'], ['Blockly.constants'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/contextmenu_registry.js', ['Blockly.ContextMenuRegistry'], ['Blockly.ContextMenuItems'], {'lang': 'es5'});
|
||||
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'], {});
|
||||
@@ -57,19 +56,19 @@ goog.addDependency('../../core/field_multilineinput.js', ['Blockly.FieldMultilin
|
||||
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'], ['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/field_variable.js', ['Blockly.FieldVariable'], ['Blockly.Events', 'Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml', 'Blockly.constants', '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.Svg', 'Blockly.utils.dom', 'Blockly.utils.toolbox'], {});
|
||||
goog.addDependency('../../core/flyout_button.js', ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/flyout_dragger.js', ['Blockly.FlyoutDragger'], ['Blockly.WorkspaceDragger', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly.Block'], {});
|
||||
goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly.Block', 'Blockly.constants'], {});
|
||||
goog.addDependency('../../core/gesture.js', ['Blockly.Gesture'], ['Blockly.ASTNode', 'Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.FlyoutDragger', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceDragger', 'Blockly.blockAnimations', 'Blockly.constants', 'Blockly.navigation', 'Blockly.utils', 'Blockly.utils.Coordinate'], {});
|
||||
goog.addDependency('../../core/grid.js', ['Blockly.Grid'], ['Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/inject.js', ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Component', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg', 'Blockly.user.keyMap', 'Blockly.utils', 'Blockly.utils.Svg', '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/inject.js', ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg', 'Blockly.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel', 'Blockly.constants'], {});
|
||||
goog.addDependency('../../core/insertion_marker_manager.js', ['Blockly.InsertionMarkerManager'], ['Blockly.Events', 'Blockly.blockAnimations', 'Blockly.constants'], {'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_bubble.js', ['Blockly.IBubble'], [], {});
|
||||
@@ -86,39 +85,37 @@ goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectabl
|
||||
goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_toolbox_item.js', ['Blockly.ICollapsibleToolboxItem', 'Blockly.ISelectableToolboxItem', 'Blockly.IToolboxItem'], [], {});
|
||||
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/ast_node.js', ['Blockly.ASTNode'], ['Blockly.constants', 'Blockly.utils.Coordinate'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/keyboard_nav/basic_cursor.js', ['Blockly.BasicCursor'], ['Blockly.ASTNode', 'Blockly.Cursor'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/keyboard_nav/cursor.js', ['Blockly.Cursor'], ['Blockly.ASTNode', 'Blockly.Action', 'Blockly.Marker', 'Blockly.navigation', 'Blockly.utils.object'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/keyboard_nav/cursor.js', ['Blockly.Cursor'], ['Blockly.ASTNode', 'Blockly.Marker', 'Blockly.navigation', 'Blockly.utils.object'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/keyboard_nav/flyout_cursor.js', ['Blockly.FlyoutCursor'], ['Blockly.Cursor', 'Blockly.navigation', 'Blockly.utils.object'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/keyboard_nav/key_map.js', ['Blockly.user.keyMap'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/keyboard_nav/marker.js', ['Blockly.Marker'], ['Blockly.ASTNode', 'Blockly.navigation'], {});
|
||||
goog.addDependency('../../core/keyboard_nav/navigation.js', ['Blockly.navigation'], ['Blockly.ASTNode', 'Blockly.Action', 'Blockly.user.keyMap', 'Blockly.utils.Coordinate'], {});
|
||||
goog.addDependency('../../core/keyboard_nav/navigation.js', ['Blockly.navigation'], ['Blockly.ASTNode', 'Blockly.constants', '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.Svg', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.toolbox', '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.registry', 'Blockly.user.keyMap', 'Blockly.utils.IdGenerator', 'Blockly.utils.Metrics', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent'], {});
|
||||
goog.addDependency('../../core/names.js', ['Blockly.Names'], ['Blockly.Msg', 'Blockly.constants'], {});
|
||||
goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.Xml', 'Blockly.registry', '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.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.Events', 'Blockly.constants', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.deprecation', 'Blockly.utils.dom', '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.Svg', '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', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/renderers/common/constants.js', ['Blockly.blockRendering.ConstantProvider'], ['Blockly.constants', 'Blockly.utils', 'Blockly.utils.Svg', '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', 'Blockly.constants', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/renderers/common/drawer.js', ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {});
|
||||
goog.addDependency('../../core/renderers/common/i_path_object.js', ['Blockly.blockRendering.IPathObject'], [], {});
|
||||
goog.addDependency('../../core/renderers/common/info.js', ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types'], {});
|
||||
goog.addDependency('../../core/renderers/common/marker_svg.js', ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/renderers/common/info.js', ['Blockly.blockRendering.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.constants'], {});
|
||||
goog.addDependency('../../core/renderers/common/marker_svg.js', ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode', 'Blockly.constants', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/renderers/common/path_object.js', ['Blockly.blockRendering.PathObject'], ['Blockly.Theme', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.IPathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/renderers/common/renderer.js', ['Blockly.blockRendering.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo'], {});
|
||||
goog.addDependency('../../core/renderers/common/renderer.js', ['Blockly.blockRendering.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.constants'], {});
|
||||
goog.addDependency('../../core/renderers/geras/constants.js', ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/renderers/geras/drawer.js', ['Blockly.geras.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.geras.Highlighter', 'Blockly.geras.RenderInfo', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {});
|
||||
goog.addDependency('../../core/renderers/geras/highlight_constants.js', ['Blockly.geras.HighlightConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.svgPaths'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/renderers/geras/highlighter.js', ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {});
|
||||
goog.addDependency('../../core/renderers/geras/info.js', ['Blockly.geras', 'Blockly.geras.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/geras/info.js', ['Blockly.geras', 'Blockly.geras.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.constants', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/geras/measurables/inputs.js', ['Blockly.geras.InlineInput', 'Blockly.geras.StatementInput'], ['Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/geras/path_object.js', ['Blockly.geras.PathObject'], ['Blockly.Theme', 'Blockly.blockRendering.PathObject', 'Blockly.geras.ConstantProvider', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/geras/renderer.js', ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object'], {});
|
||||
@@ -134,17 +131,19 @@ goog.addDependency('../../core/renderers/minimalist/info.js', ['Blockly.minimali
|
||||
goog.addDependency('../../core/renderers/minimalist/renderer.js', ['Blockly.minimalist.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/thrasos/info.js', ['Blockly.thrasos', 'Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/thrasos/renderer.js', ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/constants.js', ['Blockly.zelos.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/renderers/zelos/constants.js', ['Blockly.zelos.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.constants', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/renderers/zelos/drawer.js', ['Blockly.zelos.Drawer'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.Types', 'Blockly.utils.object', 'Blockly.zelos.RenderInfo'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/info.js', ['Blockly.zelos', 'Blockly.zelos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.utils.object', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/info.js', ['Blockly.zelos', 'Blockly.zelos.RenderInfo'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.constants', 'Blockly.utils.object', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/marker_svg.js', ['Blockly.zelos.MarkerSvg'], ['Blockly.blockRendering.MarkerSvg', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/measurables/inputs.js', ['Blockly.zelos.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['Blockly.zelos.RightConnectionShape'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/measurables/rows.js', ['Blockly.zelos.BottomRow', 'Blockly.zelos.TopRow'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.TopRow', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', '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/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.constants', '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.Metrics', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.utils.KeyCodes'], {});
|
||||
goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.ShortcutItems', 'Blockly.navigation', 'Blockly.utils.object'], {});
|
||||
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'], {});
|
||||
@@ -157,12 +156,12 @@ goog.addDependency('../../core/theme_manager.js', ['Blockly.ThemeManager'], ['Bl
|
||||
goog.addDependency('../../core/toolbox/category.js', ['Blockly.ToolboxCategory'], ['Blockly.ToolboxItem', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/toolbox/collapsible_category.js', ['Blockly.CollapsibleToolboxCategory'], ['Blockly.ToolboxCategory', 'Blockly.ToolboxItem', 'Blockly.ToolboxSeparator', 'Blockly.registry', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {});
|
||||
goog.addDependency('../../core/toolbox/separator.js', ['Blockly.ToolboxSeparator'], ['Blockly.ToolboxItem', 'Blockly.registry', 'Blockly.utils.dom'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/toolbox/toolbox.js', ['Blockly.Toolbox'], ['Blockly.CollapsibleToolboxCategory', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Touch', 'Blockly.navigation', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.toolbox'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/toolbox/toolbox.js', ['Blockly.Toolbox'], ['Blockly.CollapsibleToolboxCategory', 'Blockly.Css', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Touch', 'Blockly.constants', 'Blockly.navigation', 'Blockly.registry', 'Blockly.utils', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.toolbox'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/toolbox/toolbox_item.js', ['Blockly.ToolboxItem'], [], {});
|
||||
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.js', ['Blockly.Touch'], ['Blockly.constants', '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.Svg', 'Blockly.utils.dom', 'Blockly.utils.toolbox'], {});
|
||||
goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.Scrollbar', 'Blockly.Xml', 'Blockly.constants', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.toolbox'], {});
|
||||
goog.addDependency('../../core/ui_events.js', ['Blockly.Events.Ui'], ['Blockly.Events', 'Blockly.Events.Abstract', 'Blockly.registry', 'Blockly.utils.object'], {});
|
||||
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'], [], {});
|
||||
@@ -182,18 +181,18 @@ 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.js', ['Blockly.utils.Svg'], [], {});
|
||||
goog.addDependency('../../core/utils/svg_paths.js', ['Blockly.utils.svgPaths'], [], {});
|
||||
goog.addDependency('../../core/utils/toolbox.js', ['Blockly.utils.toolbox'], [], {});
|
||||
goog.addDependency('../../core/utils/toolbox.js', ['Blockly.utils.toolbox'], ['Blockly.constants'], {});
|
||||
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.registry', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/variable_map.js', ['Blockly.VariableMap'], ['Blockly.Events', 'Blockly.Events.VarDelete', 'Blockly.Events.VarRename', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/variable_model.js', ['Blockly.VariableModel'], ['Blockly.Events', 'Blockly.Events.VarCreate', 'Blockly.utils'], {});
|
||||
goog.addDependency('../../core/variables.js', ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Xml', 'Blockly.utils', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/variables.js', ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Xml', 'Blockly.constants', 'Blockly.utils', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/variables_dynamic.js', ['Blockly.VariablesDynamic'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/warning.js', ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/widgetdiv.js', ['Blockly.WidgetDiv'], ['Blockly.utils.style'], {});
|
||||
goog.addDependency('../../core/workspace.js', ['Blockly.Workspace'], ['Blockly.ConnectionChecker', 'Blockly.Events', 'Blockly.Options', 'Blockly.VariableMap', 'Blockly.utils', 'Blockly.utils.math'], {});
|
||||
goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.constants', 'Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/workspace_comment.js', ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/workspace_comment_render_svg.js', ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {});
|
||||
goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCommentSvg'], ['Blockly.Css', 'Blockly.Events', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Ui', 'Blockly.WorkspaceComment', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
@@ -202,8 +201,8 @@ goog.addDependency('../../core/workspace_dragger.js', ['Blockly.WorkspaceDragger
|
||||
goog.addDependency('../../core/workspace_events.js', ['Blockly.Events.FinishedLoading'], ['Blockly.Events', 'Blockly.Events.Ui', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ConnectionDB', 'Blockly.ContextMenuRegistry', '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.Svg', '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.registry', '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.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/xml.js', ['Blockly.Xml'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.VarCreate', 'Blockly.constants', '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.constants', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es5'});
|
||||
goog.addDependency("base.js", [], []);
|
||||
|
||||
// Load Blockly.
|
||||
|
||||
@@ -680,10 +680,11 @@ Blockly.BlockSvg.prototype.tab = function(start, forward) {
|
||||
var tabCursor = new Blockly.TabNavigateCursor();
|
||||
tabCursor.setCurNode(Blockly.ASTNode.createFieldNode(start));
|
||||
var currentNode = tabCursor.getCurNode();
|
||||
var action = forward ?
|
||||
Blockly.navigation.ACTION_NEXT : Blockly.navigation.ACTION_PREVIOUS;
|
||||
var actionName = forward ?
|
||||
Blockly.navigation.actionNames.NEXT : Blockly.navigation.actionNames.PREVIOUS;
|
||||
|
||||
tabCursor.onBlocklyAction(action);
|
||||
tabCursor.onBlocklyAction(
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */ ({name: actionName}));
|
||||
|
||||
var nextNode = tabCursor.getCurNode();
|
||||
if (nextNode && nextNode !== currentNode) {
|
||||
|
||||
122
core/blockly.js
122
core/blockly.js
@@ -20,8 +20,8 @@ goog.require('Blockly.constants');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.Ui');
|
||||
goog.require('Blockly.inject');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.Procedures');
|
||||
goog.require('Blockly.ShortcutRegistry');
|
||||
goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -177,89 +177,18 @@ Blockly.onKeyDown = function(e) {
|
||||
// hidden.
|
||||
return;
|
||||
}
|
||||
Blockly.ShortcutRegistry.registry.onKeyDown(mainWorkspace, e);
|
||||
};
|
||||
|
||||
if (mainWorkspace.options.readOnly) {
|
||||
// When in read only mode handle key actions for keyboard navigation.
|
||||
Blockly.navigation.onKeyPress(e);
|
||||
return;
|
||||
}
|
||||
|
||||
var deleteBlock = false;
|
||||
if (e.keyCode == Blockly.utils.KeyCodes.ESC) {
|
||||
// Pressing esc closes the context menu.
|
||||
Blockly.hideChaff();
|
||||
Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_EXIT);
|
||||
} 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 ||
|
||||
e.keyCode == Blockly.utils.KeyCodes.DELETE) {
|
||||
// Delete or backspace.
|
||||
// Stop the browser from going back to the previous page.
|
||||
// Do this first to prevent an error in the delete code from resulting in
|
||||
// data loss.
|
||||
e.preventDefault();
|
||||
// Don't delete while dragging. Jeez.
|
||||
if (Blockly.Gesture.inProgress()) {
|
||||
return;
|
||||
}
|
||||
if (Blockly.selected && Blockly.selected.isDeletable()) {
|
||||
deleteBlock = true;
|
||||
}
|
||||
} else if (e.altKey || e.ctrlKey || e.metaKey) {
|
||||
// Don't use meta keys during drags.
|
||||
if (Blockly.Gesture.inProgress()) {
|
||||
return;
|
||||
}
|
||||
if (Blockly.selected &&
|
||||
Blockly.selected.isDeletable() && Blockly.selected.isMovable()) {
|
||||
// Don't allow copying immovable or undeletable blocks. The next step
|
||||
// would be to paste, which would create additional undeletable/immovable
|
||||
// blocks on the workspace.
|
||||
if (e.keyCode == Blockly.utils.KeyCodes.C) {
|
||||
// 'c' for copy.
|
||||
Blockly.hideChaff();
|
||||
Blockly.copy_(Blockly.selected);
|
||||
} else if (e.keyCode == Blockly.utils.KeyCodes.X &&
|
||||
!Blockly.selected.workspace.isFlyout) {
|
||||
// 'x' for cut, but not in a flyout.
|
||||
// Don't even copy the selected item in the flyout.
|
||||
Blockly.copy_(Blockly.selected);
|
||||
deleteBlock = true;
|
||||
}
|
||||
}
|
||||
if (e.keyCode == Blockly.utils.KeyCodes.V) {
|
||||
// 'v' for paste.
|
||||
if (Blockly.clipboardXml_) {
|
||||
// Pasting always pastes to the main workspace, even if the copy
|
||||
// started in a flyout workspace.
|
||||
var workspace = Blockly.clipboardSource_;
|
||||
if (workspace.isFlyout) {
|
||||
workspace = workspace.targetWorkspace;
|
||||
}
|
||||
if (Blockly.clipboardTypeCounts_ &&
|
||||
workspace.isCapacityAvailable(Blockly.clipboardTypeCounts_)) {
|
||||
Blockly.Events.setGroup(true);
|
||||
workspace.paste(Blockly.clipboardXml_);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
}
|
||||
} else if (e.keyCode == Blockly.utils.KeyCodes.Z) {
|
||||
// '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.
|
||||
// Don't delete in the flyout.
|
||||
if (deleteBlock && !Blockly.selected.workspace.isFlyout) {
|
||||
/**
|
||||
* Delete the given block.
|
||||
* @param {!Blockly.BlockSvg} selected The block to delete.
|
||||
* @package
|
||||
*/
|
||||
Blockly.deleteBlock = function(selected) {
|
||||
if (!selected.workspace.isFlyout) {
|
||||
Blockly.Events.setGroup(true);
|
||||
Blockly.hideChaff();
|
||||
var selected = /** @type {!Blockly.BlockSvg} */ (Blockly.selected);
|
||||
selected.dispose(/* heal */ true, true);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
@@ -268,9 +197,9 @@ Blockly.onKeyDown = function(e) {
|
||||
/**
|
||||
* Copy a block or workspace comment onto the local clipboard.
|
||||
* @param {!Blockly.ICopyable} toCopy Block or Workspace Comment to be copied.
|
||||
* @private
|
||||
* @package
|
||||
*/
|
||||
Blockly.copy_ = function(toCopy) {
|
||||
Blockly.copy = function(toCopy) {
|
||||
var data = toCopy.toCopyData();
|
||||
if (data) {
|
||||
Blockly.clipboardXml_ = data.xml;
|
||||
@@ -279,6 +208,31 @@ Blockly.copy_ = function(toCopy) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Paste a block or workspace comment on to the main workspace.
|
||||
* @return {boolean} True if the paste was successful, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.paste = function() {
|
||||
if (!Blockly.clipboardXml_) {
|
||||
return false;
|
||||
}
|
||||
// Pasting always pastes to the main workspace, even if the copy
|
||||
// started in a flyout workspace.
|
||||
var workspace = Blockly.clipboardSource_;
|
||||
if (workspace.isFlyout) {
|
||||
workspace = workspace.targetWorkspace;
|
||||
}
|
||||
if (Blockly.clipboardTypeCounts_ &&
|
||||
workspace.isCapacityAvailable(Blockly.clipboardTypeCounts_)) {
|
||||
Blockly.Events.setGroup(true);
|
||||
workspace.paste(Blockly.clipboardXml_);
|
||||
Blockly.Events.setGroup(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Duplicate this block and its children, or a workspace comment.
|
||||
* @param {!Blockly.ICopyable} toDuplicate Block or Workspace Comment to be
|
||||
@@ -291,7 +245,7 @@ Blockly.duplicate = function(toDuplicate) {
|
||||
var clipboardSource = Blockly.clipboardSource_;
|
||||
|
||||
// Create a duplicate via a copy/paste operation.
|
||||
Blockly.copy_(toDuplicate);
|
||||
Blockly.copy(toDuplicate);
|
||||
toDuplicate.workspace.paste(Blockly.clipboardXml_);
|
||||
|
||||
// Restore the clipboard.
|
||||
|
||||
@@ -32,6 +32,7 @@ goog.requireType('Blockly.IASTNodeLocationSvg');
|
||||
goog.requireType('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IRegistrable');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
|
||||
/**
|
||||
@@ -1107,7 +1108,7 @@ Blockly.Field.prototype.isTabNavigable = function() {
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.Action} _action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ goog.require('Blockly.utils.KeyCodes');
|
||||
goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.Size');
|
||||
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
/**
|
||||
* Class for a colour input field.
|
||||
@@ -384,24 +385,27 @@ Blockly.FieldColour.prototype.onKeyDown_ = function(e) {
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.FieldColour.prototype.onBlocklyAction = function(action) {
|
||||
if (this.picker_) {
|
||||
if (action === Blockly.navigation.ACTION_PREVIOUS) {
|
||||
this.moveHighlightBy_(0, -1);
|
||||
return true;
|
||||
} else if (action === Blockly.navigation.ACTION_NEXT) {
|
||||
this.moveHighlightBy_(0, 1);
|
||||
return true;
|
||||
} else if (action === Blockly.navigation.ACTION_OUT) {
|
||||
this.moveHighlightBy_(-1, 0);
|
||||
return true;
|
||||
} else if (action === Blockly.navigation.ACTION_IN) {
|
||||
this.moveHighlightBy_(1, 0);
|
||||
return true;
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
this.moveHighlightBy_(0, -1);
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
this.moveHighlightBy_(0, 1);
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.OUT:
|
||||
this.moveHighlightBy_(-1, 0);
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.IN:
|
||||
this.moveHighlightBy_(1, 0);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Blockly.FieldColour.superClass_.onBlocklyAction.call(this, action);
|
||||
|
||||
@@ -31,6 +31,8 @@ goog.require('Blockly.utils.string');
|
||||
goog.require('Blockly.utils.Svg');
|
||||
goog.require('Blockly.utils.userAgent');
|
||||
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
|
||||
/**
|
||||
* Class for an editable dropdown field.
|
||||
@@ -739,18 +741,21 @@ Blockly.FieldDropdown.validateOptions_ = function(options) {
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.onBlocklyAction = function(action) {
|
||||
if (this.menu_) {
|
||||
if (action === Blockly.navigation.ACTION_PREVIOUS) {
|
||||
this.menu_.highlightPrevious();
|
||||
return true;
|
||||
} else if (action === Blockly.navigation.ACTION_NEXT) {
|
||||
this.menu_.highlightNext();
|
||||
return true;
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.PREVIOUS:
|
||||
this.menu_.highlightPrevious();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.NEXT:
|
||||
this.menu_.highlightNext();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Blockly.FieldDropdown.superClass_.onBlocklyAction.call(this, action);
|
||||
|
||||
@@ -34,6 +34,7 @@ goog.require('Blockly.Xml');
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
goog.requireType('Blockly.IFlyout');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
goog.requireType('Blockly.utils.Metrics');
|
||||
|
||||
|
||||
@@ -997,7 +998,7 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) {
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the flyout handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,6 @@ goog.require('Blockly.Msg');
|
||||
goog.require('Blockly.Options');
|
||||
goog.require('Blockly.ScrollbarPair');
|
||||
goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.user.keyMap');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Svg');
|
||||
@@ -68,7 +67,6 @@ Blockly.inject = function(container, opt_options) {
|
||||
|
||||
var workspace = Blockly.createMainWorkspace_(svg, options, blockDragSurface,
|
||||
workspaceDragSurface);
|
||||
Blockly.user.keyMap.setKeyMap(options.keyMap);
|
||||
|
||||
Blockly.init_(workspace);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ goog.provide('Blockly.IASTNodeLocation');
|
||||
goog.provide('Blockly.IASTNodeLocationSvg');
|
||||
goog.provide('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.provide('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
/**
|
||||
* An AST node location interface.
|
||||
@@ -66,7 +67,7 @@ Blockly.IBlocklyActionable = function() {};
|
||||
|
||||
/**
|
||||
* Handles the given action.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
*/
|
||||
Blockly.IBlocklyActionable.prototype.onBlocklyAction;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The class representing an action.
|
||||
* Used primarily for keyboard navigation.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Action');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a single action.
|
||||
* An action describes user intent. (ex go to next or go to previous)
|
||||
* @param {string} name The name of the action.
|
||||
* @param {string} desc The description of the action.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Action = function(name, desc) {
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
};
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
goog.provide('Blockly.Cursor');
|
||||
|
||||
goog.require('Blockly.Action');
|
||||
goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.Marker');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
|
||||
/**
|
||||
@@ -140,7 +140,7 @@ Blockly.Cursor.prototype.out = function() {
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard navigation is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
*/
|
||||
Blockly.Cursor.prototype.onBlocklyAction = function(action) {
|
||||
|
||||
@@ -17,6 +17,8 @@ goog.require('Blockly.Cursor');
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout cursor.
|
||||
@@ -32,7 +34,7 @@ Blockly.utils.object.inherits(Blockly.FlyoutCursor, Blockly.Cursor);
|
||||
/**
|
||||
* Handles the given action.
|
||||
* This is only triggered when keyboard navigation is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The namespace used to keep track of keyboard actions and the
|
||||
* key codes used to execute those actions.
|
||||
* This is used primarily for keyboard navigation.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.user.keyMap');
|
||||
|
||||
// TODO: Fix circular dependency.
|
||||
// goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.utils.KeyCodes');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
|
||||
/**
|
||||
* Holds the serialized key to key action mapping.
|
||||
* @type {!Object<string, Blockly.Action>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.user.keyMap.map_ = {};
|
||||
|
||||
/**
|
||||
* Object holding valid modifiers.
|
||||
* @enum {string}
|
||||
*/
|
||||
Blockly.user.keyMap.modifierKeys = {
|
||||
SHIFT: 'Shift',
|
||||
CONTROL: 'Control',
|
||||
ALT: 'Alt',
|
||||
META: 'Meta'
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the key map to contain the new action.
|
||||
* @param {string} keyCode The key code serialized by the serializeKeyEvent.
|
||||
* @param {!Blockly.Action} action The action to be executed when the keys
|
||||
* corresponding to the serialized key code is pressed.
|
||||
*/
|
||||
Blockly.user.keyMap.setActionForKey = function(keyCode, action) {
|
||||
var oldKey = Blockly.user.keyMap.getKeyByAction(action);
|
||||
// If the action already exists in the key map remove it and add the new mapping.
|
||||
if (oldKey) {
|
||||
delete Blockly.user.keyMap.map_[oldKey];
|
||||
}
|
||||
Blockly.user.keyMap.map_[keyCode] = action;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new key map.
|
||||
* @param {!Object<string, Blockly.Action>} keyMap The object holding the key
|
||||
* to action mapping.
|
||||
*/
|
||||
Blockly.user.keyMap.setKeyMap = function(keyMap) {
|
||||
Blockly.user.keyMap.map_ = keyMap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current key map.
|
||||
* @return {Object<string,Blockly.Action>} The object holding the key to
|
||||
* action mapping.
|
||||
*/
|
||||
Blockly.user.keyMap.getKeyMap = function() {
|
||||
var map = {};
|
||||
Blockly.utils.object.mixin(map, Blockly.user.keyMap.map_);
|
||||
return map;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the action by the serialized key code.
|
||||
* @param {string} keyCode The serialized key code.
|
||||
* @return {Blockly.Action|undefined} The action holding the function to
|
||||
* call when the given keyCode is used or undefined if no action exists.
|
||||
*/
|
||||
Blockly.user.keyMap.getActionByKeyCode = function(keyCode) {
|
||||
return Blockly.user.keyMap.map_[keyCode];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the serialized key that corresponds to the action.
|
||||
* @param {!Blockly.Action} action The action for which we want to get
|
||||
* the key.
|
||||
* @return {?string} The serialized key or null if the action does not have
|
||||
* a key mapping.
|
||||
*/
|
||||
Blockly.user.keyMap.getKeyByAction = function(action) {
|
||||
var keys = Object.keys(Blockly.user.keyMap.map_);
|
||||
for (var i = 0, key; (key = keys[i]); i++) {
|
||||
if (Blockly.user.keyMap.map_[key].name === action.name) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the key event.
|
||||
* @param {!KeyboardEvent} e A key up event holding the key code.
|
||||
* @return {string} A string containing the serialized key event.
|
||||
* @package
|
||||
*/
|
||||
Blockly.user.keyMap.serializeKeyEvent = function(e) {
|
||||
var modifiers = Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys);
|
||||
var key = '';
|
||||
for (var i = 0, keyName; (keyName = modifiers[i]); i++) {
|
||||
if (e.getModifierState(keyName)) {
|
||||
key += keyName;
|
||||
}
|
||||
}
|
||||
key += e.keyCode;
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether any of the given modifiers are not valid.
|
||||
* @param {!Array.<string>} modifiers List of modifiers to be used with the key.
|
||||
* @param {!Array.<string>} validModifiers List of modifiers we support.
|
||||
* @throws {Error} if the modifier is not in the valid modifiers list.
|
||||
* @private
|
||||
*/
|
||||
Blockly.user.keyMap.checkModifiers_ = function(modifiers, validModifiers) {
|
||||
for (var i = 0, modifier; (modifier = modifiers[i]); i++) {
|
||||
if (validModifiers.indexOf(modifier) < 0) {
|
||||
throw Error(modifier + ' is not a valid modifier key.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the serialized key code that will be used in the key map.
|
||||
* @param {number} keyCode Number code representing the key.
|
||||
* @param {!Array.<string>} modifiers List of modifiers to be used with the key.
|
||||
* All valid modifiers can be found in the Blockly.user.keyMap.modifierKeys.
|
||||
* @return {string} The serialized key code for the given modifiers and key.
|
||||
*/
|
||||
Blockly.user.keyMap.createSerializedKey = function(keyCode, modifiers) {
|
||||
var key = '';
|
||||
var validModifiers = Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys);
|
||||
Blockly.user.keyMap.checkModifiers_(modifiers, validModifiers);
|
||||
for (var i = 0, validModifier; (validModifier = validModifiers[i]); i++) {
|
||||
if (modifiers.indexOf(validModifier) > -1) {
|
||||
key += validModifier;
|
||||
}
|
||||
}
|
||||
key += keyCode;
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the default key map.
|
||||
* @return {!Object<string,Blockly.Action>} An object holding the default key
|
||||
* to action mapping.
|
||||
*/
|
||||
Blockly.user.keyMap.createDefaultKeyMap = function() {
|
||||
var map = {};
|
||||
var controlK = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.K, [Blockly.user.keyMap.modifierKeys.CONTROL,
|
||||
Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
var shiftW = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.W, [Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
var shiftA = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.A, [Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
var shiftS = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.S, [Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
var shiftD = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.D, [Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
|
||||
map[Blockly.utils.KeyCodes.W] = Blockly.navigation.ACTION_PREVIOUS;
|
||||
map[Blockly.utils.KeyCodes.A] = Blockly.navigation.ACTION_OUT;
|
||||
map[Blockly.utils.KeyCodes.S] = Blockly.navigation.ACTION_NEXT;
|
||||
map[Blockly.utils.KeyCodes.D] = Blockly.navigation.ACTION_IN;
|
||||
map[Blockly.utils.KeyCodes.I] = Blockly.navigation.ACTION_INSERT;
|
||||
map[Blockly.utils.KeyCodes.ENTER] = Blockly.navigation.ACTION_MARK;
|
||||
map[Blockly.utils.KeyCodes.X] = Blockly.navigation.ACTION_DISCONNECT;
|
||||
map[Blockly.utils.KeyCodes.T] = Blockly.navigation.ACTION_TOOLBOX;
|
||||
map[Blockly.utils.KeyCodes.E] = Blockly.navigation.ACTION_EXIT;
|
||||
map[Blockly.utils.KeyCodes.ESC] = Blockly.navigation.ACTION_EXIT;
|
||||
map[controlK] = Blockly.navigation.ACTION_TOGGLE_KEYBOARD_NAV;
|
||||
map[shiftW] = Blockly.navigation.ACTION_MOVE_WS_CURSOR_UP;
|
||||
map[shiftA] = Blockly.navigation.ACTION_MOVE_WS_CURSOR_LEFT;
|
||||
map[shiftS] = Blockly.navigation.ACTION_MOVE_WS_CURSOR_DOWN;
|
||||
map[shiftD] = Blockly.navigation.ACTION_MOVE_WS_CURSOR_RIGHT;
|
||||
return map;
|
||||
};
|
||||
@@ -13,11 +13,9 @@
|
||||
|
||||
goog.provide('Blockly.navigation');
|
||||
|
||||
goog.require('Blockly.Action');
|
||||
goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.constants');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.user.keyMap');
|
||||
|
||||
|
||||
/**
|
||||
@@ -113,6 +111,7 @@ Blockly.navigation.getMarker = function() {
|
||||
/**
|
||||
* Get the workspace that is being navigated.
|
||||
* @return {!Blockly.WorkspaceSvg} The workspace being navigated.
|
||||
* TODO: Remove this in favor or using passed in workspaces.
|
||||
*/
|
||||
Blockly.navigation.getNavigationWorkspace = function() {
|
||||
return /** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace());
|
||||
@@ -121,10 +120,11 @@ Blockly.navigation.getNavigationWorkspace = function() {
|
||||
/**
|
||||
* If a toolbox exists, set the navigation state to toolbox and select the first
|
||||
* category in the toolbox.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The main workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.focusToolbox_ = function() {
|
||||
var toolbox = Blockly.navigation.getNavigationWorkspace().getToolbox();
|
||||
Blockly.navigation.focusToolbox_ = function(workspace) {
|
||||
var toolbox = workspace.getToolbox();
|
||||
if (toolbox) {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
Blockly.navigation.resetFlyout_(false /* shouldHide */);
|
||||
@@ -133,26 +133,19 @@ Blockly.navigation.focusToolbox_ = function() {
|
||||
Blockly.navigation.markAtCursor_();
|
||||
}
|
||||
if (!toolbox.getSelectedItem()) {
|
||||
// Find the first item that is selectable.
|
||||
var toolboxItems = toolbox.getToolboxItems();
|
||||
for (var i = 0, toolboxItem; (toolboxItem = toolboxItems[i]); i++) {
|
||||
if (toolboxItem.isSelectable()) {
|
||||
toolbox.selectItemByPosition(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
toolbox.selectItemByPosition(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change focus to the flyout.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The main workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.focusFlyout_ = function() {
|
||||
Blockly.navigation.focusFlyout_ = function(workspace) {
|
||||
var topBlock = null;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_FLYOUT;
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
|
||||
@@ -173,11 +166,11 @@ Blockly.navigation.focusFlyout_ = function() {
|
||||
/**
|
||||
* Finds where the cursor should go on the workspace. This is either the top
|
||||
* block or a set position on the workspace.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The main workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.focusWorkspace_ = function() {
|
||||
Blockly.navigation.focusWorkspace_ = function(workspace) {
|
||||
Blockly.hideChaff();
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var cursor = workspace.getCursor();
|
||||
var reset = !!workspace.getToolbox();
|
||||
var topBlocks = workspace.getTopBlocks(true);
|
||||
@@ -218,9 +211,9 @@ Blockly.navigation.getFlyoutCursor_ = function() {
|
||||
* If there is a marked connection try connecting the block from the flyout to
|
||||
* that connection. If no connection has been marked then inserting will place
|
||||
* it on the workspace.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The main workspace.
|
||||
*/
|
||||
Blockly.navigation.insertFromFlyout = function() {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
Blockly.navigation.insertFromFlyout = function(workspace) {
|
||||
var flyout = workspace.getFlyout();
|
||||
if (!flyout || !flyout.isVisible()) {
|
||||
Blockly.navigation.warn_('Trying to insert from the flyout when the flyout does not ' +
|
||||
@@ -248,7 +241,7 @@ Blockly.navigation.insertFromFlyout = function() {
|
||||
Blockly.navigation.warn_('Something went wrong while inserting a block from the flyout.');
|
||||
}
|
||||
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
Blockly.navigation.focusWorkspace_(workspace);
|
||||
workspace.getCursor().setCurNode(Blockly.ASTNode.createTopNode(newBlock));
|
||||
Blockly.navigation.removeMark_();
|
||||
};
|
||||
@@ -569,10 +562,10 @@ Blockly.navigation.insertBlock = function(block, destConnection) {
|
||||
* Disconnect the connection that the cursor is pointing to, and bump blocks.
|
||||
* This is a no-op if the connection cannot be broken or if the cursor is not
|
||||
* pointing to a connection.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The main workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.disconnectBlocks_ = function() {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
Blockly.navigation.disconnectBlocks_ = function(workspace) {
|
||||
var curNode = workspace.getCursor().getCurNode();
|
||||
if (!curNode.isConnection()) {
|
||||
Blockly.navigation.log_('Cannot disconnect blocks when the cursor is not on a connection');
|
||||
@@ -697,7 +690,7 @@ Blockly.navigation.enableKeyboardAccessibility = function() {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
if (!workspace.keyboardAccessibilityMode) {
|
||||
workspace.keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
Blockly.navigation.focusWorkspace_(workspace);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -762,140 +755,15 @@ Blockly.navigation.error_ = function(msg) {
|
||||
/** Handle Key Press */
|
||||
/** ***************** */
|
||||
|
||||
/**
|
||||
* Handler for all the keyboard navigation events.
|
||||
* @param {!KeyboardEvent} e The keyboard event.
|
||||
* @return {boolean} True if the key was handled false otherwise.
|
||||
*/
|
||||
Blockly.navigation.onKeyPress = function(e) {
|
||||
var key = Blockly.user.keyMap.serializeKeyEvent(e);
|
||||
var action = Blockly.user.keyMap.getActionByKeyCode(key);
|
||||
|
||||
if (action) {
|
||||
return Blockly.navigation.onBlocklyAction(action);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decides which actions to handle depending on keyboard navigation and readonly
|
||||
* states.
|
||||
* @param {!Blockly.Action} action The current action.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
*/
|
||||
Blockly.navigation.onBlocklyAction = function(action) {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var readOnly = workspace.options.readOnly;
|
||||
var actionHandled = false;
|
||||
|
||||
if (workspace.keyboardAccessibilityMode) {
|
||||
if (!readOnly) {
|
||||
actionHandled = Blockly.navigation.handleActions_(action);
|
||||
// If in readonly mode only handle valid actions.
|
||||
} else if (Blockly.navigation.READONLY_ACTION_LIST.indexOf(action) > -1) {
|
||||
actionHandled = Blockly.navigation.handleActions_(action);
|
||||
}
|
||||
// If not in accessibility mode only handle turning on keyboard navigation.
|
||||
} else if (action.name === Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
actionHandled = true;
|
||||
}
|
||||
return actionHandled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the action or dispatches to the appropriate action handler.
|
||||
* @param {!Blockly.Action} action The action to handle.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.handleActions_ = function(action) {
|
||||
if (action.name == Blockly.navigation.actionNames.TOOLBOX ||
|
||||
Blockly.navigation.currentState_ == Blockly.navigation.STATE_TOOLBOX) {
|
||||
return Blockly.navigation.toolboxOnAction_(action);
|
||||
} else if (action.name == Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV) {
|
||||
Blockly.navigation.disableKeyboardAccessibility();
|
||||
return true;
|
||||
} if (Blockly.navigation.currentState_ == Blockly.navigation.STATE_WS) {
|
||||
return Blockly.navigation.workspaceOnAction_(action);
|
||||
} else if (Blockly.navigation.currentState_ == Blockly.navigation.STATE_FLYOUT) {
|
||||
return Blockly.navigation.flyoutOnAction_(action);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action for the flyout.
|
||||
* @param {!Blockly.Action} action The action to handle.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.flyoutOnAction_ = function(action) {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
|
||||
if (flyout && flyout.onBlocklyAction(action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.OUT:
|
||||
Blockly.navigation.focusToolbox_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.MARK:
|
||||
Blockly.navigation.insertFromFlyout();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.EXIT:
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action for the toolbox.
|
||||
* @param {!Blockly.Action} action The action to handle.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.toolboxOnAction_ = function(action) {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
var toolbox = workspace.getToolbox();
|
||||
var handled = toolbox && typeof toolbox.onBlocklyAction == 'function' ?
|
||||
toolbox.onBlocklyAction(action) : false;
|
||||
|
||||
if (handled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action.name === Blockly.navigation.actionNames.TOOLBOX) {
|
||||
if (!workspace.getToolbox()) {
|
||||
Blockly.navigation.focusFlyout_();
|
||||
} else {
|
||||
Blockly.navigation.focusToolbox_();
|
||||
}
|
||||
return true;
|
||||
} else if (action.name === Blockly.navigation.actionNames.IN) {
|
||||
Blockly.navigation.focusFlyout_();
|
||||
return true;
|
||||
} else if (action.name === Blockly.navigation.actionNames.EXIT) {
|
||||
Blockly.navigation.focusWorkspace_();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the workspace cursor in the given direction.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace the cursor is on.
|
||||
* @param {number} xDirection -1 to move cursor left. 1 to move cursor right.
|
||||
* @param {number} yDirection -1 to move cursor up. 1 to move cursor down.
|
||||
* @return {boolean} True if the current node is a workspace, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
Blockly.navigation.moveWSCursor_ = function(workspace, xDirection, yDirection) {
|
||||
var cursor = workspace.getCursor();
|
||||
var curNode = workspace.getCursor().getCurNode();
|
||||
|
||||
@@ -912,46 +780,14 @@ Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the given action for the workspace.
|
||||
* @param {!Blockly.Action} action The action to handle.
|
||||
* @return {boolean} True if the action has been handled, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.workspaceOnAction_ = function(action) {
|
||||
var workspace = Blockly.navigation.getNavigationWorkspace();
|
||||
if (workspace.getCursor().onBlocklyAction(action)) {
|
||||
return true;
|
||||
}
|
||||
switch (action.name) {
|
||||
case Blockly.navigation.actionNames.INSERT:
|
||||
Blockly.navigation.modify_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.MARK:
|
||||
Blockly.navigation.handleEnterForWS_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.DISCONNECT:
|
||||
Blockly.navigation.disconnectBlocks_();
|
||||
return true;
|
||||
case Blockly.navigation.actionNames.MOVE_WS_CURSOR_UP:
|
||||
return Blockly.navigation.moveWSCursor_(0, -1);
|
||||
case Blockly.navigation.actionNames.MOVE_WS_CURSOR_DOWN:
|
||||
return Blockly.navigation.moveWSCursor_(0, 1);
|
||||
case Blockly.navigation.actionNames.MOVE_WS_CURSOR_LEFT:
|
||||
return Blockly.navigation.moveWSCursor_(-1, 0);
|
||||
case Blockly.navigation.actionNames.MOVE_WS_CURSOR_RIGHT:
|
||||
return Blockly.navigation.moveWSCursor_(1, 0);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles hitting the enter key on the workspace.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace the enter event
|
||||
* originated from.
|
||||
* @private
|
||||
*/
|
||||
Blockly.navigation.handleEnterForWS_ = function() {
|
||||
var cursor = Blockly.navigation.getNavigationWorkspace().getCursor();
|
||||
Blockly.navigation.handleEnterForWS_ = function(workspace) {
|
||||
var cursor = workspace.getCursor();
|
||||
var curNode = cursor.getCurNode();
|
||||
var nodeType = curNode.getType();
|
||||
if (nodeType == Blockly.ASTNode.types.FIELD) {
|
||||
@@ -966,127 +802,423 @@ Blockly.navigation.handleEnterForWS_ = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/** ******************* */
|
||||
/** Navigation Actions */
|
||||
/** ******************* */
|
||||
/** ***************** */
|
||||
/** Register Items */
|
||||
/** ***************** */
|
||||
|
||||
/**
|
||||
* The previous action.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to go to the previous location when in keyboard navigation
|
||||
* mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_PREVIOUS = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.PREVIOUS, 'Go to the previous location.');
|
||||
Blockly.navigation.registerPrevious = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var previousShortcut = {
|
||||
name: Blockly.navigation.actionNames.PREVIOUS,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode;
|
||||
},
|
||||
callback: function(workspace, e, action) {
|
||||
var toolbox = workspace.getToolbox();
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
return workspace.getCursor().onBlocklyAction(action);
|
||||
case Blockly.navigation.STATE_FLYOUT:
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
return !!(flyout && flyout.onBlocklyAction(action));
|
||||
case Blockly.navigation.STATE_TOOLBOX:
|
||||
return toolbox && typeof toolbox.onBlocklyAction == 'function' ?
|
||||
toolbox.onBlocklyAction(action) :
|
||||
false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(previousShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.W, previousShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The out action.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to go to the out location when in keyboard navigation
|
||||
* mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_OUT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.OUT,
|
||||
'Go to the parent of the current location.');
|
||||
Blockly.navigation.registerOut = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var outShortcut = {
|
||||
name: Blockly.navigation.actionNames.OUT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode;
|
||||
},
|
||||
callback: function(workspace, e, action) {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
return workspace.getCursor().onBlocklyAction(action);
|
||||
case Blockly.navigation.STATE_FLYOUT:
|
||||
Blockly.navigation.focusToolbox_(workspace);
|
||||
return true;
|
||||
case Blockly.navigation.STATE_TOOLBOX:
|
||||
var toolbox = workspace.getToolbox();
|
||||
return toolbox && typeof toolbox.onBlocklyAction == 'function' ?
|
||||
toolbox.onBlocklyAction(action) :
|
||||
false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(outShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.A, outShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The next action.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to go to the next location when in keyboard navigation
|
||||
* mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_NEXT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.NEXT, 'Go to the next location.');
|
||||
Blockly.navigation.registerNext = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var nextShortcut = {
|
||||
name: Blockly.navigation.actionNames.NEXT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode;
|
||||
},
|
||||
callback: function(workspace, e, action) {
|
||||
var toolbox = workspace.getToolbox();
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
return workspace.getCursor().onBlocklyAction(action);
|
||||
case Blockly.navigation.STATE_FLYOUT:
|
||||
var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
|
||||
return !!(flyout && flyout.onBlocklyAction(action));
|
||||
case Blockly.navigation.STATE_TOOLBOX:
|
||||
return toolbox && typeof toolbox.onBlocklyAction == 'function' ?
|
||||
toolbox.onBlocklyAction(action) :
|
||||
false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(nextShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.S, nextShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The in action.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to go to the in location when in keyboard navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_IN = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.IN,
|
||||
'Go to the first child of the current location.');
|
||||
Blockly.navigation.registerIn = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var inShortcut = {
|
||||
name: Blockly.navigation.actionNames.IN,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode;
|
||||
},
|
||||
callback: function(workspace, e, action) {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
return workspace.getCursor().onBlocklyAction(action);
|
||||
case Blockly.navigation.STATE_TOOLBOX:
|
||||
var toolbox = workspace.getToolbox();
|
||||
var isHandled =
|
||||
toolbox && typeof toolbox.onBlocklyAction == 'function' ?
|
||||
toolbox.onBlocklyAction(action) :
|
||||
false;
|
||||
if (!isHandled) {
|
||||
Blockly.navigation.focusFlyout_(workspace);
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(inShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.D, inShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to try to insert a block.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to connect a block to a marked location when in keyboard
|
||||
* navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_INSERT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.INSERT,
|
||||
'Connect the current location to the marked location.');
|
||||
Blockly.navigation.registerInsert = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var insertShortcut = {
|
||||
name: Blockly.navigation.actionNames.INSERT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function() {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
return Blockly.navigation.modify_();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(insertShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.I, insertShortcut.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to mark a location when in keyboard navigation mode. */
|
||||
Blockly.navigation.registerMark = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var markShortcut = {
|
||||
name: Blockly.navigation.actionNames.MARK,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
Blockly.navigation.handleEnterForWS_(workspace);
|
||||
return true;
|
||||
case Blockly.navigation.STATE_FLYOUT:
|
||||
Blockly.navigation.insertFromFlyout(workspace);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(markShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.ENTER, markShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to mark a certain location.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to disconnect two blocks when in keyboard navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_MARK = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.MARK, 'Mark the current location.');
|
||||
Blockly.navigation.registerDisconnect = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var disconnectShortcut = {
|
||||
name: Blockly.navigation.actionNames.DISCONNECT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
Blockly.navigation.disconnectBlocks_(workspace);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(disconnectShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.X, disconnectShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to disconnect a block.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to focus on the toolbox when in keyboard navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_DISCONNECT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.DISCONNECT,
|
||||
'Disconnect the block at the current location from its parent.');
|
||||
Blockly.navigation.registerToolboxFocus = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var focusToolboxShortcut = {
|
||||
name: Blockly.navigation.actionNames.TOOLBOX,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_WS:
|
||||
if (!workspace.getToolbox()) {
|
||||
Blockly.navigation.focusFlyout_(workspace);
|
||||
} else {
|
||||
Blockly.navigation.focusToolbox_(workspace);
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(focusToolboxShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.T, focusToolboxShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to open the toolbox.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to exit the current location and focus on the workspace
|
||||
* when in keyboard navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_TOOLBOX = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.TOOLBOX, 'Open the toolbox.');
|
||||
Blockly.navigation.registerExit = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var exitShortcut = {
|
||||
name: Blockly.navigation.actionNames.EXIT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
switch (Blockly.navigation.currentState_) {
|
||||
case Blockly.navigation.STATE_FLYOUT:
|
||||
Blockly.navigation.focusWorkspace_(workspace);
|
||||
return true;
|
||||
case Blockly.navigation.STATE_TOOLBOX:
|
||||
Blockly.navigation.focusWorkspace_(workspace);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(exitShortcut, true);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.ESC, exitShortcut.name, true);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.E, exitShortcut.name, true);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to turn keyboard navigation on or off. */
|
||||
Blockly.navigation.registerToggleKeyboardNav = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var toggleKeyboardNavShortcut = {
|
||||
name: Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV,
|
||||
callback: function(workspace) {
|
||||
if (workspace.keyboardAccessibilityMode) {
|
||||
Blockly.navigation.disableKeyboardAccessibility();
|
||||
} else {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(toggleKeyboardNavShortcut);
|
||||
var ctrlShiftK = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.K,
|
||||
[Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
ctrlShiftK, toggleKeyboardNavShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to exit the toolbox or flyout.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to move the cursor on the workspace to the left when in
|
||||
* keyboard navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_EXIT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.EXIT,
|
||||
'Close the current modal, such as a toolbox or field editor.');
|
||||
Blockly.navigation.registerWorkspaceMoveLeft = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var wsMoveLeftShortcut = {
|
||||
name: Blockly.navigation.actionNames.MOVE_WS_CURSOR_LEFT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
return Blockly.navigation.moveWSCursor_(workspace, -1, 0);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(wsMoveLeftShortcut);
|
||||
var shiftA = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.A, [Blockly.utils.KeyCodes.SHIFT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
shiftA, wsMoveLeftShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to toggle keyboard navigation mode on and off.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to move the cursor on the workspace to the right when in
|
||||
* keyboard navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_TOGGLE_KEYBOARD_NAV = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.TOGGLE_KEYBOARD_NAV,
|
||||
'Turns on and off keyboard navigation.');
|
||||
Blockly.navigation.registerWorkspaceMoveRight = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var wsMoveRightShortcut = {
|
||||
name: Blockly.navigation.actionNames.MOVE_WS_CURSOR_RIGHT,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
return Blockly.navigation.moveWSCursor_(workspace, 1, 0);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(wsMoveRightShortcut);
|
||||
var shiftD = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.D, [Blockly.utils.KeyCodes.SHIFT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
shiftD, wsMoveRightShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to move the cursor to the left on a workspace.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to move the cursor on the workspace up when in keyboard
|
||||
* navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_LEFT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.MOVE_WS_CURSOR_LEFT,
|
||||
'Move the workspace cursor to the lefts.');
|
||||
Blockly.navigation.registerWorkspaceMoveUp = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var wsMoveUpShortcut = {
|
||||
name: Blockly.navigation.actionNames.MOVE_WS_CURSOR_UP,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
return Blockly.navigation.moveWSCursor_(workspace, 0, -1);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(wsMoveUpShortcut);
|
||||
var shiftW = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.W, [Blockly.utils.KeyCodes.SHIFT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
shiftW, wsMoveUpShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to move the cursor to the right on a workspace.
|
||||
* @type {!Blockly.Action}
|
||||
* Keyboard shortcut to move the cursor on the workspace down when in keyboard
|
||||
* navigation mode.
|
||||
*/
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_RIGHT = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.MOVE_WS_CURSOR_RIGHT,
|
||||
'Move the workspace cursor to the right.');
|
||||
Blockly.navigation.registerWorkspaceMoveDown = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var wsMoveDownShortcut = {
|
||||
name: Blockly.navigation.actionNames.MOVE_WS_CURSOR_DOWN,
|
||||
preconditionFn: function(workspace) {
|
||||
return workspace.keyboardAccessibilityMode && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
return Blockly.navigation.moveWSCursor_(workspace, 0, 1);
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(wsMoveDownShortcut);
|
||||
var shiftW = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.S, [Blockly.utils.KeyCodes.SHIFT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
shiftW, wsMoveDownShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* The action to move the cursor up on a workspace.
|
||||
* @type {!Blockly.Action}
|
||||
* Registers all default keyboard shortcut items for keyboard navigation. This
|
||||
* should be called once per instance of KeyboardShortcutRegistry.
|
||||
* @package
|
||||
*/
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_UP = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.MOVE_WS_CURSOR_UP,
|
||||
'Move the workspace cursor up.');
|
||||
Blockly.navigation.registerNavigationShortcuts = function() {
|
||||
Blockly.navigation.registerIn();
|
||||
Blockly.navigation.registerNext();
|
||||
Blockly.navigation.registerOut();
|
||||
Blockly.navigation.registerPrevious();
|
||||
|
||||
/**
|
||||
* The action to move the cursor down on a workspace.
|
||||
* @type {!Blockly.Action}
|
||||
*/
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_DOWN = new Blockly.Action(
|
||||
Blockly.navigation.actionNames.MOVE_WS_CURSOR_DOWN,
|
||||
'Move the workspace cursor down.');
|
||||
Blockly.navigation.registerWorkspaceMoveDown();
|
||||
Blockly.navigation.registerWorkspaceMoveLeft();
|
||||
Blockly.navigation.registerWorkspaceMoveRight();
|
||||
Blockly.navigation.registerWorkspaceMoveUp();
|
||||
|
||||
|
||||
/**
|
||||
* List of actions that can be performed in read only mode.
|
||||
* @type {!Array.<!Blockly.Action>}
|
||||
*/
|
||||
Blockly.navigation.READONLY_ACTION_LIST = [
|
||||
Blockly.navigation.ACTION_PREVIOUS,
|
||||
Blockly.navigation.ACTION_OUT,
|
||||
Blockly.navigation.ACTION_IN,
|
||||
Blockly.navigation.ACTION_NEXT,
|
||||
Blockly.navigation.ACTION_TOGGLE_KEYBOARD_NAV
|
||||
];
|
||||
Blockly.navigation.registerDisconnect();
|
||||
Blockly.navigation.registerExit();
|
||||
Blockly.navigation.registerInsert();
|
||||
Blockly.navigation.registerMark();
|
||||
Blockly.navigation.registerToggleKeyboardNav();
|
||||
Blockly.navigation.registerToolboxFocus();
|
||||
};
|
||||
|
||||
@@ -15,7 +15,6 @@ 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');
|
||||
@@ -108,8 +107,6 @@ Blockly.Options = function(options) {
|
||||
} else {
|
||||
var oneBasedIndex = !!options['oneBasedIndex'];
|
||||
}
|
||||
var keyMap = options['keyMap'] || Blockly.user.keyMap.createDefaultKeyMap();
|
||||
|
||||
var renderer = options['renderer'] || 'geras';
|
||||
|
||||
var plugins = options['plugins'] || {};
|
||||
@@ -158,8 +155,6 @@ Blockly.Options = function(options) {
|
||||
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} */
|
||||
|
||||
263
core/shortcut_items.js
Normal file
263
core/shortcut_items.js
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Registers default keyboard shortcuts.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @name Blockly.ShortcutItems
|
||||
* @namespace
|
||||
*/
|
||||
goog.provide('Blockly.ShortcutItems');
|
||||
|
||||
goog.require('Blockly.utils.KeyCodes');
|
||||
|
||||
|
||||
/**
|
||||
* Object holding the names of the default shortcut items.
|
||||
* @enum {string}
|
||||
*/
|
||||
Blockly.ShortcutItems.names = {
|
||||
ESCAPE: 'escape',
|
||||
DELETE: 'delete',
|
||||
COPY: 'copy',
|
||||
CUT: 'cut',
|
||||
PASTE: 'paste',
|
||||
UNDO: 'undo',
|
||||
REDO: 'redo'
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to hide chaff on escape. */
|
||||
Blockly.ShortcutItems.registerEscape = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var escapeAction = {
|
||||
name: Blockly.ShortcutItems.names.ESCAPE,
|
||||
preconditionFn: function(workspace) {
|
||||
return !workspace.options.readOnly;
|
||||
},
|
||||
callback: function() {
|
||||
Blockly.hideChaff();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Blockly.ShortcutRegistry.registry.register(escapeAction);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.ESC, escapeAction.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to delete a block on delete or backspace */
|
||||
Blockly.ShortcutItems.registerDelete = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var deleteShortcut = {
|
||||
name: Blockly.ShortcutItems.names.DELETE,
|
||||
preconditionFn: function(workspace) {
|
||||
return !workspace.options.readOnly &&
|
||||
Blockly.selected &&
|
||||
Blockly.selected.isDeletable();
|
||||
},
|
||||
callback: function(workspace, e) {
|
||||
// Delete or backspace.
|
||||
// Stop the browser from going back to the previous page.
|
||||
// Do this first to prevent an error in the delete code from resulting in
|
||||
// data loss.
|
||||
e.preventDefault();
|
||||
// Don't delete while dragging. Jeez.
|
||||
if (Blockly.Gesture.inProgress()) {
|
||||
return false;
|
||||
}
|
||||
Blockly.deleteBlock(/** @type {!Blockly.BlockSvg} */ (Blockly.selected));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Blockly.ShortcutRegistry.registry.register(deleteShortcut);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.DELETE, deleteShortcut.name);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(
|
||||
Blockly.utils.KeyCodes.BACKSPACE, deleteShortcut.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to copy a block on ctrl+c, cmd+c, or alt+c. */
|
||||
Blockly.ShortcutItems.registerCopy = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var copyShortcut = {
|
||||
name: Blockly.ShortcutItems.names.COPY,
|
||||
preconditionFn: function(workspace) {
|
||||
return !workspace.options.readOnly &&
|
||||
!Blockly.Gesture.inProgress() &&
|
||||
Blockly.selected &&
|
||||
Blockly.selected.isDeletable() &&
|
||||
Blockly.selected.isMovable();
|
||||
},
|
||||
callback: function() {
|
||||
Blockly.hideChaff();
|
||||
Blockly.copy(/** @type {!Blockly.ICopyable} */ (Blockly.selected));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Blockly.ShortcutRegistry.registry.register(copyShortcut);
|
||||
|
||||
var ctrlC = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.C, [Blockly.utils.KeyCodes.CTRL]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlC, copyShortcut.name);
|
||||
|
||||
var altC = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.C, [Blockly.utils.KeyCodes.ALT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(altC, copyShortcut.name);
|
||||
|
||||
var metaC = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.C, [Blockly.utils.KeyCodes.META]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(metaC, copyShortcut.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to copy and delete a block on ctrl+x, cmd+x, or alt+x. */
|
||||
Blockly.ShortcutItems.registerCut = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var cutShortcut = {
|
||||
name: Blockly.ShortcutItems.names.CUT,
|
||||
preconditionFn: function(workspace) {
|
||||
return !workspace.options.readOnly &&
|
||||
!Blockly.Gesture.inProgress() &&
|
||||
Blockly.selected &&
|
||||
Blockly.selected.isDeletable() &&
|
||||
Blockly.selected.isMovable() &&
|
||||
!Blockly.selected.workspace.isFlyout;
|
||||
},
|
||||
callback: function() {
|
||||
Blockly.copy(/** @type {!Blockly.ICopyable} */ (Blockly.selected));
|
||||
Blockly.deleteBlock(/** @type {!Blockly.BlockSvg} */ (Blockly.selected));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(cutShortcut);
|
||||
|
||||
var ctrlX = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.X, [Blockly.utils.KeyCodes.CTRL]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlX, cutShortcut.name);
|
||||
|
||||
var altX = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.X, [Blockly.utils.KeyCodes.ALT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(altX, cutShortcut.name);
|
||||
|
||||
var metaX = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.X, [Blockly.utils.KeyCodes.META]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(metaX, cutShortcut.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to paste a block on ctrl+v, cmd+v, or alt+v. */
|
||||
Blockly.ShortcutItems.registerPaste = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var pasteShortcut = {
|
||||
name: Blockly.ShortcutItems.names.PASTE,
|
||||
preconditionFn: function(workspace) {
|
||||
return !workspace.options.readOnly && !Blockly.Gesture.inProgress();
|
||||
},
|
||||
callback: function() {
|
||||
return Blockly.paste();
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.ShortcutRegistry.registry.register(pasteShortcut);
|
||||
|
||||
var ctrlV = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.V, [Blockly.utils.KeyCodes.CTRL]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlV, pasteShortcut.name);
|
||||
|
||||
var altV = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.V, [Blockly.utils.KeyCodes.ALT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(altV, pasteShortcut.name);
|
||||
|
||||
var metaV = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.V, [Blockly.utils.KeyCodes.META]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(metaV, pasteShortcut.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to undo the previous action on ctrl+z, cmd+z, or alt+z. */
|
||||
Blockly.ShortcutItems.registerUndo = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var undoShortcut = {
|
||||
name: Blockly.ShortcutItems.names.UNDO,
|
||||
preconditionFn: function(workspace) {
|
||||
return !workspace.options.readOnly &&
|
||||
!Blockly.Gesture.inProgress();
|
||||
},
|
||||
callback: function(workspace) {
|
||||
// 'z' for undo 'Z' is for redo.
|
||||
Blockly.hideChaff();
|
||||
workspace.undo(false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Blockly.ShortcutRegistry.registry.register(undoShortcut);
|
||||
|
||||
var ctrlZ = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Z, [Blockly.utils.KeyCodes.CTRL]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlZ, undoShortcut.name);
|
||||
|
||||
var altZ = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Z, [Blockly.utils.KeyCodes.ALT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(altZ, undoShortcut.name);
|
||||
|
||||
var metaZ = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Z, [Blockly.utils.KeyCodes.META]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(metaZ, undoShortcut.name);
|
||||
};
|
||||
|
||||
/** Keyboard shortcut to redo the previous action on ctrl+shift+z, cmd+shift+z, or alt+shift+z. */
|
||||
Blockly.ShortcutItems.registerRedo = function() {
|
||||
/** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */
|
||||
var redoShortcut = {
|
||||
name: Blockly.ShortcutItems.names.REDO,
|
||||
preconditionFn: function(workspace) {
|
||||
return !Blockly.Gesture.inProgress() && !workspace.options.readOnly;
|
||||
},
|
||||
callback: function(workspace) {
|
||||
// 'z' for undo 'Z' is for redo.
|
||||
Blockly.hideChaff();
|
||||
workspace.undo(true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Blockly.ShortcutRegistry.registry.register(redoShortcut);
|
||||
|
||||
var ctrlShiftZ = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Z, [Blockly.utils.KeyCodes.SHIFT,
|
||||
Blockly.utils.KeyCodes.CTRL]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlShiftZ, redoShortcut.name);
|
||||
|
||||
var altShiftZ = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Z, [Blockly.utils.KeyCodes.SHIFT,
|
||||
Blockly.utils.KeyCodes.ALT]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(altShiftZ, redoShortcut.name);
|
||||
|
||||
var metaShiftZ = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Z, [Blockly.utils.KeyCodes.SHIFT,
|
||||
Blockly.utils.KeyCodes.META]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(metaShiftZ, redoShortcut.name);
|
||||
|
||||
// Ctrl-y is redo in Windows. Command-y is never valid on Macs.
|
||||
var ctrlY = Blockly.ShortcutRegistry.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.Y, [Blockly.utils.KeyCodes.CTRL]);
|
||||
Blockly.ShortcutRegistry.registry.addKeyMapping(ctrlY, redoShortcut.name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers all default keyboard shortcut item. This should be called once per instance of
|
||||
* KeyboardShortcutRegistry.
|
||||
* @package
|
||||
*/
|
||||
Blockly.ShortcutItems.registerDefaultShortcuts = function() {
|
||||
Blockly.ShortcutItems.registerEscape();
|
||||
Blockly.ShortcutItems.registerDelete();
|
||||
Blockly.ShortcutItems.registerCopy();
|
||||
Blockly.ShortcutItems.registerCut();
|
||||
Blockly.ShortcutItems.registerPaste();
|
||||
Blockly.ShortcutItems.registerUndo();
|
||||
Blockly.ShortcutItems.registerRedo();
|
||||
};
|
||||
347
core/shortcut_registry.js
Normal file
347
core/shortcut_registry.js
Normal file
@@ -0,0 +1,347 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The namespace used to keep track of keyboard shortcuts and the
|
||||
* key codes used to execute those shortcuts.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.ShortcutRegistry');
|
||||
|
||||
goog.require('Blockly.navigation');
|
||||
goog.require('Blockly.ShortcutItems');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
|
||||
/**
|
||||
* Class for the registry of keyboard shortcuts. This is intended to be a
|
||||
* singleton. You should not create a new instance, and only access this class
|
||||
* from Blockly.ShortcutRegistry.registry.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ShortcutRegistry = function() {
|
||||
// Singleton instance should be registered once.
|
||||
Blockly.ShortcutRegistry.registry = this;
|
||||
|
||||
/**
|
||||
* Registry of all keyboard shortcuts, keyed by name of shortcut.
|
||||
* @type {!Object<string, !Blockly.ShortcutRegistry.KeyboardShortcut>}
|
||||
* @private
|
||||
*/
|
||||
this.registry_ = Object.create(null);
|
||||
|
||||
/**
|
||||
* Map of key codes to an array of shortcut names.
|
||||
* @type {!Object<string, !Array<string>>}
|
||||
* @private
|
||||
*/
|
||||
this.keyMap_ = Object.create(null);
|
||||
|
||||
Blockly.ShortcutItems.registerDefaultShortcuts();
|
||||
Blockly.navigation.registerNavigationShortcuts();
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum of valid modifiers.
|
||||
* @enum {!Blockly.utils.KeyCodes<number>}
|
||||
*/
|
||||
Blockly.ShortcutRegistry.modifierKeys = {
|
||||
Shift: Blockly.utils.KeyCodes.SHIFT,
|
||||
Control: Blockly.utils.KeyCodes.CTRL,
|
||||
Alt: Blockly.utils.KeyCodes.ALT,
|
||||
Meta: Blockly.utils.KeyCodes.META
|
||||
};
|
||||
|
||||
/**
|
||||
* A keyboard shortcut.
|
||||
* @typedef {{
|
||||
* callback: ((function(!Blockly.Workspace, Event,
|
||||
* !Blockly.ShortcutRegistry.KeyboardShortcut):boolean)|undefined),
|
||||
* name: string,
|
||||
* preconditionFn: ((function(!Blockly.Workspace):boolean)|undefined),
|
||||
* metadata: (Object|undefined)
|
||||
* }}
|
||||
*/
|
||||
Blockly.ShortcutRegistry.KeyboardShortcut;
|
||||
|
||||
/**
|
||||
* Registers a keyboard shortcut.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} shortcut The
|
||||
* shortcut for this key code.
|
||||
* @param {boolean=} opt_allowOverrides True to prevent a warning when
|
||||
* overriding an already registered item.
|
||||
* @throws {Error} if a shortcut with the same name already exists.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.register = function(
|
||||
shortcut, opt_allowOverrides) {
|
||||
var registeredShortcut = this.registry_[shortcut.name];
|
||||
if (registeredShortcut && !opt_allowOverrides) {
|
||||
throw new Error(
|
||||
'Shortcut with name "' + shortcut.name + '" already exists.');
|
||||
}
|
||||
this.registry_[shortcut.name] = shortcut;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregisters a keyboard shortcut registered with the given key code. This will
|
||||
* also remove any key mappings that reference this shortcut.
|
||||
* @param {string} shortcutName The name of the shortcut to unregister.
|
||||
* @return {boolean} True if an item was unregistered, false otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.unregister = function(shortcutName) {
|
||||
var shortcut = this.registry_[shortcutName];
|
||||
|
||||
if (!shortcut) {
|
||||
console.warn(
|
||||
'Keyboard shortcut with name "' + shortcutName + '" not found.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove all key mappings with this shortcut.
|
||||
for (var keyCode in this.keyMap_) {
|
||||
this.removeKeyMapping(keyCode, shortcutName, true);
|
||||
}
|
||||
|
||||
delete this.registry_[shortcutName];
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a mapping between a keycode and a keyboard shortcut.
|
||||
* @param {string} keyCode The key code for the keyboard shortcut. If
|
||||
* registering a key code with a modifier (ex: ctrl+c) use
|
||||
* Blockly.ShortcutRegistry.registry.createSerializedKey;
|
||||
* @param {string} shortcutName The name of the shortcut to execute when the
|
||||
* given keycode is pressed.
|
||||
* @param {boolean=} opt_allowCollision True to prevent an error when adding a
|
||||
* shortcut to a key that is already mapped to a shortcut.
|
||||
* @throws {Error} if the given key code is already mapped to a shortcut.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.addKeyMapping = function(
|
||||
keyCode, shortcutName, opt_allowCollision) {
|
||||
var shortcutNames = this.keyMap_[keyCode];
|
||||
if (shortcutNames && !opt_allowCollision) {
|
||||
throw new Error(
|
||||
'Shortcut with name "' + shortcutName + '" collides with shortcuts ' +
|
||||
shortcutNames.toString());
|
||||
} else if (shortcutNames && opt_allowCollision) {
|
||||
shortcutNames.unshift(shortcutName);
|
||||
} else {
|
||||
this.keyMap_[keyCode] = [shortcutName];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a mapping between a keycode and a keyboard shortcut.
|
||||
* @param {string} keyCode The key code for the keyboard shortcut. If
|
||||
* registering a key code with a modifier (ex: ctrl+c) use
|
||||
* Blockly.ShortcutRegistry.registry.createSerializedKey;
|
||||
* @param {string} shortcutName The name of the shortcut to execute when the
|
||||
* given keycode is pressed.
|
||||
* @param {boolean=} opt_quiet True to not console warn when there is no
|
||||
* shortcut to remove.
|
||||
* @return {boolean} True if a key mapping was removed, false otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.removeKeyMapping = function(
|
||||
keyCode, shortcutName, opt_quiet) {
|
||||
var shortcutNames = this.keyMap_[keyCode];
|
||||
|
||||
if (!shortcutNames && !opt_quiet) {
|
||||
console.warn(
|
||||
'No keyboard shortcut with name "' + shortcutName +
|
||||
'" registered with key code "' + keyCode + '"');
|
||||
return false;
|
||||
}
|
||||
|
||||
var shortcutIdx = shortcutNames.indexOf(shortcutName);
|
||||
if (shortcutIdx > -1) {
|
||||
shortcutNames.splice(shortcutIdx, 1);
|
||||
if (shortcutNames.length == 0) {
|
||||
delete this.keyMap_[keyCode];
|
||||
}
|
||||
return true;
|
||||
} else if (!opt_quiet) {
|
||||
console.warn(
|
||||
'No keyboard shortcut with name "' + shortcutName +
|
||||
'" registered with key code "' + keyCode + '"');
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the key map. Setting the key map will override any default key mappings.
|
||||
* @param {!Object<string, !Array<string>>} keyMap The object with key code to
|
||||
* shortcut names.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.setKeyMap = function(keyMap) {
|
||||
this.keyMap_ = keyMap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current key map.
|
||||
* @return {!Object<string,!Array<!Blockly.ShortcutRegistry.KeyboardShortcut>>}
|
||||
* The object holding key codes to Blockly.ShortcutRegistry.KeyboardShortcut.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.getKeyMap = function() {
|
||||
return Blockly.utils.object.deepMerge(Object.create(null), this.keyMap_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the registry of keyboard shortcuts.
|
||||
* @return {!Object<string, !Blockly.ShortcutRegistry.KeyboardShortcut>}
|
||||
* The registry of keyboard shortcuts.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.getRegistry = function() {
|
||||
return Blockly.utils.object.deepMerge(Object.create(null), this.registry_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles key down events.
|
||||
* @param {!Blockly.Workspace} workspace The main workspace where the event was
|
||||
* captured.
|
||||
* @param {!Event} e The key down event.
|
||||
* @return {boolean} True if the event was handled, false otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.onKeyDown = function(workspace, e) {
|
||||
var key = this.serializeKeyEvent_(e);
|
||||
var shortcutNames = this.getKeyboardShortcuts(key);
|
||||
if (!shortcutNames) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0, shortcutName; (shortcutName = shortcutNames[i]); i++) {
|
||||
var shortcut = this.registry_[shortcutName];
|
||||
if (!shortcut.preconditionFn || shortcut.preconditionFn(workspace)) {
|
||||
// If the key has been handled, stop processing shortcuts.
|
||||
if (shortcut.callback && shortcut.callback(workspace, e, shortcut)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the shortcuts registered to the given key code.
|
||||
* @param {string} keyCode The serialized key code.
|
||||
* @return {!Array<string>|undefined} The list of shortcuts to call when the
|
||||
* given keyCode is used. Undefined if no shortcuts exist.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.getKeyboardShortcuts = function(
|
||||
keyCode) {
|
||||
return this.keyMap_[keyCode] || [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the serialized key codes that the shortcut with the given name is
|
||||
* registered under.
|
||||
* @param {string} shortcutName The name of the shortcut.
|
||||
* @return {!Array<string>} An array with all the key codes the shortcut is
|
||||
* registered under.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.getKeyCodeByShortcutName = function(
|
||||
shortcutName) {
|
||||
var keys = [];
|
||||
for (var keyCode in this.keyMap_) {
|
||||
var shortcuts = this.keyMap_[keyCode];
|
||||
var shortcutIdx = shortcuts.indexOf(shortcutName);
|
||||
if (shortcutIdx > -1) {
|
||||
keys.push(keyCode);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serializes a key event.
|
||||
* @param {!Event} e A key down event.
|
||||
* @return {string} The serialized key code for the given event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.serializeKeyEvent_ = function(e) {
|
||||
var serializedKey = '';
|
||||
for (var modifier in Blockly.ShortcutRegistry.modifierKeys) {
|
||||
if (e.getModifierState(modifier)) {
|
||||
if (serializedKey != '') {
|
||||
serializedKey += '+';
|
||||
}
|
||||
serializedKey += modifier;
|
||||
}
|
||||
}
|
||||
if (serializedKey != '' && e.keyCode) {
|
||||
serializedKey = serializedKey + '+' + e.keyCode;
|
||||
} else if (e.keyCode) {
|
||||
serializedKey = e.keyCode.toString();
|
||||
}
|
||||
return serializedKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether any of the given modifiers are not valid.
|
||||
* @param {!Array<string>} modifiers List of modifiers to be used with the key.
|
||||
* @throws {Error} if the modifier is not in the valid modifiers list.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.checkModifiers_ = function(
|
||||
modifiers) {
|
||||
var validModifiers = Blockly.utils.object.values(
|
||||
Blockly.ShortcutRegistry.modifierKeys);
|
||||
for (var i = 0, modifier; (modifier = modifiers[i]); i++) {
|
||||
if (validModifiers.indexOf(modifier) < 0) {
|
||||
throw new Error(modifier + ' is not a valid modifier key.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the serialized key code that will be used in the key map.
|
||||
* @param {number} keyCode Number code representing the key.
|
||||
* @param {?Array.<string>} modifiers List of modifier key codes to be used with
|
||||
* the key. All valid modifiers can be found in the
|
||||
* Blockly.ShortcutRegistry.modifierKeys.
|
||||
* @return {string} The serialized key code for the given modifiers and key.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ShortcutRegistry.prototype.createSerializedKey = function(
|
||||
keyCode, modifiers) {
|
||||
var serializedKey = '';
|
||||
|
||||
if (modifiers) {
|
||||
this.checkModifiers_(modifiers);
|
||||
for (var modifier in Blockly.ShortcutRegistry.modifierKeys) {
|
||||
var modifierKeyCode =
|
||||
Blockly.ShortcutRegistry.modifierKeys[modifier];
|
||||
if (modifiers.indexOf(modifierKeyCode) > -1) {
|
||||
if (serializedKey != '') {
|
||||
serializedKey += '+';
|
||||
}
|
||||
serializedKey += modifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serializedKey != '' && keyCode) {
|
||||
serializedKey = serializedKey + '+' + keyCode;
|
||||
} else if (keyCode) {
|
||||
serializedKey = keyCode.toString();
|
||||
}
|
||||
return serializedKey;
|
||||
};
|
||||
|
||||
// Creates and assigns the singleton instance.
|
||||
new Blockly.ShortcutRegistry();
|
||||
@@ -26,7 +26,6 @@ goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.Rect');
|
||||
goog.require('Blockly.utils.toolbox');
|
||||
|
||||
goog.requireType('Blockly.Action');
|
||||
goog.requireType('Blockly.IBlocklyActionable');
|
||||
goog.requireType('Blockly.ICollapsibleToolboxItem');
|
||||
goog.requireType('Blockly.IDeleteArea');
|
||||
@@ -35,6 +34,7 @@ goog.requireType('Blockly.ISelectableToolboxItem');
|
||||
goog.requireType('Blockly.IStyleable');
|
||||
goog.requireType('Blockly.IToolbox');
|
||||
goog.requireType('Blockly.IToolboxItem');
|
||||
goog.requireType('Blockly.ShortcutRegistry');
|
||||
goog.requireType('Blockly.WorkspaceSvg');
|
||||
|
||||
|
||||
@@ -816,7 +816,7 @@ Blockly.Toolbox.prototype.fireSelectEvent_ = function(oldItem, newItem) {
|
||||
/**
|
||||
* Handles the given Blockly action on a toolbox.
|
||||
* This is only triggered when keyboard accessibility mode is enabled.
|
||||
* @param {!Blockly.Action} action The action to be handled.
|
||||
* @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
|
||||
* @return {boolean} True if the field handled the action, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
|
||||
@@ -1,570 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Keyboard Navigation</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<script src="line_cursor.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
.wrapper {
|
||||
display: flex;
|
||||
}
|
||||
#keyboard_nav {
|
||||
background-color: #ededed;
|
||||
border: 1px solid black;
|
||||
padding: 1em;
|
||||
}
|
||||
#keyboard_announce {
|
||||
font-size: 1.5em;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
#keyboard_mappings {
|
||||
font-size: 1.3em;
|
||||
font-weight: 400;
|
||||
}
|
||||
label {
|
||||
margin-right: .5em;
|
||||
min-width: 100px;
|
||||
}
|
||||
div[data-actionname] {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
select {
|
||||
font-size: .8em;
|
||||
}
|
||||
</style>
|
||||
<meta http-equiv="Refresh" content="0; url=https://github.com/google/blockly-samples">
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Keyboard Navigation</h1>
|
||||
|
||||
<p>Keyboard Navigation is our first step towards an accessible Blockly.<br />
|
||||
For more information on how the default keyboard navigation works please see
|
||||
the <a href="https://developers.google.com/blockly/guides/configure/web/keyboard-nav">documentation</a>.
|
||||
<br />
|
||||
<br />
|
||||
<b>Pre Order Traversal</b><br />
|
||||
Feel free to just play around in accessibility mode or hit the button below to see the demo.
|
||||
The demo uses <a href="https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR)">preorder tree traversal</a>
|
||||
as an alternative way to navigate the blocks,
|
||||
connections, and fields on the workspace.<br /><br />
|
||||
|
||||
<b>Cursors</b><br />
|
||||
The cursor controls how the user navigates the blocks, inputs, fields and connections on a workspace.
|
||||
This demo shows three different cursors:<br />
|
||||
<b>Default Cursor:</b> Explained in <a href="https://developers.google.com/blockly/guides/configure/web/keyboard-nav">documentation</a>.<br />
|
||||
<b>Basic Cursor:</b> Uses pre order traversal to allow users to navigate
|
||||
through everything using only the previous and next command.<br />
|
||||
<b>Line Cursor:</b> We tried to make this cursor mimic a text editor. Navigating
|
||||
up and down will take the cursor to the next and previous "line" of code.
|
||||
Navigating in and out will move the cursor through all the fields and inputs
|
||||
in that "line" of code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="accessibilityModeCheck">Enable Accessibility Mode:</label>
|
||||
<input type="checkbox" onclick="toggleAccessibilityMode(this.checked)" id="accessibilityModeCheck">
|
||||
<select id="cursorChanger" name="cursor" onchange="changeCursor(this.value)">
|
||||
<option value="default">Default Cursor</option>
|
||||
<option value="basic">Basic Cursor</option>
|
||||
<option value="line">Line Cursor</option>
|
||||
</select>
|
||||
|
||||
<button onclick="preOrderDemo()">Start Pre-order Demo</button>
|
||||
<button onclick="stopDemo()">Stop Pre-order Demo</button>
|
||||
<label for="displayKeyMappings">Open Key Mappings:</label>
|
||||
<input type="checkbox" onclick="toggleDisplayKeyMappings(this.checked)" id="displayKeyMappings">
|
||||
</p>
|
||||
|
||||
<div class="wrapper">
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
<div id="keyboard_nav" style="display:none">
|
||||
<p id="keyboard_announce" aria-live="assertive">Set key mappings below</p>
|
||||
<form id="keyboard_mappings"></form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text" colour="%{BKY_TEXTS_HUE}">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="startBlocks" style="display: none">
|
||||
<variables>
|
||||
<variable id="~GNXm@Z(wclI]t3zTf.g">list</variable>
|
||||
<variable id="8]s[S+Gy+%k7HoFup])m">item</variable>
|
||||
</variables>
|
||||
<block type="controls_if" x="37" y="162">
|
||||
<value name="IF0">
|
||||
<block type="logic_compare">
|
||||
<field name="OP">EQ</field>
|
||||
<value name="A">
|
||||
<block type="math_arithmetic">
|
||||
<field name="OP">ADD</field>
|
||||
<value name="A">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="B">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_single">
|
||||
<field name="OP">ROOT</field>
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">9</field>
|
||||
</shadow>
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO0">
|
||||
<block type="lists_setIndex">
|
||||
<mutation at="true"></mutation>
|
||||
<field name="MODE">SET</field>
|
||||
<field name="WHERE">FROM_START</field>
|
||||
<value name="LIST">
|
||||
<block type="variables_get">
|
||||
<field name="VAR" id="~GNXm@Z(wclI]t3zTf.g">list</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_append">
|
||||
<field name="VAR" id="8]s[S+Gy+%k7HoFup])m">item</field>
|
||||
<value name="TEXT">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
<next>
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
demoWorkspace);
|
||||
var timeout;
|
||||
|
||||
var actions = [
|
||||
Blockly.navigation.ACTION_PREVIOUS,
|
||||
Blockly.navigation.ACTION_OUT,
|
||||
Blockly.navigation.ACTION_NEXT,
|
||||
Blockly.navigation.ACTION_IN,
|
||||
Blockly.navigation.ACTION_INSERT,
|
||||
Blockly.navigation.ACTION_MARK,
|
||||
Blockly.navigation.ACTION_DISCONNECT,
|
||||
Blockly.navigation.ACTION_TOOLBOX,
|
||||
Blockly.navigation.ACTION_EXIT,
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_UP,
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_LEFT,
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_DOWN,
|
||||
Blockly.navigation.ACTION_MOVE_WS_CURSOR_RIGHT
|
||||
];
|
||||
createKeyMappingList(actions);
|
||||
|
||||
/**
|
||||
* Shows the next node in the tree traversal every second.
|
||||
* @package
|
||||
*/
|
||||
function demo() {
|
||||
var doNext = function() {
|
||||
var markerManager = Blockly.getMainWorkspace().getMarkerManager();
|
||||
var nextNode = markerManager.getCursor().next();
|
||||
if (nextNode) {
|
||||
timeout = setTimeout(doNext, 1000);
|
||||
}
|
||||
}
|
||||
doNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the running demo.
|
||||
* @package
|
||||
*/
|
||||
function stopDemo() {
|
||||
clearTimeout(timeout);
|
||||
document.getElementById('accessibilityModeCheck').disabled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets up accessibility mode and change the cursor to basic cursor so that
|
||||
* the demo can successfully run.
|
||||
* @package
|
||||
*/
|
||||
function preOrderDemo() {
|
||||
changeCursor('basic');
|
||||
document.getElementById('accessibilityModeCheck').disabled = true;
|
||||
setTimeout(demo, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on/off accessibility mode depending on the state.
|
||||
* @param {boolean} state True to turn on accessibility mode, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
function toggleAccessibilityMode(state) {
|
||||
if (state) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
} else {
|
||||
Blockly.navigation.disableKeyboardAccessibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the type of the cursor and set to the location of the old cursor.
|
||||
* Changing the cursor changes how a user navigates the blocks on the workspace.
|
||||
* @param {string} cursorType The type of the cursor.
|
||||
* @package
|
||||
*/
|
||||
function changeCursor(cursorType) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
document.getElementById('accessibilityModeCheck').checked = true;
|
||||
document.getElementById('cursorChanger').value = cursorType;
|
||||
var markerManager = Blockly.getMainWorkspace().getMarkerManager();
|
||||
var oldCurNode = markerManager.getCursor().getCurNode();
|
||||
if (cursorType === "basic") {
|
||||
Blockly.ASTNode.NAVIGATE_ALL_FIELDS = false;
|
||||
markerManager.setCursor(new Blockly.BasicCursor());
|
||||
} else if (cursorType === "line") {
|
||||
Blockly.ASTNode.NAVIGATE_ALL_FIELDS = true;
|
||||
markerManager.setCursor(new Blockly.LineCursor());
|
||||
} else {
|
||||
Blockly.ASTNode.NAVIGATE_ALL_FIELDS = false;
|
||||
markerManager.setCursor(new Blockly.Cursor());
|
||||
}
|
||||
if (oldCurNode) {
|
||||
markerManager.getCursor().setCurNode(oldCurNode);
|
||||
}
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
// Start key mapping demo functions
|
||||
|
||||
/**
|
||||
* Save the current key map in session storage.
|
||||
* @package
|
||||
*/
|
||||
function saveKeyMap() {
|
||||
var currentMap = Blockly.user.keyMap.getKeyMap();
|
||||
if (sessionStorage) {
|
||||
sessionStorage.setItem('keyMap', JSON.stringify(currentMap));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key map to the map from session storage.
|
||||
* @package
|
||||
*/
|
||||
function restoreKeyMap() {
|
||||
var defaultMap = Blockly.user.keyMap.getKeyMap();
|
||||
var stringifiedMap = sessionStorage.getItem('keyMap');
|
||||
var restoredMap = {};
|
||||
if (sessionStorage && stringifiedMap) {
|
||||
var keyMap = JSON.parse(stringifiedMap);
|
||||
var keys = Object.keys(keyMap);
|
||||
for (var i = 0, key; key = keys[i]; i++) {
|
||||
restoredMap[key] = Object.assign(new Blockly.Action, keyMap[key]);
|
||||
}
|
||||
Blockly.user.keyMap.setKeyMap(restoredMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the three dropdowns create the serialized key that will be stored
|
||||
* in the key map.
|
||||
* @param {Array.<Element>} selectDivs The three dropdown divs that display
|
||||
* the key combination.
|
||||
* @package
|
||||
*/
|
||||
function serializeKey(selectDivs) {
|
||||
var modifiers = Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys);
|
||||
var newModifiers = [];
|
||||
var newKeyCode = '';
|
||||
var keyValue = selectDivs[2].value;
|
||||
|
||||
// Get the new modifiers from the first two dropdowns.
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var selectDiv = selectDivs[i];
|
||||
var key = selectDiv.value;
|
||||
if (key !== 'None') {
|
||||
newModifiers.push(key);
|
||||
}
|
||||
}
|
||||
// Get the key code from the last dropdown.
|
||||
if (keyValue !== 'None') {
|
||||
if (keyValue === 'Escape') {
|
||||
newKeyCode = Blockly.utils.KeyCodes.ESC;
|
||||
} else if (keyValue === 'Enter') {
|
||||
newKeyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
} else {
|
||||
newKeyCode = keyValue.toUpperCase().charCodeAt(0);
|
||||
}
|
||||
}
|
||||
return Blockly.user.keyMap.createSerializedKey(newKeyCode, newModifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all dropdowns for that action to none.
|
||||
* We clear dropdowns when a user chooses the same key combination for a
|
||||
* second action.
|
||||
* @param {Blockly.Action} action The action that we want to clear the
|
||||
* dropdowns for.
|
||||
* @package
|
||||
*/
|
||||
function clearDropdown(action) {
|
||||
var actionDiv = document.querySelectorAll('[data-actionname='+ action.name +']')[0];
|
||||
var selectDivs = actionDiv.getElementsByTagName('select');
|
||||
for (var i = 0, selectDiv; selectDiv = selectDivs[i]; i++) {
|
||||
selectDiv.value = 'None';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the three dropdowns create a human readable string so the screen reader
|
||||
* can read it out.
|
||||
* @param {Array.<Element>} selectDivs The three dropdown divs that display
|
||||
* the key combination.
|
||||
* @package
|
||||
*/
|
||||
function getReadableKey(selectDivs) {
|
||||
var readableKey = '';
|
||||
|
||||
for (var i = 0, selectDiv; selectDiv = selectDivs[i]; i++) {
|
||||
if (selectDiv.value !== 'None') {
|
||||
readableKey += selectDiv.value + ' ';
|
||||
}
|
||||
}
|
||||
return readableKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the key in the key map when the user selects a new value in one of the
|
||||
* dropdowns.
|
||||
* @param {Event} e The event dispatched from changing a dropdown.
|
||||
* @package
|
||||
*/
|
||||
function updateKey(e) {
|
||||
var keyboardAnnouncerText = '';
|
||||
var actionDiv = e.srcElement.parentElement;
|
||||
var action = actionDiv.action;
|
||||
var selectDivs = actionDiv.getElementsByTagName('select');
|
||||
var key = serializeKey(selectDivs);
|
||||
var oldAction = Blockly.user.keyMap.getActionByKeyCode(key);
|
||||
|
||||
if (oldAction) {
|
||||
keyboardAnnouncerText += oldAction.name + ' action key was overwritten. \n';
|
||||
clearDropdown(oldAction);
|
||||
}
|
||||
keyboardAnnouncerText += action.name + ' key was set to ' + getReadableKey(selectDivs);
|
||||
document.getElementById('keyboard_announce').innerText = keyboardAnnouncerText;
|
||||
Blockly.user.keyMap.setActionForKey(key, action);
|
||||
saveKeyMap();
|
||||
document.activeElement.blur();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key to be the correct value from the key map.
|
||||
* @param {string} actionKey The serialized key for a given action.
|
||||
* @param {Element} keyDropdown The dropdown that displays the primary key.
|
||||
* @package
|
||||
*/
|
||||
function setKeyDropdown(actionKey, keyDropdown) {
|
||||
// Strip off any modifier to just get the key code.
|
||||
var keyCode = actionKey.match(/\d+/)[0];
|
||||
var keyValue = String.fromCharCode(keyCode);
|
||||
if (parseInt(keyCode) === Blockly.utils.KeyCodes.ESC) {
|
||||
keyValue = 'Escape';
|
||||
} else if (parseInt(keyCode) === Blockly.utils.KeyCodes.ENTER) {
|
||||
keyValue = 'Enter';
|
||||
}
|
||||
keyDropdown.value = keyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the modifiers to be the correct value from the key map.
|
||||
* @param {string} actionKey The key code holding the modifiers and key.
|
||||
* @param {Array.<Element>} modifierDropdowns A list of dropdowns for
|
||||
* the modifier values.
|
||||
* @package
|
||||
*/
|
||||
function setModifiers(actionKey, modifierDropdowns) {
|
||||
var modifiers = Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys);
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var modifierDropdown = modifierDropdowns[i];
|
||||
for (var j = 0, modifier; modifier = modifiers[j]; j++) {
|
||||
if (actionKey.indexOf(modifier) > -1) {
|
||||
modifierDropdown.value = modifier;
|
||||
actionKey = actionKey.replace(modifier, '');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dropdowns to display the correct combination of modifiers and
|
||||
* keys for the action key.
|
||||
* @param {Blockly.Action} action The Blockly action.
|
||||
* @param {Element} actionDiv The div holding the dropdowns and label for the
|
||||
* given action.
|
||||
* @param {string} actionKey The key corresponding to the given action.
|
||||
* @package
|
||||
*/
|
||||
function setDropdowns(action, actionDiv, actionKey) {
|
||||
var selectDivs = actionDiv.getElementsByTagName('select');
|
||||
if (actionKey) {
|
||||
setModifiers(actionKey, selectDivs);
|
||||
setKeyDropdown(actionKey, selectDivs[selectDivs.length - 1]);
|
||||
} else {
|
||||
clearDropdown(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a dropdown with the given list of possible keys.
|
||||
* @param {Blockly.Action} action The Blockly action.
|
||||
* @param {Element} actionDiv The div holding the dropdowns and labels for
|
||||
* a given action.
|
||||
* @param {Array.<string>} keys The list of keys to add to the dropdown.
|
||||
* @package
|
||||
*/
|
||||
function createDropdown(action, actionDiv, keys) {
|
||||
var select = document.createElement('select');
|
||||
select.addEventListener('change', updateKey);
|
||||
select.setAttribute('aria-labelledby', action.name + '_label');
|
||||
for (var i = 0, key; key = keys[i]; i++) {
|
||||
select.options.add(new Option(key, key));
|
||||
}
|
||||
actionDiv.appendChild(select);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create two dropdowns that display possible modifiers and a single dropdown
|
||||
* displaying a list of keys.
|
||||
* @param {Blockly.Action} action The Blockly action.
|
||||
* @param {string} actionKey The key corresponding to the given action.
|
||||
* @param {Element} actionDiv The div holding the dropdowns and label for the
|
||||
* given action.
|
||||
* @package
|
||||
*/
|
||||
function createDropdowns(action, actionKey, actionDiv) {
|
||||
var modifiers = ['None'].concat(Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys));
|
||||
var keys = ['None', 'Enter', 'Escape'].concat("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".split(''));
|
||||
createDropdown(action, actionDiv, modifiers);
|
||||
createDropdown(action, actionDiv, modifiers);
|
||||
createDropdown(action, actionDiv, keys);
|
||||
setDropdowns(action, actionDiv, actionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* For each action create a row of 3 dropdowns and an action label. Update
|
||||
* the dropdowns to reflect the value in the key map.
|
||||
* @param {Array.<Blockly.Action>} actions List of blockly actions.
|
||||
* @package
|
||||
*/
|
||||
function createKeyMappingList(actions) {
|
||||
// Update the key map to reflect the key map saved in session storage.
|
||||
restoreKeyMap();
|
||||
var keyMapDiv = document.getElementById('keyboard_mappings');
|
||||
for (var i = 0, action; action = actions[i]; i++) {
|
||||
var actionDiv = document.createElement('div');
|
||||
actionDiv.setAttribute('data-actionname', action.name);
|
||||
actionDiv.action = action;
|
||||
|
||||
var labelDiv = document.createElement('label');
|
||||
labelDiv.innerText = action.name;
|
||||
labelDiv.setAttribute('id', action.name + '_label');
|
||||
|
||||
actionDiv.appendChild(labelDiv);
|
||||
keyMapDiv.appendChild(actionDiv);
|
||||
|
||||
var actionKey = Blockly.user.keyMap.getKeyByAction(action);
|
||||
createDropdowns(action, actionKey, actionDiv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide/show the key map panel.
|
||||
* @param {boolean} state The state of the checkbox. True if checked, false
|
||||
* otherwise.
|
||||
* @package
|
||||
*/
|
||||
function toggleDisplayKeyMappings(state) {
|
||||
if (state) {
|
||||
document.getElementById('keyboard_nav').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('keyboard_nav').style.display = 'none';
|
||||
}
|
||||
}
|
||||
// End key mapping demo functions
|
||||
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<p>This demo is in the process of moving to a repository!</p>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The class representing a line cursor.
|
||||
* A line cursor traverses the blocks as if they were
|
||||
* lines of code in a text editor.
|
||||
* Previous and next go up and down lines. In and out go
|
||||
* through the elements in a line.
|
||||
* @author aschmiedt@google.com (Abby Schmiedt)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
||||
/**
|
||||
* Class for a line cursor.
|
||||
* This will allow the user to get to all nodes in the AST by hitting next or
|
||||
* previous.
|
||||
* @constructor
|
||||
* @extends {Blockly.BasicCursor}
|
||||
*/
|
||||
Blockly.LineCursor = function() {
|
||||
Blockly.LineCursor.superClass_.constructor.call(this);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.LineCursor, Blockly.BasicCursor);
|
||||
|
||||
|
||||
/**
|
||||
* Find the next node in the pre order traversal.
|
||||
* @return {Blockly.ASTNode} The next node, or null if the current node is
|
||||
* not set or there is no next value.
|
||||
* @override
|
||||
*/
|
||||
Blockly.LineCursor.prototype.next = function() {
|
||||
var curNode = this.getCurNode();
|
||||
if (!curNode) {
|
||||
return null;
|
||||
}
|
||||
var newNode = this.getNextNode_(curNode, this.validLineNode_);
|
||||
|
||||
// Skip the input or next value if there is a connected block.
|
||||
if (newNode && (newNode.getType() == Blockly.ASTNode.types.INPUT ||
|
||||
newNode.getType() == Blockly.ASTNode.types.NEXT) &&
|
||||
newNode.getLocation().targetBlock()) {
|
||||
newNode = this.getNextNode_(newNode, this.validLineNode_);
|
||||
}
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* For a basic cursor we only have the ability to go next and previous, so
|
||||
* in will also allow the user to get to the next node in the pre order traversal.
|
||||
* @return {Blockly.ASTNode} The next node, or null if the current node is
|
||||
* not set or there is no next value.
|
||||
* @override
|
||||
*/
|
||||
Blockly.LineCursor.prototype.in = function() {
|
||||
var curNode = this.getCurNode();
|
||||
if (!curNode) {
|
||||
return null;
|
||||
}
|
||||
var newNode = this.getNextNode_(curNode, this.validInLineNode_);
|
||||
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the previous node in the pre order traversal.
|
||||
* @return {Blockly.ASTNode} The previous node, or null if the current node
|
||||
* is not set or there is no previous value.
|
||||
* @override
|
||||
*/
|
||||
Blockly.LineCursor.prototype.prev = function() {
|
||||
var curNode = this.getCurNode();
|
||||
if (!curNode) {
|
||||
return null;
|
||||
}
|
||||
var newNode = this.getPreviousNode_(curNode, this.validLineNode_);
|
||||
|
||||
if (newNode && (newNode.getType() == Blockly.ASTNode.types.INPUT ||
|
||||
newNode.getType() == Blockly.ASTNode.types.NEXT) &&
|
||||
newNode.getLocation().targetBlock()) {
|
||||
newNode = this.getPreviousNode_(newNode, this.validLineNode_);
|
||||
}
|
||||
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* For a basic cursor we only have the ability to go next and previous, so
|
||||
* out will allow the user to get to the previous node in the pre order traversal.
|
||||
* @return {Blockly.ASTNode} The previous node, or null if the current node is
|
||||
* not set or there is no previous value.
|
||||
* @override
|
||||
*/
|
||||
Blockly.LineCursor.prototype.out = function() {
|
||||
var curNode = this.getCurNode();
|
||||
if (!curNode) {
|
||||
return null;
|
||||
}
|
||||
var newNode = this.getPreviousNode_(curNode, this.validInLineNode_);
|
||||
|
||||
if (newNode) {
|
||||
this.setCurNode(newNode);
|
||||
}
|
||||
return newNode;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Meant to traverse by lines of code. This is blocks, statement inputs and
|
||||
* next connections.
|
||||
* @param {Blockly.ASTNode} node The AST node to check whether it is valid.
|
||||
* @return {boolean} True if the node should be visited, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.LineCursor.prototype.validLineNode_ = function(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
var isValid = false;
|
||||
var location = node.getLocation();
|
||||
var type = node && node.getType();
|
||||
if (type == Blockly.ASTNode.types.BLOCK) {
|
||||
if (location.outputConnection === null) {
|
||||
isValid = true;
|
||||
}
|
||||
} else if (type == Blockly.ASTNode.types.INPUT &&
|
||||
location.type == Blockly.NEXT_STATEMENT) {
|
||||
isValid = true;
|
||||
} else if (type == Blockly.ASTNode.types.NEXT) {
|
||||
isValid = true;
|
||||
}
|
||||
return isValid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Meant to traverse within a block. These are fields and input values.
|
||||
* @param {Blockly.ASTNode} node The AST node to check whether it is valid.
|
||||
* @return {boolean} True if the node should be visited, false otherwise.
|
||||
* @private
|
||||
*/
|
||||
Blockly.LineCursor.prototype.validInLineNode_ = function(node) {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
var isValid = false;
|
||||
var location = node.getLocation();
|
||||
var type = node && node.getType();
|
||||
if (type == Blockly.ASTNode.types.FIELD) {
|
||||
isValid = true;
|
||||
} else if (type == Blockly.ASTNode.types.INPUT &&
|
||||
location.type == Blockly.INPUT_VALUE) {
|
||||
isValid = true;
|
||||
}
|
||||
return isValid;
|
||||
};
|
||||
@@ -81,7 +81,7 @@
|
||||
<script src="input_test.js"></script>
|
||||
<script src="insertion_marker_test.js"></script>
|
||||
<script src="json_test.js"></script>
|
||||
<script src="key_map_test.js"></script>
|
||||
<script src="shortcut_registry_test.js"></script>
|
||||
<script src="keydown_test.js"></script>
|
||||
<script src="logic_ternary_test.js"></script>
|
||||
<script src="metrics_test.js"></script>
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Key Map Tests', function() {
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap());
|
||||
});
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
});
|
||||
|
||||
test('Test adding a new action to key map', function() {
|
||||
var newAction = new Blockly.Action('test_action', 'test', function(){
|
||||
return "test";
|
||||
});
|
||||
Blockly.user.keyMap.setActionForKey('65', newAction);
|
||||
chai.assert.equal(Blockly.user.keyMap.map_['65'].name, 'test_action');
|
||||
});
|
||||
|
||||
test('Test giving an old action a new key', function() {
|
||||
Blockly.user.keyMap.setActionForKey(Blockly.utils.KeyCodes.F,
|
||||
Blockly.navigation.ACTION_PREVIOUS);
|
||||
chai.assert.isUndefined(Blockly.user.keyMap.map_[Blockly.utils.KeyCodes.W]);
|
||||
chai.assert.equal(Blockly.user.keyMap.map_[Blockly.utils.KeyCodes.F],
|
||||
Blockly.navigation.ACTION_PREVIOUS);
|
||||
});
|
||||
|
||||
test('Test get key by action defined', function() {
|
||||
var key = Blockly.user.keyMap.getKeyByAction(Blockly.navigation.ACTION_PREVIOUS);
|
||||
chai.assert.equal(key, Blockly.utils.KeyCodes.W);
|
||||
});
|
||||
|
||||
test('Test get key by action not defined', function() {
|
||||
var key = Blockly.user.keyMap.getKeyByAction(new Blockly.Action('something'));
|
||||
chai.assert.notExists(key);
|
||||
});
|
||||
|
||||
test('Test set key map', function() {
|
||||
var testKeyMap = Blockly.user.keyMap.createDefaultKeyMap();
|
||||
testKeyMap['randomKey'] = new Blockly.Action('test','',null);
|
||||
Blockly.user.keyMap.setKeyMap(testKeyMap);
|
||||
chai.assert.equal(Blockly.user.keyMap.map_['randomKey'].name, 'test');
|
||||
});
|
||||
|
||||
test('Test get key map returns a clone', function() {
|
||||
var keyMap = Blockly.user.keyMap.getKeyMap();
|
||||
keyMap['randomKey'] = new Blockly.Action('test', '', null);
|
||||
chai.assert.isUndefined(Blockly.user.keyMap.map_['randomKey']);
|
||||
});
|
||||
|
||||
test('Test serialize key code with modifiers', function() {
|
||||
var mockEvent = {
|
||||
getModifierState: function(){
|
||||
return true;
|
||||
},
|
||||
keyCode: 65
|
||||
};
|
||||
var serializedKey = Blockly.user.keyMap.serializeKeyEvent(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'ShiftControlAltMeta65');
|
||||
});
|
||||
|
||||
test('Test serialize key code without modifiers', function() {
|
||||
var mockEvent = {
|
||||
getModifierState: function(){
|
||||
return false;
|
||||
},
|
||||
keyCode: 65
|
||||
};
|
||||
var serializedKey = Blockly.user.keyMap.serializeKeyEvent(mockEvent);
|
||||
chai.assert.equal(serializedKey, '65');
|
||||
});
|
||||
|
||||
test('Test modifiers in reverse order', function() {
|
||||
var testKey = Blockly.user.keyMap.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.K, [Blockly.user.keyMap.modifierKeys.CONTROL,
|
||||
Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
Blockly.user.keyMap.setActionForKey(testKey, new Blockly.Action('test', '', null));
|
||||
var action = Blockly.user.keyMap.getActionByKeyCode('ShiftControl75');
|
||||
chai.assert.isNotNull(action);
|
||||
chai.assert.equal(action.name, 'test');
|
||||
});
|
||||
|
||||
test('Test report invalid modifiers', function() {
|
||||
var shouldThrow = function() {
|
||||
Blockly.user.keyMap.createSerializedKey(Blockly.utils.KeyCodes.K, ['s',
|
||||
Blockly.user.keyMap.modifierKeys.SHIFT]);
|
||||
};
|
||||
chai.assert.throws(shouldThrow, Error, 's is not a valid modifier key.');
|
||||
});
|
||||
|
||||
|
||||
teardown(function() {});
|
||||
});
|
||||
@@ -93,7 +93,7 @@ suite('Key Down', function() {
|
||||
suite('Copy', function() {
|
||||
setup(function() {
|
||||
setSelectedBlock(this.workspace);
|
||||
this.copySpy = sinon.spy(Blockly, 'copy_');
|
||||
this.copySpy = sinon.spy(Blockly, 'copy');
|
||||
this.hideChaffSpy = sinon.spy(Blockly, 'hideChaff');
|
||||
});
|
||||
var testCases = [
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
|
||||
suite('Navigation', function() {
|
||||
function createNavigationWorkspace(enableKeyboardNav) {
|
||||
function createNavigationWorkspace(enableKeyboardNav, readOnly) {
|
||||
var toolbox = document.getElementById('toolbox-categories');
|
||||
var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
|
||||
var workspace =
|
||||
Blockly.inject('blocklyDiv', {toolbox: toolbox, readOnly: readOnly});
|
||||
if (enableKeyboardNav) {
|
||||
Blockly.navigation.enableKeyboardAccessibility();
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
@@ -45,64 +46,88 @@ suite('Navigation', function() {
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.navigation.focusToolbox_();
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
function testToolboxSelectMethodCalled(ws, mockEvent, keyCode, selectMethodName) {
|
||||
mockEvent.keyCode = keyCode;
|
||||
var toolbox = ws.getToolbox();
|
||||
toolbox.selectedItem_ = toolbox.contents_[0];
|
||||
var selectNextStub = sinon.stub(toolbox, selectMethodName);
|
||||
Blockly.navigation.onKeyPress(mockEvent);
|
||||
sinon.assert.called(selectNextStub);
|
||||
}
|
||||
var testCases = [
|
||||
[
|
||||
'Calls toolbox selectNext_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.S, 'NotAField'), 'selectNext_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectPrevious_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField'),
|
||||
'selectPrevious_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectParent_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.D, 'NotAField'),
|
||||
'selectChild_'
|
||||
],
|
||||
[
|
||||
'Calls toolbox selectChild_',
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.A, 'NotAField'),
|
||||
'selectParent_'
|
||||
]
|
||||
];
|
||||
|
||||
test('Calls toolbox selectNext_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.S, 'selectNext_');
|
||||
});
|
||||
test('Calls toolbox selectPrevious_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.W, 'selectPrevious_');
|
||||
});
|
||||
test('Calls toolbox selectParent_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.D, 'selectChild_');
|
||||
});
|
||||
test('Calls toolbox selectChild_', function() {
|
||||
testToolboxSelectMethodCalled(this.workspace, this.mockEvent, Blockly.utils.KeyCodes.A, 'selectParent_');
|
||||
testCases.forEach(function(testCase) {
|
||||
var testCaseName = testCase[0];
|
||||
var mockEvent = testCase[1];
|
||||
var stubName = testCase[2];
|
||||
test(testCaseName, function() {
|
||||
var toolbox = this.workspace.getToolbox();
|
||||
var selectStub = sinon.stub(toolbox, stubName);
|
||||
toolbox.selectedItem_ = toolbox.contents_[0];
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
sinon.assert.called(selectStub);
|
||||
});
|
||||
});
|
||||
|
||||
test('Go to flyout', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.D;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var flyoutCursor = Blockly.navigation.getFlyoutCursor_();
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.D, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
|
||||
var flyoutCursor = Blockly.navigation.getFlyoutCursor_();
|
||||
chai.assert.equal(flyoutCursor.getCurNode().getLocation().getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Focuses workspace from toolbox (e)', function() {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.E;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.E, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
test('Focuses workspace from toolbox (escape)', function() {
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.E;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ESC, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
// More tests:
|
||||
// - nested categories
|
||||
@@ -124,14 +149,8 @@ suite('Navigation', function() {
|
||||
]
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -140,10 +159,15 @@ suite('Navigation', function() {
|
||||
|
||||
// Should be a no-op
|
||||
test('Previous at beginning', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
chai.assert.equal(Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation().getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
@@ -155,45 +179,72 @@ suite('Navigation', function() {
|
||||
var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-SecondBlock");
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-FirstBlock");
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_FLYOUT);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_FLYOUT);
|
||||
var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
|
||||
chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
|
||||
"FirstCategory-SecondBlock");
|
||||
});
|
||||
|
||||
test('Out', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.A;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
|
||||
test('MARK', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
test('Mark', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
chai.assert.equal(this.workspace.getTopBlocks().length, 1);
|
||||
});
|
||||
|
||||
test('EXIT', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ESC;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
test('Exit', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(Blockly.utils.KeyCodes.ESC, 'NotAField');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -216,12 +267,6 @@ suite('Navigation', function() {
|
||||
}]);
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
this.firstCategory_ = this.workspace.getToolbox().contents_[0];
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -229,77 +274,106 @@ suite('Navigation', function() {
|
||||
});
|
||||
|
||||
test('Previous', function() {
|
||||
sinon.spy(this.workspace.getCursor(), 'prev');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.W;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(this.workspace.getCursor().prev);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
this.workspace.getCursor().prev.restore();
|
||||
var prevSpy = sinon.spy(this.workspace.getCursor(), 'prev');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var wEvent = createKeyDownEvent(Blockly.utils.KeyCodes.W, '');
|
||||
|
||||
Blockly.onKeyDown(wEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(prevSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Next', function() {
|
||||
var cursor = this.workspace.getCursor();
|
||||
sinon.spy(cursor, 'next');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(cursor.next);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
cursor.next.restore();
|
||||
var nextSpy = sinon.spy(this.workspace.getCursor(), 'next');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var sEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, '');
|
||||
|
||||
Blockly.onKeyDown(sEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(nextSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Out', function() {
|
||||
var cursor = this.workspace.getCursor();
|
||||
sinon.spy(cursor, 'out');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.A;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(cursor.out);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
cursor.out.restore();
|
||||
var outSpy = sinon.spy(this.workspace.getCursor(), 'out');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var aEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
|
||||
Blockly.onKeyDown(aEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(outSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('In', function() {
|
||||
var cursor = this.workspace.getCursor();
|
||||
sinon.spy(cursor, 'in');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.D;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(cursor.in);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
cursor.in.restore();
|
||||
var inSpy = sinon.spy(this.workspace.getCursor(), 'in');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var dEvent = createKeyDownEvent(Blockly.utils.KeyCodes.D, '');
|
||||
|
||||
Blockly.onKeyDown(dEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(inSpy);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Insert', function() {
|
||||
// Stub modify as we are not testing its behavior, only if it was called.
|
||||
// Otherwise, there is a warning because there is no marked node.
|
||||
sinon.stub(Blockly.navigation, 'modify_');
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.I;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
sinon.assert.calledOnce(Blockly.navigation.modify_);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
Blockly.navigation.modify_.restore();
|
||||
var modifyStub = sinon.stub(Blockly.navigation, 'modify_').returns(true);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var iEvent = createKeyDownEvent(Blockly.utils.KeyCodes.I, '');
|
||||
|
||||
Blockly.onKeyDown(iEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(modifyStub);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Mark', function() {
|
||||
this.workspace.getCursor().setCurNode(
|
||||
Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection));
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var enterEvent = createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, '');
|
||||
|
||||
Blockly.onKeyDown(enterEvent);
|
||||
|
||||
var markedNode = this.workspace.getMarker(Blockly.navigation.MARKER_NAME).getCurNode();
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(markedNode.getLocation(), this.basicBlock.previousConnection);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_WS);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_WS);
|
||||
});
|
||||
|
||||
test('Toolbox', function() {
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.T;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
chai.assert.equal(this.workspace.getToolbox().getSelectedItem(), this.firstCategory_);
|
||||
chai.assert.equal(Blockly.navigation.currentState_,
|
||||
Blockly.navigation.STATE_TOOLBOX);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
var tEvent = createKeyDownEvent(Blockly.utils.KeyCodes.T, '');
|
||||
|
||||
Blockly.onKeyDown(tEvent);
|
||||
|
||||
var firstCategory = this.workspace.getToolbox().contents_[0];
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.equal(
|
||||
this.workspace.getToolbox().getSelectedItem(), firstCategory);
|
||||
chai.assert.equal(
|
||||
Blockly.navigation.currentState_, Blockly.navigation.STATE_TOOLBOX);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -325,83 +399,84 @@ suite('Navigation', function() {
|
||||
this.workspace = createNavigationWorkspace(true);
|
||||
this.workspace.getCursor().drawer_ = null;
|
||||
this.basicBlock = this.workspace.newBlock('basic_block');
|
||||
Blockly.user.keyMap.setKeyMap(Blockly.user.keyMap.createDefaultKeyMap());
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
teardown(function() {
|
||||
workspaceTeardown.call(this, this.workspace);
|
||||
});
|
||||
|
||||
|
||||
test('Action does not exist', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
sinon.spy(field, 'onBlocklyAction');
|
||||
var fieldSpy = sinon.spy(field, 'onBlocklyAction');
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.N, '');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.N;
|
||||
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
chai.assert.isFalse(isHandled);
|
||||
sinon.assert.notCalled(field.onBlocklyAction);
|
||||
|
||||
field.onBlocklyAction.restore();
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isFalse(keyDownSpy.returned(true));
|
||||
sinon.assert.notCalled(fieldSpy);
|
||||
});
|
||||
|
||||
test('Action exists - field handles action', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
|
||||
sinon.stub(field, 'onBlocklyAction').returns(true);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var fieldSpy = sinon.stub(field, 'onBlocklyAction').returns(true);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
var isHandled = Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_OUT);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(field.onBlocklyAction);
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(fieldSpy);
|
||||
|
||||
field.onBlocklyAction.restore();
|
||||
});
|
||||
|
||||
test('Action exists - field does not handle action', function() {
|
||||
var block = this.workspace.getTopBlocks()[0];
|
||||
var field = block.inputList[0].fieldRow[0];
|
||||
sinon.spy(field, 'onBlocklyAction');
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var fieldSpy = sinon.spy(field, 'onBlocklyAction');
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.getCursor().setCurNode(Blockly.ASTNode.createFieldNode(field));
|
||||
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.A;
|
||||
var isHandled = Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_OUT);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(field.onBlocklyAction);
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
field.onBlocklyAction.restore();
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
sinon.assert.calledOnce(fieldSpy);
|
||||
});
|
||||
|
||||
test('Toggle Action Off', function() {
|
||||
this.mockEvent.keyCode = 'ShiftControl75';
|
||||
sinon.spy(Blockly.navigation, 'onBlocklyAction');
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.K, '',
|
||||
[Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.keyboardAccessibilityMode = true;
|
||||
|
||||
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(Blockly.navigation.onBlocklyAction);
|
||||
chai.assert.isFalse(Blockly.getMainWorkspace().keyboardAccessibilityMode);
|
||||
Blockly.navigation.onBlocklyAction.restore();
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.isFalse(this.workspace.keyboardAccessibilityMode);
|
||||
});
|
||||
|
||||
test('Toggle Action On', function() {
|
||||
this.mockEvent.keyCode = 'ShiftControl75';
|
||||
sinon.stub(Blockly.navigation, 'focusWorkspace_');
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = false;
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.K, '',
|
||||
[Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
this.workspace.keyboardAccessibilityMode = false;
|
||||
|
||||
var isHandled = Blockly.navigation.onKeyPress(this.mockEvent);
|
||||
chai.assert.isTrue(isHandled);
|
||||
sinon.assert.calledOnce(Blockly.navigation.focusWorkspace_);
|
||||
chai.assert.isTrue(Blockly.getMainWorkspace().keyboardAccessibilityMode);
|
||||
Blockly.navigation.focusWorkspace_.restore();
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
chai.assert.isTrue(this.workspace.keyboardAccessibilityMode);
|
||||
});
|
||||
|
||||
suite('Test key press in read only mode', function() {
|
||||
@@ -431,19 +506,12 @@ suite('Navigation', function() {
|
||||
"tooltip": "",
|
||||
"helpUrl": ""
|
||||
}]);
|
||||
this.workspace = Blockly.inject('blocklyDiv', {readOnly: true});
|
||||
this.workspace = createNavigationWorkspace(true, true);
|
||||
|
||||
Blockly.mainWorkspace = this.workspace;
|
||||
this.workspace.getCursor().drawer_ = null;
|
||||
Blockly.getMainWorkspace().keyboardAccessibilityMode = true;
|
||||
Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
|
||||
|
||||
this.fieldBlock1 = this.workspace.newBlock('field_block');
|
||||
this.mockEvent = {
|
||||
getModifierState: function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
@@ -452,24 +520,39 @@ suite('Navigation', function() {
|
||||
|
||||
test('Perform valid action for read only', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.S, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.S;
|
||||
chai.assert.isTrue(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(true));
|
||||
});
|
||||
|
||||
test('Perform invalid action for read only', function() {
|
||||
var astNode = Blockly.ASTNode.createBlockNode(this.fieldBlock1);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.I, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.I;
|
||||
chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(false));
|
||||
});
|
||||
|
||||
test('Try to perform action on a field', function() {
|
||||
var field = this.fieldBlock1.inputList[0].fieldRow[0];
|
||||
var astNode = Blockly.ASTNode.createFieldNode(field);
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.ENTER, '');
|
||||
this.workspace.getCursor().setCurNode(astNode);
|
||||
this.mockEvent.keyCode = Blockly.utils.KeyCodes.ENTER;
|
||||
chai.assert.isFalse(Blockly.navigation.onKeyPress(this.mockEvent));
|
||||
var keyDownSpy =
|
||||
sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
|
||||
|
||||
Blockly.onKeyDown(mockEvent);
|
||||
|
||||
chai.assert.isTrue(keyDownSpy.returned(false));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -508,9 +591,9 @@ suite('Navigation', function() {
|
||||
var prevNode = Blockly.ASTNode.createConnectionNode(previousConnection);
|
||||
this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(prevNode);
|
||||
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
Blockly.navigation.insertFromFlyout();
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
Blockly.navigation.insertFromFlyout(this.workspace);
|
||||
|
||||
var insertedBlock = this.basicBlock.previousConnection.targetBlock();
|
||||
|
||||
@@ -520,9 +603,9 @@ suite('Navigation', function() {
|
||||
});
|
||||
|
||||
test('Insert Block from flyout without marking a connection', function() {
|
||||
Blockly.navigation.focusToolbox_();
|
||||
Blockly.navigation.focusFlyout_();
|
||||
Blockly.navigation.insertFromFlyout();
|
||||
Blockly.navigation.focusToolbox_(this.workspace);
|
||||
Blockly.navigation.focusFlyout_(this.workspace);
|
||||
Blockly.navigation.insertFromFlyout(this.workspace);
|
||||
|
||||
var numBlocks = this.workspace.getTopBlocks().length;
|
||||
|
||||
|
||||
355
tests/mocha/shortcut_registry_test.js
Normal file
355
tests/mocha/shortcut_registry_test.js
Normal file
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Keyboard Shortcut Registry Test', function() {
|
||||
setup(function() {
|
||||
sharedTestSetup.call(this);
|
||||
this.registry = new Blockly.ShortcutRegistry();
|
||||
});
|
||||
teardown(function() {
|
||||
sharedTestTeardown.call(this);
|
||||
});
|
||||
|
||||
suite('Registering', function() {
|
||||
test('Registering a shortcut', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.register(testShortcut, true);
|
||||
var shortcut = this.registry.registry_['test_shortcut'];
|
||||
chai.assert.equal(shortcut.name, 'test_shortcut');
|
||||
});
|
||||
test('Registers shortcut with same name', function() {
|
||||
var registry = this.registry;
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
|
||||
registry.registry_['test_shortcut'] = [testShortcut];
|
||||
|
||||
var shouldThrow = function() {
|
||||
registry.register(testShortcut);
|
||||
};
|
||||
chai.assert.throws(
|
||||
shouldThrow, Error,
|
||||
'Shortcut with name "test_shortcut" already exists.');
|
||||
});
|
||||
test(
|
||||
'Registers shortcut with same name opt_allowOverrides=true',
|
||||
function() {
|
||||
var registry = this.registry;
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
var otherShortcut = {
|
||||
'name': 'test_shortcut',
|
||||
'callback': function() {}
|
||||
};
|
||||
|
||||
registry.registry_['test_shortcut'] = [testShortcut];
|
||||
|
||||
var shouldNotThrow = function() {
|
||||
registry.register(otherShortcut, true);
|
||||
};
|
||||
chai.assert.doesNotThrow(shouldNotThrow);
|
||||
chai.assert.exists(registry.registry_['test_shortcut'].callback);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Unregistering', function() {
|
||||
test('Unregistering a shortcut', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.registry_['test'] = [testShortcut];
|
||||
chai.assert.isOk(this.registry.registry_['test']);
|
||||
this.registry.unregister('test', 'test_shortcut');
|
||||
chai.assert.isUndefined(this.registry.registry_['test']);
|
||||
});
|
||||
test('Unregistering a nonexistent shortcut', function() {
|
||||
var consoleStub = sinon.stub(console, 'warn');
|
||||
chai.assert.isUndefined(this.registry.registry_['test']);
|
||||
|
||||
var registry = this.registry;
|
||||
chai.assert.isFalse(registry.unregister('test', 'test_shortcut'));
|
||||
sinon.assert.calledOnceWithExactly(consoleStub, 'Keyboard shortcut with name "test" not found.');
|
||||
});
|
||||
test('Unregistering a shortcut with key mappings', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut'];
|
||||
this.registry.registry_['test_shortcut'] = testShortcut;
|
||||
|
||||
this.registry.unregister('test_shortcut');
|
||||
|
||||
var shortcut = this.registry.registry_['test'];
|
||||
var keyMappings = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.isUndefined(shortcut);
|
||||
chai.assert.isUndefined(keyMappings);
|
||||
});
|
||||
test('Unregistering a shortcut with colliding key mappings', function() {
|
||||
var testShortcut = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut', 'other_shortcutt'];
|
||||
this.registry.registry_['test_shortcut'] = testShortcut;
|
||||
|
||||
this.registry.unregister('test_shortcut');
|
||||
|
||||
var shortcut = this.registry.registry_['test'];
|
||||
var keyMappings = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(keyMappings, 1);
|
||||
chai.assert.isUndefined(shortcut);
|
||||
});
|
||||
});
|
||||
|
||||
suite('addKeyMapping', function() {
|
||||
test('Adds a key mapping', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
|
||||
this.registry.addKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(shortcutNames, 1);
|
||||
chai.assert.equal(shortcutNames[0], 'test_shortcut');
|
||||
});
|
||||
test('Adds a colliding key mapping - opt_allowCollision=true', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut_2'];
|
||||
|
||||
this.registry.addKeyMapping('keyCode', 'test_shortcut', true);
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(shortcutNames, 2);
|
||||
chai.assert.equal(shortcutNames[0], 'test_shortcut');
|
||||
chai.assert.equal(shortcutNames[1], 'test_shortcut_2');
|
||||
});
|
||||
test('Adds a colliding key mapping - opt_allowCollision=false', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut_2'];
|
||||
|
||||
var registry = this.registry;
|
||||
var shouldThrow = function() {
|
||||
registry.addKeyMapping('keyCode', 'test_shortcut');
|
||||
};
|
||||
chai.assert.throws(
|
||||
shouldThrow, Error,
|
||||
'Shortcut with name "test_shortcut" collides with shortcuts test_shortcut_2');
|
||||
});
|
||||
});
|
||||
|
||||
suite('removeKeyMapping', function() {
|
||||
test('Removes a key mapping', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut', 'test_shortcut_2'];
|
||||
|
||||
var isRemoved =
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.lengthOf(shortcutNames, 1);
|
||||
chai.assert.equal(shortcutNames[0], 'test_shortcut_2');
|
||||
chai.assert.isTrue(isRemoved);
|
||||
});
|
||||
test('Removes last key mapping for a key', function() {
|
||||
this.registry.registry_['test_shortcut'] = {'name': 'test_shortcut'};
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut'];
|
||||
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
var shortcutNames = this.registry.keyMap_['keyCode'];
|
||||
chai.assert.isUndefined(shortcutNames);
|
||||
});
|
||||
test('Removes a key map that does not exist opt_quiet=false', function() {
|
||||
var consoleStub = sinon.stub(console, 'warn');
|
||||
this.registry.keyMap_['keyCode'] = ['test_shortcut_2'];
|
||||
|
||||
var isRemoved =
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
chai.assert.isFalse(isRemoved);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
consoleStub,
|
||||
'No keyboard shortcut with name "test_shortcut" registered with key code "keyCode"');
|
||||
});
|
||||
test(
|
||||
'Removes a key map that does not exist from empty key mapping opt_quiet=false',
|
||||
function() {
|
||||
var consoleStub = sinon.stub(console, 'warn');
|
||||
|
||||
var isRemoved =
|
||||
this.registry.removeKeyMapping('keyCode', 'test_shortcut');
|
||||
|
||||
chai.assert.isFalse(isRemoved);
|
||||
sinon.assert.calledOnceWithExactly(
|
||||
consoleStub,
|
||||
'No keyboard shortcut with name "test_shortcut" registered with key code "keyCode"');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Setters/Getters', function() {
|
||||
test('Sets the key map', function() {
|
||||
this.registry.setKeyMap({'keyCode': ['test_shortcut']});
|
||||
chai.assert.lengthOf(Object.keys(this.registry.keyMap_), 1);
|
||||
chai.assert.equal(this.registry.keyMap_['keyCode'][0], 'test_shortcut');
|
||||
});
|
||||
test('Gets a copy of the key map', function() {
|
||||
this.registry.keyMap_['keyCode'] = ['a'];
|
||||
var keyMapCopy = this.registry.getKeyMap();
|
||||
keyMapCopy['keyCode'] = ['b'];
|
||||
chai.assert.equal(this.registry.keyMap_['keyCode'][0], 'a');
|
||||
});
|
||||
test('Gets a copy of the registry', function() {
|
||||
this.registry.registry_['shortcutName'] = {'name': 'shortcutName'};
|
||||
var registrycopy = this.registry.getRegistry();
|
||||
registrycopy['shortcutName']['name'] = 'shortcutName1';
|
||||
chai.assert.equal(
|
||||
this.registry.registry_['shortcutName']['name'], 'shortcutName');
|
||||
});
|
||||
test('Gets keyboard shortcuts from a key code', function() {
|
||||
this.registry.keyMap_['keyCode'] = ['shortcutName'];
|
||||
var shortcutNames = this.registry.getKeyboardShortcuts('keyCode');
|
||||
chai.assert.equal(shortcutNames[0], 'shortcutName');
|
||||
});
|
||||
test('Gets keycodes by shortcut name', function() {
|
||||
this.registry.keyMap_['keyCode'] = ['shortcutName'];
|
||||
this.registry.keyMap_['keyCode1'] = ['shortcutName'];
|
||||
var shortcutNames =
|
||||
this.registry.getKeyCodeByShortcutName('shortcutName');
|
||||
chai.assert.lengthOf(shortcutNames, 2);
|
||||
chai.assert.equal(shortcutNames[0], 'keyCode');
|
||||
chai.assert.equal(shortcutNames[1], 'keyCode1');
|
||||
});
|
||||
});
|
||||
|
||||
suite('onKeyDown', function() {
|
||||
function addShortcut(registry, shortcut, keyCode, returns) {
|
||||
registry.register(shortcut, true);
|
||||
registry.addKeyMapping(keyCode, shortcut.name, true);
|
||||
return sinon.stub(shortcut, 'callback').returns(returns);
|
||||
}
|
||||
|
||||
setup(function() {
|
||||
this.testShortcut = {
|
||||
'name': 'test_shortcut',
|
||||
'callback': function() {
|
||||
return true;
|
||||
},
|
||||
'precondition': function() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
this.callBackStub =
|
||||
addShortcut(this.registry, this.testShortcut, 'keyCode', true);
|
||||
});
|
||||
test('Execute a shortcut from event', function() {
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(this.callBackStub);
|
||||
});
|
||||
test('No shortcut executed from event', function() {
|
||||
var event = createKeyDownEvent('nonExistentKeyCode', '');
|
||||
chai.assert.isFalse(this.registry.onKeyDown(this.workspace, event));
|
||||
});
|
||||
test('No precondition available - execute callback', function() {
|
||||
delete this.testShortcut['precondition'];
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(this.callBackStub);
|
||||
});
|
||||
test('Execute all shortcuts in list', function() {
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
var testShortcut2 = {
|
||||
'name': 'test_shortcut_2',
|
||||
'callback': function() {
|
||||
return false;
|
||||
},
|
||||
'precondition': function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
var testShortcut2Stub =
|
||||
addShortcut(this.registry, testShortcut2, 'keyCode', false);
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(testShortcut2Stub);
|
||||
sinon.assert.calledOnce(this.callBackStub);
|
||||
});
|
||||
test('Stop executing shortcut when event is handled', function() {
|
||||
var event = createKeyDownEvent('keyCode', '');
|
||||
var testShortcut2 = {
|
||||
'name': 'test_shortcut_2',
|
||||
'callback': function() {
|
||||
return false;
|
||||
},
|
||||
'precondition': function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
var testShortcut2Stub =
|
||||
addShortcut(this.registry, testShortcut2, 'keyCode', true);
|
||||
chai.assert.isTrue(this.registry.onKeyDown(this.workspace, event));
|
||||
sinon.assert.calledOnce(testShortcut2Stub);
|
||||
sinon.assert.notCalled(this.callBackStub);
|
||||
});
|
||||
});
|
||||
|
||||
suite('createSerializedKey', function() {
|
||||
test('Serialize key', function() {
|
||||
var serializedKey =
|
||||
this.registry.createSerializedKey(Blockly.utils.KeyCodes.A);
|
||||
chai.assert.equal(serializedKey, '65');
|
||||
});
|
||||
|
||||
test('Serialize key code and modifier', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
Blockly.utils.KeyCodes.A, [Blockly.utils.KeyCodes.CTRL]);
|
||||
chai.assert.equal(serializedKey, 'Control+65');
|
||||
});
|
||||
test('Serialize only a modifier', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.CTRL]);
|
||||
chai.assert.equal(serializedKey, 'Control');
|
||||
});
|
||||
test('Serialize multiple modifiers', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
chai.assert.equal(serializedKey, 'Shift+Control');
|
||||
});
|
||||
test('Order of modifiers should result in same serialized key', function() {
|
||||
var serializedKey = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
chai.assert.equal(serializedKey, 'Shift+Control');
|
||||
var serializedKeyNewOrder = this.registry.createSerializedKey(
|
||||
null, [Blockly.utils.KeyCodes.SHIFT, Blockly.utils.KeyCodes.CTRL]);
|
||||
chai.assert.equal(serializedKeyNewOrder, 'Shift+Control');
|
||||
});
|
||||
});
|
||||
|
||||
suite('serializeKeyEvent', function() {
|
||||
test('Serialize key', function() {
|
||||
var mockEvent = createKeyDownEvent(Blockly.utils.KeyCodes.A, '');
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, '65');
|
||||
});
|
||||
test('Serialize key code and modifier', function() {
|
||||
var mockEvent = createKeyDownEvent(
|
||||
Blockly.utils.KeyCodes.A, '', [Blockly.utils.KeyCodes.CTRL]);
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'Control+65');
|
||||
});
|
||||
test('Serialize only a modifier', function() {
|
||||
var mockEvent =
|
||||
createKeyDownEvent(null, '', [Blockly.utils.KeyCodes.CTRL]);
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'Control');
|
||||
});
|
||||
test('Serialize multiple modifiers', function() {
|
||||
var mockEvent = createKeyDownEvent(
|
||||
null, '',
|
||||
[Blockly.utils.KeyCodes.CTRL, Blockly.utils.KeyCodes.SHIFT]);
|
||||
var serializedKey = this.registry.serializeKeyEvent_(mockEvent);
|
||||
chai.assert.equal(serializedKey, 'Shift+Control');
|
||||
});
|
||||
test('Throw error when incorrect modifier', function() {
|
||||
var registry = this.registry;
|
||||
var shouldThrow = function() {
|
||||
registry.createSerializedKey(Blockly.utils.KeyCodes.K, ['s']);
|
||||
};
|
||||
chai.assert.throws(shouldThrow, Error, 's is not a valid modifier key.');
|
||||
});
|
||||
});
|
||||
|
||||
teardown(function() {});
|
||||
});
|
||||
@@ -576,10 +576,17 @@ function dispatchPointerEvent(target, type, properties) {
|
||||
function createKeyDownEvent(keyCode, type, modifiers) {
|
||||
var event = {
|
||||
keyCode: keyCode,
|
||||
target: {
|
||||
type: type
|
||||
},
|
||||
getModifierState: function() {
|
||||
target: {type: type},
|
||||
getModifierState: function(name) {
|
||||
if (name == 'Shift' && this.shiftKey) {
|
||||
return true;
|
||||
} else if (name == 'Control' && this.ctrlKey) {
|
||||
return true;
|
||||
} else if (name == 'Meta' && this.metaKey) {
|
||||
return true;
|
||||
} else if (name == 'Alt' && this.altKey) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
preventDefault: function() {}
|
||||
|
||||
Reference in New Issue
Block a user