mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
* Rename Blockly.Blocks to Blockly.blocks Because it does not export a type as its default export. Part of #5073. * Name default export of Blockly.blocks Blocks. Use named exports in Blockly.blocks by giving the former default export the name Blocks. Part of #5153. * Reexport Blockly.blocks from blockly.js * Document the format of renamings.js better.
661 lines
20 KiB
JavaScript
661 lines
20 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2012 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Logic blocks for Blockly.
|
|
*
|
|
* This file is scraped to extract a .json file of block definitions. The array
|
|
* passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes
|
|
* only, no outside references, no functions, no trailing commas, etc. The one
|
|
* exception is end-of-line comments, which the scraper will remove.
|
|
* @author q.neutron@gmail.com (Quynh Neutron)
|
|
*/
|
|
'use strict';
|
|
|
|
goog.provide('Blockly.Blocks.logic'); // Deprecated
|
|
goog.provide('Blockly.Constants.Logic');
|
|
|
|
goog.require('Blockly');
|
|
goog.require('Blockly.FieldDropdown');
|
|
goog.require('Blockly.FieldLabel');
|
|
goog.require('Blockly.Mutator');
|
|
|
|
|
|
/**
|
|
* Unused constant for the common HSV hue for all blocks in this category.
|
|
* @deprecated Use Blockly.Msg['LOGIC_HUE']. (2018 April 5)
|
|
*/
|
|
Blockly.Constants.Logic.HUE = 210;
|
|
|
|
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
|
|
// Block for boolean data type: true and false.
|
|
{
|
|
"type": "logic_boolean",
|
|
"message0": "%1",
|
|
"args0": [
|
|
{
|
|
"type": "field_dropdown",
|
|
"name": "BOOL",
|
|
"options": [
|
|
["%{BKY_LOGIC_BOOLEAN_TRUE}", "TRUE"],
|
|
["%{BKY_LOGIC_BOOLEAN_FALSE}", "FALSE"]
|
|
]
|
|
}
|
|
],
|
|
"output": "Boolean",
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_LOGIC_BOOLEAN_TOOLTIP}",
|
|
"helpUrl": "%{BKY_LOGIC_BOOLEAN_HELPURL}"
|
|
},
|
|
// Block for if/elseif/else condition.
|
|
{
|
|
"type": "controls_if",
|
|
"message0": "%{BKY_CONTROLS_IF_MSG_IF} %1",
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "IF0",
|
|
"check": "Boolean"
|
|
}
|
|
],
|
|
"message1": "%{BKY_CONTROLS_IF_MSG_THEN} %1",
|
|
"args1": [
|
|
{
|
|
"type": "input_statement",
|
|
"name": "DO0"
|
|
}
|
|
],
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"style": "logic_blocks",
|
|
"helpUrl": "%{BKY_CONTROLS_IF_HELPURL}",
|
|
"suppressPrefixSuffix": true,
|
|
"mutator": "controls_if_mutator",
|
|
"extensions": ["controls_if_tooltip"]
|
|
},
|
|
// If/else block that does not use a mutator.
|
|
{
|
|
"type": "controls_ifelse",
|
|
"message0": "%{BKY_CONTROLS_IF_MSG_IF} %1",
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "IF0",
|
|
"check": "Boolean"
|
|
}
|
|
],
|
|
"message1": "%{BKY_CONTROLS_IF_MSG_THEN} %1",
|
|
"args1": [
|
|
{
|
|
"type": "input_statement",
|
|
"name": "DO0"
|
|
}
|
|
],
|
|
"message2": "%{BKY_CONTROLS_IF_MSG_ELSE} %1",
|
|
"args2": [
|
|
{
|
|
"type": "input_statement",
|
|
"name": "ELSE"
|
|
}
|
|
],
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKYCONTROLS_IF_TOOLTIP_2}",
|
|
"helpUrl": "%{BKY_CONTROLS_IF_HELPURL}",
|
|
"suppressPrefixSuffix": true,
|
|
"extensions": ["controls_if_tooltip"]
|
|
},
|
|
// Block for comparison operator.
|
|
{
|
|
"type": "logic_compare",
|
|
"message0": "%1 %2 %3",
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "A"
|
|
},
|
|
{
|
|
"type": "field_dropdown",
|
|
"name": "OP",
|
|
"options": [
|
|
["=", "EQ"],
|
|
["\u2260", "NEQ"],
|
|
["\u200F<", "LT"],
|
|
["\u200F\u2264", "LTE"],
|
|
["\u200F>", "GT"],
|
|
["\u200F\u2265", "GTE"]
|
|
]
|
|
},
|
|
{
|
|
"type": "input_value",
|
|
"name": "B"
|
|
}
|
|
],
|
|
"inputsInline": true,
|
|
"output": "Boolean",
|
|
"style": "logic_blocks",
|
|
"helpUrl": "%{BKY_LOGIC_COMPARE_HELPURL}",
|
|
"extensions": ["logic_compare", "logic_op_tooltip"]
|
|
},
|
|
// Block for logical operations: 'and', 'or'.
|
|
{
|
|
"type": "logic_operation",
|
|
"message0": "%1 %2 %3",
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "A",
|
|
"check": "Boolean"
|
|
},
|
|
{
|
|
"type": "field_dropdown",
|
|
"name": "OP",
|
|
"options": [
|
|
["%{BKY_LOGIC_OPERATION_AND}", "AND"],
|
|
["%{BKY_LOGIC_OPERATION_OR}", "OR"]
|
|
]
|
|
},
|
|
{
|
|
"type": "input_value",
|
|
"name": "B",
|
|
"check": "Boolean"
|
|
}
|
|
],
|
|
"inputsInline": true,
|
|
"output": "Boolean",
|
|
"style": "logic_blocks",
|
|
"helpUrl": "%{BKY_LOGIC_OPERATION_HELPURL}",
|
|
"extensions": ["logic_op_tooltip"]
|
|
},
|
|
// Block for negation.
|
|
{
|
|
"type": "logic_negate",
|
|
"message0": "%{BKY_LOGIC_NEGATE_TITLE}",
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "BOOL",
|
|
"check": "Boolean"
|
|
}
|
|
],
|
|
"output": "Boolean",
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_LOGIC_NEGATE_TOOLTIP}",
|
|
"helpUrl": "%{BKY_LOGIC_NEGATE_HELPURL}"
|
|
},
|
|
// Block for null data type.
|
|
{
|
|
"type": "logic_null",
|
|
"message0": "%{BKY_LOGIC_NULL}",
|
|
"output": null,
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_LOGIC_NULL_TOOLTIP}",
|
|
"helpUrl": "%{BKY_LOGIC_NULL_HELPURL}"
|
|
},
|
|
// Block for ternary operator.
|
|
{
|
|
"type": "logic_ternary",
|
|
"message0": "%{BKY_LOGIC_TERNARY_CONDITION} %1",
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "IF",
|
|
"check": "Boolean"
|
|
}
|
|
],
|
|
"message1": "%{BKY_LOGIC_TERNARY_IF_TRUE} %1",
|
|
"args1": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "THEN"
|
|
}
|
|
],
|
|
"message2": "%{BKY_LOGIC_TERNARY_IF_FALSE} %1",
|
|
"args2": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "ELSE"
|
|
}
|
|
],
|
|
"output": null,
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_LOGIC_TERNARY_TOOLTIP}",
|
|
"helpUrl": "%{BKY_LOGIC_TERNARY_HELPURL}",
|
|
"extensions": ["logic_ternary"]
|
|
}
|
|
]); // END JSON EXTRACT (Do not delete this comment.)
|
|
|
|
Blockly.defineBlocksWithJsonArray([ // Mutator blocks. Do not extract.
|
|
// Block representing the if statement in the controls_if mutator.
|
|
{
|
|
"type": "controls_if_if",
|
|
"message0": "%{BKY_CONTROLS_IF_IF_TITLE_IF}",
|
|
"nextStatement": null,
|
|
"enableContextMenu": false,
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_CONTROLS_IF_IF_TOOLTIP}"
|
|
},
|
|
// Block representing the else-if statement in the controls_if mutator.
|
|
{
|
|
"type": "controls_if_elseif",
|
|
"message0": "%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"enableContextMenu": false,
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"
|
|
},
|
|
// Block representing the else statement in the controls_if mutator.
|
|
{
|
|
"type": "controls_if_else",
|
|
"message0": "%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",
|
|
"previousStatement": null,
|
|
"enableContextMenu": false,
|
|
"style": "logic_blocks",
|
|
"tooltip": "%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"
|
|
}
|
|
]);
|
|
|
|
/**
|
|
* Tooltip text, keyed by block OP value. Used by logic_compare and
|
|
* logic_operation blocks.
|
|
* @see {Blockly.Extensions#buildTooltipForDropdown}
|
|
* @package
|
|
* @readonly
|
|
*/
|
|
Blockly.Constants.Logic.TOOLTIPS_BY_OP = {
|
|
// logic_compare
|
|
'EQ': '%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}',
|
|
'NEQ': '%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}',
|
|
'LT': '%{BKY_LOGIC_COMPARE_TOOLTIP_LT}',
|
|
'LTE': '%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}',
|
|
'GT': '%{BKY_LOGIC_COMPARE_TOOLTIP_GT}',
|
|
'GTE': '%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}',
|
|
|
|
// logic_operation
|
|
'AND': '%{BKY_LOGIC_OPERATION_TOOLTIP_AND}',
|
|
'OR': '%{BKY_LOGIC_OPERATION_TOOLTIP_OR}'
|
|
};
|
|
|
|
Blockly.Extensions.register('logic_op_tooltip',
|
|
Blockly.Extensions.buildTooltipForDropdown(
|
|
'OP', Blockly.Constants.Logic.TOOLTIPS_BY_OP));
|
|
|
|
/**
|
|
* Mutator methods added to controls_if blocks.
|
|
* @mixin
|
|
* @augments Blockly.Block
|
|
* @package
|
|
* @readonly
|
|
*/
|
|
Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = {
|
|
elseifCount_: 0,
|
|
elseCount_: 0,
|
|
|
|
/**
|
|
* Create XML to represent the number of else-if and else inputs.
|
|
* Backwards compatible serialization implementation.
|
|
* @return {Element} XML storage element.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
mutationToDom: function() {
|
|
if (!this.elseifCount_ && !this.elseCount_) {
|
|
return null;
|
|
}
|
|
var container = Blockly.utils.xml.createElement('mutation');
|
|
if (this.elseifCount_) {
|
|
container.setAttribute('elseif', this.elseifCount_);
|
|
}
|
|
if (this.elseCount_) {
|
|
container.setAttribute('else', 1);
|
|
}
|
|
return container;
|
|
},
|
|
/**
|
|
* Parse XML to restore the else-if and else inputs.
|
|
* Backwards compatible serialization implementation.
|
|
* @param {!Element} xmlElement XML storage element.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
domToMutation: function(xmlElement) {
|
|
this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0;
|
|
this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0;
|
|
this.rebuildShape_();
|
|
},
|
|
/**
|
|
* Returns the state of this block as a JSON serializable object.
|
|
* @return {?{elseIfCount: (number|undefined), haseElse: (boolean|undefined)}}
|
|
* The state of this block, ie the else if count and else state.
|
|
*/
|
|
saveExtraState: function() {
|
|
if (!this.elseifCount_ && !this.elseCount_) {
|
|
return null;
|
|
}
|
|
var state = Object.create(null);
|
|
if (this.elseifCount_) {
|
|
state['elseIfCount'] = this.elseifCount_;
|
|
}
|
|
if (this.elseCount_) {
|
|
state['hasElse'] = true;
|
|
}
|
|
return state;
|
|
},
|
|
/**
|
|
* Applies the given state to this block.
|
|
* @param {*} state The state to apply to this block, ie the else if count and
|
|
* else state.
|
|
*/
|
|
loadExtraState: function(state) {
|
|
this.elseifCount_ = state['elseIfCount'] || 0;
|
|
this.elseCount_ = state['hasElse'] ? 1 : 0;
|
|
this.updateShape_();
|
|
},
|
|
/**
|
|
* Populate the mutator's dialog with this block's components.
|
|
* @param {!Blockly.Workspace} workspace Mutator's workspace.
|
|
* @return {!Blockly.Block} Root block in mutator.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
decompose: function(workspace) {
|
|
var containerBlock = workspace.newBlock('controls_if_if');
|
|
containerBlock.initSvg();
|
|
var connection = containerBlock.nextConnection;
|
|
for (var i = 1; i <= this.elseifCount_; i++) {
|
|
var elseifBlock = workspace.newBlock('controls_if_elseif');
|
|
elseifBlock.initSvg();
|
|
connection.connect(elseifBlock.previousConnection);
|
|
connection = elseifBlock.nextConnection;
|
|
}
|
|
if (this.elseCount_) {
|
|
var elseBlock = workspace.newBlock('controls_if_else');
|
|
elseBlock.initSvg();
|
|
connection.connect(elseBlock.previousConnection);
|
|
}
|
|
return containerBlock;
|
|
},
|
|
/**
|
|
* Reconfigure this block based on the mutator dialog's components.
|
|
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
compose: function(containerBlock) {
|
|
var clauseBlock = containerBlock.nextConnection.targetBlock();
|
|
// Count number of inputs.
|
|
this.elseifCount_ = 0;
|
|
this.elseCount_ = 0;
|
|
var valueConnections = [null];
|
|
var statementConnections = [null];
|
|
var elseStatementConnection = null;
|
|
while (clauseBlock && !clauseBlock.isInsertionMarker()) {
|
|
switch (clauseBlock.type) {
|
|
case 'controls_if_elseif':
|
|
this.elseifCount_++;
|
|
valueConnections.push(clauseBlock.valueConnection_);
|
|
statementConnections.push(clauseBlock.statementConnection_);
|
|
break;
|
|
case 'controls_if_else':
|
|
this.elseCount_++;
|
|
elseStatementConnection = clauseBlock.statementConnection_;
|
|
break;
|
|
default:
|
|
throw TypeError('Unknown block type: ' + clauseBlock.type);
|
|
}
|
|
clauseBlock = clauseBlock.nextConnection &&
|
|
clauseBlock.nextConnection.targetBlock();
|
|
}
|
|
this.updateShape_();
|
|
// Reconnect any child blocks.
|
|
this.reconnectChildBlocks_(valueConnections, statementConnections,
|
|
elseStatementConnection);
|
|
},
|
|
/**
|
|
* Store pointers to any connected child blocks.
|
|
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
saveConnections: function(containerBlock) {
|
|
var clauseBlock = containerBlock.nextConnection.targetBlock();
|
|
var i = 1;
|
|
while (clauseBlock) {
|
|
switch (clauseBlock.type) {
|
|
case 'controls_if_elseif':
|
|
var inputIf = this.getInput('IF' + i);
|
|
var inputDo = this.getInput('DO' + i);
|
|
clauseBlock.valueConnection_ =
|
|
inputIf && inputIf.connection.targetConnection;
|
|
clauseBlock.statementConnection_ =
|
|
inputDo && inputDo.connection.targetConnection;
|
|
i++;
|
|
break;
|
|
case 'controls_if_else':
|
|
var inputDo = this.getInput('ELSE');
|
|
clauseBlock.statementConnection_ =
|
|
inputDo && inputDo.connection.targetConnection;
|
|
break;
|
|
default:
|
|
throw TypeError('Unknown block type: ' + clauseBlock.type);
|
|
}
|
|
clauseBlock = clauseBlock.nextConnection &&
|
|
clauseBlock.nextConnection.targetBlock();
|
|
}
|
|
},
|
|
/**
|
|
* Reconstructs the block with all child blocks attached.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
rebuildShape_: function() {
|
|
var valueConnections = [null];
|
|
var statementConnections = [null];
|
|
var elseStatementConnection = null;
|
|
|
|
if (this.getInput('ELSE')) {
|
|
elseStatementConnection = this.getInput('ELSE').connection.targetConnection;
|
|
}
|
|
var i = 1;
|
|
while (this.getInput('IF' + i)) {
|
|
var inputIf = this.getInput('IF' + i);
|
|
var inputDo = this.getInput('DO' + i);
|
|
valueConnections.push(inputIf.connection.targetConnection);
|
|
statementConnections.push(inputDo.connection.targetConnection);
|
|
i++;
|
|
}
|
|
this.updateShape_();
|
|
this.reconnectChildBlocks_(valueConnections, statementConnections,
|
|
elseStatementConnection);
|
|
},
|
|
/**
|
|
* Modify this block to have the correct number of inputs.
|
|
* @this {Blockly.Block}
|
|
* @private
|
|
*/
|
|
updateShape_: function() {
|
|
// Delete everything.
|
|
if (this.getInput('ELSE')) {
|
|
this.removeInput('ELSE');
|
|
}
|
|
var i = 1;
|
|
while (this.getInput('IF' + i)) {
|
|
this.removeInput('IF' + i);
|
|
this.removeInput('DO' + i);
|
|
i++;
|
|
}
|
|
// Rebuild block.
|
|
for (i = 1; i <= this.elseifCount_; i++) {
|
|
this.appendValueInput('IF' + i)
|
|
.setCheck('Boolean')
|
|
.appendField(Blockly.Msg['CONTROLS_IF_MSG_ELSEIF']);
|
|
this.appendStatementInput('DO' + i)
|
|
.appendField(Blockly.Msg['CONTROLS_IF_MSG_THEN']);
|
|
}
|
|
if (this.elseCount_) {
|
|
this.appendStatementInput('ELSE')
|
|
.appendField(Blockly.Msg['CONTROLS_IF_MSG_ELSE']);
|
|
}
|
|
},
|
|
/**
|
|
* Reconnects child blocks.
|
|
* @param {!Array<?Blockly.RenderedConnection>} valueConnections List of
|
|
* value connections for 'if' input.
|
|
* @param {!Array<?Blockly.RenderedConnection>} statementConnections List of
|
|
* statement connections for 'do' input.
|
|
* @param {?Blockly.RenderedConnection} elseStatementConnection Statement
|
|
* connection for else input.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
reconnectChildBlocks_: function(valueConnections, statementConnections,
|
|
elseStatementConnection) {
|
|
for (var i = 1; i <= this.elseifCount_; i++) {
|
|
Blockly.Mutator.reconnect(valueConnections[i], this, 'IF' + i);
|
|
Blockly.Mutator.reconnect(statementConnections[i], this, 'DO' + i);
|
|
}
|
|
Blockly.Mutator.reconnect(elseStatementConnection, this, 'ELSE');
|
|
}
|
|
};
|
|
|
|
Blockly.Extensions.registerMutator('controls_if_mutator',
|
|
Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN, null,
|
|
['controls_if_elseif', 'controls_if_else']);
|
|
/**
|
|
* "controls_if" extension function. Adds mutator, shape updating methods, and
|
|
* dynamic tooltip to "controls_if" blocks.
|
|
* @this {Blockly.Block}
|
|
* @package
|
|
*/
|
|
Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION = function() {
|
|
|
|
this.setTooltip(function() {
|
|
if (!this.elseifCount_ && !this.elseCount_) {
|
|
return Blockly.Msg['CONTROLS_IF_TOOLTIP_1'];
|
|
} else if (!this.elseifCount_ && this.elseCount_) {
|
|
return Blockly.Msg['CONTROLS_IF_TOOLTIP_2'];
|
|
} else if (this.elseifCount_ && !this.elseCount_) {
|
|
return Blockly.Msg['CONTROLS_IF_TOOLTIP_3'];
|
|
} else if (this.elseifCount_ && this.elseCount_) {
|
|
return Blockly.Msg['CONTROLS_IF_TOOLTIP_4'];
|
|
}
|
|
return '';
|
|
}.bind(this));
|
|
};
|
|
|
|
Blockly.Extensions.register('controls_if_tooltip',
|
|
Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION);
|
|
|
|
/**
|
|
* Adds dynamic type validation for the left and right sides of a logic_compare
|
|
* block.
|
|
* @mixin
|
|
* @augments Blockly.Block
|
|
* @package
|
|
* @readonly
|
|
*/
|
|
Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN = {
|
|
/**
|
|
* Called whenever anything on the workspace changes.
|
|
* Prevent mismatched types from being compared.
|
|
* @param {!Blockly.Events.Abstract} e Change event.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
onchange: function(e) {
|
|
if (!this.prevBlocks_) {
|
|
this.prevBlocks_ = [null, null];
|
|
}
|
|
|
|
var blockA = this.getInputTargetBlock('A');
|
|
var blockB = this.getInputTargetBlock('B');
|
|
// Disconnect blocks that existed prior to this change if they don't match.
|
|
if (blockA && blockB &&
|
|
!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);
|
|
var prevA = this.prevBlocks_[0];
|
|
if (prevA !== blockA) {
|
|
blockA.unplug();
|
|
if (prevA && !prevA.isDisposed() && !prevA.isShadow()) {
|
|
// The shadow block is automatically replaced during unplug().
|
|
this.getInput('A').connection.connect(prevA.outputConnection);
|
|
}
|
|
}
|
|
var prevB = this.prevBlocks_[1];
|
|
if (prevB !== blockB) {
|
|
blockB.unplug();
|
|
if (prevB && !prevB.isDisposed() && !prevB.isShadow()) {
|
|
// The shadow block is automatically replaced during unplug().
|
|
this.getInput('B').connection.connect(prevB.outputConnection);
|
|
}
|
|
}
|
|
this.bumpNeighbours();
|
|
Blockly.Events.setGroup(false);
|
|
}
|
|
this.prevBlocks_[0] = this.getInputTargetBlock('A');
|
|
this.prevBlocks_[1] = this.getInputTargetBlock('B');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* "logic_compare" extension function. Adds type left and right side type
|
|
* checking to "logic_compare" blocks.
|
|
* @this {Blockly.Block}
|
|
* @package
|
|
* @readonly
|
|
*/
|
|
Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION = function() {
|
|
// Add onchange handler to ensure types are compatible.
|
|
this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN);
|
|
};
|
|
|
|
Blockly.Extensions.register('logic_compare',
|
|
Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION);
|
|
|
|
/**
|
|
* Adds type coordination between inputs and output.
|
|
* @mixin
|
|
* @augments Blockly.Block
|
|
* @package
|
|
* @readonly
|
|
*/
|
|
Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN = {
|
|
prevParentConnection_: null,
|
|
|
|
/**
|
|
* Called whenever anything on the workspace changes.
|
|
* Prevent mismatched types.
|
|
* @param {!Blockly.Events.Abstract} e Change event.
|
|
* @this {Blockly.Block}
|
|
*/
|
|
onchange: function(e) {
|
|
var blockA = this.getInputTargetBlock('THEN');
|
|
var blockB = this.getInputTargetBlock('ELSE');
|
|
var parentConnection = this.outputConnection.targetConnection;
|
|
// Disconnect blocks that existed prior to this change if they don't match.
|
|
if ((blockA || blockB) && parentConnection) {
|
|
for (var i = 0; i < 2; i++) {
|
|
var block = (i == 1) ? blockA : blockB;
|
|
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_) {
|
|
this.unplug();
|
|
parentConnection.getSourceBlock().bumpNeighbours();
|
|
} else {
|
|
block.unplug();
|
|
block.bumpNeighbours();
|
|
}
|
|
Blockly.Events.setGroup(false);
|
|
}
|
|
}
|
|
}
|
|
this.prevParentConnection_ = parentConnection;
|
|
}
|
|
};
|
|
|
|
Blockly.Extensions.registerMixin('logic_ternary',
|
|
Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);
|