diff --git a/core/block.js b/core/block.js index 6c36c989b..5addb931e 100644 --- a/core/block.js +++ b/core/block.js @@ -30,6 +30,8 @@ goog.require('Blockly.utils.object'); goog.require('Blockly.utils.string'); goog.require('Blockly.Workspace'); +goog.requireType('Blockly.IASTNodeLocation'); + /** * Class for one block. @@ -40,6 +42,7 @@ goog.require('Blockly.Workspace'); * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise * create a new ID. * @constructor + * @implements {Blockly.IASTNodeLocation} * @throws When block is not valid or block name is not allowed. */ Blockly.Block = function(workspace, prototypeName, opt_id) { diff --git a/core/block_svg.js b/core/block_svg.js index 57b793be5..4ed10091b 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -32,9 +32,11 @@ goog.require('Blockly.utils.dom'); goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); +goog.requireType('Blockly.IASTNodeLocationSvg'); goog.requireType('Blockly.IBoundedElement'); goog.requireType('Blockly.ICopyable'); + /** * Class for a block's SVG representation. * Not normally called directly, workspace.newBlock() is preferred. @@ -44,6 +46,7 @@ goog.requireType('Blockly.ICopyable'); * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise * create a new ID. * @extends {Blockly.Block} + * @implements {Blockly.IASTNodeLocationSvg} * @implements {Blockly.IBoundedElement} * @implements {Blockly.ICopyable} * @constructor diff --git a/core/blockly.js b/core/blockly.js index 4295c00bd..edd13d71f 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -160,7 +160,7 @@ Blockly.svgResize = function(workspace) { /** * Handle a key-down on SVG drawing surface. Does nothing if the main workspace * is not visible. - * @param {!Event} e Key down event. + * @param {!KeyboardEvent} e Key down event. * @package */ // TODO (https://github.com/google/blockly/issues/1998) handle cases where there diff --git a/core/connection.js b/core/connection.js index 1b309a4c4..427a6ed68 100644 --- a/core/connection.js +++ b/core/connection.js @@ -16,12 +16,15 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.BlockMove'); goog.require('Blockly.Xml'); +goog.requireType('Blockly.IASTNodeLocationWithBlock'); + /** * Class for a connection between blocks. * @param {!Blockly.Block} source The block establishing this connection. * @param {number} type The type of the connection. * @constructor + * @implements {Blockly.IASTNodeLocationWithBlock} */ Blockly.Connection = function(source, type) { /** diff --git a/core/field.js b/core/field.js index 274bebf86..961f07451 100644 --- a/core/field.js +++ b/core/field.js @@ -24,6 +24,9 @@ goog.require('Blockly.utils.style'); goog.require('Blockly.utils.userAgent'); goog.requireType('Blockly.blockRendering.ConstantProvider'); +goog.requireType('Blockly.IASTNodeLocationSvg'); +goog.requireType('Blockly.IASTNodeLocationWithBlock'); +goog.requireType('Blockly.IBlocklyActionable'); /** @@ -36,6 +39,9 @@ goog.requireType('Blockly.blockRendering.ConstantProvider'); * the individual field's documentation for a list of properties this * parameter supports. * @constructor + * @implements {Blockly.IASTNodeLocationSvg} + * @implements {Blockly.IASTNodeLocationWithBlock} + * @implements {Blockly.IBlocklyActionable} */ Blockly.Field = function(value, opt_validator, opt_config) { /** @@ -1105,7 +1111,8 @@ Blockly.Field.prototype.setMarkerSvg = function(markerSvg) { * @protected */ Blockly.Field.prototype.updateMarkers_ = function() { - var workspace = this.sourceBlock_.workspace; + var workspace = + /** @type {!Blockly.WorkspaceSvg} */ (this.sourceBlock_.workspace); if (workspace.keyboardAccessibilityMode && this.cursorSvg_) { workspace.getCursor().draw(); } diff --git a/core/flyout_base.js b/core/flyout_base.js index fe7659386..5d1bd2b23 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -29,12 +29,15 @@ goog.require('Blockly.utils.dom'); goog.require('Blockly.WorkspaceSvg'); goog.require('Blockly.Xml'); +goog.requireType('Blockly.IBlocklyActionable'); + /** * Class for a flyout. * @param {!Blockly.Options} workspaceOptions Dictionary of options for the * workspace. * @constructor + * @implements {Blockly.IBlocklyActionable} */ Blockly.Flyout = function(workspaceOptions) { workspaceOptions.getMetrics = this.getMetrics_.bind(this); diff --git a/core/interfaces/i_accessibility.js b/core/interfaces/i_accessibility.js new file mode 100644 index 000000000..c31461b6d --- /dev/null +++ b/core/interfaces/i_accessibility.js @@ -0,0 +1,72 @@ +/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview AST Node and keyboard navigation interfaces. + * @author samelh@google.com (Sam El-Husseini) + */ + +'use strict'; + +goog.provide('Blockly.IASTNodeLocation'); +goog.provide('Blockly.IASTNodeLocationSvg'); +goog.provide('Blockly.IASTNodeLocationWithBlock'); +goog.provide('Blockly.IBlocklyActionable'); + +/** + * An AST node location interface. + * @interface + */ +Blockly.IASTNodeLocation = function() {}; + +/** + * An AST node location SVG interface. + * @interface + * @extends {Blockly.IASTNodeLocation} + */ +Blockly.IASTNodeLocationSvg = function() {}; + +/** + * Add the marker svg to this node's svg group. + * @param {SVGElement} markerSvg The svg root of the marker to be added to the + * svg group. + */ +Blockly.IASTNodeLocationSvg.prototype.setMarkerSvg; + +/** + * Add the cursor svg to this node's svg group. + * @param {SVGElement} cursorSvg The svg root of the cursor to be added to the + * svg group. + */ +Blockly.IASTNodeLocationSvg.prototype.setCursorSvg; + +/** + * An AST node location that has an associated block. + * @interface + * @extends {Blockly.IASTNodeLocation} + */ +Blockly.IASTNodeLocationWithBlock = function() {}; + +/** + * Get the source block associated with this node. + * @return {Blockly.Block} The source block. + */ +Blockly.IASTNodeLocationWithBlock.prototype.getSourceBlock; + + +/** + * An interface for an object that handles Blockly actions when keyboard + * navigation is enabled. + * @interface + */ +Blockly.IBlocklyActionable = function() {}; + +/** + * Handles the given action. + * @param {!Blockly.Action} action The action to be handled. + * @return {boolean} True if the action has been handled, false otherwise. + */ +Blockly.IBlocklyActionable.prototype.onBlocklyAction; diff --git a/core/keyboard_nav/ast_node.js b/core/keyboard_nav/ast_node.js index cd9c94c67..e869493b4 100644 --- a/core/keyboard_nav/ast_node.js +++ b/core/keyboard_nav/ast_node.js @@ -14,6 +14,9 @@ goog.provide('Blockly.ASTNode'); goog.require('Blockly.utils.Coordinate'); +goog.requireType('Blockly.IASTNodeLocation'); +goog.requireType('Blockly.IASTNodeLocationWithBlock'); + /** * Class for an AST node. @@ -21,9 +24,8 @@ goog.require('Blockly.utils.Coordinate'); * creating a node directly. * @param {string} type The type of the location. * Must be in Blockly.ASTNode.types. - * @param {!(Blockly.Block|Blockly.Connection|Blockly.Field|Blockly.Workspace)} - * location The position in the AST. - * @param {!Object=} opt_params Optional dictionary of options. + * @param {!Blockly.IASTNodeLocation} location The position in the AST. + * @param {!Blockly.ASTNode.Params=} opt_params Optional dictionary of options. * @constructor */ Blockly.ASTNode = function(type, location, opt_params) { @@ -48,14 +50,28 @@ Blockly.ASTNode = function(type, location, opt_params) { /** * The location of the AST node. - * @type {!(Blockly.Block|Blockly.Connection|Blockly.Field|Blockly.Workspace)} + * @type {!Blockly.IASTNodeLocation} * @private */ this.location_ = location; + /** + * The coordinate on the workspace. + * @type {Blockly.utils.Coordinate} + * @private + */ + this.wsCoordinate_ = null; + this.processParams_(opt_params || null); }; +/** + * @typedef {{ + * wsCoordinate: Blockly.utils.Coordinate, + * }} + */ +Blockly.ASTNode.Params; + /** * Object holding different types for an AST node. * @enum {string} @@ -219,7 +235,7 @@ Blockly.ASTNode.createTopNode = function(block) { /** * Parse the optional parameters. - * @param {Object} params The user specified parameters. + * @param {?Blockly.ASTNode.Params} params The user specified parameters. * @private */ Blockly.ASTNode.prototype.processParams_ = function(params) { @@ -235,8 +251,8 @@ Blockly.ASTNode.prototype.processParams_ = function(params) { * Gets the value pointed to by this node. * It is the callers responsibility to check the node type to figure out what * type of object they get back from this. - * @return {!(Blockly.Field|Blockly.Connection|Blockly.Block|Blockly.Workspace)} - * The current field, connection, workspace, or block the cursor is on. + * @return {!Blockly.IASTNodeLocation} The current field, connection, workspace, or + * block the cursor is on. */ Blockly.ASTNode.prototype.getLocation = function() { return this.location_; @@ -279,7 +295,8 @@ Blockly.ASTNode.prototype.isConnection = function() { * @private */ Blockly.ASTNode.prototype.findNextForInput_ = function() { - var parentInput = this.location_.getParentInput(); + var location = /** @type {!Blockly.Connection} */ (this.location_); + var parentInput = location.getParentInput(); var block = parentInput.getSourceBlock(); var curIdx = block.inputList.indexOf(parentInput); for (var i = curIdx + 1, input; (input = block.inputList[i]); i++) { @@ -335,11 +352,12 @@ Blockly.ASTNode.prototype.findNextForField_ = function() { * @private */ Blockly.ASTNode.prototype.findPrevForInput_ = function() { - var location = this.location_.getParentInput(); - var block = location.getSourceBlock(); - var curIdx = block.inputList.indexOf(location); + var location = /** @type {!Blockly.Connection} */ (this.location_); + var parentInput = location.getParentInput(); + var block = parentInput.getSourceBlock(); + var curIdx = block.inputList.indexOf(parentInput); for (var i = curIdx, input; (input = block.inputList[i]); i--) { - if (input.connection && input !== location) { + if (input.connection && input !== parentInput) { return Blockly.ASTNode.createInputNode(input); } var fieldRow = input.fieldRow; @@ -394,7 +412,8 @@ Blockly.ASTNode.prototype.findPrevForField_ = function() { Blockly.ASTNode.prototype.navigateBetweenStacks_ = function(forward) { var curLocation = this.getLocation(); if (!(curLocation instanceof Blockly.Block)) { - curLocation = curLocation.getSourceBlock(); + curLocation = /** @type {!Blockly.IASTNodeLocationWithBlock} */ ( + curLocation).getSourceBlock(); } if (!curLocation || !curLocation.workspace) { return null; @@ -499,7 +518,8 @@ Blockly.ASTNode.prototype.getSourceBlock = function() { } else if (this.getType() === Blockly.ASTNode.types.WORKSPACE) { return null; } else { - return this.getLocation().getSourceBlock(); + return /** @type {Blockly.IASTNodeLocationWithBlock} */ ( + this.getLocation()).getSourceBlock(); } }; @@ -514,7 +534,8 @@ Blockly.ASTNode.prototype.next = function() { return this.navigateBetweenStacks_(true); case Blockly.ASTNode.types.OUTPUT: - return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock()); + var connection = /** @type {!Blockly.Connection} */ (this.location_); + return Blockly.ASTNode.createBlockNode(connection.getSourceBlock()); case Blockly.ASTNode.types.FIELD: return this.findNextForField_(); @@ -523,14 +544,17 @@ Blockly.ASTNode.prototype.next = function() { return this.findNextForInput_(); case Blockly.ASTNode.types.BLOCK: - var nextConnection = this.location_.nextConnection; + var block = /** @type {!Blockly.Block} */ (this.location_); + var nextConnection = block.nextConnection; return Blockly.ASTNode.createConnectionNode(nextConnection); case Blockly.ASTNode.types.PREVIOUS: - return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock()); + var connection = /** @type {!Blockly.Connection} */ (this.location_); + return Blockly.ASTNode.createBlockNode(connection.getSourceBlock()); case Blockly.ASTNode.types.NEXT: - var targetConnection = this.location_.targetConnection; + var connection = /** @type {!Blockly.Connection} */ (this.location_); + var targetConnection = connection.targetConnection; return Blockly.ASTNode.createConnectionNode(targetConnection); } @@ -546,7 +570,8 @@ Blockly.ASTNode.prototype.next = function() { Blockly.ASTNode.prototype.in = function() { switch (this.type_) { case Blockly.ASTNode.types.WORKSPACE: - var topBlocks = this.location_.getTopBlocks(true); + var workspace = /** @type {!Blockly.Workspace} */ (this.location_); + var topBlocks = workspace.getTopBlocks(true); if (topBlocks.length > 0) { return Blockly.ASTNode.createStackNode(topBlocks[0]); } @@ -561,7 +586,8 @@ Blockly.ASTNode.prototype.in = function() { return this.findFirstFieldOrInput_(block); case Blockly.ASTNode.types.INPUT: - var targetConnection = this.location_.targetConnection; + var connection = /** @type {!Blockly.Connection} */ (this.location_); + var targetConnection = connection.targetConnection; return Blockly.ASTNode.createConnectionNode(targetConnection); } @@ -589,19 +615,21 @@ Blockly.ASTNode.prototype.prev = function() { return this.findPrevForInput_(); case Blockly.ASTNode.types.BLOCK: - var block = this.location_; + var block = /** @type {!Blockly.Block} */ (this.location_); var topConnection = block.previousConnection || block.outputConnection; return Blockly.ASTNode.createConnectionNode(topConnection); case Blockly.ASTNode.types.PREVIOUS: - var targetConnection = this.location_.targetConnection; + var connection = /** @type {!Blockly.Connection} */ (this.location_); + var targetConnection = connection.targetConnection; if (targetConnection && !targetConnection.getParentInput()) { return Blockly.ASTNode.createConnectionNode(targetConnection); } break; case Blockly.ASTNode.types.NEXT: - return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock()); + var connection = /** @type {!Blockly.Connection} */ (this.location_); + return Blockly.ASTNode.createBlockNode(connection.getSourceBlock()); } return null; @@ -616,35 +644,40 @@ Blockly.ASTNode.prototype.prev = function() { Blockly.ASTNode.prototype.out = function() { switch (this.type_) { case Blockly.ASTNode.types.STACK: - var blockPos = this.location_.getRelativeToSurfaceXY(); + var block = /** @type {!Blockly.Block} */ (this.location_); + var blockPos = block.getRelativeToSurfaceXY(); // TODO: Make sure this is in the bounds of the workspace. var wsCoordinate = new Blockly.utils.Coordinate( blockPos.x, blockPos.y + Blockly.ASTNode.DEFAULT_OFFSET_Y); - return Blockly.ASTNode.createWorkspaceNode( - this.location_.workspace, wsCoordinate); + return Blockly.ASTNode.createWorkspaceNode(block.workspace, wsCoordinate); case Blockly.ASTNode.types.OUTPUT: - var target = this.location_.targetConnection; + var connection = /** @type {!Blockly.Connection} */ (this.location_); + var target = connection.targetConnection; if (target) { return Blockly.ASTNode.createConnectionNode(target); } - return Blockly.ASTNode.createStackNode(this.location_.getSourceBlock()); + return Blockly.ASTNode.createStackNode(connection.getSourceBlock()); case Blockly.ASTNode.types.FIELD: - return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock()); + var field = /** @type {!Blockly.Field} */ (this.location_); + return Blockly.ASTNode.createBlockNode(field.getSourceBlock()); case Blockly.ASTNode.types.INPUT: - return Blockly.ASTNode.createBlockNode(this.location_.getSourceBlock()); + var connection = /** @type {!Blockly.Connection} */ (this.location_); + return Blockly.ASTNode.createBlockNode(connection.getSourceBlock()); case Blockly.ASTNode.types.BLOCK: var block = /** @type {!Blockly.Block} */ (this.location_); return this.getOutAstNodeForBlock_(block); case Blockly.ASTNode.types.PREVIOUS: - return this.getOutAstNodeForBlock_(this.location_.getSourceBlock()); + var connection = /** @type {!Blockly.Connection} */ (this.location_); + return this.getOutAstNodeForBlock_(connection.getSourceBlock()); case Blockly.ASTNode.types.NEXT: - return this.getOutAstNodeForBlock_(this.location_.getSourceBlock()); + var connection = /** @type {!Blockly.Connection} */ (this.location_); + return this.getOutAstNodeForBlock_(connection.getSourceBlock()); } return null; diff --git a/core/keyboard_nav/cursor.js b/core/keyboard_nav/cursor.js index 1d8b4ae78..ab5aa9218 100644 --- a/core/keyboard_nav/cursor.js +++ b/core/keyboard_nav/cursor.js @@ -19,12 +19,15 @@ goog.require('Blockly.Marker'); goog.require('Blockly.navigation'); goog.require('Blockly.utils.object'); +goog.requireType('Blockly.IBlocklyActionable'); + /** * Class for a cursor. * A cursor controls how a user navigates the Blockly AST. * @constructor * @extends {Blockly.Marker} + * @implements {Blockly.IBlocklyActionable} */ Blockly.Cursor = function() { Blockly.Cursor.superClass_.constructor.call(this); @@ -144,7 +147,8 @@ Blockly.Cursor.prototype.onBlocklyAction = function(action) { // If we are on a field give it the option to handle the action if (this.getCurNode() && this.getCurNode().getType() === Blockly.ASTNode.types.FIELD && - this.getCurNode().getLocation().onBlocklyAction(action)) { + (/** @type {!Blockly.Field} */ (this.getCurNode().getLocation())) + .onBlocklyAction(action)) { return true; } switch (action.name) { diff --git a/core/keyboard_nav/key_map.js b/core/keyboard_nav/key_map.js index e0c4b7957..9ae9bc1b0 100644 --- a/core/keyboard_nav/key_map.js +++ b/core/keyboard_nav/key_map.js @@ -101,7 +101,7 @@ Blockly.user.keyMap.getKeyByAction = function(action) { /** * Serialize the key event. - * @param {!Event} e A key up event holding the key code. + * @param {!KeyboardEvent} e A key up event holding the key code. * @return {string} A string containing the serialized key event. * @package */ diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js index d37fe9973..6364c5556 100644 --- a/core/keyboard_nav/navigation.js +++ b/core/keyboard_nav/navigation.js @@ -102,10 +102,19 @@ Blockly.navigation.MARKER_NAME = 'local_marker_1'; /** * Get the local marker. - * @return {!Blockly.Marker} The local marker for the main workspace. + * @return {Blockly.Marker} The local marker for the main workspace. */ Blockly.navigation.getMarker = function() { - return Blockly.getMainWorkspace().getMarker(Blockly.navigation.MARKER_NAME); + return Blockly.navigation.getNavigationWorkspace() + .getMarker(Blockly.navigation.MARKER_NAME); +}; + +/** + * Get the workspace that is being navigated. + * @return {!Blockly.WorkspaceSvg} The workspace being navigated. + */ +Blockly.navigation.getNavigationWorkspace = function() { + return /** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace()); }; /** @@ -114,8 +123,7 @@ Blockly.navigation.getMarker = function() { * @private */ Blockly.navigation.focusToolbox_ = function() { - var workspace = Blockly.getMainWorkspace(); - var toolbox = workspace.getToolbox(); + var toolbox = Blockly.navigation.getNavigationWorkspace().getToolbox(); if (toolbox) { Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX; Blockly.navigation.resetFlyout_(false /* shouldHide */); @@ -134,9 +142,9 @@ Blockly.navigation.focusToolbox_ = function() { Blockly.navigation.focusFlyout_ = function() { var topBlock = null; Blockly.navigation.currentState_ = Blockly.navigation.STATE_FLYOUT; - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var toolbox = workspace.getToolbox(); - var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); + var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout(); if (!Blockly.navigation.getMarker().getCurNode()) { Blockly.navigation.markAtCursor_(); @@ -159,7 +167,7 @@ Blockly.navigation.focusFlyout_ = function() { */ Blockly.navigation.focusWorkspace_ = function() { Blockly.hideChaff(); - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var cursor = workspace.getCursor(); var reset = !!workspace.getToolbox(); var topBlocks = workspace.getTopBlocks(true); @@ -186,14 +194,14 @@ Blockly.navigation.focusWorkspace_ = function() { * @private */ Blockly.navigation.getFlyoutCursor_ = function() { - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var cursor = null; if (workspace.rendered) { var toolbox = workspace.getToolbox(); - var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); - cursor = flyout ? flyout.workspace_.getCursor() : null; + var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout(); + cursor = flyout ? flyout.getWorkspace().getCursor() : null; } - return cursor; + return /** @type {Blockly.FlyoutCursor} */ (cursor); }; /** @@ -202,7 +210,7 @@ Blockly.navigation.getFlyoutCursor_ = function() { * it on the workspace. */ Blockly.navigation.insertFromFlyout = function() { - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var flyout = workspace.getFlyout(); if (!flyout || !flyout.isVisible()) { Blockly.navigation.warn_('Trying to insert from the flyout when the flyout does not ' + @@ -210,7 +218,8 @@ Blockly.navigation.insertFromFlyout = function() { return; } - var curBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation(); + var curBlock = /** @type {!Blockly.BlockSvg} */ ( + Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation()); if (!curBlock.isEnabled()) { Blockly.navigation.warn_('Can\'t insert a disabled block.'); return; @@ -243,7 +252,7 @@ Blockly.navigation.resetFlyout_ = function(shouldHide) { if (Blockly.navigation.getFlyoutCursor_()) { Blockly.navigation.getFlyoutCursor_().hide(); if (shouldHide) { - Blockly.getMainWorkspace().getFlyout().hide(); + Blockly.navigation.getNavigationWorkspace().getFlyout().hide(); } } }; @@ -260,7 +269,8 @@ Blockly.navigation.resetFlyout_ = function(shouldHide) { */ Blockly.navigation.modifyWarn_ = function() { var markerNode = Blockly.navigation.getMarker().getCurNode(); - var cursorNode = Blockly.getMainWorkspace().getCursor().getCurNode(); + var cursorNode = Blockly.navigation.getNavigationWorkspace() + .getCursor().getCurNode(); if (!markerNode) { Blockly.navigation.warn_('Cannot insert with no marked node.'); @@ -300,7 +310,7 @@ Blockly.navigation.modifyWarn_ = function() { /** * Disconnect the block from its parent and move to the position of the * workspace node. - * @param {Blockly.Block} block The block to be moved to the workspace. + * @param {Blockly.BlockSvg} block The block to be moved to the workspace. * @param {!Blockly.ASTNode} wsNode The workspace node holding the position the * block will be moved to. * @return {boolean} True if the block can be moved to the workspace, @@ -331,7 +341,8 @@ Blockly.navigation.moveBlockToWorkspace_ = function(block, wsNode) { */ Blockly.navigation.modify_ = function() { var markerNode = Blockly.navigation.getMarker().getCurNode(); - var cursorNode = Blockly.getMainWorkspace().getCursor().getCurNode(); + var cursorNode = Blockly.navigation.getNavigationWorkspace() + .getCursor().getCurNode(); if (!Blockly.navigation.modifyWarn_()) { return false; } @@ -343,18 +354,19 @@ Blockly.navigation.modify_ = function() { var markerLoc = markerNode.getLocation(); if (markerNode.isConnection() && cursorNode.isConnection()) { - cursorLoc = /** @type {!Blockly.Connection} */ (cursorLoc); - markerLoc = /** @type {!Blockly.Connection} */ (markerLoc); + cursorLoc = /** @type {!Blockly.RenderedConnection} */ (cursorLoc); + markerLoc = /** @type {!Blockly.RenderedConnection} */ (markerLoc); return Blockly.navigation.connect_(cursorLoc, markerLoc); } else if (markerNode.isConnection() && (cursorType == Blockly.ASTNode.types.BLOCK || cursorType == Blockly.ASTNode.types.STACK)) { - cursorLoc = /** @type {!Blockly.Block} */ (cursorLoc); - markerLoc = /** @type {!Blockly.Connection} */ (markerLoc); + cursorLoc = /** @type {!Blockly.BlockSvg} */ (cursorLoc); + markerLoc = /** @type {!Blockly.RenderedConnection} */ (markerLoc); return Blockly.navigation.insertBlock(cursorLoc, markerLoc); } else if (markerType == Blockly.ASTNode.types.WORKSPACE) { var block = cursorNode ? cursorNode.getSourceBlock() : null; - return Blockly.navigation.moveBlockToWorkspace_(block, markerNode); + return Blockly.navigation.moveBlockToWorkspace_( + /** @type {Blockly.BlockSvg} */ (block), markerNode); } Blockly.navigation.warn_('Unexpected state in Blockly.navigation.modify_.'); return false; @@ -363,9 +375,10 @@ Blockly.navigation.modify_ = function() { /** * If one of the connections source blocks is a child of the other, disconnect * the child. - * @param {!Blockly.Connection} movingConnection The connection that is being - * moved. - * @param {!Blockly.Connection} destConnection The connection to be moved to. + * @param {!Blockly.RenderedConnection} movingConnection The connection that is + * being moved. + * @param {!Blockly.RenderedConnection} destConnection The connection to be + * moved to. * @private */ Blockly.navigation.disconnectChild_ = function(movingConnection, destConnection) { @@ -384,9 +397,10 @@ Blockly.navigation.disconnectChild_ = function(movingConnection, destConnection) /** * If the two blocks are compatible move the moving connection to the target * connection and connect them. - * @param {Blockly.Connection} movingConnection The connection that is being - * moved. - * @param {Blockly.Connection} destConnection The connection to be moved to. + * @param {Blockly.RenderedConnection} movingConnection The connection that is + * being moved. + * @param {Blockly.RenderedConnection} destConnection The connection to be moved + * to. * @return {boolean} True if the connections were connected, false otherwise. * @private */ @@ -414,8 +428,10 @@ Blockly.navigation.moveAndConnect_ = function(movingConnection, destConnection) /** * If the given connection is superior find the inferior connection on the * source block. - * @param {Blockly.Connection} connection The connection trying to be connected. - * @return {Blockly.Connection} The inferior connection or null if none exists. + * @param {Blockly.RenderedConnection} connection The connection trying to be + * connected. + * @return {Blockly.RenderedConnection} The inferior connection or null if none + * exists. * @private */ Blockly.navigation.getInferiorConnection_ = function(connection) { @@ -434,8 +450,10 @@ Blockly.navigation.getInferiorConnection_ = function(connection) { /** * If the given connection is inferior tries to find a superior connection to * connect to. - * @param {Blockly.Connection} connection The connection trying to be connected. - * @return {Blockly.Connection} The superior connection or null if none exists. + * @param {Blockly.RenderedConnection} connection The connection trying to be + * connected. + * @return {Blockly.RenderedConnection} The superior connection or null if none + * exists. * @private */ Blockly.navigation.getSuperiorConnection_ = function(connection) { @@ -453,9 +471,10 @@ Blockly.navigation.getSuperiorConnection_ = function(connection) { * If the given connections are not compatible try finding compatible connections * on the source blocks of the given connections. * - * @param {Blockly.Connection} movingConnection The connection that is being - * moved. - * @param {Blockly.Connection} destConnection The connection to be moved to. + * @param {Blockly.RenderedConnection} movingConnection The connection that is + * being moved. + * @param {Blockly.RenderedConnection} destConnection The connection to be moved + * to. * @return {boolean} True if the two connections or their target connections * were connected, false otherwise. * @private @@ -495,8 +514,9 @@ Blockly.navigation.connect_ = function(movingConnection, destConnection) { /** * Tries to connect the given block to the destination connection, making an * intelligent guess about which connection to use to on the moving block. - * @param {!Blockly.Block} block The block to move. - * @param {!Blockly.Connection} destConnection The connection to connect to. + * @param {!Blockly.BlockSvg} block The block to move. + * @param {!Blockly.RenderedConnection} destConnection The connection to connect + * to. * @return {boolean} Whether the connection was successful. */ Blockly.navigation.insertBlock = function(block, destConnection) { @@ -518,7 +538,8 @@ Blockly.navigation.insertBlock = function(block, destConnection) { break; case Blockly.OUTPUT_VALUE: for (var i = 0; i < block.inputList.length; i++) { - var inputConnection = block.inputList[i].connection; + var inputConnection = /** @type {Blockly.RenderedConnection} */ ( + block.inputList[i].connection); if (inputConnection && inputConnection.type === Blockly.INPUT_VALUE && Blockly.navigation.connect_(inputConnection, destConnection)) { return true; @@ -543,13 +564,14 @@ Blockly.navigation.insertBlock = function(block, destConnection) { * @private */ Blockly.navigation.disconnectBlocks_ = function() { - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var curNode = workspace.getCursor().getCurNode(); if (!curNode.isConnection()) { Blockly.navigation.log_('Cannot disconnect blocks when the cursor is not on a connection'); return; } - var curConnection = /** @type {!Blockly.Connection} */ (curNode.getLocation()); + var curConnection = + /** @type {!Blockly.RenderedConnection} */ (curNode.getLocation()); if (!curConnection.isConnected()) { Blockly.navigation.log_('Cannot disconnect unconnected connection'); return; @@ -584,7 +606,7 @@ Blockly.navigation.disconnectBlocks_ = function() { */ Blockly.navigation.markAtCursor_ = function() { Blockly.navigation.getMarker().setCurNode( - Blockly.getMainWorkspace().getCursor().getCurNode()); + Blockly.navigation.getNavigationWorkspace().getCursor().getCurNode()); }; /** @@ -608,10 +630,10 @@ Blockly.navigation.setState = function(newState) { /** * Before a block is deleted move the cursor to the appropriate position. - * @param {!Blockly.Block} deletedBlock The block that is being deleted. + * @param {!Blockly.BlockSvg} deletedBlock The block that is being deleted. */ Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) { - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); if (!workspace) { return; } @@ -645,11 +667,11 @@ Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) { /** * When a block that the cursor is on is mutated move the cursor to the block * level. - * @param {!Blockly.Block} mutatedBlock The block that is being mutated. + * @param {!Blockly.BlockSvg} mutatedBlock The block that is being mutated. * @package */ Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) { - var cursor = Blockly.getMainWorkspace().getCursor(); + var cursor = Blockly.navigation.getNavigationWorkspace().getCursor(); if (cursor) { var curNode = cursor.getCurNode(); var block = curNode ? curNode.getSourceBlock() : null; @@ -664,8 +686,9 @@ Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) { * Enable accessibility mode. */ Blockly.navigation.enableKeyboardAccessibility = function() { - if (!Blockly.getMainWorkspace().keyboardAccessibilityMode) { - Blockly.getMainWorkspace().keyboardAccessibilityMode = true; + var workspace = Blockly.navigation.getNavigationWorkspace(); + if (!workspace.keyboardAccessibilityMode) { + workspace.keyboardAccessibilityMode = true; Blockly.navigation.focusWorkspace_(); } }; @@ -674,9 +697,9 @@ Blockly.navigation.enableKeyboardAccessibility = function() { * Disable accessibility mode. */ Blockly.navigation.disableKeyboardAccessibility = function() { - if (Blockly.getMainWorkspace().keyboardAccessibilityMode) { - var workspace = Blockly.getMainWorkspace(); - Blockly.getMainWorkspace().keyboardAccessibilityMode = false; + var workspace = Blockly.navigation.getNavigationWorkspace(); + if (workspace.keyboardAccessibilityMode) { + workspace.keyboardAccessibilityMode = false; workspace.getCursor().hide(); Blockly.navigation.getMarker().hide(); if (Blockly.navigation.getFlyoutCursor_()) { @@ -733,7 +756,7 @@ Blockly.navigation.error_ = function(msg) { /** * Handler for all the keyboard navigation events. - * @param {!Event} e The keyboard event. + * @param {!KeyboardEvent} e The keyboard event. * @return {boolean} True if the key was handled false otherwise. */ Blockly.navigation.onKeyPress = function(e) { @@ -753,10 +776,11 @@ Blockly.navigation.onKeyPress = function(e) { * @return {boolean} True if the action has been handled, false otherwise. */ Blockly.navigation.onBlocklyAction = function(action) { - var readOnly = Blockly.getMainWorkspace().options.readOnly; + var workspace = Blockly.navigation.getNavigationWorkspace(); + var readOnly = workspace.options.readOnly; var actionHandled = false; - if (Blockly.getMainWorkspace().keyboardAccessibilityMode) { + if (workspace.keyboardAccessibilityMode) { if (!readOnly) { actionHandled = Blockly.navigation.handleActions_(action); // If in readonly mode only handle valid actions. @@ -799,9 +823,9 @@ Blockly.navigation.handleActions_ = function(action) { * @private */ Blockly.navigation.flyoutOnAction_ = function(action) { - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var toolbox = workspace.getToolbox(); - var flyout = toolbox ? toolbox.flyout_ : workspace.getFlyout(); + var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout(); if (flyout && flyout.onBlocklyAction(action)) { return true; @@ -829,7 +853,7 @@ Blockly.navigation.flyoutOnAction_ = function(action) { * @private */ Blockly.navigation.toolboxOnAction_ = function(action) { - var workspace = Blockly.getMainWorkspace(); + var workspace = Blockly.navigation.getNavigationWorkspace(); var toolbox = workspace.getToolbox(); var handled = toolbox ? toolbox.onBlocklyAction(action) : false; @@ -862,8 +886,9 @@ Blockly.navigation.toolboxOnAction_ = function(action) { * @private */ Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) { - var cursor = Blockly.getMainWorkspace().getCursor(); - var curNode = Blockly.getMainWorkspace().getCursor().getCurNode(); + var workspace = Blockly.navigation.getNavigationWorkspace(); + var cursor = workspace.getCursor(); + var curNode = workspace.getCursor().getCurNode(); if (curNode.getType() !== Blockly.ASTNode.types.WORKSPACE) { return false; @@ -874,7 +899,7 @@ Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) { var newY = yDirection * Blockly.navigation.WS_MOVE_DISTANCE + wsCoord.y; cursor.setCurNode(Blockly.ASTNode.createWorkspaceNode( - Blockly.getMainWorkspace(), new Blockly.utils.Coordinate(newX, newY))); + workspace, new Blockly.utils.Coordinate(newX, newY))); return true; }; @@ -885,7 +910,8 @@ Blockly.navigation.moveWSCursor_ = function(xDirection, yDirection) { * @private */ Blockly.navigation.workspaceOnAction_ = function(action) { - if (Blockly.getMainWorkspace().getCursor().onBlocklyAction(action)) { + var workspace = Blockly.navigation.getNavigationWorkspace(); + if (workspace.getCursor().onBlocklyAction(action)) { return true; } switch (action.name) { @@ -916,11 +942,11 @@ Blockly.navigation.workspaceOnAction_ = function(action) { * @private */ Blockly.navigation.handleEnterForWS_ = function() { - var cursor = Blockly.getMainWorkspace().getCursor(); + var cursor = Blockly.navigation.getNavigationWorkspace().getCursor(); var curNode = cursor.getCurNode(); var nodeType = curNode.getType(); if (nodeType == Blockly.ASTNode.types.FIELD) { - curNode.getLocation().showEditor(); + (/** @type {!Blockly.Field} */(curNode.getLocation())).showEditor(); } else if (curNode.isConnection() || nodeType == Blockly.ASTNode.types.WORKSPACE) { Blockly.navigation.markAtCursor_(); diff --git a/core/keyboard_nav/tab_navigate_cursor.js b/core/keyboard_nav/tab_navigate_cursor.js index 5e85136a9..9b403bd0a 100644 --- a/core/keyboard_nav/tab_navigate_cursor.js +++ b/core/keyboard_nav/tab_navigate_cursor.js @@ -38,10 +38,9 @@ Blockly.TabNavigateCursor.prototype.validNode_ = function(node) { var isValid = false; var type = node && node.getType(); if (node) { - var location = node.getLocation(); + var location = /** @type {Blockly.Field} */ (node.getLocation()); if (type == Blockly.ASTNode.types.FIELD && - location && location.isTabNavigable() && - (/** @type {!Blockly.Field} */ (location)).isClickable()) { + location && location.isTabNavigable() && location.isClickable()) { isValid = true; } } diff --git a/core/mutator.js b/core/mutator.js index 972809928..dd5c844e8 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -378,7 +378,8 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) { block.initSvg(); block.render(); - if (Blockly.getMainWorkspace().keyboardAccessibilityMode) { + if ((/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace())) + .keyboardAccessibilityMode) { Blockly.navigation.moveCursorOnBlockMutation(block); } var newMutationDom = block.mutationToDom(); diff --git a/core/rendered_connection.js b/core/rendered_connection.js index fa6c45643..811a0fe6b 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -59,6 +59,12 @@ Blockly.RenderedConnection = function(source, type) { * @private */ this.trackedState_ = Blockly.RenderedConnection.TrackedState.WILL_TRACK; + + /** + * Connection this connection connects to. Null if not connected. + * @type {Blockly.RenderedConnection} + */ + this.targetConnection = null; }; Blockly.utils.object.inherits(Blockly.RenderedConnection, Blockly.Connection); diff --git a/core/renderers/common/marker_svg.js b/core/renderers/common/marker_svg.js index ce3225890..2fda38c66 100644 --- a/core/renderers/common/marker_svg.js +++ b/core/renderers/common/marker_svg.js @@ -42,7 +42,7 @@ Blockly.blockRendering.MarkerSvg = function(workspace, constants, marker) { /** * The workspace, field, or block that the marker SVG element should be * attached to. - * @type {Blockly.WorkspaceSvg|Blockly.Field|Blockly.BlockSvg} + * @type {Blockly.IASTNodeLocationSvg} * @private */ this.parent_ = null; @@ -135,9 +135,8 @@ Blockly.blockRendering.MarkerSvg.prototype.createDom = function() { /** * Attaches the SVG root of the marker to the SVG group of the parent. - * @param {!Blockly.WorkspaceSvg|!Blockly.Field|!Blockly.BlockSvg} newParent - * The workspace, field, or block that the marker SVG element should be - * attached to. + * @param {!Blockly.IASTNodeLocationSvg} newParent The workspace, field, or + * block that the marker SVG element should be attached to. * @protected */ Blockly.blockRendering.MarkerSvg.prototype.setParent_ = function(newParent) { @@ -191,13 +190,15 @@ Blockly.blockRendering.MarkerSvg.prototype.draw = function(oldNode, curNode) { * @protected */ Blockly.blockRendering.MarkerSvg.prototype.showAtLocation_ = function(curNode) { + var curNodeAsConnection = + /** @type {!Blockly.Connection} */ (curNode.getLocation()); if (curNode.getType() == Blockly.ASTNode.types.BLOCK) { this.showWithBlock_(curNode); } else if (curNode.getType() == Blockly.ASTNode.types.OUTPUT) { this.showWithOutput_(curNode); - } else if (curNode.getLocation().type == Blockly.INPUT_VALUE) { + } else if (curNodeAsConnection.type == Blockly.INPUT_VALUE) { this.showWithInput_(curNode); - } else if (curNode.getLocation().type == Blockly.NEXT_STATEMENT) { + } else if (curNodeAsConnection.type == Blockly.NEXT_STATEMENT) { this.showWithNext_(curNode); } else if (curNode.getType() == Blockly.ASTNode.types.PREVIOUS) { this.showWithPrevious_(curNode); @@ -330,8 +331,10 @@ Blockly.blockRendering.MarkerSvg.prototype.showWithInput_ = function(curNode) { * @protected */ Blockly.blockRendering.MarkerSvg.prototype.showWithNext_ = function(curNode) { - var connection = curNode.getLocation(); - var targetBlock = /** @type {Blockly.BlockSvg} */ (connection.getSourceBlock()); + var connection = + /** @type {!Blockly.RenderedConnection} */ (curNode.getLocation()); + var targetBlock = + /** @type {Blockly.BlockSvg} */ (connection.getSourceBlock()); var x = 0; var y = connection.getOffsetInBlock().y; var width = targetBlock.getHeightWidth().width; @@ -548,7 +551,8 @@ Blockly.blockRendering.MarkerSvg.prototype.fireMarkerEvent_ = function( var eventType = this.isCursor() ? 'cursorMove' : 'markerMove'; var event = new Blockly.Events.Ui(curBlock, eventType, oldNode, curNode); if (curNode.getType() == Blockly.ASTNode.types.WORKSPACE) { - event.workspaceId = curNode.getLocation().id; + event.workspaceId = + (/** @type {!Blockly.Workspace} */ (curNode.getLocation())).id; } Blockly.Events.fire(event); }; diff --git a/core/renderers/zelos/marker_svg.js b/core/renderers/zelos/marker_svg.js index a118f76c4..0b5d31f12 100644 --- a/core/renderers/zelos/marker_svg.js +++ b/core/renderers/zelos/marker_svg.js @@ -38,7 +38,7 @@ Blockly.utils.object.inherits(Blockly.zelos.MarkerSvg, */ Blockly.zelos.MarkerSvg.prototype.showWithInputOutput_ = function(curNode) { var block = /** @type {!Blockly.BlockSvg} */ (curNode.getSourceBlock()); - var connection = curNode.getLocation(); + var connection = /** @type {!Blockly.Connection} */ (curNode.getLocation()); var offsetInBlock = connection.getOffsetInBlock(); this.positionCircle_(offsetInBlock.x, offsetInBlock.y); diff --git a/core/toolbox.js b/core/toolbox.js index fcf2b7cf1..892eafc28 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -27,6 +27,8 @@ goog.require('Blockly.utils.object'); goog.require('Blockly.utils.Rect'); goog.require('Blockly.utils.toolbox'); +goog.requireType('Blockly.IBlocklyActionable'); + /** * Class for a Toolbox. @@ -34,6 +36,7 @@ goog.require('Blockly.utils.toolbox'); * @param {!Blockly.WorkspaceSvg} workspace The workspace in which to create new * blocks. * @constructor + * @implements {Blockly.IBlocklyActionable} */ Blockly.Toolbox = function(workspace) { /** diff --git a/core/workspace.js b/core/workspace.js index f16a2850e..364aa1222 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -18,12 +18,15 @@ goog.require('Blockly.utils'); goog.require('Blockly.utils.math'); goog.require('Blockly.VariableMap'); +goog.requireType('Blockly.IASTNodeLocation'); + /** * Class for a workspace. This is a data structure that contains blocks. * There is no UI, and can be created headlessly. * @param {!Blockly.Options=} opt_options Dictionary of options. * @constructor + * @implements {Blockly.IASTNodeLocation} */ Blockly.Workspace = function(opt_options) { /** @type {string} */ diff --git a/core/workspace_svg.js b/core/workspace_svg.js index bfd1057de..3037e2c9f 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -39,6 +39,7 @@ goog.require('Blockly.WorkspaceDragSurfaceSvg'); goog.require('Blockly.Xml'); goog.requireType('Blockly.blockRendering.Renderer'); +goog.requireType('Blockly.IASTNodeLocationSvg'); goog.requireType('Blockly.IBoundedElement'); @@ -51,6 +52,7 @@ goog.requireType('Blockly.IBoundedElement'); * @param {Blockly.WorkspaceDragSurfaceSvg=} opt_wsDragSurface Drag surface for * the workspace. * @extends {Blockly.Workspace} + * @implements {Blockly.IASTNodeLocationSvg} * @constructor */ Blockly.WorkspaceSvg = function(options, @@ -1298,8 +1300,9 @@ Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) { if (this.keyboardAccessibilityMode && markedNode && markedNode.isConnection()) { var markedLocation = - /** @type {!Blockly.Connection} */ (markedNode.getLocation()); - Blockly.navigation.insertBlock(block, markedLocation); + /** @type {!Blockly.RenderedConnection} */ (markedNode.getLocation()); + Blockly.navigation.insertBlock(/** @type {!Blockly.BlockSvg} */ (block), + markedLocation); return; }