2013-10-30 14:46:03 -07:00
|
|
|
/**
|
2014-01-28 03:00:09 -08:00
|
|
|
* @license
|
2013-10-30 14:46:03 -07:00
|
|
|
* Visual Blocks Editor
|
|
|
|
*
|
|
|
|
* Copyright 2011 Google Inc.
|
2014-10-07 13:09:55 -07:00
|
|
|
* https://developers.google.com/blockly/
|
2013-10-30 14:46:03 -07:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @fileoverview Components for creating connections between blocks.
|
|
|
|
* @author fraser@google.com (Neil Fraser)
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
goog.provide('Blockly.Connection');
|
|
|
|
|
2016-03-03 17:48:54 -08:00
|
|
|
goog.require('goog.asserts');
|
2015-02-06 15:27:25 -08:00
|
|
|
goog.require('goog.dom');
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Class for a connection between blocks.
|
|
|
|
* @param {!Blockly.Block} source The block establishing this connection.
|
|
|
|
* @param {number} type The type of the connection.
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
Blockly.Connection = function(source, type) {
|
2015-07-27 14:18:43 -05:00
|
|
|
/** @type {!Blockly.Block} */
|
2013-10-30 14:46:03 -07:00
|
|
|
this.sourceBlock_ = source;
|
2015-07-13 16:35:54 -05:00
|
|
|
/** @type {number} */
|
2013-10-30 14:46:03 -07:00
|
|
|
this.type = type;
|
|
|
|
// Shortcut for the databases for this connection's workspace.
|
2015-10-09 13:53:14 -07:00
|
|
|
if (source.workspace.connectionDBList) {
|
|
|
|
this.db_ = source.workspace.connectionDBList[type];
|
|
|
|
this.dbOpposite_ =
|
|
|
|
source.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[type]];
|
|
|
|
this.hidden_ = !this.db_;
|
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
2016-02-25 13:35:46 -08:00
|
|
|
/**
|
|
|
|
* Constants for checking whether two connections are compatible.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.CAN_CONNECT = 0;
|
|
|
|
Blockly.Connection.REASON_SELF_CONNECTION = 1;
|
|
|
|
Blockly.Connection.REASON_WRONG_TYPE = 2;
|
2016-03-03 17:48:54 -08:00
|
|
|
Blockly.Connection.REASON_TARGET_NULL = 3;
|
|
|
|
Blockly.Connection.REASON_CHECKS_FAILED = 4;
|
|
|
|
Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connect two connections together.
|
|
|
|
* @param {!Blockly.Connection} parentConnection Connection on superior block.
|
|
|
|
* @param {!Blockly.Connection} childConnection Connection on inferior block.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.connect_ = function(parentConnection, childConnection) {
|
2016-03-15 16:03:49 -07:00
|
|
|
var parentBlock = parentConnection.getSourceBlock();
|
|
|
|
var childBlock = childConnection.getSourceBlock();
|
2016-03-03 17:48:54 -08:00
|
|
|
// Disconnect any existing parent on the child connection.
|
|
|
|
if (childConnection.targetConnection) {
|
|
|
|
childConnection.disconnect();
|
|
|
|
}
|
|
|
|
if (parentConnection.targetConnection) {
|
|
|
|
// Other connection is already connected to something.
|
|
|
|
// Disconnect it and reattach it or bump it as needed.
|
|
|
|
var orphanBlock = parentConnection.targetBlock();
|
|
|
|
var shadowDom = parentConnection.getShadowDom();
|
|
|
|
// Temporarily set the shadow DOM to null so it does not respawn.
|
|
|
|
parentConnection.setShadowDom(null);
|
|
|
|
// Displaced shadow blocks dissolve rather than reattaching or bumping.
|
|
|
|
if (orphanBlock.isShadow()) {
|
|
|
|
// Save the shadow block so that field values are preserved.
|
|
|
|
shadowDom = Blockly.Xml.blockToDom(orphanBlock);
|
|
|
|
orphanBlock.dispose();
|
|
|
|
orphanBlock = null;
|
|
|
|
} else if (parentConnection.type == Blockly.INPUT_VALUE) {
|
|
|
|
// Value connections.
|
|
|
|
// If female block is already connected, disconnect and bump the male.
|
|
|
|
if (!orphanBlock.outputConnection) {
|
|
|
|
throw 'Orphan block does not have an output connection.';
|
|
|
|
}
|
|
|
|
// Attempt to reattach the orphan at the end of the newly inserted
|
|
|
|
// block. Since this block may be a row, walk down to the end
|
|
|
|
// or to the first (and only) shadow block.
|
|
|
|
var connection = Blockly.Connection.lastConnectionInRow_(
|
|
|
|
childBlock, orphanBlock);
|
|
|
|
if (connection) {
|
|
|
|
orphanBlock.outputConnection.connect(connection);
|
|
|
|
orphanBlock = null;
|
|
|
|
}
|
|
|
|
} else if (parentConnection.type == Blockly.NEXT_STATEMENT) {
|
|
|
|
// Statement connections.
|
|
|
|
// Statement blocks may be inserted into the middle of a stack.
|
|
|
|
// Split the stack.
|
|
|
|
if (!orphanBlock.previousConnection) {
|
|
|
|
throw 'Orphan block does not have a previous connection.';
|
|
|
|
}
|
|
|
|
// Attempt to reattach the orphan at the bottom of the newly inserted
|
|
|
|
// block. Since this block may be a stack, walk down to the end.
|
|
|
|
var newBlock = childBlock;
|
|
|
|
while (newBlock.nextConnection) {
|
|
|
|
if (newBlock.nextConnection.targetConnection) {
|
|
|
|
newBlock = newBlock.getNextBlock();
|
|
|
|
} else {
|
|
|
|
if (orphanBlock.previousConnection.checkType_(
|
|
|
|
newBlock.nextConnection)) {
|
|
|
|
newBlock.nextConnection.connect(orphanBlock.previousConnection);
|
|
|
|
orphanBlock = null;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (orphanBlock) {
|
|
|
|
// Unable to reattach orphan.
|
|
|
|
parentConnection.disconnect();
|
|
|
|
if (Blockly.Events.recordUndo) {
|
|
|
|
// Bump it off to the side after a moment.
|
|
|
|
var group = Blockly.Events.getGroup();
|
|
|
|
setTimeout(function() {
|
|
|
|
// Verify orphan hasn't been deleted or reconnected (user on meth).
|
|
|
|
if (orphanBlock.workspace && !orphanBlock.getParent()) {
|
|
|
|
Blockly.Events.setGroup(group);
|
|
|
|
if (orphanBlock.outputConnection) {
|
|
|
|
orphanBlock.outputConnection.bumpAwayFrom_(parentConnection);
|
|
|
|
} else if (orphanBlock.previousConnection) {
|
|
|
|
orphanBlock.previousConnection.bumpAwayFrom_(parentConnection);
|
|
|
|
}
|
|
|
|
Blockly.Events.setGroup(false);
|
|
|
|
}
|
|
|
|
}, Blockly.BUMP_DELAY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Restore the shadow DOM.
|
|
|
|
parentConnection.setShadowDom(shadowDom);
|
|
|
|
}
|
|
|
|
|
|
|
|
var event;
|
|
|
|
if (Blockly.Events.isEnabled()) {
|
|
|
|
event = new Blockly.Events.Move(childBlock);
|
|
|
|
}
|
|
|
|
// Establish the connections.
|
|
|
|
Blockly.Connection.connectReciprocally_(parentConnection, childConnection);
|
|
|
|
// Demote the inferior block so that one is a child of the superior one.
|
|
|
|
childBlock.setParent(parentBlock);
|
|
|
|
if (event) {
|
|
|
|
event.recordNew();
|
|
|
|
Blockly.Events.fire(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parentBlock.rendered) {
|
|
|
|
parentBlock.updateDisabled();
|
|
|
|
}
|
|
|
|
if (childBlock.rendered) {
|
|
|
|
childBlock.updateDisabled();
|
|
|
|
}
|
|
|
|
if (parentBlock.rendered && childBlock.rendered) {
|
|
|
|
if (parentConnection.type == Blockly.NEXT_STATEMENT ||
|
|
|
|
parentConnection.type == Blockly.PREVIOUS_STATEMENT) {
|
|
|
|
// Child block may need to square off its corners if it is in a stack.
|
|
|
|
// Rendering a child will render its parent.
|
|
|
|
childBlock.render();
|
|
|
|
} else {
|
|
|
|
// Child block does not change shape. Rendering the parent node will
|
|
|
|
// move its connected children into position.
|
|
|
|
parentBlock.render();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2016-02-25 13:35:46 -08:00
|
|
|
|
2015-09-12 18:44:14 -07:00
|
|
|
/**
|
|
|
|
* Connection this connection connects to. Null if not connected.
|
|
|
|
* @type {Blockly.Connection}
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.targetConnection = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of compatible value types. Null if all types are compatible.
|
|
|
|
* @type {Array}
|
2015-09-12 19:31:22 -07:00
|
|
|
* @private
|
2015-09-12 18:44:14 -07:00
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.check_ = null;
|
|
|
|
|
2015-10-06 18:09:27 -07:00
|
|
|
/**
|
|
|
|
* DOM representation of a shadow block, or null if none.
|
|
|
|
* @type {Element}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.shadowDom_ = null;
|
|
|
|
|
2015-09-12 18:44:14 -07:00
|
|
|
/**
|
|
|
|
* Horizontal location of this connection.
|
|
|
|
* @type {number}
|
2015-09-12 19:31:22 -07:00
|
|
|
* @private
|
2015-09-12 18:44:14 -07:00
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.x_ = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Vertical location of this connection.
|
|
|
|
* @type {number}
|
2015-09-12 19:31:22 -07:00
|
|
|
* @private
|
2015-09-12 18:44:14 -07:00
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.y_ = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Has this connection been added to the connection database?
|
|
|
|
* @type {boolean}
|
2015-09-12 19:31:22 -07:00
|
|
|
* @private
|
2015-09-12 18:44:14 -07:00
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.inDB_ = false;
|
|
|
|
|
2015-10-09 13:53:14 -07:00
|
|
|
/**
|
|
|
|
* Connection database for connections of this type on the current workspace.
|
|
|
|
* @type {Blockly.ConnectionDB}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.db_ = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connection database for connections compatible with this type on the
|
|
|
|
* current workspace.
|
|
|
|
* @type {Blockly.ConnectionDB}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.dbOpposite_ = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether this connections is hidden (not tracked in a database) or not.
|
|
|
|
* @type {boolean}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.hidden_ = null;
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Sever all links to this connection (not including from the source object).
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.dispose = function() {
|
|
|
|
if (this.targetConnection) {
|
|
|
|
throw 'Disconnect connection before disposing of it.';
|
|
|
|
}
|
|
|
|
if (this.inDB_) {
|
2015-09-29 23:12:32 -07:00
|
|
|
this.db_.removeConnection_(this);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
if (Blockly.highlightedConnection_ == this) {
|
|
|
|
Blockly.highlightedConnection_ = null;
|
|
|
|
}
|
|
|
|
if (Blockly.localConnection_ == this) {
|
|
|
|
Blockly.localConnection_ = null;
|
|
|
|
}
|
2015-09-29 23:12:32 -07:00
|
|
|
this.db_ = null;
|
|
|
|
this.dbOpposite_ = null;
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
2016-03-15 16:03:49 -07:00
|
|
|
/**
|
|
|
|
* Get the source block for this connection.
|
|
|
|
* @return {Blockly.Block} The source block, or null if there is none.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.getSourceBlock = function() {
|
|
|
|
return this.sourceBlock_;
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Does the connection belong to a superior block (higher in the source stack)?
|
|
|
|
* @return {boolean} True if connection faces down or right.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.isSuperior = function() {
|
|
|
|
return this.type == Blockly.INPUT_VALUE ||
|
|
|
|
this.type == Blockly.NEXT_STATEMENT;
|
|
|
|
};
|
|
|
|
|
2016-02-26 16:17:17 -08:00
|
|
|
/**
|
|
|
|
* Returns the distance between this connection and another connection.
|
2016-02-29 15:04:07 -08:00
|
|
|
* @param {!Blockly.Connection} otherConnection The other connection to measure
|
|
|
|
* the distance to.
|
2016-02-26 16:17:17 -08:00
|
|
|
* @return {number} The distance between connections.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.distanceFrom = function(otherConnection) {
|
|
|
|
var xDiff = this.x_ - otherConnection.x_;
|
|
|
|
var yDiff = this.y_ - otherConnection.y_;
|
|
|
|
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
|
|
|
};
|
|
|
|
|
2016-02-25 13:35:46 -08:00
|
|
|
/**
|
|
|
|
* Checks whether the current connection can connect with the target
|
|
|
|
* connection.
|
2016-02-25 15:30:05 -08:00
|
|
|
* @param {Blockly.Connection} target Connection to check compatibility with.
|
|
|
|
* @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
|
2016-02-25 13:35:46 -08:00
|
|
|
* an error code otherwise.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.canConnectWithReason_ = function(target) {
|
|
|
|
if (!target) {
|
|
|
|
return Blockly.Connection.REASON_TARGET_NULL;
|
2016-03-15 16:03:49 -07:00
|
|
|
} else if (this.sourceBlock_ &&
|
|
|
|
target.getSourceBlock() == this.sourceBlock_) {
|
2016-02-25 13:35:46 -08:00
|
|
|
return Blockly.Connection.REASON_SELF_CONNECTION;
|
|
|
|
} else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) {
|
|
|
|
return Blockly.Connection.REASON_WRONG_TYPE;
|
2016-03-15 16:03:49 -07:00
|
|
|
} else if (this.sourceBlock_ && target.getSourceBlock() &&
|
|
|
|
this.sourceBlock_.workspace !== target.getSourceBlock().workspace) {
|
2016-02-25 13:35:46 -08:00
|
|
|
return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
|
|
|
|
} else if (!this.checkType_(target)) {
|
|
|
|
return Blockly.Connection.REASON_CHECKS_FAILED;
|
|
|
|
}
|
|
|
|
return Blockly.Connection.CAN_CONNECT;
|
2016-02-25 15:30:05 -08:00
|
|
|
};
|
2016-02-25 13:35:46 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the current connection and target connection are compatible
|
|
|
|
* and throws an exception if they are not.
|
|
|
|
* @param {Blockly.Connection} target The connection to check compatibility
|
|
|
|
* with.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.checkConnection_ = function(target) {
|
|
|
|
switch (this.canConnectWithReason_(target)) {
|
|
|
|
case Blockly.Connection.CAN_CONNECT:
|
|
|
|
break;
|
|
|
|
case Blockly.Connection.REASON_SELF_CONNECTION:
|
|
|
|
throw 'Attempted to connect a block to itself.';
|
|
|
|
case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
|
2016-03-24 15:30:29 -07:00
|
|
|
// Usually this means one block has been deleted.
|
|
|
|
throw 'Blocks not on same workspace.';
|
2016-02-25 13:35:46 -08:00
|
|
|
case Blockly.Connection.REASON_WRONG_TYPE:
|
|
|
|
throw 'Attempt to connect incompatible types.';
|
|
|
|
case Blockly.Connection.REASON_TARGET_NULL:
|
|
|
|
throw 'Target connection is null.';
|
2016-02-25 15:16:32 -08:00
|
|
|
case Blockly.Connection.REASON_CHECKS_FAILED:
|
|
|
|
throw 'Connection checks failed.';
|
2016-02-25 13:35:46 -08:00
|
|
|
default:
|
|
|
|
throw 'Unknown connection failure: this should never happen!';
|
|
|
|
}
|
2016-02-25 15:30:05 -08:00
|
|
|
};
|
2016-02-25 13:35:46 -08:00
|
|
|
|
2016-02-29 15:04:07 -08:00
|
|
|
/**
|
|
|
|
* Check if the two connections can be dragged to connect to each other.
|
2016-03-06 14:51:03 -08:00
|
|
|
* @param {!Blockly.Connection} candidate A nearby connection to check.
|
2016-02-29 15:04:07 -08:00
|
|
|
* @param {number} maxRadius The maximum radius allowed for connections.
|
|
|
|
* @return {boolean} True if the connection is allowed, false otherwise.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.isConnectionAllowed = function(candidate,
|
2016-02-29 15:50:12 -08:00
|
|
|
maxRadius) {
|
2016-02-29 15:04:07 -08:00
|
|
|
if (this.distanceFrom(candidate) > maxRadius) {
|
2016-02-29 15:50:12 -08:00
|
|
|
return false;
|
2016-02-29 15:04:07 -08:00
|
|
|
}
|
|
|
|
|
2016-03-06 14:51:03 -08:00
|
|
|
// Type checking.
|
2016-02-29 15:04:07 -08:00
|
|
|
var canConnect = this.canConnectWithReason_(candidate);
|
2016-02-29 15:50:12 -08:00
|
|
|
if (canConnect != Blockly.Connection.CAN_CONNECT &&
|
|
|
|
canConnect != Blockly.Connection.REASON_MUST_DISCONNECT) {
|
|
|
|
return false;
|
2016-02-29 15:04:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Don't offer to connect an already connected left (male) value plug to
|
|
|
|
// an available right (female) value plug. Don't offer to connect the
|
|
|
|
// bottom of a statement block to one that's already connected.
|
2016-02-29 15:50:12 -08:00
|
|
|
if (candidate.type == Blockly.OUTPUT_VALUE ||
|
|
|
|
candidate.type == Blockly.PREVIOUS_STATEMENT) {
|
2016-02-29 15:04:07 -08:00
|
|
|
if (candidate.targetConnection || this.targetConnection) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Offering to connect the left (male) of a value block to an already
|
|
|
|
// connected value pair is ok, we'll splice it in.
|
2016-03-27 01:46:53 -07:00
|
|
|
// However, don't offer to splice into an immovable block.
|
|
|
|
if (candidate.type == Blockly.INPUT_VALUE && candidate.targetConnection &&
|
2016-02-29 15:04:07 -08:00
|
|
|
!candidate.targetBlock().isMovable() &&
|
|
|
|
!candidate.targetBlock().isShadow()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-11 17:38:59 -08:00
|
|
|
// Don't let a block with no next connection bump other blocks out of the
|
|
|
|
// stack.
|
|
|
|
if (this.type == Blockly.PREVIOUS_STATEMENT &&
|
|
|
|
candidate.targetConnection &&
|
|
|
|
!this.sourceBlock_.nextConnection) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-02-29 15:04:07 -08:00
|
|
|
// Don't let blocks try to connect to themselves or ones they nest.
|
2016-03-15 16:03:49 -07:00
|
|
|
var targetSourceBlock = candidate.getSourceBlock();
|
2016-02-29 15:04:07 -08:00
|
|
|
var sourceBlock = this.sourceBlock_;
|
|
|
|
if (targetSourceBlock && sourceBlock) {
|
|
|
|
do {
|
|
|
|
if (sourceBlock == targetSourceBlock) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
targetSourceBlock = targetSourceBlock.getParent();
|
|
|
|
} while (targetSourceBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Connect this connection to another connection.
|
|
|
|
* @param {!Blockly.Connection} otherConnection Connection to connect to.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.connect = function(otherConnection) {
|
2016-03-03 17:48:54 -08:00
|
|
|
if (this.targetConnection == otherConnection) {
|
|
|
|
// Already connected together. NOP.
|
|
|
|
return;
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2016-03-03 17:48:54 -08:00
|
|
|
this.checkConnection_(otherConnection);
|
2013-10-30 14:46:03 -07:00
|
|
|
// Determine which block is superior (higher in the source stack).
|
|
|
|
var parentBlock, childBlock;
|
|
|
|
if (this.isSuperior()) {
|
|
|
|
// Superior block.
|
2016-03-03 17:48:54 -08:00
|
|
|
Blockly.Connection.connect_(this, otherConnection);
|
2013-10-30 14:46:03 -07:00
|
|
|
} else {
|
|
|
|
// Inferior block.
|
2016-03-03 17:48:54 -08:00
|
|
|
Blockly.Connection.connect_(otherConnection, this);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-02-25 15:16:32 -08:00
|
|
|
/**
|
|
|
|
* Update two connections to target each other.
|
|
|
|
* @param {Blockly.Connection} first The first connection to update.
|
|
|
|
* @param {Blockly.Connection} second The second conneciton to update.
|
2016-03-03 17:48:54 -08:00
|
|
|
* @private
|
2016-02-25 15:16:32 -08:00
|
|
|
*/
|
2016-03-03 17:48:54 -08:00
|
|
|
Blockly.Connection.connectReciprocally_ = function(first, second) {
|
|
|
|
goog.asserts.assert(first && second, 'Cannot connect null connections.');
|
2016-02-25 15:16:32 -08:00
|
|
|
first.targetConnection = second;
|
|
|
|
second.targetConnection = first;
|
2016-02-26 14:13:18 -08:00
|
|
|
};
|
2016-02-25 15:16:32 -08:00
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Does the given block have one and only one connection point that will accept
|
2015-10-06 18:09:27 -07:00
|
|
|
* an orphaned block?
|
2013-10-30 14:46:03 -07:00
|
|
|
* @param {!Blockly.Block} block The superior block.
|
|
|
|
* @param {!Blockly.Block} orphanBlock The inferior block.
|
|
|
|
* @return {Blockly.Connection} The suitable connection point on 'block',
|
|
|
|
* or null.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.singleConnection_ = function(block, orphanBlock) {
|
|
|
|
var connection = false;
|
2015-09-29 23:12:32 -07:00
|
|
|
for (var i = 0; i < block.inputList.length; i++) {
|
|
|
|
var thisConnection = block.inputList[i].connection;
|
2013-10-30 14:46:03 -07:00
|
|
|
if (thisConnection && thisConnection.type == Blockly.INPUT_VALUE &&
|
|
|
|
orphanBlock.outputConnection.checkType_(thisConnection)) {
|
|
|
|
if (connection) {
|
|
|
|
return null; // More than one connection.
|
|
|
|
}
|
|
|
|
connection = thisConnection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return connection;
|
|
|
|
};
|
|
|
|
|
2016-02-25 13:35:46 -08:00
|
|
|
/**
|
|
|
|
* Walks down a row a blocks, at each stage checking if there are any
|
|
|
|
* connections that will accept the orphaned block. If at any point there
|
|
|
|
* are zero or multiple eligible connections, returns null. Otherwise
|
|
|
|
* returns the only input on the last block in the chain.
|
|
|
|
* Terminates early for shadow blocks.
|
2016-03-18 15:19:26 -07:00
|
|
|
* @param {!Blockly.Block} startBlock The block on which to start the search.
|
2016-02-25 13:35:46 -08:00
|
|
|
* @param {!Blockly.Block} orphanBlock The block that is looking for a home.
|
|
|
|
* @return {Blockly.Connection} The suitable connection point on the chain
|
|
|
|
* of blocks, or null.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.lastConnectionInRow_ = function(startBlock, orphanBlock) {
|
|
|
|
var newBlock = startBlock;
|
|
|
|
var connection;
|
|
|
|
while (connection = Blockly.Connection.singleConnection_(
|
|
|
|
/** @type {!Blockly.Block} */ (newBlock), orphanBlock)) {
|
|
|
|
// '=' is intentional in line above.
|
|
|
|
newBlock = connection.targetBlock();
|
|
|
|
if (!newBlock || newBlock.isShadow()) {
|
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Disconnect this connection.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.disconnect = function() {
|
|
|
|
var otherConnection = this.targetConnection;
|
2016-03-03 17:48:54 -08:00
|
|
|
goog.asserts.assert(otherConnection, 'Source connection not connected.');
|
|
|
|
goog.asserts.assert(otherConnection.targetConnection == this,
|
|
|
|
'Target connection not connected to source connection.');
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2015-10-06 18:09:27 -07:00
|
|
|
var parentBlock, childBlock, parentConnection;
|
2013-10-30 14:46:03 -07:00
|
|
|
if (this.isSuperior()) {
|
|
|
|
// Superior block.
|
|
|
|
parentBlock = this.sourceBlock_;
|
2016-03-15 16:03:49 -07:00
|
|
|
childBlock = otherConnection.getSourceBlock();
|
2015-10-06 18:09:27 -07:00
|
|
|
parentConnection = this;
|
2013-10-30 14:46:03 -07:00
|
|
|
} else {
|
|
|
|
// Inferior block.
|
2016-03-15 16:03:49 -07:00
|
|
|
parentBlock = otherConnection.getSourceBlock();
|
2013-10-30 14:46:03 -07:00
|
|
|
childBlock = this.sourceBlock_;
|
2015-10-06 18:09:27 -07:00
|
|
|
parentConnection = otherConnection;
|
|
|
|
}
|
2016-03-03 17:48:54 -08:00
|
|
|
|
|
|
|
var event;
|
|
|
|
if (Blockly.Events.isEnabled()) {
|
|
|
|
event = new Blockly.Events.Move(childBlock);
|
|
|
|
}
|
|
|
|
otherConnection.targetConnection = null;
|
|
|
|
this.targetConnection = null;
|
|
|
|
childBlock.setParent(null);
|
|
|
|
if (event) {
|
|
|
|
event.recordNew();
|
|
|
|
Blockly.Events.fire(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Respawn the shadow block if there is one.
|
2015-10-06 18:09:27 -07:00
|
|
|
var shadow = parentConnection.getShadowDom();
|
2016-03-03 17:48:54 -08:00
|
|
|
if (parentBlock.workspace && shadow && Blockly.Events.recordUndo) {
|
2015-10-06 18:09:27 -07:00
|
|
|
var blockShadow =
|
|
|
|
Blockly.Xml.domToBlock(parentBlock.workspace, shadow);
|
|
|
|
if (blockShadow.outputConnection) {
|
|
|
|
parentConnection.connect(blockShadow.outputConnection);
|
|
|
|
} else if (blockShadow.previousConnection) {
|
|
|
|
parentConnection.connect(blockShadow.previousConnection);
|
|
|
|
} else {
|
|
|
|
throw 'Child block does not have output or previous statement.';
|
|
|
|
}
|
|
|
|
blockShadow.initSvg();
|
|
|
|
blockShadow.render(false);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2016-03-03 17:48:54 -08:00
|
|
|
|
|
|
|
// Rerender the parent so that it may reflow.
|
2013-10-30 14:46:03 -07:00
|
|
|
if (parentBlock.rendered) {
|
|
|
|
parentBlock.render();
|
|
|
|
}
|
|
|
|
if (childBlock.rendered) {
|
2014-12-23 11:22:02 -08:00
|
|
|
childBlock.updateDisabled();
|
2013-10-30 14:46:03 -07:00
|
|
|
childBlock.render();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the block that this connection connects to.
|
|
|
|
* @return {Blockly.Block} The connected block or null if none is connected.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.targetBlock = function() {
|
|
|
|
if (this.targetConnection) {
|
2016-03-15 16:03:49 -07:00
|
|
|
return this.targetConnection.getSourceBlock();
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the block(s) belonging to the connection to a point where they don't
|
|
|
|
* visually interfere with the specified connection.
|
|
|
|
* @param {!Blockly.Connection} staticConnection The connection to move away
|
|
|
|
* from.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.bumpAwayFrom_ = function(staticConnection) {
|
2016-03-29 08:36:11 -07:00
|
|
|
if (Blockly.dragMode_ != Blockly.DRAG_NONE) {
|
2013-10-30 14:46:03 -07:00
|
|
|
// Don't move blocks around while the user is doing the same.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Move the root block.
|
|
|
|
var rootBlock = this.sourceBlock_.getRootBlock();
|
|
|
|
if (rootBlock.isInFlyout) {
|
|
|
|
// Don't move blocks around in a flyout.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var reverse = false;
|
|
|
|
if (!rootBlock.isMovable()) {
|
|
|
|
// Can't bump an uneditable block away.
|
|
|
|
// Check to see if the other block is movable.
|
2016-03-15 16:03:49 -07:00
|
|
|
rootBlock = staticConnection.getSourceBlock().getRootBlock();
|
2013-10-30 14:46:03 -07:00
|
|
|
if (!rootBlock.isMovable()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Swap the connections and move the 'static' connection instead.
|
|
|
|
staticConnection = this;
|
|
|
|
reverse = true;
|
|
|
|
}
|
|
|
|
// Raise it to the top for extra visibility.
|
|
|
|
rootBlock.getSvgRoot().parentNode.appendChild(rootBlock.getSvgRoot());
|
|
|
|
var dx = (staticConnection.x_ + Blockly.SNAP_RADIUS) - this.x_;
|
2014-09-08 14:26:52 -07:00
|
|
|
var dy = (staticConnection.y_ + Blockly.SNAP_RADIUS) - this.y_;
|
2013-10-30 14:46:03 -07:00
|
|
|
if (reverse) {
|
|
|
|
// When reversing a bump due to an uneditable block, bump up.
|
|
|
|
dy = -dy;
|
|
|
|
}
|
2015-04-28 13:51:25 -07:00
|
|
|
if (rootBlock.RTL) {
|
2013-10-30 14:46:03 -07:00
|
|
|
dx = -dx;
|
|
|
|
}
|
|
|
|
rootBlock.moveBy(dx, dy);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change the connection's coordinates.
|
|
|
|
* @param {number} x New absolute x coordinate.
|
|
|
|
* @param {number} y New absolute y coordinate.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.moveTo = function(x, y) {
|
|
|
|
// Remove it from its old location in the database (if already present)
|
|
|
|
if (this.inDB_) {
|
2015-09-29 23:12:32 -07:00
|
|
|
this.db_.removeConnection_(this);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
this.x_ = x;
|
|
|
|
this.y_ = y;
|
|
|
|
// Insert it into its new location in the database.
|
2015-01-26 04:56:58 -08:00
|
|
|
if (!this.hidden_) {
|
2016-02-29 15:04:07 -08:00
|
|
|
this.db_.addConnection(this);
|
2015-01-26 02:45:03 -08:00
|
|
|
}
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change the connection's coordinates.
|
|
|
|
* @param {number} dx Change to x coordinate.
|
|
|
|
* @param {number} dy Change to y coordinate.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.moveBy = function(dx, dy) {
|
|
|
|
this.moveTo(this.x_ + dx, this.y_ + dy);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move the blocks on either side of this connection right next to each other.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.tighten_ = function() {
|
2015-05-27 18:54:37 -07:00
|
|
|
var dx = this.targetConnection.x_ - this.x_;
|
|
|
|
var dy = this.targetConnection.y_ - this.y_;
|
2013-10-30 14:46:03 -07:00
|
|
|
if (dx != 0 || dy != 0) {
|
|
|
|
var block = this.targetBlock();
|
|
|
|
var svgRoot = block.getSvgRoot();
|
|
|
|
if (!svgRoot) {
|
|
|
|
throw 'block is not rendered.';
|
|
|
|
}
|
|
|
|
var xy = Blockly.getRelativeXY_(svgRoot);
|
|
|
|
block.getSvgRoot().setAttribute('transform',
|
2015-08-18 11:34:22 -07:00
|
|
|
'translate(' + (xy.x - dx) + ',' + (xy.y - dy) + ')');
|
2013-10-30 14:46:03 -07:00
|
|
|
block.moveConnections_(-dx, -dy);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the closest compatible connection to this connection.
|
|
|
|
* @param {number} maxLimit The maximum radius to another connection.
|
|
|
|
* @param {number} dx Horizontal offset between this connection's location
|
|
|
|
* in the database and the current location (as a result of dragging).
|
|
|
|
* @param {number} dy Vertical offset between this connection's location
|
|
|
|
* in the database and the current location (as a result of dragging).
|
2016-02-29 15:04:07 -08:00
|
|
|
* @return {!{connection: ?Blockly.Connection, radius: number}} Contains two
|
|
|
|
* properties:' connection' which is either another connection or null,
|
|
|
|
* and 'radius' which is the distance.
|
2013-10-30 14:46:03 -07:00
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.closest = function(maxLimit, dx, dy) {
|
2016-03-11 13:13:34 -08:00
|
|
|
return this.dbOpposite_.searchForClosest(this, maxLimit, dx, dy);
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is this connection compatible with another connection with respect to the
|
|
|
|
* 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.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
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.
|
2015-09-29 23:12:32 -07:00
|
|
|
for (var i = 0; i < this.check_.length; i++) {
|
|
|
|
if (otherConnection.check_.indexOf(this.check_[i]) != -1) {
|
2013-10-30 14:46:03 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// No intersection.
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change a connection's compatibility.
|
|
|
|
* @param {*} check Compatible value type or list of value types.
|
|
|
|
* Null if all types are compatible.
|
|
|
|
* @return {!Blockly.Connection} The connection being modified
|
|
|
|
* (to allow chaining).
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.setCheck = function(check) {
|
|
|
|
if (check) {
|
|
|
|
// Ensure that check is in an array.
|
2014-09-08 14:26:52 -07:00
|
|
|
if (!goog.isArray(check)) {
|
2013-10-30 14:46:03 -07:00
|
|
|
check = [check];
|
|
|
|
}
|
|
|
|
this.check_ = check;
|
|
|
|
// The new value type may not be compatible with the existing connection.
|
|
|
|
if (this.targetConnection && !this.checkType_(this.targetConnection)) {
|
2016-01-28 14:25:38 -05:00
|
|
|
var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
|
2016-03-03 17:48:54 -08:00
|
|
|
child.unplug();
|
2013-10-30 14:46:03 -07:00
|
|
|
// Bump away.
|
|
|
|
this.sourceBlock_.bumpNeighbours_();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.check_ = null;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2015-10-06 18:09:27 -07:00
|
|
|
/**
|
|
|
|
* Change a connection's shadow block.
|
|
|
|
* @param {Element} shadow DOM representation of a block or null.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.setShadowDom = function(shadow) {
|
|
|
|
this.shadowDom_ = shadow;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a connection's shadow block.
|
|
|
|
* @return {Element} shadow DOM representation of a block or null.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.getShadowDom = function() {
|
|
|
|
return this.shadowDom_;
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Find all nearby compatible connections to this connection.
|
|
|
|
* Type checking does not apply, since this function is used for bumping.
|
|
|
|
* @param {number} maxLimit The maximum radius to another connection.
|
|
|
|
* @return {!Array.<Blockly.Connection>} List of connections.
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.neighbours_ = function(maxLimit) {
|
2016-02-26 14:13:18 -08:00
|
|
|
return this.dbOpposite_.getNeighbours(this, maxLimit);
|
|
|
|
};
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2016-02-26 14:13:18 -08:00
|
|
|
// Appearance or lack thereof.
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2015-02-27 16:11:04 -08:00
|
|
|
/**
|
|
|
|
* Set whether this connections is hidden (not tracked in a database) or not.
|
|
|
|
* @param {boolean} hidden True if connection is hidden.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.setHidden = function(hidden) {
|
|
|
|
this.hidden_ = hidden;
|
|
|
|
if (hidden && this.inDB_) {
|
2015-09-29 23:12:32 -07:00
|
|
|
this.db_.removeConnection_(this);
|
2015-02-27 16:11:04 -08:00
|
|
|
} else if (!hidden && !this.inDB_) {
|
2016-02-29 15:04:07 -08:00
|
|
|
this.db_.addConnection(this);
|
2015-02-27 16:11:04 -08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-10-30 14:46:03 -07:00
|
|
|
/**
|
|
|
|
* Hide this connection, as well as all down-stream connections on any block
|
|
|
|
* attached to this connection. This happens when a block is collapsed.
|
|
|
|
* Also hides down-stream comments.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.hideAll = function() {
|
2015-02-27 16:11:04 -08:00
|
|
|
this.setHidden(true);
|
2013-10-30 14:46:03 -07:00
|
|
|
if (this.targetConnection) {
|
|
|
|
var blocks = this.targetBlock().getDescendants();
|
|
|
|
for (var b = 0; b < blocks.length; b++) {
|
|
|
|
var block = blocks[b];
|
|
|
|
// Hide all connections of all children.
|
|
|
|
var connections = block.getConnections_(true);
|
|
|
|
for (var c = 0; c < connections.length; c++) {
|
2015-02-27 16:11:04 -08:00
|
|
|
connections[c].setHidden(true);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
// Close all bubbles of all children.
|
|
|
|
var icons = block.getIcons();
|
2015-09-29 23:12:32 -07:00
|
|
|
for (var i = 0; i < icons.length; i++) {
|
|
|
|
icons[i].setVisible(false);
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unhide this connection, as well as all down-stream connections on any block
|
|
|
|
* attached to this connection. This happens when a block is expanded.
|
|
|
|
* Also unhides down-stream comments.
|
|
|
|
* @return {!Array.<!Blockly.Block>} List of blocks to render.
|
|
|
|
*/
|
|
|
|
Blockly.Connection.prototype.unhideAll = function() {
|
2015-02-27 16:11:04 -08:00
|
|
|
this.setHidden(false);
|
2013-10-30 14:46:03 -07:00
|
|
|
// All blocks that need unhiding must be unhidden before any rendering takes
|
|
|
|
// place, since rendering requires knowing the dimensions of lower blocks.
|
|
|
|
// Also, since rendering a block renders all its parents, we only need to
|
|
|
|
// render the leaf nodes.
|
|
|
|
var renderList = [];
|
|
|
|
if (this.type != Blockly.INPUT_VALUE && this.type != Blockly.NEXT_STATEMENT) {
|
|
|
|
// Only spider down.
|
|
|
|
return renderList;
|
|
|
|
}
|
|
|
|
var block = this.targetBlock();
|
|
|
|
if (block) {
|
|
|
|
var connections;
|
|
|
|
if (block.isCollapsed()) {
|
|
|
|
// This block should only be partially revealed since it is collapsed.
|
|
|
|
connections = [];
|
|
|
|
block.outputConnection && connections.push(block.outputConnection);
|
|
|
|
block.nextConnection && connections.push(block.nextConnection);
|
|
|
|
block.previousConnection && connections.push(block.previousConnection);
|
|
|
|
} else {
|
|
|
|
// Show all connections of this block.
|
|
|
|
connections = block.getConnections_(true);
|
|
|
|
}
|
|
|
|
for (var c = 0; c < connections.length; c++) {
|
2014-09-08 14:26:52 -07:00
|
|
|
renderList.push.apply(renderList, connections[c].unhideAll());
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2016-03-14 16:00:25 -07:00
|
|
|
if (!renderList.length) {
|
2013-10-30 14:46:03 -07:00
|
|
|
// Leaf block.
|
|
|
|
renderList[0] = block;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return renderList;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-02-26 14:13:18 -08:00
|
|
|
* Add highlighting around this connection.
|
2013-10-30 14:46:03 -07:00
|
|
|
*/
|
2016-02-26 14:13:18 -08:00
|
|
|
Blockly.Connection.prototype.highlight = function() {
|
|
|
|
var steps;
|
|
|
|
if (this.type == Blockly.INPUT_VALUE || this.type == Blockly.OUTPUT_VALUE) {
|
|
|
|
var tabWidth = this.sourceBlock_.RTL ? -Blockly.BlockSvg.TAB_WIDTH :
|
|
|
|
Blockly.BlockSvg.TAB_WIDTH;
|
2016-02-29 15:04:07 -08:00
|
|
|
steps = 'm 0,0 ' + Blockly.BlockSvg.TAB_PATH_DOWN + ' v 5';
|
2013-10-30 14:46:03 -07:00
|
|
|
|
2016-02-26 14:13:18 -08:00
|
|
|
} else {
|
2016-02-29 15:04:07 -08:00
|
|
|
steps = 'm -20,0 h 5 ' + Blockly.BlockSvg.NOTCH_PATH_LEFT + ' h 5';
|
2013-10-30 14:46:03 -07:00
|
|
|
}
|
2016-02-26 14:13:18 -08:00
|
|
|
var xy = this.sourceBlock_.getRelativeToSurfaceXY();
|
|
|
|
var x = this.x_ - xy.x;
|
|
|
|
var y = this.y_ - xy.y;
|
|
|
|
Blockly.Connection.highlightedPath_ = Blockly.createSvgElement('path',
|
|
|
|
{'class': 'blocklyHighlightedConnectionPath',
|
|
|
|
'd': steps,
|
|
|
|
transform: 'translate(' + x + ',' + y + ')' +
|
|
|
|
(this.sourceBlock_.RTL ? ' scale(-1 1)' : '')},
|
|
|
|
this.sourceBlock_.getSvgRoot());
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-02-26 14:13:18 -08:00
|
|
|
* Remove the highlighting around this connection.
|
2013-10-30 14:46:03 -07:00
|
|
|
*/
|
2016-02-26 14:13:18 -08:00
|
|
|
Blockly.Connection.prototype.unhighlight = function() {
|
|
|
|
goog.dom.removeNode(Blockly.Connection.highlightedPath_);
|
|
|
|
delete Blockly.Connection.highlightedPath_;
|
2013-10-30 14:46:03 -07:00
|
|
|
};
|