diff --git a/core/block.js b/core/block.js
index 5b84b62ec..9b6d0031a 100644
--- a/core/block.js
+++ b/core/block.js
@@ -24,7 +24,6 @@ goog.require('Blockly.Events.BlockMove');
goog.require('Blockly.Extensions');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.Input');
-goog.require('Blockly.navigation');
goog.require('Blockly.Tooltip');
goog.require('Blockly.utils');
goog.require('Blockly.utils.deprecation');
diff --git a/core/block_svg.js b/core/block_svg.js
index ed9b58fee..b391fb7a2 100644
--- a/core/block_svg.js
+++ b/core/block_svg.js
@@ -23,7 +23,6 @@ goog.require('Blockly.Events');
goog.require('Blockly.Events.BlockMove');
goog.require('Blockly.Events.Selected');
goog.require('Blockly.Msg');
-goog.require('Blockly.navigation');
goog.require('Blockly.RenderedConnection');
goog.require('Blockly.TabNavigateCursor');
goog.require('Blockly.Tooltip');
@@ -692,11 +691,12 @@ Blockly.BlockSvg.prototype.tab = function(start, forward) {
var tabCursor = new Blockly.TabNavigateCursor();
tabCursor.setCurNode(Blockly.ASTNode.createFieldNode(start));
var currentNode = tabCursor.getCurNode();
- var actionName = forward ?
- Blockly.navigation.actionNames.NEXT : Blockly.navigation.actionNames.PREVIOUS;
- tabCursor.onBlocklyAction(
- /** @type {!Blockly.ShortcutRegistry.KeyboardShortcut} */ ({name: actionName}));
+ if (forward) {
+ tabCursor.next();
+ } else {
+ tabCursor.prev();
+ }
var nextNode = tabCursor.getCurNode();
if (nextNode && nextNode !== currentNode) {
@@ -907,10 +907,6 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
Blockly.ContextMenu.hide();
}
- if (this.workspace.keyboardAccessibilityMode) {
- Blockly.navigation.moveCursorOnBlockDelete(this);
- }
-
if (animate && this.rendered) {
this.unplug(healStack);
Blockly.blockAnimations.disposeUiEffect(this);
@@ -1660,7 +1656,8 @@ Blockly.BlockSvg.prototype.updateMarkers_ = function() {
this.workspace.getCursor().draw();
}
if (this.workspace.keyboardAccessibilityMode && this.pathObject.markerSvg) {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).draw();
+ // TODO(#4592): Update all markers on the block.
+ this.workspace.getMarker(Blockly.MarkerManager.LOCAL_MARKER).draw();
}
};
diff --git a/core/field.js b/core/field.js
index 94b67f89f..94f4a18fc 100644
--- a/core/field.js
+++ b/core/field.js
@@ -29,7 +29,7 @@ goog.require('Blockly.utils.userAgent');
goog.requireType('Blockly.blockRendering.ConstantProvider');
goog.requireType('Blockly.IASTNodeLocationSvg');
goog.requireType('Blockly.IASTNodeLocationWithBlock');
-goog.requireType('Blockly.IBlocklyActionable');
+goog.requireType('Blockly.IKeyboardAccessible');
goog.requireType('Blockly.IRegistrable');
goog.requireType('Blockly.ShortcutRegistry');
@@ -46,7 +46,7 @@ goog.requireType('Blockly.ShortcutRegistry');
* @constructor
* @implements {Blockly.IASTNodeLocationSvg}
* @implements {Blockly.IASTNodeLocationWithBlock}
- * @implements {Blockly.IBlocklyActionable}
+ * @implements {Blockly.IKeyboardAccessible}
* @implements {Blockly.IRegistrable}
*/
Blockly.Field = function(value, opt_validator, opt_config) {
@@ -1024,13 +1024,12 @@ Blockly.Field.prototype.isTabNavigable = function() {
};
/**
- * Handles the given action.
- * This is only triggered when keyboard accessibility mode is enabled.
- * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _action The action to be handled.
- * @return {boolean} True if the field handled the action, false otherwise.
- * @package
+ * Handles the given keyboard shortcut.
+ * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _shortcut The shortcut to be handled.
+ * @return {boolean} True if the shortcut has been handled, false otherwise.
+ * @public
*/
-Blockly.Field.prototype.onBlocklyAction = function(_action) {
+Blockly.Field.prototype.onShortcut = function(_shortcut) {
return false;
};
@@ -1077,6 +1076,7 @@ Blockly.Field.prototype.updateMarkers_ = function() {
workspace.getCursor().draw();
}
if (workspace.keyboardAccessibilityMode && this.markerSvg_) {
- workspace.getMarker(Blockly.navigation.MARKER_NAME).draw();
+ // TODO(#4592): Update all markers on the field.
+ workspace.getMarker(Blockly.MarkerManager.LOCAL_MARKER).draw();
}
};
diff --git a/core/field_colour.js b/core/field_colour.js
index 6a316237c..7ff85e5cd 100644
--- a/core/field_colour.js
+++ b/core/field_colour.js
@@ -18,7 +18,6 @@ goog.require('Blockly.Events');
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
-goog.require('Blockly.navigation');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.colour');
goog.require('Blockly.utils.dom');
@@ -382,35 +381,6 @@ Blockly.FieldColour.prototype.onKeyDown_ = function(e) {
}
};
-/**
- * Handles the given action.
- * This is only triggered when keyboard accessibility mode is enabled.
- * @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_) {
- 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);
-};
-
/**
* Move the currently highlighted position by dx and dy.
* @param {number} dx Change of x
diff --git a/core/field_dropdown.js b/core/field_dropdown.js
index d1b0af929..58bc6b5c5 100644
--- a/core/field_dropdown.js
+++ b/core/field_dropdown.js
@@ -20,7 +20,6 @@ goog.require('Blockly.Field');
goog.require('Blockly.fieldRegistry');
goog.require('Blockly.Menu');
goog.require('Blockly.MenuItem');
-goog.require('Blockly.navigation');
goog.require('Blockly.utils');
goog.require('Blockly.utils.aria');
goog.require('Blockly.utils.Coordinate');
@@ -738,28 +737,4 @@ Blockly.FieldDropdown.validateOptions_ = function(options) {
}
};
-/**
- * Handles the given action.
- * This is only triggered when keyboard accessibility mode is enabled.
- * @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_) {
- 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);
-};
-
-
Blockly.fieldRegistry.register('field_dropdown', Blockly.FieldDropdown);
diff --git a/core/flyout_base.js b/core/flyout_base.js
index 14000f898..a1765e898 100644
--- a/core/flyout_base.js
+++ b/core/flyout_base.js
@@ -17,7 +17,6 @@ goog.require('Blockly.blockRendering');
goog.require('Blockly.Events');
goog.require('Blockly.Events.BlockCreate');
goog.require('Blockly.Events.VarCreate');
-goog.require('Blockly.FlyoutCursor');
goog.require('Blockly.Gesture');
goog.require('Blockly.Marker');
goog.require('Blockly.Scrollbar');
@@ -31,7 +30,6 @@ goog.require('Blockly.utils.toolbox');
goog.require('Blockly.WorkspaceSvg');
goog.require('Blockly.Xml');
-goog.requireType('Blockly.IBlocklyActionable');
goog.requireType('Blockly.IDeleteArea');
goog.requireType('Blockly.IFlyout');
goog.requireType('Blockly.ShortcutRegistry');
@@ -44,7 +42,6 @@ goog.requireType('Blockly.utils.Metrics');
* workspace.
* @constructor
* @abstract
- * @implements {Blockly.IBlocklyActionable}
* @implements {Blockly.IDeleteArea}
* @implements {Blockly.IFlyout}
*/
@@ -253,7 +250,6 @@ Blockly.Flyout.prototype.createDom = function(tagName) {
this.svgBackground_, 'flyoutBackgroundColour', 'fill');
this.workspace_.getThemeManager().subscribe(
this.svgBackground_, 'flyoutOpacity', 'fill-opacity');
- this.workspace_.getMarkerManager().setCursor(new Blockly.FlyoutCursor());
return this.svgGroup_;
};
@@ -995,18 +991,6 @@ Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) {
return block;
};
-/**
- * Handles the given action.
- * This is only triggered when keyboard accessibility mode is enabled.
- * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
- * @return {boolean} True if the flyout handled the action, false otherwise.
- * @package
- */
-Blockly.Flyout.prototype.onBlocklyAction = function(action) {
- var cursor = this.workspace_.getCursor();
- return cursor.onBlocklyAction(action);
-};
-
/**
* Return the deletion rectangle for this flyout in viewport coordinates.
* @return {Blockly.utils.Rect} Rectangle in which to delete.
diff --git a/core/gesture.js b/core/gesture.js
index 30a1faa5e..7fe1d07fa 100644
--- a/core/gesture.js
+++ b/core/gesture.js
@@ -21,7 +21,6 @@ goog.require('Blockly.constants');
goog.require('Blockly.Events');
goog.require('Blockly.Events.Click');
goog.require('Blockly.FlyoutDragger');
-goog.require('Blockly.navigation');
goog.require('Blockly.Tooltip');
goog.require('Blockly.Touch');
goog.require('Blockly.utils');
@@ -488,13 +487,7 @@ Blockly.Gesture.prototype.doStart = function(e) {
Blockly.Tooltip.block();
if (this.targetBlock_) {
- if (!this.targetBlock_.isInFlyout && e.shiftKey &&
- this.targetBlock_.workspace.keyboardAccessibilityMode) {
- this.creatorWorkspace_.getCursor().setCurNode(
- Blockly.ASTNode.createTopNode(this.targetBlock_));
- } else {
- this.targetBlock_.select();
- }
+ this.targetBlock_.select();
}
if (Blockly.utils.isRightButton(e)) {
@@ -582,7 +575,7 @@ Blockly.Gesture.prototype.handleUp = function(e) {
} else if (this.isBlockClick_()) {
this.doBlockClick_();
} else if (this.isWorkspaceClick_()) {
- this.doWorkspaceClick_(e);
+ this.doWorkspaceClick_();
}
e.preventDefault();
@@ -654,9 +647,6 @@ Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
this.setStartWorkspace_(ws);
this.mostRecentEvent_ = e;
this.doStart(e);
- if (this.startWorkspace_.keyboardAccessibilityMode) {
- Blockly.navigation.setState(Blockly.navigation.STATE_WS);
- }
};
/**
@@ -767,17 +757,11 @@ Blockly.Gesture.prototype.doBlockClick_ = function() {
/**
* Execute a workspace click. When in accessibility mode shift clicking will
* move the cursor.
- * @param {!Event} e A mouse up or touch end event.
* @private
*/
-Blockly.Gesture.prototype.doWorkspaceClick_ = function(e) {
+Blockly.Gesture.prototype.doWorkspaceClick_ = function() {
var ws = this.creatorWorkspace_;
- if (e.shiftKey && ws.keyboardAccessibilityMode) {
- var screenCoord = new Blockly.utils.Coordinate(e.clientX, e.clientY);
- var wsCoord = Blockly.utils.screenToWsCoordinates(ws, screenCoord);
- var wsNode = Blockly.ASTNode.createWorkspaceNode(ws, wsCoord);
- ws.getCursor().setCurNode(wsNode);
- } else if (Blockly.selected) {
+ if (Blockly.selected) {
Blockly.selected.unselect();
}
this.fireWorkspaceClick_(this.startWorkspace_ || ws);
diff --git a/core/interfaces/i_accessibility.js b/core/interfaces/i_accessibility.js
index d1275500d..ab05e16d4 100644
--- a/core/interfaces/i_accessibility.js
+++ b/core/interfaces/i_accessibility.js
@@ -14,7 +14,7 @@
goog.provide('Blockly.IASTNodeLocation');
goog.provide('Blockly.IASTNodeLocationSvg');
goog.provide('Blockly.IASTNodeLocationWithBlock');
-goog.provide('Blockly.IBlocklyActionable');
+goog.provide('Blockly.IKeyboardAccessible');
goog.requireType('Blockly.ShortcutRegistry');
/**
@@ -59,15 +59,14 @@ Blockly.IASTNodeLocationWithBlock.prototype.getSourceBlock;
/**
- * An interface for an object that handles Blockly actions when keyboard
- * navigation is enabled.
+ * An interface for an object that handles keyboard shortcuts.
* @interface
*/
-Blockly.IBlocklyActionable = function() {};
+Blockly.IKeyboardAccessible = function() {};
/**
- * Handles the given action.
- * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
- * @return {boolean} True if the action has been handled, false otherwise.
+ * Handles the given keyboard shortcut.
+ * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} shortcut The shortcut to be handled.
+ * @return {boolean} True if the shortcut has been handled, false otherwise.
*/
-Blockly.IBlocklyActionable.prototype.onBlocklyAction;
+Blockly.IKeyboardAccessible.prototype.onShortcut;
diff --git a/core/keyboard_nav/cursor.js b/core/keyboard_nav/cursor.js
index 8a2a3e97a..501bb3c5c 100644
--- a/core/keyboard_nav/cursor.js
+++ b/core/keyboard_nav/cursor.js
@@ -15,10 +15,8 @@ goog.provide('Blockly.Cursor');
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');
@@ -27,7 +25,6 @@ goog.requireType('Blockly.ShortcutRegistry');
* 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);
@@ -43,7 +40,7 @@ Blockly.utils.object.inherits(Blockly.Cursor, Blockly.Marker);
* Find the next connection, field, or block.
* @return {Blockly.ASTNode} The next element, or null if the current node is
* not set or there is no next value.
- * @protected
+ * @public
*/
Blockly.Cursor.prototype.next = function() {
var curNode = this.getCurNode();
@@ -68,7 +65,7 @@ Blockly.Cursor.prototype.next = function() {
* Find the in connection or field.
* @return {Blockly.ASTNode} The in element, or null if the current node is
* not set or there is no in value.
- * @protected
+ * @public
*/
Blockly.Cursor.prototype.in = function() {
var curNode = this.getCurNode();
@@ -93,7 +90,7 @@ Blockly.Cursor.prototype.in = function() {
* Find the previous connection, field, or block.
* @return {Blockly.ASTNode} The previous element, or null if the current node
* is not set or there is no previous value.
- * @protected
+ * @public
*/
Blockly.Cursor.prototype.prev = function() {
var curNode = this.getCurNode();
@@ -118,7 +115,7 @@ Blockly.Cursor.prototype.prev = function() {
* Find the out connection, field, or block.
* @return {Blockly.ASTNode} The out element, or null if the current node is
* not set or there is no out value.
- * @protected
+ * @public
*/
Blockly.Cursor.prototype.out = function() {
var curNode = this.getCurNode();
@@ -136,35 +133,3 @@ Blockly.Cursor.prototype.out = function() {
}
return newNode;
};
-
-/**
- * Handles the given action.
- * This is only triggered when keyboard navigation is enabled.
- * @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) {
- // If we are on a field give it the option to handle the action
- if (this.getCurNode() &&
- this.getCurNode().getType() === Blockly.ASTNode.types.FIELD &&
- (/** @type {!Blockly.Field} */ (this.getCurNode().getLocation()))
- .onBlocklyAction(action)) {
- return true;
- }
- switch (action.name) {
- case Blockly.navigation.actionNames.PREVIOUS:
- this.prev();
- return true;
- case Blockly.navigation.actionNames.OUT:
- this.out();
- return true;
- case Blockly.navigation.actionNames.NEXT:
- this.next();
- return true;
- case Blockly.navigation.actionNames.IN:
- this.in();
- return true;
- default:
- return false;
- }
-};
diff --git a/core/keyboard_nav/flyout_cursor.js b/core/keyboard_nav/flyout_cursor.js
deleted file mode 100644
index 3db0a5257..000000000
--- a/core/keyboard_nav/flyout_cursor.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * @license
- * Copyright 2019 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * @fileoverview The class representing a cursor used to navigate the flyout.
- * Used primarily for keyboard navigation.
- * @author aschmiedt@google.com (Abby Schmiedt)
- */
-'use strict';
-
-goog.provide('Blockly.FlyoutCursor');
-
-goog.require('Blockly.Cursor');
-goog.require('Blockly.navigation');
-goog.require('Blockly.utils.object');
-
-goog.requireType('Blockly.ShortcutRegistry');
-
-
-/**
- * Class for a flyout cursor.
- * This controls how a user navigates blocks in the flyout.
- * @constructor
- * @extends {Blockly.Cursor}
- */
-Blockly.FlyoutCursor = function() {
- Blockly.FlyoutCursor.superClass_.constructor.call(this);
-};
-Blockly.utils.object.inherits(Blockly.FlyoutCursor, Blockly.Cursor);
-
-/**
- * Handles the given action.
- * This is only triggered when keyboard navigation is enabled.
- * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
- * @return {boolean} True if the action has been handled, false otherwise.
- * @override
- */
-Blockly.FlyoutCursor.prototype.onBlocklyAction = function(action) {
- switch (action.name) {
- case Blockly.navigation.actionNames.PREVIOUS:
- this.prev();
- return true;
- case Blockly.navigation.actionNames.NEXT:
- this.next();
- return true;
- default:
- return false;
- }
-};
-
-/**
- * Find the next connection, field, or block.
- * @return {Blockly.ASTNode} The next element, or null if the current node is
- * not set or there is no next value.
- * @override
- */
-Blockly.FlyoutCursor.prototype.next = function() {
- var curNode = this.getCurNode();
- if (!curNode) {
- return null;
- }
- var newNode = curNode.next();
-
- if (newNode) {
- this.setCurNode(newNode);
- }
- return newNode;
-};
-
-/**
- * This is a no-op since a flyout cursor can not go in.
- * @return {null} Always null.
- * @override
- */
-Blockly.FlyoutCursor.prototype.in = function() {
- return null;
-};
-
-/**
- * Find the previous connection, field, or block.
- * @return {Blockly.ASTNode} The previous element, or null if the current node
- * is not set or there is no previous value.
- * @override
- */
-Blockly.FlyoutCursor.prototype.prev = function() {
- var curNode = this.getCurNode();
- if (!curNode) {
- return null;
- }
- var newNode = curNode.prev();
-
- if (newNode) {
- this.setCurNode(newNode);
- }
- return newNode;
-};
-
-/**
- * This is a no-op since a flyout cursor can not go out.
- * @return {null} Always null.
- * @override
- */
-Blockly.FlyoutCursor.prototype.out = function() {
- return null;
-};
diff --git a/core/keyboard_nav/marker.js b/core/keyboard_nav/marker.js
index 2394e393e..c5805cd51 100644
--- a/core/keyboard_nav/marker.js
+++ b/core/keyboard_nav/marker.js
@@ -14,7 +14,6 @@
goog.provide('Blockly.Marker');
goog.require('Blockly.ASTNode');
-goog.require('Blockly.navigation');
/**
diff --git a/core/keyboard_nav/navigation.js b/core/keyboard_nav/navigation.js
deleted file mode 100644
index 774544b09..000000000
--- a/core/keyboard_nav/navigation.js
+++ /dev/null
@@ -1,1224 +0,0 @@
-/**
- * @license
- * Copyright 2019 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * @fileoverview The class in charge of handling actions related to keyboard
- * navigation.
- * @author aschmiedt@google.com (Abby Schmiedt)
- */
-'use strict';
-
-goog.provide('Blockly.navigation');
-
-goog.require('Blockly.ASTNode');
-goog.require('Blockly.constants');
-goog.require('Blockly.utils.Coordinate');
-
-
-/**
- * A function to call to give feedback to the user about logs, warnings, and
- * errors. You can override this to customize feedback (e.g. warning sounds,
- * reading out the warning text, etc).
- * Null by default.
- * The first argument is one of 'log', 'warn', and 'error'.
- * The second argument is the message.
- * @type {?function(string, string)}
- * @public
- */
-Blockly.navigation.loggingCallback = null;
-
-/**
- * State indicating focus is currently on the flyout.
- * @type {number}
- * @const
- */
-Blockly.navigation.STATE_FLYOUT = 1;
-
-/**
- * State indicating focus is currently on the workspace.
- * @type {number}
- * @const
- */
-Blockly.navigation.STATE_WS = 2;
-
-/**
- * State indicating focus is currently on the toolbox.
- * @type {number}
- * @const
- */
-Blockly.navigation.STATE_TOOLBOX = 3;
-
-/**
- * The distance to move the cursor on the workspace.
- * @type {number}
- * @const
- */
-Blockly.navigation.WS_MOVE_DISTANCE = 40;
-
-/**
- * The current state the user is in.
- * Initialized to workspace state since a user enters navigation mode by shift
- * clicking on a block or workspace.
- * @type {number}
- * @private
- */
-Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
-
-/**
- * Object holding default action names.
- * @enum {string}
- */
-Blockly.navigation.actionNames = {
- PREVIOUS: 'previous',
- NEXT: 'next',
- IN: 'in',
- OUT: 'out',
- INSERT: 'insert',
- MARK: 'mark',
- DISCONNECT: 'disconnect',
- TOOLBOX: 'toolbox',
- EXIT: 'exit',
- TOGGLE_KEYBOARD_NAV: 'toggle_keyboard_nav',
- MOVE_WS_CURSOR_UP: 'move workspace cursor up',
- MOVE_WS_CURSOR_DOWN: 'move workspace cursor down',
- MOVE_WS_CURSOR_LEFT: 'move workspace cursor left',
- MOVE_WS_CURSOR_RIGHT: 'move workspace cursor right'
-};
-
-/**
- * The name of the marker reserved for internal use.
- * @type {string}
- * @const
- */
-Blockly.navigation.MARKER_NAME = 'local_marker_1';
-
-/** ****** */
-/** Focus */
-/** ****** */
-
-/**
- * Get the local marker.
- * @return {Blockly.Marker} The local marker for the main workspace.
- */
-Blockly.navigation.getMarker = function() {
- return Blockly.navigation.getNavigationWorkspace()
- .getMarker(Blockly.navigation.MARKER_NAME);
-};
-
-/**
- * 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());
-};
-
-/**
- * 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(workspace) {
- var toolbox = workspace.getToolbox();
- if (toolbox) {
- Blockly.navigation.currentState_ = Blockly.navigation.STATE_TOOLBOX;
- Blockly.navigation.resetFlyout_(false /* shouldHide */);
-
- if (!Blockly.navigation.getMarker().getCurNode()) {
- Blockly.navigation.markAtCursor_();
- }
- if (!toolbox.getSelectedItem()) {
- toolbox.selectItemByPosition(0);
- }
- }
-};
-
-/**
- * Change focus to the flyout.
- * @param {!Blockly.WorkspaceSvg} workspace The main workspace.
- * @private
- */
-Blockly.navigation.focusFlyout_ = function(workspace) {
- var topBlock = null;
- Blockly.navigation.currentState_ = Blockly.navigation.STATE_FLYOUT;
- var toolbox = workspace.getToolbox();
- var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
-
- if (!Blockly.navigation.getMarker().getCurNode()) {
- Blockly.navigation.markAtCursor_();
- }
-
- if (flyout && flyout.getWorkspace()) {
- var topBlocks = flyout.getWorkspace().getTopBlocks(true);
- if (topBlocks.length > 0) {
- topBlock = topBlocks[0];
- var astNode = Blockly.ASTNode.createStackNode(topBlock);
- Blockly.navigation.getFlyoutCursor_().setCurNode(astNode);
- }
- }
-};
-
-/**
- * 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(workspace) {
- Blockly.hideChaff();
- var cursor = workspace.getCursor();
- var reset = !!workspace.getToolbox();
- var topBlocks = workspace.getTopBlocks(true);
-
- Blockly.navigation.resetFlyout_(reset);
- Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
- if (topBlocks.length > 0) {
- cursor.setCurNode(Blockly.ASTNode.createTopNode(topBlocks[0]));
- } else {
- // TODO: Find the center of the visible workspace.
- var wsCoord = new Blockly.utils.Coordinate(100, 100);
- var wsNode = Blockly.ASTNode.createWorkspaceNode(workspace, wsCoord);
- cursor.setCurNode(wsNode);
- }
-};
-
-/** ****************** */
-/** Flyout Navigation */
-/** ****************** */
-
-/**
- * Get the cursor from the flyouts workspace.
- * @return {Blockly.FlyoutCursor} The flyouts cursor or null if no flyout exists.
- * @private
- */
-Blockly.navigation.getFlyoutCursor_ = function() {
- var workspace = Blockly.navigation.getNavigationWorkspace();
- var cursor = null;
- if (workspace.rendered) {
- var toolbox = workspace.getToolbox();
- var flyout = toolbox ? toolbox.getFlyout() : workspace.getFlyout();
- cursor = flyout ? flyout.getWorkspace().getCursor() : null;
- }
- return /** @type {Blockly.FlyoutCursor} */ (cursor);
-};
-
-/**
- * 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(workspace) {
- var flyout = workspace.getFlyout();
- if (!flyout || !flyout.isVisible()) {
- Blockly.navigation.warn_('Trying to insert from the flyout when the flyout does not ' +
- ' exist or is not visible');
- return;
- }
-
- var curBlock = /** @type {!Blockly.BlockSvg} */ (
- Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation());
- if (!curBlock.isEnabled()) {
- Blockly.navigation.warn_('Can\'t insert a disabled block.');
- return;
- }
-
- var newBlock = flyout.createBlock(curBlock);
- // Render to get the sizing right.
- newBlock.render();
- // Connections are not tracked when the block is first created. Normally
- // there's enough time for them to become tracked in the user's mouse
- // movements, but not here.
- newBlock.setConnectionTracking(true);
- workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(newBlock));
- if (!Blockly.navigation.modify_()) {
- Blockly.navigation.warn_('Something went wrong while inserting a block from the flyout.');
- }
-
- Blockly.navigation.focusWorkspace_(workspace);
- workspace.getCursor().setCurNode(Blockly.ASTNode.createTopNode(newBlock));
- Blockly.navigation.removeMark_();
-};
-
-/**
- * Reset flyout information, and optionally close the flyout.
- * @param {boolean} shouldHide True if the flyout should be hidden.
- * @private
- */
-Blockly.navigation.resetFlyout_ = function(shouldHide) {
- if (Blockly.navigation.getFlyoutCursor_()) {
- Blockly.navigation.getFlyoutCursor_().hide();
- if (shouldHide) {
- Blockly.navigation.getNavigationWorkspace().getFlyout().hide();
- }
- }
-};
-
-/** **************** */
-/** Modify Workspace */
-/** **************** */
-
-/**
- * Warns the user if the cursor or marker is on a type that can not be connected.
- * @return {boolean} True if the marker and cursor are valid types, false
- * otherwise.
- * @private
- */
-Blockly.navigation.modifyWarn_ = function() {
- var markerNode = Blockly.navigation.getMarker().getCurNode();
- var cursorNode = Blockly.navigation.getNavigationWorkspace()
- .getCursor().getCurNode();
-
- if (!markerNode) {
- Blockly.navigation.warn_('Cannot insert with no marked node.');
- return false;
- }
-
- if (!cursorNode) {
- Blockly.navigation.warn_('Cannot insert with no cursor node.');
- return false;
- }
- var markerType = markerNode.getType();
- var cursorType = cursorNode.getType();
-
- // Check the marker for invalid types.
- if (markerType == Blockly.ASTNode.types.FIELD) {
- Blockly.navigation.warn_('Should not have been able to mark a field.');
- return false;
- } else if (markerType == Blockly.ASTNode.types.BLOCK) {
- Blockly.navigation.warn_('Should not have been able to mark a block.');
- return false;
- } else if (markerType == Blockly.ASTNode.types.STACK) {
- Blockly.navigation.warn_('Should not have been able to mark a stack.');
- return false;
- }
-
- // Check the cursor for invalid types.
- if (cursorType == Blockly.ASTNode.types.FIELD) {
- Blockly.navigation.warn_('Cannot attach a field to anything else.');
- return false;
- } else if (cursorType == Blockly.ASTNode.types.WORKSPACE) {
- Blockly.navigation.warn_('Cannot attach a workspace to anything else.');
- return false;
- }
- return true;
-};
-
-/**
- * Disconnect the block from its parent and move to the position of the
- * workspace node.
- * @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,
- * false otherwise.
- * @private
- */
-Blockly.navigation.moveBlockToWorkspace_ = function(block, wsNode) {
- if (!block) {
- return false;
- }
- if (block.isShadow()) {
- Blockly.navigation.warn_('Cannot move a shadow block to the workspace.');
- return false;
- }
- if (block.getParent()) {
- block.unplug(false);
- }
- block.moveTo(wsNode.getWsCoordinate());
- return true;
-};
-
-/**
- * Handle the modifier key (currently I for Insert).
- * Tries to connect the current marker and cursor location. Warns the user if
- * the two locations can not be connected.
- * @return {boolean} True if the key was handled; false if something went wrong.
- * @private
- */
-Blockly.navigation.modify_ = function() {
- var markerNode = Blockly.navigation.getMarker().getCurNode();
- var cursorNode = Blockly.navigation.getNavigationWorkspace()
- .getCursor().getCurNode();
- if (!Blockly.navigation.modifyWarn_()) {
- return false;
- }
-
- var markerType = markerNode.getType();
- var cursorType = cursorNode.getType();
-
- var cursorLoc = cursorNode.getLocation();
- var markerLoc = markerNode.getLocation();
-
- if (markerNode.isConnection() && cursorNode.isConnection()) {
- 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.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_(
- /** @type {Blockly.BlockSvg} */ (block), markerNode);
- }
- Blockly.navigation.warn_('Unexpected state in Blockly.navigation.modify_.');
- return false;
-};
-
-/**
- * If one of the connections source blocks is a child of the other, disconnect
- * the child.
- * @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) {
- var movingBlock = movingConnection.getSourceBlock();
- var destBlock = destConnection.getSourceBlock();
-
- if (movingBlock.getRootBlock() == destBlock.getRootBlock()) {
- if (movingBlock.getDescendants(false).indexOf(destBlock) > -1) {
- Blockly.navigation.getInferiorConnection_(destConnection).disconnect();
- } else {
- Blockly.navigation.getInferiorConnection_(movingConnection).disconnect();
- }
- }
-};
-
-/**
- * If the two blocks are compatible move the moving connection to the target
- * connection and connect them.
- * @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
- */
-Blockly.navigation.moveAndConnect_ = function(movingConnection, destConnection) {
- if (!movingConnection || !destConnection) {
- return false;
- }
- var movingBlock = movingConnection.getSourceBlock();
-
- var checker = movingConnection.getConnectionChecker();
-
- if (checker.canConnect(movingConnection, destConnection, false)) {
- Blockly.navigation.disconnectChild_(movingConnection, destConnection);
-
- if (!destConnection.isSuperior()) {
- var rootBlock = movingBlock.getRootBlock();
- rootBlock.positionNearConnection(movingConnection, destConnection);
- }
- destConnection.connect(movingConnection);
- return true;
- }
- return false;
-};
-
-/**
- * If the given connection is superior find the inferior connection on the
- * source block.
- * @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) {
- var block = connection.getSourceBlock();
- if (!connection.isSuperior()) {
- return connection;
- } else if (block.previousConnection) {
- return block.previousConnection;
- } else if (block.outputConnection) {
- return block.outputConnection;
- } else {
- return null;
- }
-};
-
-/**
- * If the given connection is inferior tries to find a superior connection to
- * connect to.
- * @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) {
- if (connection.isSuperior()) {
- return connection;
- } else if (connection.targetConnection) {
- return connection.targetConnection;
- }
- return null;
-};
-
-/**
- * Tries to connect the given connections.
- *
- * If the given connections are not compatible try finding compatible connections
- * on the source blocks of the given connections.
- *
- * @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
- */
-Blockly.navigation.connect_ = function(movingConnection, destConnection) {
- if (!movingConnection || !destConnection) {
- return false;
- }
-
- var movingInferior = Blockly.navigation.getInferiorConnection_(movingConnection);
- var destSuperior = Blockly.navigation.getSuperiorConnection_(destConnection);
-
- var movingSuperior = Blockly.navigation.getSuperiorConnection_(movingConnection);
- var destInferior = Blockly.navigation.getInferiorConnection_(destConnection);
-
- if (movingInferior && destSuperior &&
- Blockly.navigation.moveAndConnect_(movingInferior, destSuperior)) {
- return true;
- // Try swapping the inferior and superior connections on the blocks.
- } else if (movingSuperior && destInferior &&
- Blockly.navigation.moveAndConnect_(movingSuperior, destInferior)) {
- return true;
- } else if (Blockly.navigation.moveAndConnect_(movingConnection, destConnection)){
- return true;
- } else {
- var checker = movingConnection.getConnectionChecker();
- var reason = checker.canConnectWithReason(
- movingConnection, destConnection, false);
- Blockly.navigation.warn_('Connection failed with error: ' +
- checker.getErrorMessage(reason, movingConnection, destConnection));
- return false;
- }
-};
-
-/**
- * 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.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) {
- switch (destConnection.type) {
- case Blockly.PREVIOUS_STATEMENT:
- if (Blockly.navigation.connect_(block.nextConnection, destConnection)) {
- return true;
- }
- break;
- case Blockly.NEXT_STATEMENT:
- if (Blockly.navigation.connect_(block.previousConnection, destConnection)) {
- return true;
- }
- break;
- case Blockly.INPUT_VALUE:
- if (Blockly.navigation.connect_(block.outputConnection, destConnection)) {
- return true;
- }
- break;
- case Blockly.OUTPUT_VALUE:
- for (var i = 0; i < block.inputList.length; i++) {
- var inputConnection = /** @type {Blockly.RenderedConnection} */ (
- block.inputList[i].connection);
- if (inputConnection && inputConnection.type === Blockly.INPUT_VALUE &&
- Blockly.navigation.connect_(inputConnection, destConnection)) {
- return true;
- }
- }
- // If there are no input values pass the output and destination connections
- // to connect_ to find a way to connect the two.
- if (block.outputConnection &&
- Blockly.navigation.connect_(block.outputConnection, destConnection)) {
- return true;
- }
- break;
- }
- Blockly.navigation.warn_('This block can not be inserted at the marked location.');
- return false;
-};
-
-/**
- * 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(workspace) {
- 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.RenderedConnection} */ (curNode.getLocation());
- if (!curConnection.isConnected()) {
- Blockly.navigation.log_('Cannot disconnect unconnected connection');
- return;
- }
- var superiorConnection =
- curConnection.isSuperior() ? curConnection : curConnection.targetConnection;
-
- var inferiorConnection =
- curConnection.isSuperior() ? curConnection.targetConnection : curConnection;
-
- if (inferiorConnection.getSourceBlock().isShadow()) {
- Blockly.navigation.log_('Cannot disconnect a shadow block');
- return;
- }
- superiorConnection.disconnect();
- inferiorConnection.bumpAwayFrom(superiorConnection);
-
- var rootBlock = superiorConnection.getSourceBlock().getRootBlock();
- rootBlock.bringToFront();
-
- var connectionNode = Blockly.ASTNode.createConnectionNode(superiorConnection);
- workspace.getCursor().setCurNode(connectionNode);
-};
-
-/** ***************** */
-/** Helper Functions */
-/** ***************** */
-
-/**
- * Move the marker to the cursor's current location.
- * @private
- */
-Blockly.navigation.markAtCursor_ = function() {
- Blockly.navigation.getMarker().setCurNode(
- Blockly.navigation.getNavigationWorkspace().getCursor().getCurNode());
-};
-
-/**
- * Remove the marker from its current location and hide it.
- * @private
- */
-Blockly.navigation.removeMark_ = function() {
- var marker = Blockly.navigation.getMarker();
- marker.setCurNode(null);
- marker.hide();
-};
-
-/**
- * Set the current navigation state.
- * @param {number} newState The new navigation state.
- * @package
- */
-Blockly.navigation.setState = function(newState) {
- Blockly.navigation.currentState_ = newState;
-};
-
-/**
- * Before a block is deleted move the cursor to the appropriate position.
- * @param {!Blockly.BlockSvg} deletedBlock The block that is being deleted.
- */
-Blockly.navigation.moveCursorOnBlockDelete = function(deletedBlock) {
- var workspace = Blockly.navigation.getNavigationWorkspace();
- if (!workspace) {
- return;
- }
- var cursor = workspace.getCursor();
- if (cursor) {
- var curNode = cursor.getCurNode();
- var block = curNode ? curNode.getSourceBlock() : null;
-
- if (block === deletedBlock) {
- // If the block has a parent move the cursor to their connection point.
- if (block.getParent()) {
- var topConnection = block.previousConnection || block.outputConnection;
- if (topConnection) {
- cursor.setCurNode(
- Blockly.ASTNode.createConnectionNode(topConnection.targetConnection));
- }
- } else {
- // If the block is by itself move the cursor to the workspace.
- cursor.setCurNode(Blockly.ASTNode.createWorkspaceNode(block.workspace,
- block.getRelativeToSurfaceXY()));
- }
- // If the cursor is on a block whose parent is being deleted, move the
- // cursor to the workspace.
- } else if (block && deletedBlock.getChildren(false).indexOf(block) > -1) {
- cursor.setCurNode(Blockly.ASTNode.createWorkspaceNode(block.workspace,
- block.getRelativeToSurfaceXY()));
- }
- }
-};
-
-/**
- * When a block that the cursor is on is mutated move the cursor to the block
- * level.
- * @param {!Blockly.BlockSvg} mutatedBlock The block that is being mutated.
- * @package
- */
-Blockly.navigation.moveCursorOnBlockMutation = function(mutatedBlock) {
- var cursor = Blockly.navigation.getNavigationWorkspace().getCursor();
- if (cursor) {
- var curNode = cursor.getCurNode();
- var block = curNode ? curNode.getSourceBlock() : null;
-
- if (block === mutatedBlock) {
- cursor.setCurNode(Blockly.ASTNode.createBlockNode(block));
- }
- }
-};
-
-/**
- * Enable accessibility mode.
- */
-Blockly.navigation.enableKeyboardAccessibility = function() {
- var workspace = Blockly.navigation.getNavigationWorkspace();
- if (!workspace.keyboardAccessibilityMode) {
- workspace.keyboardAccessibilityMode = true;
- Blockly.navigation.focusWorkspace_(workspace);
- }
-};
-
-/**
- * Disable accessibility mode.
- */
-Blockly.navigation.disableKeyboardAccessibility = function() {
- var workspace = Blockly.navigation.getNavigationWorkspace();
- if (workspace.keyboardAccessibilityMode) {
- workspace.keyboardAccessibilityMode = false;
- workspace.getCursor().hide();
- Blockly.navigation.getMarker().hide();
- if (Blockly.navigation.getFlyoutCursor_()) {
- Blockly.navigation.getFlyoutCursor_().hide();
- }
- }
-};
-
-/**
- * Navigation log handler. If loggingCallback is defined, use it.
- * Otherwise just log to the console.
- * @param {string} msg The message to log.
- * @private
- */
-Blockly.navigation.log_ = function(msg) {
- if (Blockly.navigation.loggingCallback) {
- Blockly.navigation.loggingCallback('log', msg);
- } else {
- console.log(msg);
- }
-};
-
-/**
- * Navigation warning handler. If loggingCallback is defined, use it.
- * Otherwise call Blockly.navigation.warn_.
- * @param {string} msg The warning message.
- * @private
- */
-Blockly.navigation.warn_ = function(msg) {
- if (Blockly.navigation.loggingCallback) {
- Blockly.navigation.loggingCallback('warn', msg);
- } else {
- console.warn(msg);
- }
-};
-
-/**
- * Navigation error handler. If loggingCallback is defined, use it.
- * Otherwise call console.error.
- * @param {string} msg The error message.
- * @private
- */
-Blockly.navigation.error_ = function(msg) {
- if (Blockly.navigation.loggingCallback) {
- Blockly.navigation.loggingCallback('error', msg);
- } else {
- console.error(msg);
- }
-};
-
-/** ***************** */
-/** Handle Key Press */
-/** ***************** */
-
-/**
- * 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(workspace, xDirection, yDirection) {
- var cursor = workspace.getCursor();
- var curNode = workspace.getCursor().getCurNode();
-
- if (curNode.getType() !== Blockly.ASTNode.types.WORKSPACE) {
- return false;
- }
-
- var wsCoord = curNode.getWsCoordinate();
- var newX = xDirection * Blockly.navigation.WS_MOVE_DISTANCE + wsCoord.x;
- var newY = yDirection * Blockly.navigation.WS_MOVE_DISTANCE + wsCoord.y;
-
- cursor.setCurNode(Blockly.ASTNode.createWorkspaceNode(
- workspace, new Blockly.utils.Coordinate(newX, newY)));
- return true;
-};
-
-/**
- * Handles hitting the enter key on the workspace.
- * @param {!Blockly.WorkspaceSvg} workspace The workspace the enter event
- * originated from.
- * @private
- */
-Blockly.navigation.handleEnterForWS_ = function(workspace) {
- var cursor = workspace.getCursor();
- var curNode = cursor.getCurNode();
- var nodeType = curNode.getType();
- if (nodeType == Blockly.ASTNode.types.FIELD) {
- (/** @type {!Blockly.Field} */(curNode.getLocation())).showEditor();
- } else if (curNode.isConnection() ||
- nodeType == Blockly.ASTNode.types.WORKSPACE) {
- Blockly.navigation.markAtCursor_();
- } else if (nodeType == Blockly.ASTNode.types.BLOCK) {
- Blockly.navigation.warn_('Cannot mark a block.');
- } else if (nodeType == Blockly.ASTNode.types.STACK) {
- Blockly.navigation.warn_('Cannot mark a stack.');
- }
-};
-
-/** ***************** */
-/** Register Items */
-/** ***************** */
-
-/**
- * Keyboard shortcut to go to the previous location when in keyboard navigation
- * mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to go to the out location when in keyboard navigation
- * mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to go to the next location when in keyboard navigation
- * mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to go to the in location when in keyboard navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to connect a block to a marked location when in keyboard
- * navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to disconnect two blocks when in keyboard navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to focus on the toolbox when in keyboard navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to exit the current location and focus on the workspace
- * when in keyboard navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to move the cursor on the workspace to the left when in
- * keyboard navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to move the cursor on the workspace to the right when in
- * keyboard navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to move the cursor on the workspace up when in keyboard
- * navigation mode.
- */
-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);
-};
-
-/**
- * Keyboard shortcut to move the cursor on the workspace down when in keyboard
- * navigation mode.
- */
-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);
-};
-
-/**
- * Registers all default keyboard shortcut items for keyboard navigation. This
- * should be called once per instance of KeyboardShortcutRegistry.
- * @package
- */
-Blockly.navigation.registerNavigationShortcuts = function() {
- Blockly.navigation.registerIn();
- Blockly.navigation.registerNext();
- Blockly.navigation.registerOut();
- Blockly.navigation.registerPrevious();
-
- Blockly.navigation.registerWorkspaceMoveDown();
- Blockly.navigation.registerWorkspaceMoveLeft();
- Blockly.navigation.registerWorkspaceMoveRight();
- Blockly.navigation.registerWorkspaceMoveUp();
-
- Blockly.navigation.registerDisconnect();
- Blockly.navigation.registerExit();
- Blockly.navigation.registerInsert();
- Blockly.navigation.registerMark();
- Blockly.navigation.registerToggleKeyboardNav();
- Blockly.navigation.registerToolboxFocus();
-};
diff --git a/core/marker_manager.js b/core/marker_manager.js
index d8081714f..d1d233359 100644
--- a/core/marker_manager.js
+++ b/core/marker_manager.js
@@ -52,6 +52,13 @@ Blockly.MarkerManager = function(workspace){
this.workspace_ = workspace;
};
+/**
+ * The name of the local marker.
+ * @type {string}
+ * @const
+ */
+Blockly.MarkerManager.LOCAL_MARKER = 'local_marker_1';
+
/**
* Register the marker by adding it to the map of markers.
* @param {string} id A unique identifier for the marker.
diff --git a/core/mutator.js b/core/mutator.js
index 093a01df6..f6032a86d 100644
--- a/core/mutator.js
+++ b/core/mutator.js
@@ -18,7 +18,6 @@ goog.require('Blockly.Events');
goog.require('Blockly.Events.BlockChange');
goog.require('Blockly.Events.BubbleOpen');
goog.require('Blockly.Icon');
-goog.require('Blockly.navigation');
goog.require('Blockly.utils');
goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.global');
@@ -414,11 +413,6 @@ Blockly.Mutator.prototype.workspaceChanged_ = function(e) {
// Mutation may have added some elements that need initializing.
block.initSvg();
- if ((/** @type {!Blockly.WorkspaceSvg} */ (Blockly.getMainWorkspace()))
- .keyboardAccessibilityMode) {
- Blockly.navigation.moveCursorOnBlockMutation(block);
- }
-
if (block.rendered) {
block.render();
}
diff --git a/core/shortcut_registry.js b/core/shortcut_registry.js
index 5a28c2ef5..c7c7098e1 100644
--- a/core/shortcut_registry.js
+++ b/core/shortcut_registry.js
@@ -13,7 +13,6 @@
goog.provide('Blockly.ShortcutRegistry');
-goog.require('Blockly.navigation');
goog.require('Blockly.ShortcutItems');
goog.require('Blockly.utils.object');
@@ -43,7 +42,6 @@ Blockly.ShortcutRegistry = function() {
this.keyMap_ = Object.create(null);
Blockly.ShortcutItems.registerDefaultShortcuts();
- Blockly.navigation.registerNavigationShortcuts();
};
/**
diff --git a/core/toolbox/toolbox.js b/core/toolbox/toolbox.js
index 5b0fa2982..8dca3ce26 100644
--- a/core/toolbox/toolbox.js
+++ b/core/toolbox/toolbox.js
@@ -17,7 +17,6 @@ goog.require('Blockly.constants');
goog.require('Blockly.Css');
goog.require('Blockly.Events');
goog.require('Blockly.Events.ToolboxItemSelect');
-goog.require('Blockly.navigation');
goog.require('Blockly.registry');
goog.require('Blockly.Touch');
goog.require('Blockly.utils');
@@ -26,7 +25,7 @@ goog.require('Blockly.utils.dom');
goog.require('Blockly.utils.Rect');
goog.require('Blockly.utils.toolbox');
-goog.requireType('Blockly.IBlocklyActionable');
+goog.requireType('Blockly.IKeyboardAccessible');
goog.requireType('Blockly.ICollapsibleToolboxItem');
goog.requireType('Blockly.IDeleteArea');
goog.requireType('Blockly.IFlyout');
@@ -44,7 +43,7 @@ goog.requireType('Blockly.WorkspaceSvg');
* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to create new
* blocks.
* @constructor
- * @implements {Blockly.IBlocklyActionable}
+ * @implements {Blockly.IKeyboardAccessible}
* @implements {Blockly.IDeleteArea}
* @implements {Blockly.IStyleable}
* @implements {Blockly.IToolbox}
@@ -155,6 +154,16 @@ Blockly.Toolbox = function(workspace) {
this.boundEvents_ = [];
};
+/**
+ * Handles the given keyboard shortcut.
+ * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} _shortcut The shortcut to be handled.
+ * @return {boolean} True if the shortcut has been handled, false otherwise.
+ * @public
+ */
+Blockly.Toolbox.prototype.onShortcut = function(_shortcut) {
+ return false;
+};
+
/**
* Initializes the toolbox
* @public
@@ -811,32 +820,6 @@ Blockly.Toolbox.prototype.fireSelectEvent_ = function(oldItem, newItem) {
Blockly.Events.fire(event);
};
-/**
- * Handles the given Blockly action on a toolbox.
- * This is only triggered when keyboard accessibility mode is enabled.
- * @param {!Blockly.ShortcutRegistry.KeyboardShortcut} action The action to be handled.
- * @return {boolean} True if the field handled the action, false otherwise.
- * @package
- */
-Blockly.Toolbox.prototype.onBlocklyAction = function(action) {
- var selected = this.selectedItem_;
- if (!selected) {
- return false;
- }
- switch (action.name) {
- case Blockly.navigation.actionNames.PREVIOUS:
- return this.selectPrevious_();
- case Blockly.navigation.actionNames.OUT:
- return this.selectParent_();
- case Blockly.navigation.actionNames.NEXT:
- return this.selectNext_();
- case Blockly.navigation.actionNames.IN:
- return this.selectChild_();
- default:
- return false;
- }
-};
-
/**
* Closes the current item if it is expanded, or selects the parent.
* @return {boolean} True if a parent category was selected, false otherwise.
diff --git a/core/workspace_svg.js b/core/workspace_svg.js
index 7476f411b..419a18a45 100644
--- a/core/workspace_svg.js
+++ b/core/workspace_svg.js
@@ -25,7 +25,6 @@ goog.require('Blockly.Gesture');
goog.require('Blockly.Grid');
goog.require('Blockly.MarkerManager');
goog.require('Blockly.Msg');
-goog.require('Blockly.navigation');
goog.require('Blockly.Options');
goog.require('Blockly.registry');
goog.require('Blockly.ThemeManager');
@@ -790,8 +789,6 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
this.recordDeleteAreas();
this.markerManager_.setCursor(new Blockly.Cursor());
- this.markerManager_.registerMarker(Blockly.navigation.MARKER_NAME,
- new Blockly.Marker());
this.renderer_.createDom(this.svgGroup_, this.getTheme());
return this.svgGroup_;
@@ -1358,17 +1355,6 @@ Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) {
try {
var block = Blockly.Xml.domToBlock(xmlBlock, this);
- // Handle paste for keyboard navigation
- var markedNode = this.getMarker(Blockly.navigation.MARKER_NAME).getCurNode();
- if (this.keyboardAccessibilityMode && markedNode &&
- markedNode.isConnection()) {
- var markedLocation =
- /** @type {!Blockly.RenderedConnection} */ (markedNode.getLocation());
- Blockly.navigation.insertBlock(/** @type {!Blockly.BlockSvg} */ (block),
- markedLocation);
- return;
- }
-
// Move the duplicate to original position.
var blockX = parseInt(xmlBlock.getAttribute('x'), 10);
var blockY = parseInt(xmlBlock.getAttribute('y'), 10);
diff --git a/tests/mocha/gesture_test.js b/tests/mocha/gesture_test.js
index 71c84cbe2..7cf114633 100644
--- a/tests/mocha/gesture_test.js
+++ b/tests/mocha/gesture_test.js
@@ -88,16 +88,4 @@ suite('Gesture', function() {
var block = getTopFlyoutBlock(flyout);
testGestureIsFieldClick(block, true, this.eventsFireStub);
});
-
- test('Shift click in accessibility mode - moves the cursor', function() {
- this.workspace.keyboardAccessibilityMode = true;
-
- var eventTarget = this.workspace.svgGroup_;
- simulateClick(eventTarget, {shiftKey: true});
-
- var cursor = this.workspace.getCursor();
- var cursorNode = cursor.getCurNode();
- chai.assert.exists(cursorNode);
- chai.assert.equal(cursorNode.getType(), Blockly.ASTNode.types.WORKSPACE);
- });
});
diff --git a/tests/mocha/index.html b/tests/mocha/index.html
index bd9cde0ce..8bd37aab2 100644
--- a/tests/mocha/index.html
+++ b/tests/mocha/index.html
@@ -86,8 +86,6 @@
-
-
@@ -113,22 +111,6 @@
-
-
-
-
-
-
-
-
- FirstCategory-FirstBlock
-
-
- FirstCategory-SecondBlock
-
-
-
-
@@ -144,7 +126,6 @@
-
@@ -162,10 +143,6 @@
-
-
-
-
diff --git a/tests/mocha/navigation_modify_test.js b/tests/mocha/navigation_modify_test.js
deleted file mode 100644
index 38da4c90c..000000000
--- a/tests/mocha/navigation_modify_test.js
+++ /dev/null
@@ -1,420 +0,0 @@
-/**
- * @license
- * Copyright 2019 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-suite('Insert/Modify', function() {
- function assertModifyFails() {
- var modifyResult;
- var warnings = captureWarnings(function() {
- modifyResult = Blockly.navigation.modify_();
- });
- chai.assert.isFalse(modifyResult);
- chai.assert.equal(warnings.length, 1,
- 'Expecting 1 warnings for why modify failed.');
- }
- setup(function() {
- sharedTestSetup.call(this);
- // NOTE: block positions chosen such that they aren't unintentionally
- // bumped out of bounds during tests.
- var xmlText = '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '' +
- '';
- defineStackBlock(this.sharedCleanup);
- defineRowBlock(this.sharedCleanup);
- defineStatementBlock(this.sharedCleanup);
-
- var toolbox = document.getElementById('toolbox-connections');
- this.workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
- Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xmlText), this.workspace);
-
- this.stack_block_1 = this.workspace.getBlockById('stack_block_1');
- this.stack_block_2 = this.workspace.getBlockById('stack_block_2');
- this.row_block_1 = this.workspace.getBlockById('row_block_1');
- this.row_block_2 = this.workspace.getBlockById('row_block_2');
- this.statement_block_1 = this.workspace.getBlockById('statement_block_1');
- this.statement_block_2 = this.workspace.getBlockById('statement_block_2');
-
- Blockly.navigation.enableKeyboardAccessibility();
- });
-
- teardown(function() {
- sharedTestTeardown.call(this);
- });
-
- suite('Marked Connection', function() {
- // TODO: Marked connection or cursor connection is already connected.
- suite('Marker on next', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_1.nextConnection));
- });
- test('Cursor on workspace', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createWorkspaceNode(this.workspace,
- new Blockly.utils.Coordinate(0, 0)));
- assertModifyFails();
- });
- test('Cursor on compatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.previousConnection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.stack_block_1.getNextBlock().id, 'stack_block_2');
- });
- test('Cursor on incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.nextConnection));
- // Connect method will try to find a way to connect blocks with
- // incompatible types.
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.stack_block_1.getNextBlock(), this.stack_block_2);
- });
- test('Cursor on really incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.outputConnection));
- assertModifyFails();
- chai.assert.isNull(this.stack_block_1.getNextBlock());
- });
- test('Cursor on block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_2));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.stack_block_1.getNextBlock().id, 'stack_block_2');
- });
- });
-
- suite('Marker on previous', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_1.previousConnection));
- });
- test('Cursor on compatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.nextConnection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.stack_block_1.getPreviousBlock().id, 'stack_block_2');
- });
- test('Cursor on incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.previousConnection));
- assertModifyFails();
- chai.assert.isNull(this.stack_block_1.getPreviousBlock());
- });
- test('Cursor on really incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.outputConnection));
- assertModifyFails();
- chai.assert.isNull(this.stack_block_1.getNextBlock());
- });
- test('Cursor on block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_2));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.stack_block_1.getPreviousBlock().id, 'stack_block_2');
- });
- test('Cursor on incompatible block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_1));
- assertModifyFails();
- chai.assert.isNull(this.stack_block_1.getPreviousBlock());
- });
- });
-
- suite('Marker on value input', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.inputList[0].connection));
- });
- test('Cursor on compatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_2.outputConnection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.row_block_2.getParent().id, 'row_block_1');
- });
- test('Cursor on incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_2.inputList[0].connection));
- // Connect method will try to find a way to connect blocks with
- // incompatible types.
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.row_block_1.inputList[0].connection.targetBlock(),
- this.row_block_2);
- });
- test('Cursor on really incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_1.previousConnection));
- assertModifyFails();
- });
- test('Cursor on block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_2));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.row_block_2.getParent().id, 'row_block_1');
- });
- });
-
- suite('Marked Statement input', function() {
- setup(function() {
- this.statement_block_1.inputList[0].connection.connect(
- this.stack_block_1.previousConnection);
- this.stack_block_1.nextConnection.connect(this.stack_block_2.previousConnection);
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createInputNode(
- this.statement_block_1.inputList[0]));
- });
- test('Cursor on block inside statement', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.previousConnection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.stack_block_2.previousConnection.targetBlock(),
- this.statement_block_1);
- });
- test('Cursor on stack', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createStackNode(
- this.statement_block_2));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.statement_block_2.getParent().id, 'statement_block_1');
- });
- test('Cursor on incompatible type', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.outputConnection));
- assertModifyFails();
- chai.assert.isNull(this.row_block_1.getParent());
- });
-
- });
-
- suite('Marker on output', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.outputConnection));
- });
- test('Cursor on compatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_2.inputList[0].connection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.row_block_1.getParent().id, 'row_block_2');
- });
- test('Cursor on incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_2.outputConnection));
- assertModifyFails();
- });
- test('Cursor on really incompatible connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_1.previousConnection));
- assertModifyFails();
- });
- test('Cursor on block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_2));
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.equal(this.row_block_1.getParent().id, 'row_block_2');
- });
- });
- });
-
-
- suite('Marked Workspace', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).drawer_ = null;
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createWorkspaceNode(
- this.workspace, new Blockly.utils.Coordinate(100, 200)));
- });
- test('Cursor on row block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_1));
- chai.assert.isTrue(Blockly.navigation.modify_());
- var pos = this.row_block_1.getRelativeToSurfaceXY();
- chai.assert.equal(pos.x, 100);
- chai.assert.equal(pos.y, 200);
- });
-
- test('Cursor on output connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.outputConnection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- var pos = this.row_block_1.getRelativeToSurfaceXY();
- chai.assert.equal(pos.x, 100);
- chai.assert.equal(pos.y, 200);
- });
-
- test('Cursor on previous connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_1.previousConnection));
- chai.assert.isTrue(Blockly.navigation.modify_());
- var pos = this.stack_block_1.getRelativeToSurfaceXY();
- chai.assert.equal(pos.x, 100);
- chai.assert.equal(pos.y, 200);
- });
-
- test('Cursor on input connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_1.inputList[0].connection));
- // Move the source block to the marked location on the workspace.
- chai.assert.isTrue(Blockly.navigation.modify_());
- });
-
- test('Cursor on next connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_1.nextConnection));
- // Move the source block to the marked location on the workspace.
- chai.assert.isTrue(Blockly.navigation.modify_());
- });
-
- test('Cursor on child block (row)', function() {
- this.row_block_1.inputList[0].connection.connect(
- this.row_block_2.outputConnection);
-
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_2));
-
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.isNull(this.row_block_2.getParent());
- var pos = this.row_block_2.getRelativeToSurfaceXY();
- chai.assert.equal(pos.x, 100);
- chai.assert.equal(pos.y, 200);
- });
-
- test('Cursor on child block (stack)', function() {
- this.stack_block_1.nextConnection.connect(
- this.stack_block_2.previousConnection);
-
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_2));
-
- chai.assert.isTrue(Blockly.navigation.modify_());
- chai.assert.isNull(this.stack_block_2.getParent());
- var pos = this.stack_block_2.getRelativeToSurfaceXY();
- chai.assert.equal(pos.x, 100);
- chai.assert.equal(pos.y, 200);
- });
-
- test('Cursor on workspace', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createWorkspaceNode(
- this.workspace, new Blockly.utils.Coordinate(100, 100)));
- assertModifyFails();
- });
- });
-
- suite('Marked Block', function() {
- // TODO: Decide whether it ever makes sense to mark a block, and what to do
- // if so. For now all of these attempted modifications will fail.
- suite('Marked any block', function() {
- // These tests are using a stack block, but do not depend on the type of
- // the block.
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_1));
- });
- test('Cursor on workspace', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createWorkspaceNode(
- this.workspace, new Blockly.utils.Coordinate(100, 100)));
- assertModifyFails();
- });
- });
- suite('Marked stack block', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_1));
- });
- test('Cursor on row block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_1));
- assertModifyFails();
- });
- test('Cursor on stack block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_1));
- assertModifyFails();
- });
- test('Cursor on next connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.nextConnection));
- assertModifyFails();
- });
- test('Cursor on previous connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.stack_block_2.previousConnection));
- assertModifyFails();
- });
- });
- suite('Marked row block', function() {
- setup(function() {
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_1));
- });
- test('Cursor on stack block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.stack_block_1));
- assertModifyFails();
- });
- test('Cursor on row block', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createBlockNode(
- this.row_block_1));
- assertModifyFails();
- });
- test('Cursor on value input connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_2.inputList[0].connection));
- assertModifyFails();
- });
- test('Cursor on output connection', function() {
- this.workspace.getCursor().setCurNode(
- Blockly.ASTNode.createConnectionNode(
- this.row_block_2.outputConnection));
- assertModifyFails();
- });
- });
- });
-});
diff --git a/tests/mocha/navigation_test.js b/tests/mocha/navigation_test.js
deleted file mode 100644
index 9e3e31901..000000000
--- a/tests/mocha/navigation_test.js
+++ /dev/null
@@ -1,803 +0,0 @@
-/**
- * @license
- * Copyright 2019 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-/**
- * @fileoverview Navigation tests.
- * @author aschmiedt@google.com (Abby Schmiedt)
- */
-'use strict';
-
-
-suite('Navigation', function() {
- function createNavigationWorkspace(enableKeyboardNav, readOnly) {
- var toolbox = document.getElementById('toolbox-categories');
- var workspace =
- Blockly.inject('blocklyDiv', {toolbox: toolbox, readOnly: readOnly});
- if (enableKeyboardNav) {
- Blockly.navigation.enableKeyboardAccessibility();
- Blockly.navigation.currentState_ = Blockly.navigation.STATE_WS;
- }
- return workspace;
- }
-
- setup(function() {
- sharedTestSetup.call(this);
- });
- teardown(function() {
- sharedTestTeardown.call(this);
- });
-
- // Test that toolbox key handlers call through to the right functions and
- // transition correctly between toolbox, workspace, and flyout.
- suite('Tests toolbox keys', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "%1",
- "args0": [
- {
- "type": "field_input",
- "name": "TEXT",
- "text": "default"
- }
- ]
- }]);
- this.workspace = createNavigationWorkspace(true);
- Blockly.navigation.focusToolbox_(this.workspace);
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- 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_'
- ]
- ];
-
- 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() {
- 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;
- 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;
- 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
- });
-
- // Test that flyout key handlers call through to the right functions and
- // transition correctly between toolbox, workspace, and flyout.
- suite('Tests flyout keys', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "%1",
- "args0": [
- {
- "type": "field_input",
- "name": "TEXT",
- "text": "default"
- }
- ]
- }]);
- this.workspace = createNavigationWorkspace(true);
- Blockly.navigation.focusToolbox_(this.workspace);
- Blockly.navigation.focusFlyout_(this.workspace);
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- // Should be a no-op
- test('Previous at beginning', function() {
- 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");
- });
-
- test('Previous', function() {
- var flyoutBlocks = this.workspace.getFlyout().getWorkspace().getTopBlocks();
- Blockly.navigation.getFlyoutCursor_().setCurNode(
- Blockly.ASTNode.createStackNode(flyoutBlocks[1]));
- var flyoutBlock = Blockly.navigation.getFlyoutCursor_().getCurNode().getLocation();
- chai.assert.equal(flyoutBlock.getFieldValue("TEXT"),
- "FirstCategory-SecondBlock");
- 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() {
- 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() {
- 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() {
- 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() {
- 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);
- });
- });
-
- // Test that workspace key handlers call through to the right functions and
- // transition correctly between toolbox, workspace, and flyout.
- suite('Tests workspace keys', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "%1",
- "args0": [
- {
- "type": "field_input",
- "name": "TEXT",
- "text": "default"
- }
- ],
- "previousStatement": null,
- "nextStatement": null
- }]);
- this.workspace = createNavigationWorkspace(true);
- this.basicBlock = this.workspace.newBlock('basic_block');
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- test('Previous', function() {
- 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 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 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 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.
- 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));
- 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);
- });
-
- test('Toolbox', function() {
- 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);
- });
- });
-
- suite('Test key press', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "%1",
- "args0": [
- {
- "type": "field_dropdown",
- "name": "OP",
- "options": [
- ["%{BKY_MATH_ADDITION_SYMBOL}", "ADD"],
- ["%{BKY_MATH_SUBTRACTION_SYMBOL}", "MINUS"],
- ["%{BKY_MATH_MULTIPLICATION_SYMBOL}", "MULTIPLY"],
- ["%{BKY_MATH_DIVISION_SYMBOL}", "DIVIDE"],
- ["%{BKY_MATH_POWER_SYMBOL}", "POWER"]
- ]
- }
- ]
- }]);
- this.workspace = createNavigationWorkspace(true);
- this.workspace.getCursor().drawer_ = null;
- this.basicBlock = this.workspace.newBlock('basic_block');
- });
- 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];
- 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));
-
- 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];
- 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));
-
- Blockly.onKeyDown(mockEvent);
-
- chai.assert.isTrue(keyDownSpy.returned(true));
- sinon.assert.calledOnce(fieldSpy);
-
- });
-
- test('Action exists - field does not handle action', function() {
- var block = this.workspace.getTopBlocks()[0];
- var field = block.inputList[0].fieldRow[0];
- 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));
-
- Blockly.onKeyDown(mockEvent);
-
- chai.assert.isTrue(keyDownSpy.returned(true));
- sinon.assert.calledOnce(fieldSpy);
- });
-
- test('Toggle Action Off', function() {
- 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;
-
- Blockly.onKeyDown(mockEvent);
-
- chai.assert.isTrue(keyDownSpy.returned(true));
- chai.assert.isFalse(this.workspace.keyboardAccessibilityMode);
- });
-
- test('Toggle Action On', function() {
- 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;
-
- Blockly.onKeyDown(mockEvent);
-
- chai.assert.isTrue(keyDownSpy.returned(true));
- chai.assert.isTrue(this.workspace.keyboardAccessibilityMode);
- });
-
- suite('Test key press in read only mode', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "field_block",
- "message0": "%1 %2",
- "args0": [
- {
- "type": "field_dropdown",
- "name": "NAME",
- "options": [
- [
- "a",
- "optionA"
- ]
- ]
- },
- {
- "type": "input_value",
- "name": "NAME"
- }
- ],
- "previousStatement": null,
- "nextStatement": null,
- "colour": 230,
- "tooltip": "",
- "helpUrl": ""
- }]);
- this.workspace = createNavigationWorkspace(true, true);
-
- Blockly.mainWorkspace = this.workspace;
- this.workspace.getCursor().drawer_ = null;
-
- this.fieldBlock1 = this.workspace.newBlock('field_block');
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- 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);
- 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);
- 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);
- var keyDownSpy =
- sinon.spy(Blockly.ShortcutRegistry.registry, 'onKeyDown');
-
- Blockly.onKeyDown(mockEvent);
-
- chai.assert.isTrue(keyDownSpy.returned(false));
- });
- });
- });
-
- suite('Insert Functions', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "%1",
- "args0": [
- {
- "type": "field_input",
- "name": "TEXT",
- "text": "default"
- }
- ],
- "previousStatement": null,
- "nextStatement": null,
- }]);
-
- this.workspace = createNavigationWorkspace(true);
-
- var basicBlock = this.workspace.newBlock('basic_block');
- var basicBlock2 = this.workspace.newBlock('basic_block');
-
- this.basicBlock = basicBlock;
- this.basicBlock2 = basicBlock2;
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- test('Insert from flyout with a valid connection marked', function() {
- var previousConnection = this.basicBlock.previousConnection;
- var prevNode = Blockly.ASTNode.createConnectionNode(previousConnection);
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(prevNode);
-
- Blockly.navigation.focusToolbox_(this.workspace);
- Blockly.navigation.focusFlyout_(this.workspace);
- Blockly.navigation.insertFromFlyout(this.workspace);
-
- var insertedBlock = this.basicBlock.previousConnection.targetBlock();
-
- chai.assert.isTrue(insertedBlock !== null);
- chai.assert.equal(Blockly.navigation.currentState_,
- Blockly.navigation.STATE_WS);
- });
-
- test('Insert Block from flyout without marking a connection', function() {
- Blockly.navigation.focusToolbox_(this.workspace);
- Blockly.navigation.focusFlyout_(this.workspace);
- Blockly.navigation.insertFromFlyout(this.workspace);
-
- var numBlocks = this.workspace.getTopBlocks().length;
-
- // Make sure the block was not connected to anything
- chai.assert.isNull(this.basicBlock.previousConnection.targetConnection);
- chai.assert.isNull(this.basicBlock.nextConnection.targetConnection);
-
- // Make sure that the block was added to the workspace
- chai.assert.equal(numBlocks, 3);
- chai.assert.equal(Blockly.navigation.currentState_,
- Blockly.navigation.STATE_WS);
- });
-
- test('Connect two blocks that are on the workspace', function() {
- var targetNode = Blockly.ASTNode.createConnectionNode(this.basicBlock.previousConnection);
- this.workspace.getMarker(Blockly.navigation.MARKER_NAME).setCurNode(targetNode);
-
- var sourceNode = Blockly.ASTNode.createConnectionNode(this.basicBlock2.nextConnection);
- this.workspace.getCursor().setCurNode(sourceNode);
-
- Blockly.navigation.modify_();
- var insertedBlock = this.basicBlock.previousConnection.targetBlock();
-
- chai.assert.isNotNull(insertedBlock);
- });
- });
-
- suite('Connect Blocks', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "",
- "previousStatement": null,
- "nextStatement": null,
- },
- {
- "type": "inline_block",
- "message0": "%1 %2",
- "args0": [
- {
- "type": "input_value",
- "name": "NAME"
- },
- {
- "type": "input_value",
- "name": "NAME"
- }
- ],
- "inputsInline": true,
- "output": null,
- "tooltip": "",
- "helpUrl": ""
- }]);
-
- this.workspace = createNavigationWorkspace(true);
-
- var basicBlock = this.workspace.newBlock('basic_block');
- var basicBlock2 = this.workspace.newBlock('basic_block');
- var basicBlock3 = this.workspace.newBlock('basic_block');
- var basicBlock4 = this.workspace.newBlock('basic_block');
-
- var inlineBlock1 = this.workspace.newBlock('inline_block');
- var inlineBlock2 = this.workspace.newBlock('inline_block');
-
-
- this.basicBlock = basicBlock;
- this.basicBlock2 = basicBlock2;
- this.basicBlock3 = basicBlock3;
- this.basicBlock4 = basicBlock4;
-
- this.inlineBlock1 = inlineBlock1;
- this.inlineBlock2 = inlineBlock2;
-
- this.basicBlock.nextConnection.connect(this.basicBlock2.previousConnection);
-
- this.basicBlock3.nextConnection.connect(this.basicBlock4.previousConnection);
-
- this.inlineBlock1.inputList[0].connection.connect(this.inlineBlock2.outputConnection);
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- test('Connect cursor on previous into stack', function() {
- var markedLocation = this.basicBlock2.previousConnection;
- var cursorLocation = this.basicBlock3.previousConnection;
-
- Blockly.navigation.connect_(cursorLocation, markedLocation);
-
- chai.assert.equal(this.basicBlock.nextConnection.targetBlock(), this.basicBlock3);
- chai.assert.equal(this.basicBlock2.previousConnection.targetBlock(), this.basicBlock4);
- });
-
- test('Connect marker on previous into stack', function() {
- var markedLocation = this.basicBlock3.previousConnection;
- var cursorLocation = this.basicBlock2.previousConnection;
-
- Blockly.navigation.connect_(cursorLocation, markedLocation);
-
- chai.assert.equal(this.basicBlock.nextConnection.targetBlock(), this.basicBlock3);
- chai.assert.equal(this.basicBlock2.previousConnection.targetBlock(), this.basicBlock4);
-
- });
-
- test('Connect cursor on next into stack', function() {
- var markedLocation = this.basicBlock2.previousConnection;
- var cursorLocation = this.basicBlock4.nextConnection;
-
- Blockly.navigation.connect_(cursorLocation, markedLocation);
-
- chai.assert.equal(this.basicBlock.nextConnection.targetBlock(), this.basicBlock4);
- chai.assert.isNull(this.basicBlock3.nextConnection.targetConnection);
- });
-
- test('Connect cursor with parents', function() {
- var markedLocation = this.basicBlock3.previousConnection;
- var cursorLocation = this.basicBlock2.nextConnection;
-
- Blockly.navigation.connect_(cursorLocation, markedLocation);
-
- chai.assert.equal(this.basicBlock3.previousConnection.targetBlock(), this.basicBlock2);
- });
-
- test('Try to connect input that is descendant of output', function() {
- var markedLocation = this.inlineBlock2.inputList[0].connection;
- var cursorLocation = this.inlineBlock1.outputConnection;
-
- Blockly.navigation.connect_(cursorLocation, markedLocation);
-
- chai.assert.isNull(this.inlineBlock2.outputConnection.targetBlock());
- chai.assert.equal(this.inlineBlock1.outputConnection.targetBlock(), this.inlineBlock2);
- });
- });
-
-
- suite('Test cursor move on block delete', function() {
- setup(function() {
- Blockly.defineBlocksWithJsonArray([{
- "type": "basic_block",
- "message0": "",
- "previousStatement": null,
- "nextStatement": null,
- }]);
- this.workspace = createNavigationWorkspace(true);
- this.basicBlockA = this.workspace.newBlock('basic_block');
- this.basicBlockB = this.workspace.newBlock('basic_block');
- });
-
- teardown(function() {
- workspaceTeardown.call(this, this.workspace);
- });
-
- test('Delete block - has parent ', function() {
- this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
- var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
- // Set the cursor to be on the child block
- this.workspace.getCursor().setCurNode(astNode);
- // Remove the child block
- this.basicBlockB.dispose();
- chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
- Blockly.ASTNode.types.NEXT);
- });
-
- test('Delete block - no parent ', function() {
- var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
- this.workspace.getCursor().setCurNode(astNode);
- this.basicBlockB.dispose();
- chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
- Blockly.ASTNode.types.WORKSPACE);
- });
-
- test('Delete parent block', function() {
- this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
- var astNode = Blockly.ASTNode.createBlockNode(this.basicBlockB);
- // Set the cursor to be on the child block
- this.workspace.getCursor().setCurNode(astNode);
- // Remove the parent block
- this.basicBlockA.dispose();
- chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
- Blockly.ASTNode.types.WORKSPACE);
- });
-
- test('Delete top block in stack', function() {
- this.basicBlockA.nextConnection.connect(this.basicBlockB.previousConnection);
- var astNode = Blockly.ASTNode.createStackNode(this.basicBlockA);
- // Set the cursor to be on the stack
- this.workspace.getCursor().setCurNode(astNode);
- // Remove the top block in the stack
- this.basicBlockA.dispose();
- chai.assert.equal(this.workspace.getCursor().getCurNode().getType(),
- Blockly.ASTNode.types.WORKSPACE);
- });
- });
-});