mirror of
https://github.com/google/blockly.git
synced 2026-03-10 07:10:11 +01:00
Merge pull request #4050 from rachel-fenichel/connection_checks
Connection checker object
This commit is contained in:
@@ -37,6 +37,7 @@ goog.addDependency('../../core/components/tree/basenode.js', ['Blockly.tree.Base
|
||||
goog.addDependency('../../core/components/tree/treecontrol.js', ['Blockly.tree.TreeControl'], ['Blockly.tree.BaseNode', 'Blockly.tree.TreeNode', 'Blockly.utils.aria', 'Blockly.utils.object', 'Blockly.utils.style'], {});
|
||||
goog.addDependency('../../core/components/tree/treenode.js', ['Blockly.tree.TreeNode'], ['Blockly.tree.BaseNode', 'Blockly.utils.KeyCodes', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.Events', 'Blockly.Events.BlockMove', 'Blockly.Xml'], {});
|
||||
goog.addDependency('../../core/connection_checker.js', ['Blockly.ConnectionChecker'], [], {});
|
||||
goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.RenderedConnection'], {});
|
||||
goog.addDependency('../../core/constants.js', ['Blockly.constants'], [], {});
|
||||
goog.addDependency('../../core/contextmenu.js', ['Blockly.ContextMenu'], ['Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.Msg', 'Blockly.Xml', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {});
|
||||
@@ -74,6 +75,7 @@ goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.Connectio
|
||||
goog.addDependency('../../core/insertion_marker_manager.js', ['Blockly.InsertionMarkerManager'], ['Blockly.Events', 'Blockly.blockAnimations'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/interfaces/i_accessibility.js', ['Blockly.IASTNodeLocation', 'Blockly.IASTNodeLocationSvg', 'Blockly.IASTNodeLocationWithBlock', 'Blockly.IBlocklyActionable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_bounded_element.js', ['Blockly.IBoundedElement'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_connection_checker.js', ['Blockly.IConnectionChecker'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_copyable.js', ['Blockly.ICopyable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_deletable.js', ['Blockly.IDeletable'], [], {});
|
||||
goog.addDependency('../../core/interfaces/i_deletearea.js', ['Blockly.IDeleteArea'], [], {});
|
||||
@@ -183,7 +185,7 @@ goog.addDependency('../../core/variables.js', ['Blockly.Variables'], ['Blockly.B
|
||||
goog.addDependency('../../core/variables_dynamic.js', ['Blockly.VariablesDynamic'], ['Blockly.Blocks', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/warning.js', ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events', 'Blockly.Events.Ui', 'Blockly.Icon', 'Blockly.utils.dom', 'Blockly.utils.object'], {});
|
||||
goog.addDependency('../../core/widgetdiv.js', ['Blockly.WidgetDiv'], ['Blockly.utils.style'], {});
|
||||
goog.addDependency('../../core/workspace.js', ['Blockly.Workspace'], ['Blockly.Events', 'Blockly.Options', 'Blockly.VariableMap', 'Blockly.utils', 'Blockly.utils.math'], {});
|
||||
goog.addDependency('../../core/workspace.js', ['Blockly.Workspace'], ['Blockly.ConnectionChecker', 'Blockly.Events', 'Blockly.Options', 'Blockly.VariableMap', 'Blockly.utils', 'Blockly.utils.math'], {});
|
||||
goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.utils', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es5'});
|
||||
goog.addDependency('../../core/workspace_comment.js', ['Blockly.WorkspaceComment'], ['Blockly.Events', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.xml'], {});
|
||||
goog.addDependency('../../core/workspace_comment_render_svg.js', ['Blockly.WorkspaceCommentSvg.render'], ['Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {});
|
||||
|
||||
@@ -543,7 +543,8 @@ Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
||||
var blockB = this.getInputTargetBlock('B');
|
||||
// Disconnect blocks that existed prior to this change if they don't match.
|
||||
if (blockA && blockB &&
|
||||
!blockA.outputConnection.checkType(blockB.outputConnection)) {
|
||||
!this.workspace.connectionChecker.doTypeChecks(
|
||||
blockA.outputConnection, blockB.outputConnection)) {
|
||||
// Mismatch between two inputs. Revert the block connections,
|
||||
// bumping away the newly connected block(s).
|
||||
Blockly.Events.setGroup(e.group);
|
||||
@@ -610,7 +611,9 @@ Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN = {
|
||||
if ((blockA || blockB) && parentConnection) {
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var block = (i == 1) ? blockA : blockB;
|
||||
if (block && !block.outputConnection.checkType(parentConnection)) {
|
||||
if (block &&
|
||||
!block.workspace.connectionChecker.doTypeChecks(
|
||||
block.outputConnection, parentConnection)) {
|
||||
// Ensure that any disconnections are grouped with the causing event.
|
||||
Blockly.Events.setGroup(e.group);
|
||||
if (parentConnection === this.prevParentConnection_) {
|
||||
|
||||
@@ -456,7 +456,8 @@ Blockly.Block.prototype.unplugFromRow_ = function(opt_healStack) {
|
||||
// Disconnect the child block.
|
||||
childConnection.disconnect();
|
||||
// Connect child to the parent if possible, otherwise bump away.
|
||||
if (childConnection.checkType(parentConnection)) {
|
||||
if (this.workspace.connectionChecker.canConnect(
|
||||
childConnection, parentConnection, false)) {
|
||||
parentConnection.connect(childConnection);
|
||||
} else {
|
||||
childConnection.onFailedConnect(parentConnection);
|
||||
@@ -508,7 +509,9 @@ Blockly.Block.prototype.unplugFromStack_ = function(opt_healStack) {
|
||||
// Disconnect the next statement.
|
||||
var nextTarget = this.nextConnection.targetConnection;
|
||||
nextTarget.disconnect();
|
||||
if (previousTarget && previousTarget.checkType(nextTarget)) {
|
||||
if (previousTarget &&
|
||||
this.workspace.connectionChecker.canConnect(
|
||||
previousTarget, nextTarget, false)) {
|
||||
// Attach the next statement to the previous statement.
|
||||
previousTarget.connect(nextTarget);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,11 @@ goog.provide('Blockly.Connection');
|
||||
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Events.BlockMove');
|
||||
goog.require('Blockly.utils.deprecation');
|
||||
goog.require('Blockly.Xml');
|
||||
|
||||
goog.requireType('Blockly.IASTNodeLocationWithBlock');
|
||||
goog.requireType('Blockly.IConnectionChecker');
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,6 +48,7 @@ Blockly.Connection.REASON_TARGET_NULL = 3;
|
||||
Blockly.Connection.REASON_CHECKS_FAILED = 4;
|
||||
Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5;
|
||||
Blockly.Connection.REASON_SHADOW_PARENT = 6;
|
||||
Blockly.Connection.REASON_DRAG_CHECKS_FAILED = 7;
|
||||
|
||||
/**
|
||||
* Connection this connection connects to. Null if not connected.
|
||||
@@ -145,8 +148,9 @@ Blockly.Connection.prototype.connect_ = function(childConnection) {
|
||||
if (nextBlock && !nextBlock.isShadow()) {
|
||||
newBlock = nextBlock;
|
||||
} else {
|
||||
if (orphanBlock.previousConnection.checkType(
|
||||
newBlock.nextConnection)) {
|
||||
var checker = orphanBlock.workspace.connectionChecker;
|
||||
if (checker.canConnect(
|
||||
orphanBlock.previousConnection, newBlock.nextConnection, false)) {
|
||||
newBlock.nextConnection.connect(orphanBlock.previousConnection);
|
||||
orphanBlock = null;
|
||||
}
|
||||
@@ -245,30 +249,17 @@ Blockly.Connection.prototype.isConnected = function() {
|
||||
* @param {Blockly.Connection} target Connection to check compatibility with.
|
||||
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
|
||||
* an error code otherwise.
|
||||
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
|
||||
* connectionChecker instead.
|
||||
*/
|
||||
Blockly.Connection.prototype.canConnectWithReason = function(target) {
|
||||
if (!target) {
|
||||
return Blockly.Connection.REASON_TARGET_NULL;
|
||||
}
|
||||
if (this.isSuperior()) {
|
||||
var blockA = this.sourceBlock_;
|
||||
var blockB = target.getSourceBlock();
|
||||
} else {
|
||||
var blockB = this.sourceBlock_;
|
||||
var blockA = target.getSourceBlock();
|
||||
}
|
||||
if (blockA && blockA == blockB) {
|
||||
return Blockly.Connection.REASON_SELF_CONNECTION;
|
||||
} else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) {
|
||||
return Blockly.Connection.REASON_WRONG_TYPE;
|
||||
} else if (blockA && blockB && blockA.workspace !== blockB.workspace) {
|
||||
return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
|
||||
} else if (!this.checkType(target)) {
|
||||
return Blockly.Connection.REASON_CHECKS_FAILED;
|
||||
} else if (blockA.isShadow() && !blockB.isShadow()) {
|
||||
return Blockly.Connection.REASON_SHADOW_PARENT;
|
||||
}
|
||||
return Blockly.Connection.CAN_CONNECT;
|
||||
Blockly.utils.deprecation.warn(
|
||||
'Connection.prototype.canConnectWithReason',
|
||||
'July 2020',
|
||||
'July 2021',
|
||||
'the workspace\'s connection checker');
|
||||
return this.getConnectionChecker().canConnectWithReason(
|
||||
this, target, false);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -277,130 +268,46 @@ Blockly.Connection.prototype.canConnectWithReason = function(target) {
|
||||
* @param {Blockly.Connection} target The connection to check compatibility
|
||||
* with.
|
||||
* @package
|
||||
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
|
||||
* connectionChecker instead.
|
||||
*/
|
||||
Blockly.Connection.prototype.checkConnection = function(target) {
|
||||
switch (this.canConnectWithReason(target)) {
|
||||
case Blockly.Connection.CAN_CONNECT:
|
||||
break;
|
||||
case Blockly.Connection.REASON_SELF_CONNECTION:
|
||||
throw Error('Attempted to connect a block to itself.');
|
||||
case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
|
||||
// Usually this means one block has been deleted.
|
||||
throw Error('Blocks not on same workspace.');
|
||||
case Blockly.Connection.REASON_WRONG_TYPE:
|
||||
throw Error('Attempt to connect incompatible types.');
|
||||
case Blockly.Connection.REASON_TARGET_NULL:
|
||||
throw Error('Target connection is null.');
|
||||
case Blockly.Connection.REASON_CHECKS_FAILED:
|
||||
var msg = 'Connection checks failed. ';
|
||||
msg += this + ' expected ' + this.check_ + ', found ' + target.check_;
|
||||
throw Error(msg);
|
||||
case Blockly.Connection.REASON_SHADOW_PARENT:
|
||||
throw Error('Connecting non-shadow to shadow block.');
|
||||
default:
|
||||
throw Error('Unknown connection failure: this should never happen!');
|
||||
Blockly.utils.deprecation.warn(
|
||||
'Connection.prototype.checkConnection',
|
||||
'July 2020',
|
||||
'July 2021',
|
||||
'the workspace\'s connection checker');
|
||||
var checker = this.getConnectionChecker();
|
||||
var reason = checker.canConnectWithReason(this, target, false);
|
||||
if (reason != Blockly.Connection.CAN_CONNECT) {
|
||||
throw new Error(checker.getErrorMessage(reason, this, target));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the two connections can be dragged to connect to each other.
|
||||
* This is used by the connection database when searching for the closest
|
||||
* connection.
|
||||
* @param {!Blockly.Connection} candidate A nearby connection to check, which
|
||||
* must be a previous connection.
|
||||
* @return {boolean} True if the connection is allowed, false otherwise.
|
||||
* @private
|
||||
* Get the workspace's connection type checker object.
|
||||
* @return {!Blockly.IConnectionChecker} The connection type checker for the
|
||||
* source block's workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Connection.prototype.canConnectToPrevious_ = function(candidate) {
|
||||
if (this.targetConnection) {
|
||||
// This connection is already occupied.
|
||||
// A next connection will never disconnect itself mid-drag.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
if (Blockly.draggingConnections.indexOf(candidate) != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!candidate.targetConnection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var targetBlock = candidate.targetBlock();
|
||||
// If it is connected to a real block, game over.
|
||||
if (!targetBlock.isInsertionMarker()) {
|
||||
return false;
|
||||
}
|
||||
// If it's connected to an insertion marker but that insertion marker
|
||||
// is the first block in a stack, it's still fine. If that insertion
|
||||
// marker is in the middle of a stack, it won't work.
|
||||
return !targetBlock.getPreviousBlock();
|
||||
Blockly.Connection.prototype.getConnectionChecker = function() {
|
||||
return this.sourceBlock_.workspace.connectionChecker;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the two connections can be dragged to connect to each other.
|
||||
* @param {!Blockly.Connection} candidate A nearby connection to check.
|
||||
* @return {boolean} True if the connection is allowed, false otherwise.
|
||||
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
|
||||
* connectionChecker instead.
|
||||
*/
|
||||
Blockly.Connection.prototype.isConnectionAllowed = function(candidate) {
|
||||
// Don't consider insertion markers.
|
||||
if (candidate.sourceBlock_.isInsertionMarker()) {
|
||||
return false;
|
||||
}
|
||||
// Type checking.
|
||||
var canConnect = this.canConnectWithReason(candidate);
|
||||
if (canConnect != Blockly.Connection.CAN_CONNECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (candidate.type) {
|
||||
case Blockly.PREVIOUS_STATEMENT:
|
||||
return this.canConnectToPrevious_(candidate);
|
||||
case Blockly.OUTPUT_VALUE: {
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug.
|
||||
if ((candidate.isConnected() &&
|
||||
!candidate.targetBlock().isInsertionMarker()) ||
|
||||
this.isConnected()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blockly.INPUT_VALUE: {
|
||||
// Offering to connect the left (male) of a value block to an already
|
||||
// connected value pair is ok, we'll splice it in.
|
||||
// However, don't offer to splice into an immovable block.
|
||||
if (candidate.isConnected() &&
|
||||
!candidate.targetBlock().isMovable() &&
|
||||
!candidate.targetBlock().isShadow()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blockly.NEXT_STATEMENT: {
|
||||
// Don't let a block with no next connection bump other blocks out of the
|
||||
// stack. But covering up a shadow block or stack of shadow blocks is
|
||||
// fine. Similarly, replacing a terminal statement with another terminal
|
||||
// statement is allowed.
|
||||
if (candidate.isConnected() &&
|
||||
!this.sourceBlock_.nextConnection &&
|
||||
!candidate.targetBlock().isShadow() &&
|
||||
candidate.targetBlock().nextConnection) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw Error('Unknown connection type in isConnectionAllowed');
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
if (Blockly.draggingConnections.indexOf(candidate) != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
Blockly.utils.deprecation.warn(
|
||||
'Connection.prototype.isConnectionAllowed',
|
||||
'July 2020',
|
||||
'July 2021',
|
||||
'the workspace\'s connection checker');
|
||||
return this.getConnectionChecker().canConnect(this, candidate, true);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -422,21 +329,24 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
|
||||
// Already connected together. NOP.
|
||||
return;
|
||||
}
|
||||
this.checkConnection(otherConnection);
|
||||
var eventGroup = Blockly.Events.getGroup();
|
||||
if (!eventGroup) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
// Determine which block is superior (higher in the source stack).
|
||||
if (this.isSuperior()) {
|
||||
// Superior block.
|
||||
this.connect_(otherConnection);
|
||||
} else {
|
||||
// Inferior block.
|
||||
otherConnection.connect_(this);
|
||||
}
|
||||
if (!eventGroup) {
|
||||
Blockly.Events.setGroup(false);
|
||||
|
||||
var checker = this.getConnectionChecker();
|
||||
if (checker.canConnect(this, otherConnection, false)) {
|
||||
var eventGroup = Blockly.Events.getGroup();
|
||||
if (!eventGroup) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
// Determine which block is superior (higher in the source stack).
|
||||
if (this.isSuperior()) {
|
||||
// Superior block.
|
||||
this.connect_(otherConnection);
|
||||
} else {
|
||||
// Inferior block.
|
||||
otherConnection.connect_(this);
|
||||
}
|
||||
if (!eventGroup) {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -465,10 +375,12 @@ Blockly.Connection.connectReciprocally_ = function(first, second) {
|
||||
*/
|
||||
Blockly.Connection.singleConnection_ = function(block, orphanBlock) {
|
||||
var connection = null;
|
||||
var output = orphanBlock.outputConnection;
|
||||
for (var i = 0; i < block.inputList.length; i++) {
|
||||
var thisConnection = block.inputList[i].connection;
|
||||
var typeChecker = output.getConnectionChecker();
|
||||
if (thisConnection && thisConnection.type == Blockly.INPUT_VALUE &&
|
||||
orphanBlock.outputConnection.checkType(thisConnection)) {
|
||||
typeChecker.canConnect(output, thisConnection, false)) {
|
||||
if (connection) {
|
||||
return null; // More than one connection.
|
||||
}
|
||||
@@ -596,20 +508,17 @@ Blockly.Connection.prototype.targetBlock = function() {
|
||||
* value type system. E.g. square_root("Hello") is not compatible.
|
||||
* @param {!Blockly.Connection} otherConnection Connection to compare against.
|
||||
* @return {boolean} True if the connections share a type.
|
||||
* @deprecated July 2020. Will be deleted July 2021. Use the workspace's
|
||||
* connectionChecker instead.
|
||||
*/
|
||||
Blockly.Connection.prototype.checkType = function(otherConnection) {
|
||||
if (!this.check_ || !otherConnection.check_) {
|
||||
// One or both sides are promiscuous enough that anything will fit.
|
||||
return true;
|
||||
}
|
||||
// Find any intersection in the check lists.
|
||||
for (var i = 0; i < this.check_.length; i++) {
|
||||
if (otherConnection.check_.indexOf(this.check_[i]) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No intersection.
|
||||
return false;
|
||||
Blockly.utils.deprecation.warn(
|
||||
'Connection.prototype.checkType',
|
||||
'October 2019',
|
||||
'January 2021',
|
||||
'the workspace\'s connection checker');
|
||||
return this.getConnectionChecker().canConnect(this, otherConnection,
|
||||
false);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -618,12 +527,16 @@ Blockly.Connection.prototype.checkType = function(otherConnection) {
|
||||
* @param {!Blockly.Connection} otherConnection Connection to compare against.
|
||||
* @return {boolean} True if the connections share a type.
|
||||
* @private
|
||||
* @deprecated October 2019, use connection.checkType instead.
|
||||
* @deprecated October 2019. Will be deleted January 2021. Use the workspace's
|
||||
* connectionChecker instead.
|
||||
* @suppress {unusedPrivateMembers}
|
||||
*/
|
||||
Blockly.Connection.prototype.checkType_ = function(otherConnection) {
|
||||
console.warn('Deprecated call to Blockly.Connection.prototype.checkType_, ' +
|
||||
'use Blockly.Connection.prototype.checkType instead.');
|
||||
Blockly.utils.deprecation.warn(
|
||||
'Connection.prototype.checkType_',
|
||||
'October 2019',
|
||||
'January 2021',
|
||||
'the workspace\'s connection checker');
|
||||
return this.checkType(otherConnection);
|
||||
};
|
||||
|
||||
@@ -634,7 +547,8 @@ Blockly.Connection.prototype.checkType_ = function(otherConnection) {
|
||||
Blockly.Connection.prototype.onCheckChanged_ = function() {
|
||||
// The new value type may not be compatible with the existing connection.
|
||||
if (this.isConnected() && (!this.targetConnection ||
|
||||
!this.checkType(this.targetConnection))) {
|
||||
!this.getConnectionChecker().canConnect(
|
||||
this, this.targetConnection, false))) {
|
||||
var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
|
||||
child.unplug();
|
||||
}
|
||||
|
||||
280
core/connection_checker.js
Normal file
280
core/connection_checker.js
Normal file
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview An object that encapsulates logic for checking whether a potential
|
||||
* connection is safe and valid.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.ConnectionChecker');
|
||||
|
||||
goog.requireType('Blockly.Connection');
|
||||
goog.requireType('Blockly.IConnectionChecker');
|
||||
|
||||
|
||||
/**
|
||||
* Class for connection type checking logic.
|
||||
* @implements {Blockly.IConnectionChecker}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ConnectionChecker = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the current connection can connect with the target
|
||||
* connection.
|
||||
* @param {Blockly.Connection} a Connection to check compatibility with.
|
||||
* @param {Blockly.Connection} b Connection to check compatibility with.
|
||||
* @param {boolean} isDragging True if the connection is being made by dragging
|
||||
* a block.
|
||||
* @param {number=} opt_distance The max allowable distance between the
|
||||
* connections for drag checks.
|
||||
* @return {boolean} Whether the connection is legal.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.canConnect = function(a, b,
|
||||
isDragging, opt_distance) {
|
||||
return this.canConnectWithReason(a, b, isDragging, opt_distance) ==
|
||||
Blockly.Connection.CAN_CONNECT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the current connection can connect with the target
|
||||
* connection, and return an error code if there are problems.
|
||||
* @param {Blockly.Connection} a Connection to check compatibility with.
|
||||
* @param {Blockly.Connection} b Connection to check compatibility with.
|
||||
* @param {boolean} isDragging True if the connection is being made by dragging
|
||||
* a block.
|
||||
* @param {number=} opt_distance The max allowable distance between the
|
||||
* connections for drag checks.
|
||||
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
|
||||
* an error code otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.canConnectWithReason = function(
|
||||
a, b, isDragging, opt_distance) {
|
||||
var safety = this.doSafetyChecks(a, b);
|
||||
if (safety != Blockly.Connection.CAN_CONNECT) {
|
||||
return safety;
|
||||
}
|
||||
|
||||
// If the safety checks passed, both connections are non-null.
|
||||
var connOne = /** @type {!Blockly.Connection} **/ (a);
|
||||
var connTwo = /** @type {!Blockly.Connection} **/ (b);
|
||||
if (!this.doTypeChecks(connOne, connTwo)) {
|
||||
return Blockly.Connection.REASON_CHECKS_FAILED;
|
||||
}
|
||||
|
||||
if (isDragging &&
|
||||
!this.doDragChecks(
|
||||
/** @type {!Blockly.RenderedConnection} **/ (a),
|
||||
/** @type {!Blockly.RenderedConnection} **/ (b),
|
||||
opt_distance || 0)) {
|
||||
return Blockly.Connection.REASON_DRAG_CHECKS_FAILED;
|
||||
}
|
||||
|
||||
return Blockly.Connection.CAN_CONNECT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper method that translates a connection error code into a string.
|
||||
* @param {number} errorCode The error code.
|
||||
* @param {Blockly.Connection} a One of the two connections being checked.
|
||||
* @param {Blockly.Connection} b The second of the two connections being
|
||||
* checked.
|
||||
* @return {string} A developer-readable error string.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.getErrorMessage = function(errorCode,
|
||||
a, b) {
|
||||
switch (errorCode) {
|
||||
case Blockly.Connection.REASON_SELF_CONNECTION:
|
||||
return 'Attempted to connect a block to itself.';
|
||||
case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
|
||||
// Usually this means one block has been deleted.
|
||||
return 'Blocks not on same workspace.';
|
||||
case Blockly.Connection.REASON_WRONG_TYPE:
|
||||
return 'Attempt to connect incompatible types.';
|
||||
case Blockly.Connection.REASON_TARGET_NULL:
|
||||
return 'Target connection is null.';
|
||||
case Blockly.Connection.REASON_CHECKS_FAILED:
|
||||
var connOne = /** @type {!Blockly.Connection} **/ (a);
|
||||
var connTwo = /** @type {!Blockly.Connection} **/ (b);
|
||||
var msg = 'Connection checks failed. ';
|
||||
msg += connOne + ' expected ' + connOne.getCheck() + ', found ' + connTwo.getCheck();
|
||||
return msg;
|
||||
case Blockly.Connection.REASON_SHADOW_PARENT:
|
||||
return 'Connecting non-shadow to shadow block.';
|
||||
case Blockly.Connection.REASON_DRAG_CHECKS_FAILED:
|
||||
return 'Drag checks failed.';
|
||||
default:
|
||||
return 'Unknown connection failure: this should never happen!';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check that connecting the given connections is safe, meaning that it would
|
||||
* not break any of Blockly's basic assumptions (e.g. no self connections).
|
||||
* @param {Blockly.Connection} a The first of the connections to check.
|
||||
* @param {Blockly.Connection} b The second of the connections to check.
|
||||
* @return {number} An enum with the reason this connection is safe or unsafe.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.doSafetyChecks = function(a, b) {
|
||||
if (!a || !b) {
|
||||
return Blockly.Connection.REASON_TARGET_NULL;
|
||||
}
|
||||
if (a.isSuperior()) {
|
||||
var blockA = a.getSourceBlock();
|
||||
var blockB = b.getSourceBlock();
|
||||
} else {
|
||||
var blockB = a.getSourceBlock();
|
||||
var blockA = b.getSourceBlock();
|
||||
}
|
||||
if (blockA == blockB) {
|
||||
return Blockly.Connection.REASON_SELF_CONNECTION;
|
||||
} else if (b.type != Blockly.OPPOSITE_TYPE[a.type]) {
|
||||
return Blockly.Connection.REASON_WRONG_TYPE;
|
||||
} else if (blockA.workspace !== blockB.workspace) {
|
||||
return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
|
||||
} else if (blockA.isShadow() && !blockB.isShadow()) {
|
||||
return Blockly.Connection.REASON_SHADOW_PARENT;
|
||||
}
|
||||
return Blockly.Connection.CAN_CONNECT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether this connection is compatible with another connection with
|
||||
* respect to the value type system. E.g. square_root("Hello") is not
|
||||
* compatible.
|
||||
* @param {!Blockly.Connection} a Connection to compare.
|
||||
* @param {!Blockly.Connection} b Connection to compare against.
|
||||
* @return {boolean} True if the connections share a type.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.doTypeChecks = function(a, b) {
|
||||
var checkArrayOne = a.getCheck();
|
||||
var checkArrayTwo = b.getCheck();
|
||||
|
||||
if (!checkArrayOne || !checkArrayTwo) {
|
||||
// One or both sides are promiscuous enough that anything will fit.
|
||||
return true;
|
||||
}
|
||||
// Find any intersection in the check lists.
|
||||
for (var i = 0; i < checkArrayOne.length; i++) {
|
||||
if (checkArrayTwo.indexOf(checkArrayOne[i]) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No intersection.
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether this connection can be made by dragging.
|
||||
* @param {!Blockly.RenderedConnection} a Connection to compare.
|
||||
* @param {!Blockly.RenderedConnection} b Connection to compare against.
|
||||
* @param {number} distance The maximum allowable distance between connections.
|
||||
* @return {boolean} True if the connection is allowed during a drag.
|
||||
* @public
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.doDragChecks = function(a, b, distance) {
|
||||
if (a.distanceFrom(b) > distance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't consider insertion markers.
|
||||
if (b.getSourceBlock().isInsertionMarker()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (b.type) {
|
||||
case Blockly.PREVIOUS_STATEMENT:
|
||||
return this.canConnectToPrevious_(a, b);
|
||||
case Blockly.OUTPUT_VALUE: {
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug.
|
||||
if ((b.isConnected() &&
|
||||
!b.targetBlock().isInsertionMarker()) ||
|
||||
a.isConnected()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blockly.INPUT_VALUE: {
|
||||
// Offering to connect the left (male) of a value block to an already
|
||||
// connected value pair is ok, we'll splice it in.
|
||||
// However, don't offer to splice into an immovable block.
|
||||
if (b.isConnected() &&
|
||||
!b.targetBlock().isMovable() &&
|
||||
!b.targetBlock().isShadow()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blockly.NEXT_STATEMENT: {
|
||||
// Don't let a block with no next connection bump other blocks out of the
|
||||
// stack. But covering up a shadow block or stack of shadow blocks is
|
||||
// fine. Similarly, replacing a terminal statement with another terminal
|
||||
// statement is allowed.
|
||||
if (b.isConnected() &&
|
||||
!a.getSourceBlock().nextConnection &&
|
||||
!b.targetBlock().isShadow() &&
|
||||
b.targetBlock().nextConnection) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Unexpected connection type.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
if (Blockly.draggingConnections.indexOf(b) != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for drag checking.
|
||||
* @param {!Blockly.Connection} a The connection to check, which must be a
|
||||
* statement input or next connection.
|
||||
* @param {!Blockly.Connection} b A nearby connection to check, which
|
||||
* must be a previous connection.
|
||||
* @return {boolean} True if the connection is allowed, false otherwise.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.ConnectionChecker.prototype.canConnectToPrevious_ = function(a, b) {
|
||||
if (a.targetConnection) {
|
||||
// This connection is already occupied.
|
||||
// A next connection will never disconnect itself mid-drag.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
if (Blockly.draggingConnections.indexOf(b) != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!b.targetConnection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var targetBlock = b.targetBlock();
|
||||
// If it is connected to a real block, game over.
|
||||
if (!targetBlock.isInsertionMarker()) {
|
||||
return false;
|
||||
}
|
||||
// If it's connected to an insertion marker but that insertion marker
|
||||
// is the first block in a stack, it's still fine. If that insertion
|
||||
// marker is in the middle of a stack, it won't work.
|
||||
return !targetBlock.getPreviousBlock();
|
||||
};
|
||||
@@ -16,20 +16,32 @@ goog.provide('Blockly.ConnectionDB');
|
||||
|
||||
goog.require('Blockly.RenderedConnection');
|
||||
|
||||
goog.requireType('Blockly.IConnectionChecker');
|
||||
|
||||
|
||||
/**
|
||||
* Database of connections.
|
||||
* Connections are stored in order of their vertical component. This way
|
||||
* connections in an area may be looked up quickly using a binary search.
|
||||
* @param {!Blockly.IConnectionChecker} checker The workspace's
|
||||
* connection type checker, used to decide if connections are valid during a
|
||||
* drag.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ConnectionDB = function() {
|
||||
Blockly.ConnectionDB = function(checker) {
|
||||
/**
|
||||
* Array of connections sorted by y position in workspace units.
|
||||
* @type {!Array.<!Blockly.RenderedConnection>}
|
||||
* @private
|
||||
*/
|
||||
this.connections_ = [];
|
||||
/**
|
||||
* The workspace's connection type checker, used to decide if connections are
|
||||
* valid during a drag.
|
||||
* @type {!Blockly.IConnectionChecker}
|
||||
* @private
|
||||
*/
|
||||
this.connectionChecker_ = checker;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -240,7 +252,7 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
|
||||
var pointerMin = closestIndex - 1;
|
||||
while (pointerMin >= 0 && this.isInYRange_(pointerMin, conn.y, maxRadius)) {
|
||||
temp = this.connections_[pointerMin];
|
||||
if (conn.isConnectionAllowed(temp, bestRadius)) {
|
||||
if (this.connectionChecker_.canConnect(conn, temp, true, bestRadius)) {
|
||||
bestConnection = temp;
|
||||
bestRadius = temp.distanceFrom(conn);
|
||||
}
|
||||
@@ -251,7 +263,7 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
|
||||
while (pointerMax < this.connections_.length &&
|
||||
this.isInYRange_(pointerMax, conn.y, maxRadius)) {
|
||||
temp = this.connections_[pointerMax];
|
||||
if (conn.isConnectionAllowed(temp, bestRadius)) {
|
||||
if (this.connectionChecker_.canConnect(conn, temp, true, bestRadius)) {
|
||||
bestConnection = temp;
|
||||
bestRadius = temp.distanceFrom(conn);
|
||||
}
|
||||
@@ -268,14 +280,16 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius,
|
||||
|
||||
/**
|
||||
* Initialize a set of connection DBs for a workspace.
|
||||
* @param {!Blockly.IConnectionChecker} checker The workspace's
|
||||
* connection checker, used to decide if connections are valid during a drag.
|
||||
* @return {!Array.<!Blockly.ConnectionDB>} Array of databases.
|
||||
*/
|
||||
Blockly.ConnectionDB.init = function() {
|
||||
Blockly.ConnectionDB.init = function(checker) {
|
||||
// Create four databases, one for each connection type.
|
||||
var dbList = [];
|
||||
dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB(checker);
|
||||
dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB(checker);
|
||||
dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB(checker);
|
||||
dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB(checker);
|
||||
return dbList;
|
||||
};
|
||||
|
||||
94
core/interfaces/i_connection_checker.js
Normal file
94
core/interfaces/i_connection_checker.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The interface for an object that encapsulates logic for
|
||||
* checking whether a potential connection is safe and valid.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.IConnectionChecker');
|
||||
|
||||
goog.requireType('Blockly.Connection');
|
||||
|
||||
|
||||
/**
|
||||
* Class for connection type checking logic.
|
||||
* @interface
|
||||
*/
|
||||
Blockly.IConnectionChecker = function() {};
|
||||
|
||||
/**
|
||||
* Check whether the current connection can connect with the target
|
||||
* connection.
|
||||
* @param {Blockly.Connection} a Connection to check compatibility with.
|
||||
* @param {Blockly.Connection} b Connection to check compatibility with.
|
||||
* @param {boolean} isDragging True if the connection is being made by dragging
|
||||
* a block.
|
||||
* @param {number=} opt_distance The max allowable distance between the
|
||||
* connections for drag checks.
|
||||
* @return {boolean} Whether the connection is legal.
|
||||
* @public
|
||||
*/
|
||||
Blockly.IConnectionChecker.prototype.canConnect;
|
||||
|
||||
/**
|
||||
* Checks whether the current connection can connect with the target
|
||||
* connection, and return an error code if there are problems.
|
||||
* @param {Blockly.Connection} a Connection to check compatibility with.
|
||||
* @param {Blockly.Connection} b Connection to check compatibility with.
|
||||
* @param {boolean} isDragging True if the connection is being made by dragging
|
||||
* a block.
|
||||
* @param {number=} opt_distance The max allowable distance between the
|
||||
* connections for drag checks.
|
||||
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
|
||||
* an error code otherwise.
|
||||
* @public
|
||||
*/
|
||||
Blockly.IConnectionChecker.prototype.canConnectWithReason;
|
||||
|
||||
/**
|
||||
* Helper method that translates a connection error code into a string.
|
||||
* @param {number} errorCode The error code.
|
||||
* @param {Blockly.Connection} a One of the two connections being checked.
|
||||
* @param {Blockly.Connection} b The second of the two connections being
|
||||
* checked.
|
||||
* @return {string} A developer-readable error string.
|
||||
* @public
|
||||
*/
|
||||
Blockly.IConnectionChecker.prototype.getErrorMessage;
|
||||
|
||||
/**
|
||||
* Check that connecting the given connections is safe, meaning that it would
|
||||
* not break any of Blockly's basic assumptions (e.g. no self connections).
|
||||
* @param {Blockly.Connection} a The first of the connections to check.
|
||||
* @param {Blockly.Connection} b The second of the connections to check.
|
||||
* @return {number} An enum with the reason this connection is safe or unsafe.
|
||||
* @public
|
||||
*/
|
||||
Blockly.IConnectionChecker.prototype.doSafetyChecks;
|
||||
|
||||
/**
|
||||
* Check whether this connection is compatible with another connection with
|
||||
* respect to the value type system. E.g. square_root("Hello") is not
|
||||
* compatible.
|
||||
* @param {!Blockly.Connection} a Connection to compare.
|
||||
* @param {!Blockly.Connection} b Connection to compare against.
|
||||
* @return {boolean} True if the connections share a type.
|
||||
* @public
|
||||
*/
|
||||
Blockly.IConnectionChecker.prototype.doTypeChecks;
|
||||
|
||||
/**
|
||||
* Check whether this connection can be made by dragging.
|
||||
* @param {!Blockly.RenderedConnection} a Connection to compare.
|
||||
* @param {!Blockly.RenderedConnection} b Connection to compare against.
|
||||
* @param {number} distance The maximum allowable distance between connections.
|
||||
* @return {boolean} True if the connection is allowed during a drag.
|
||||
* @public
|
||||
*/
|
||||
Blockly.IConnectionChecker.prototype.doDragChecks;
|
||||
@@ -18,6 +18,7 @@ goog.require('Blockly.ASTNode');
|
||||
goog.require('Blockly.utils.Coordinate');
|
||||
goog.require('Blockly.user.keyMap');
|
||||
|
||||
|
||||
/**
|
||||
* A function to call to give feedback to the user about logs, warnings, and
|
||||
* errors. You can override this to customize feedback (e.g. warning sounds,
|
||||
@@ -409,9 +410,9 @@ Blockly.navigation.moveAndConnect_ = function(movingConnection, destConnection)
|
||||
}
|
||||
var movingBlock = movingConnection.getSourceBlock();
|
||||
|
||||
if (destConnection.canConnectWithReason(movingConnection) ==
|
||||
Blockly.Connection.CAN_CONNECT) {
|
||||
var checker = movingConnection.getConnectionChecker();
|
||||
|
||||
if (checker.canConnect(movingConnection, destConnection, false)) {
|
||||
Blockly.navigation.disconnectChild_(movingConnection, destConnection);
|
||||
|
||||
if (!destConnection.isSuperior()) {
|
||||
@@ -499,13 +500,11 @@ Blockly.navigation.connect_ = function(movingConnection, destConnection) {
|
||||
} else if (Blockly.navigation.moveAndConnect_(movingConnection, destConnection)){
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
destConnection.checkConnection(movingConnection);
|
||||
}
|
||||
catch (e) {
|
||||
// If nothing worked report the error from the original connections.
|
||||
Blockly.navigation.warn_('Connection failed with error: ' + e);
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -420,6 +420,7 @@ Blockly.RenderedConnection.prototype.startTrackingAll = function() {
|
||||
* @param {number=} maxRadius The maximum radius allowed for connections, in
|
||||
* workspace units.
|
||||
* @return {boolean} True if the connection is allowed, false otherwise.
|
||||
* @deprecated July 2020
|
||||
*/
|
||||
Blockly.RenderedConnection.prototype.isConnectionAllowed = function(candidate,
|
||||
maxRadius) {
|
||||
@@ -549,7 +550,8 @@ Blockly.RenderedConnection.prototype.connect_ = function(childConnection) {
|
||||
Blockly.RenderedConnection.prototype.onCheckChanged_ = function() {
|
||||
// The new value type may not be compatible with the existing connection.
|
||||
if (this.isConnected() && (!this.targetConnection ||
|
||||
!this.checkType(this.targetConnection))) {
|
||||
!this.getConnectionChecker().canConnect(
|
||||
this, this.targetConnection, false))) {
|
||||
var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
|
||||
child.unplug();
|
||||
// Bump away.
|
||||
|
||||
@@ -254,7 +254,8 @@ Blockly.blockRendering.Renderer.prototype.orphanCanConnectAtEnd =
|
||||
if (!lastConnection) {
|
||||
return false;
|
||||
}
|
||||
return orphanConnection.checkType(lastConnection);
|
||||
return orphanConnection.getConnectionChecker().canConnect(
|
||||
lastConnection, orphanConnection, false);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
goog.provide('Blockly.Workspace');
|
||||
|
||||
goog.require('Blockly.ConnectionChecker');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Options');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -19,6 +20,7 @@ goog.require('Blockly.utils.math');
|
||||
goog.require('Blockly.VariableMap');
|
||||
|
||||
goog.requireType('Blockly.IASTNodeLocation');
|
||||
goog.requireType('Blockly.IConnectionChecker');
|
||||
|
||||
|
||||
/**
|
||||
@@ -42,6 +44,12 @@ Blockly.Workspace = function(opt_options) {
|
||||
/** @type {number} */
|
||||
this.toolboxPosition = this.options.toolboxPosition;
|
||||
|
||||
/**
|
||||
* An object that encapsulates logic for safety, type, and dragging checks.
|
||||
* @type {!Blockly.IConnectionChecker}
|
||||
*/
|
||||
this.connectionChecker = new Blockly.ConnectionChecker();
|
||||
|
||||
/**
|
||||
* @type {!Array.<!Blockly.Block>}
|
||||
* @private
|
||||
|
||||
@@ -68,7 +68,8 @@ Blockly.WorkspaceSvg = function(options,
|
||||
this.setMetrics =
|
||||
options.setMetrics || Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_;
|
||||
|
||||
this.connectionDBList = Blockly.ConnectionDB.init();
|
||||
|
||||
this.connectionDBList = Blockly.ConnectionDB.init(this.connectionChecker);
|
||||
|
||||
if (opt_blockDragSurface) {
|
||||
this.blockDragSurface_ = opt_blockDragSurface;
|
||||
|
||||
264
tests/mocha/connection_checker_test.js
Normal file
264
tests/mocha/connection_checker_test.js
Normal file
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Connection checker', function() {
|
||||
suiteSetup(function() {
|
||||
this.checker = new Blockly.ConnectionChecker();
|
||||
});
|
||||
suite('Safety checks', function() {
|
||||
function assertReasonHelper(checker, one, two, reason) {
|
||||
chai.assert.equal(checker.canConnectWithReason(one, two), reason);
|
||||
// Order should not matter.
|
||||
chai.assert.equal(checker.canConnectWithReason(two, one), reason);
|
||||
}
|
||||
|
||||
test('Target Null', function() {
|
||||
var connection = new Blockly.Connection({}, Blockly.INPUT_VALUE);
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
connection,
|
||||
null,
|
||||
Blockly.Connection.REASON_TARGET_NULL);
|
||||
});
|
||||
test('Target Self', function() {
|
||||
var block = {workspace: 1};
|
||||
var connection1 = new Blockly.Connection(block, Blockly.INPUT_VALUE);
|
||||
var connection2 = new Blockly.Connection(block, Blockly.OUTPUT_VALUE);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
connection1,
|
||||
connection2,
|
||||
Blockly.Connection.REASON_SELF_CONNECTION);
|
||||
});
|
||||
test('Different Workspaces', function() {
|
||||
var connection1 = new Blockly.Connection(
|
||||
{workspace: 1}, Blockly.INPUT_VALUE);
|
||||
var connection2 = new Blockly.Connection(
|
||||
{workspace: 2}, Blockly.OUTPUT_VALUE);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
connection1,
|
||||
connection2,
|
||||
Blockly.Connection.REASON_DIFFERENT_WORKSPACES);
|
||||
});
|
||||
suite('Types', function() {
|
||||
setup(function() {
|
||||
// We have to declare each separately so that the connections belong
|
||||
// on different blocks.
|
||||
var prevBlock = { isShadow: function() {}};
|
||||
var nextBlock = { isShadow: function() {}};
|
||||
var outBlock = { isShadow: function() {}};
|
||||
var inBlock = { isShadow: function() {}};
|
||||
this.previous = new Blockly.Connection(
|
||||
prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
this.next = new Blockly.Connection(
|
||||
nextBlock, Blockly.NEXT_STATEMENT);
|
||||
this.output = new Blockly.Connection(
|
||||
outBlock, Blockly.OUTPUT_VALUE);
|
||||
this.input = new Blockly.Connection(
|
||||
inBlock, Blockly.INPUT_VALUE);
|
||||
});
|
||||
test('Previous, Next', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.previous,
|
||||
this.next,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Previous, Output', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.previous,
|
||||
this.output,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Previous, Input', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.previous,
|
||||
this.input,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Next, Previous', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.next,
|
||||
this.previous,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Next, Output', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.next,
|
||||
this.output,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Next, Input', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.next,
|
||||
this.input,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Output, Previous', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.previous,
|
||||
this.output,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Output, Next', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.output,
|
||||
this.next,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Output, Input', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.output,
|
||||
this.input,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Input, Previous', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.previous,
|
||||
this.input,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Input, Next', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.input,
|
||||
this.next,
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Input, Output', function() {
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
this.input,
|
||||
this.output,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
});
|
||||
suite('Shadows', function() {
|
||||
test('Previous Shadow', function() {
|
||||
var prevBlock = { isShadow: function() { return true; }};
|
||||
var nextBlock = { isShadow: function() { return false; }};
|
||||
var prev = new Blockly.Connection(prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
var next = new Blockly.Connection(nextBlock, Blockly.NEXT_STATEMENT);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
prev,
|
||||
next,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Next Shadow', function() {
|
||||
var prevBlock = { isShadow: function() { return false; }};
|
||||
var nextBlock = { isShadow: function() { return true; }};
|
||||
var prev = new Blockly.Connection(prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
var next = new Blockly.Connection(nextBlock, Blockly.NEXT_STATEMENT);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
prev,
|
||||
next,
|
||||
Blockly.Connection.REASON_SHADOW_PARENT);
|
||||
});
|
||||
test('Prev and Next Shadow', function() {
|
||||
var prevBlock = { isShadow: function() { return true; }};
|
||||
var nextBlock = { isShadow: function() { return true; }};
|
||||
var prev = new Blockly.Connection(prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
var next = new Blockly.Connection(nextBlock, Blockly.NEXT_STATEMENT);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
prev,
|
||||
next,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Output Shadow', function() {
|
||||
var outBlock = { isShadow: function() { return true; }};
|
||||
var inBlock = { isShadow: function() { return false; }};
|
||||
var outCon = new Blockly.Connection(outBlock, Blockly.OUTPUT_VALUE);
|
||||
var inCon = new Blockly.Connection(inBlock, Blockly.INPUT_VALUE);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
outCon,
|
||||
inCon,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Input Shadow', function() {
|
||||
var outBlock = { isShadow: function() { return false; }};
|
||||
var inBlock = { isShadow: function() { return true; }};
|
||||
var outCon = new Blockly.Connection(outBlock, Blockly.OUTPUT_VALUE);
|
||||
var inCon = new Blockly.Connection(inBlock, Blockly.INPUT_VALUE);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
outCon,
|
||||
inCon,
|
||||
Blockly.Connection.REASON_SHADOW_PARENT);
|
||||
});
|
||||
test('Output and Input Shadow', function() {
|
||||
var outBlock = { isShadow: function() { return true; }};
|
||||
var inBlock = { isShadow: function() { return true; }};
|
||||
var outCon = new Blockly.Connection(outBlock, Blockly.OUTPUT_VALUE);
|
||||
var inCon = new Blockly.Connection(inBlock, Blockly.INPUT_VALUE);
|
||||
|
||||
assertReasonHelper(
|
||||
this.checker,
|
||||
outCon,
|
||||
inCon,
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
});
|
||||
});
|
||||
suite('Check Types', function() {
|
||||
setup(function() {
|
||||
this.con1 = new Blockly.Connection({}, Blockly.PREVIOUS_STATEMENT);
|
||||
this.con2 = new Blockly.Connection({}, Blockly.NEXT_STATEMENT);
|
||||
});
|
||||
function assertCheckTypes(checker, one, two) {
|
||||
chai.assert.isTrue(checker.doTypeChecks(one, two));
|
||||
// Order should not matter.
|
||||
chai.assert.isTrue(checker.doTypeChecks(one, two));
|
||||
}
|
||||
test('No Types', function() {
|
||||
assertCheckTypes(this.checker, this.con1, this.con2);
|
||||
});
|
||||
test('Same Type', function() {
|
||||
this.con1.setCheck('type1');
|
||||
this.con2.setCheck('type1');
|
||||
assertCheckTypes(this.checker, this.con1, this.con2);
|
||||
});
|
||||
test('Same Types', function() {
|
||||
this.con1.setCheck(['type1', 'type2']);
|
||||
this.con2.setCheck(['type1', 'type2']);
|
||||
assertCheckTypes(this.checker, this.con1, this.con2);
|
||||
});
|
||||
test('Single Same Type', function() {
|
||||
this.con1.setCheck(['type1', 'type2']);
|
||||
this.con2.setCheck(['type1', 'type3']);
|
||||
assertCheckTypes(this.checker, this.con1, this.con2);
|
||||
});
|
||||
test('One Typed, One Promiscuous', function() {
|
||||
this.con1.setCheck('type1');
|
||||
assertCheckTypes(this.checker, this.con1, this.con2);
|
||||
});
|
||||
test('No Compatible Types', function() {
|
||||
this.con1.setCheck('type1');
|
||||
this.con2.setCheck('type2');
|
||||
chai.assert.isFalse(this.checker.doTypeChecks(this.con1, this.con2));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
suite('Connection Database', function() {
|
||||
setup(function() {
|
||||
this.database = new Blockly.ConnectionDB();
|
||||
this.database = new Blockly.ConnectionDB(new Blockly.ConnectionChecker());
|
||||
|
||||
this.assertOrder = function() {
|
||||
var length = this.database.connections_.length;
|
||||
@@ -190,28 +190,35 @@ suite('Connection Database', function() {
|
||||
this.assertOrder();
|
||||
});
|
||||
});
|
||||
// Does not cover logic for isConnectionAllowed
|
||||
|
||||
suite('Search For Closest', function() {
|
||||
setup(function() {
|
||||
this.allowedStub = null;
|
||||
this.allowedStubs = [];
|
||||
// Ignore type checks.
|
||||
this.allowedStubs.push(sinon.stub(this.database.connectionChecker_, 'doTypeChecks')
|
||||
.callsFake(function(_a, _b) {
|
||||
return true;
|
||||
}));
|
||||
// Ignore safety checks.
|
||||
this.allowedStubs.push(sinon.stub(this.database.connectionChecker_, 'doSafetyChecks')
|
||||
.callsFake(function(_a, _b) {
|
||||
return Blockly.Connection.CAN_CONNECT;
|
||||
}));
|
||||
// Skip everything but the distance checks.
|
||||
this.allowedStubs.push(sinon.stub(this.database.connectionChecker_, 'doDragChecks')
|
||||
.callsFake(function(a, b, distance) {
|
||||
return a.distanceFrom(b) <= distance;
|
||||
}));
|
||||
|
||||
this.createCheckConnection = function(x, y) {
|
||||
var checkConnection = this.createConnection(x, y, Blockly.NEXT_STATEMENT,
|
||||
new Blockly.ConnectionDB());
|
||||
this.allowedStub = sinon.stub(checkConnection, 'isConnectionAllowed')
|
||||
.callsFake(function(candidate, maxRadius) {
|
||||
if (this.distanceFrom(candidate) > maxRadius) {
|
||||
return false;
|
||||
}
|
||||
// Ignore non-distance parameters.
|
||||
return true;
|
||||
});
|
||||
return checkConnection;
|
||||
};
|
||||
});
|
||||
teardown(function() {
|
||||
if (this.allowedStub) {
|
||||
this.allowedStub.restore();
|
||||
for (var i = 0; i < this.allowedStubs.length; i++) {
|
||||
this.allowedStubs[i].restore();
|
||||
}
|
||||
});
|
||||
test('Empty Database', function() {
|
||||
|
||||
@@ -4,184 +4,44 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
suite('Connections', function() {
|
||||
suite('Can Connect With Reason', function() {
|
||||
test('Target Null', function() {
|
||||
var connection = new Blockly.Connection({}, Blockly.INPUT_VALUE);
|
||||
chai.assert.equal(connection.canConnectWithReason(null),
|
||||
Blockly.Connection.REASON_TARGET_NULL);
|
||||
});
|
||||
test('Target Self', function() {
|
||||
var block = {workspace: 1};
|
||||
var connection1 = new Blockly.Connection(block, Blockly.INPUT_VALUE);
|
||||
var connection2 = new Blockly.Connection(block, Blockly.OUTPUT_VALUE);
|
||||
|
||||
chai.assert.equal(connection1.canConnectWithReason(connection2),
|
||||
Blockly.Connection.REASON_SELF_CONNECTION);
|
||||
});
|
||||
test('Different Workspaces', function() {
|
||||
var connection1 = new Blockly.Connection(
|
||||
{workspace: 1}, Blockly.INPUT_VALUE);
|
||||
var connection2 = new Blockly.Connection(
|
||||
{workspace: 2}, Blockly.OUTPUT_VALUE);
|
||||
|
||||
chai.assert.equal(connection1.canConnectWithReason(connection2),
|
||||
Blockly.Connection.REASON_DIFFERENT_WORKSPACES);
|
||||
});
|
||||
suite('Types', function() {
|
||||
setup(function() {
|
||||
// We have to declare each separately so that the connections belong
|
||||
// on different blocks.
|
||||
var prevBlock = { isShadow: function() {}};
|
||||
var nextBlock = { isShadow: function() {}};
|
||||
var outBlock = { isShadow: function() {}};
|
||||
var inBlock = { isShadow: function() {}};
|
||||
this.previous = new Blockly.Connection(
|
||||
prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
this.next = new Blockly.Connection(
|
||||
nextBlock, Blockly.NEXT_STATEMENT);
|
||||
this.output = new Blockly.Connection(
|
||||
outBlock, Blockly.OUTPUT_VALUE);
|
||||
this.input = new Blockly.Connection(
|
||||
inBlock, Blockly.INPUT_VALUE);
|
||||
});
|
||||
test('Previous, Next', function() {
|
||||
chai.assert.equal(this.previous.canConnectWithReason(this.next),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Previous, Output', function() {
|
||||
chai.assert.equal(this.previous.canConnectWithReason(this.output),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Previous, Input', function() {
|
||||
chai.assert.equal(this.previous.canConnectWithReason(this.input),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Next, Previous', function() {
|
||||
chai.assert.equal(this.next.canConnectWithReason(this.previous),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Next, Output', function() {
|
||||
chai.assert.equal(this.next.canConnectWithReason(this.output),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Next, Input', function() {
|
||||
chai.assert.equal(this.next.canConnectWithReason(this.input),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Output, Previous', function() {
|
||||
chai.assert.equal(this.output.canConnectWithReason(this.previous),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Output, Next', function() {
|
||||
chai.assert.equal(this.output.canConnectWithReason(this.next),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Output, Input', function() {
|
||||
chai.assert.equal(this.output.canConnectWithReason(this.input),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Input, Previous', function() {
|
||||
chai.assert.equal(this.input.canConnectWithReason(this.previous),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Input, Next', function() {
|
||||
chai.assert.equal(this.input.canConnectWithReason(this.next),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('Input, Output', function() {
|
||||
chai.assert.equal(this.input.canConnectWithReason(this.output),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
});
|
||||
suite('Shadows', function() {
|
||||
test('Previous Shadow', function() {
|
||||
var prevBlock = { isShadow: function() { return true; }};
|
||||
var nextBlock = { isShadow: function() { return false; }};
|
||||
var prev = new Blockly.Connection(prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
var next = new Blockly.Connection(nextBlock, Blockly.NEXT_STATEMENT);
|
||||
|
||||
chai.assert.equal(prev.canConnectWithReason(next),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Next Shadow', function() {
|
||||
var prevBlock = { isShadow: function() { return false; }};
|
||||
var nextBlock = { isShadow: function() { return true; }};
|
||||
var prev = new Blockly.Connection(prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
var next = new Blockly.Connection(nextBlock, Blockly.NEXT_STATEMENT);
|
||||
|
||||
chai.assert.equal(prev.canConnectWithReason(next),
|
||||
Blockly.Connection.REASON_SHADOW_PARENT);
|
||||
});
|
||||
test('Prev and Next Shadow', function() {
|
||||
var prevBlock = { isShadow: function() { return true; }};
|
||||
var nextBlock = { isShadow: function() { return true; }};
|
||||
var prev = new Blockly.Connection(prevBlock, Blockly.PREVIOUS_STATEMENT);
|
||||
var next = new Blockly.Connection(nextBlock, Blockly.NEXT_STATEMENT);
|
||||
|
||||
chai.assert.equal(prev.canConnectWithReason(next),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Output Shadow', function() {
|
||||
var outBlock = { isShadow: function() { return true; }};
|
||||
var inBlock = { isShadow: function() { return false; }};
|
||||
var outCon = new Blockly.Connection(outBlock, Blockly.OUTPUT_VALUE);
|
||||
var inCon = new Blockly.Connection(inBlock, Blockly.INPUT_VALUE);
|
||||
|
||||
chai.assert.equal(outCon.canConnectWithReason(inCon),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('Input Shadow', function() {
|
||||
var outBlock = { isShadow: function() { return false; }};
|
||||
var inBlock = { isShadow: function() { return true; }};
|
||||
var outCon = new Blockly.Connection(outBlock, Blockly.OUTPUT_VALUE);
|
||||
var inCon = new Blockly.Connection(inBlock, Blockly.INPUT_VALUE);
|
||||
|
||||
chai.assert.equal(outCon.canConnectWithReason(inCon),
|
||||
Blockly.Connection.REASON_SHADOW_PARENT);
|
||||
});
|
||||
test('Output and Input Shadow', function() {
|
||||
var outBlock = { isShadow: function() { return true; }};
|
||||
var inBlock = { isShadow: function() { return true; }};
|
||||
var outCon = new Blockly.Connection(outBlock, Blockly.OUTPUT_VALUE);
|
||||
var inCon = new Blockly.Connection(inBlock, Blockly.INPUT_VALUE);
|
||||
|
||||
chai.assert.equal(outCon.canConnectWithReason(inCon),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
suite('Connection', function() {
|
||||
suiteSetup(function() {
|
||||
this.workspace = {
|
||||
connectionChecker: new Blockly.ConnectionChecker()
|
||||
};
|
||||
this.createConnection = function(type) {
|
||||
var block = {
|
||||
workspace: this.workspace,
|
||||
isShadow: function() { return false; }
|
||||
};
|
||||
var connection = new Blockly.Connection(block, type);
|
||||
return connection;
|
||||
};
|
||||
});
|
||||
test('canConnectWithReason passes', function() {
|
||||
var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT);
|
||||
var conn2 = this.createConnection(Blockly.NEXT_STATEMENT);
|
||||
chai.assert.equal(conn1.canConnectWithReason(conn2),
|
||||
Blockly.Connection.CAN_CONNECT);
|
||||
});
|
||||
test('canConnectWithReason fails', function() {
|
||||
var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT);
|
||||
var conn2 = this.createConnection(Blockly.OUTPUT_VALUE);
|
||||
chai.assert.equal(conn1.canConnectWithReason(conn2),
|
||||
Blockly.Connection.REASON_WRONG_TYPE);
|
||||
});
|
||||
test('checkConnection passes', function() {
|
||||
var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT);
|
||||
var conn2 = this.createConnection(Blockly.NEXT_STATEMENT);
|
||||
chai.assert.doesNotThrow(function() {
|
||||
conn1.checkConnection(conn2);
|
||||
});
|
||||
});
|
||||
suite('Check Types', function() {
|
||||
setup(function() {
|
||||
this.con1 = new Blockly.Connection({}, Blockly.PREVIOUS_STATEMENT);
|
||||
this.con2 = new Blockly.Connection({}, Blockly.NEXT_STATEMENT);
|
||||
});
|
||||
test('No Types', function() {
|
||||
chai.assert.isTrue(this.con1.checkType((this.con2)));
|
||||
});
|
||||
test('Same Type', function() {
|
||||
this.con1.setCheck('type1');
|
||||
this.con2.setCheck('type1');
|
||||
chai.assert.isTrue(this.con1.checkType((this.con2)));
|
||||
});
|
||||
test('Same Types', function() {
|
||||
this.con1.setCheck(['type1', 'type2']);
|
||||
this.con2.setCheck(['type1', 'type2']);
|
||||
chai.assert.isTrue(this.con1.checkType((this.con2)));
|
||||
});
|
||||
test('Single Same Type', function() {
|
||||
this.con1.setCheck(['type1', 'type2']);
|
||||
this.con2.setCheck(['type1', 'type3']);
|
||||
chai.assert.isTrue(this.con1.checkType((this.con2)));
|
||||
});
|
||||
test('One Typed, One Promiscuous', function() {
|
||||
this.con1.setCheck('type1');
|
||||
chai.assert.isTrue(this.con1.checkType((this.con2)));
|
||||
});
|
||||
test('No Compatible Types', function() {
|
||||
this.con1.setCheck('type1');
|
||||
this.con2.setCheck('type2');
|
||||
chai.assert.isFalse(this.con1.checkType((this.con2)));
|
||||
test('checkConnection fails', function() {
|
||||
var conn1 = this.createConnection(Blockly.PREVIOUS_STATEMENT);
|
||||
var conn2 = this.createConnection(Blockly.OUTPUT_VALUE);
|
||||
chai.assert.throws(function() {
|
||||
conn1.checkConnection(conn2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
<script src="block_test.js"></script>
|
||||
<script src="comment_test.js"></script>
|
||||
<script src="connection_db_test.js"></script>
|
||||
<script src="connection_checker_test.js"></script>
|
||||
<script src="connection_test.js"></script>
|
||||
<script src="connection_db_test.js"></script>
|
||||
<script src="cursor_test.js"></script>
|
||||
<script src="dropdowndiv_test.js"></script>
|
||||
<script src="event_test.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user