Files
blockly/accessible/block-connection.service.js
2016-11-30 16:45:06 -08:00

112 lines
3.9 KiB
JavaScript

/**
* AccessibleBlockly
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
*
* 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 Angular2 Service for handling the mechanics of how blocks
* get connected to each other.
* @author sll@google.com (Sean Lip)
*/
blocklyApp.BlockConnectionService = ng.core.Class({
constructor: [
blocklyApp.NotificationsService, blocklyApp.AudioService,
function(_notificationsService, _audioService) {
this.notificationsService = _notificationsService;
this.audioService = _audioService;
// When a user "adds a link" to a block, the connection representing this
// link is stored here.
this.markedConnection_ = null;
}],
findCompatibleConnection_: function(block) {
// Locates and returns a connection on the given block that is compatible
// with the marked connection, if one exists. Returns null if no such
// connection exists.
// Note: this currently ignores input connections on the given block, since
// one doesn't usually mark an output connection and attach a block to it.
if (!this.markedConnection_ ||
!this.markedConnection_.getSourceBlock().workspace) {
return null;
}
var desiredType = Blockly.OPPOSITE_TYPE[this.markedConnection_.type];
var potentialConnection = (
desiredType == Blockly.OUTPUT_VALUE ? block.outputConnection :
desiredType == Blockly.PREVIOUS_STATEMENT ? block.previousConnection :
desiredType == Blockly.NEXT_STATEMENT ? block.nextConnection :
null);
if (potentialConnection &&
potentialConnection.checkType_(this.markedConnection_)) {
return potentialConnection;
} else {
return null;
}
},
isAnyConnectionMarked: function() {
return Boolean(this.markedConnection_);
},
getMarkedConnectionSourceBlock: function() {
return this.markedConnection_ ?
this.markedConnection_.getSourceBlock() : null;
},
canBeAttachedToMarkedConnection: function(block) {
return Boolean(this.findCompatibleConnection_(block));
},
canBeMovedToMarkedConnection: function(block) {
if (!this.markedConnection_) {
return false;
}
// It should not be possible to move any ancestor of the block containing
// the marked connection to the marked connection.
var ancestorBlock = this.getMarkedConnectionSourceBlock();
while (ancestorBlock) {
if (ancestorBlock.id == block.id) {
return false;
}
ancestorBlock = ancestorBlock.getParent();
}
return this.canBeAttachedToMarkedConnection(block);
},
markConnection: function(connection) {
this.markedConnection_ = connection;
this.notificationsService.speak(Blockly.Msg.ADDED_LINK_MSG);
},
attachToMarkedConnection: function(block) {
var xml = Blockly.Xml.blockToDom(block);
var reconstitutedBlock = Blockly.Xml.domToBlock(blocklyApp.workspace, xml);
var connection = this.findCompatibleConnection_(reconstitutedBlock);
if (connection) {
this.markedConnection_.connect(connection);
this.markedConnection_ = null;
this.audioService.playConnectSound();
return reconstitutedBlock.id;
} else {
// We throw an error here, because we expect any UI controls that would
// result in a non-connection to be disabled or hidden.
throw Error(
'Unable to connect block to marked connection. This should not ' +
'happen.');
}
}
});