mixio 1.10.0

This commit is contained in:
Eason010212
2023-03-10 18:03:02 +08:00
parent 5ac1c6853a
commit 5d80728be9
3574 changed files with 9983 additions and 562000 deletions

122
blockly/blocks/colour.js Normal file
View File

@@ -0,0 +1,122 @@
/**
* @license
* Copyright 2012 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Colour 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 fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.Blocks.colour'); // Deprecated
goog.provide('Blockly.Constants.Colour');
goog.require('Blockly');
goog.require('Blockly.Blocks');
goog.require('Blockly.FieldColour');
goog.require('Blockly.FieldLabel');
/**
* Unused constant for the common HSV hue for all blocks in this category.
* @deprecated Use Blockly.Msg['COLOUR_HUE']. (2018 April 5)
*/
Blockly.Constants.Colour.HUE = 20;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for colour picker.
{
"type": "colour_picker",
"message0": "%1",
"args0": [
{
"type": "field_colour",
"name": "COLOUR",
"colour": "#ff0000"
}
],
"output": "Colour",
"helpUrl": "%{BKY_COLOUR_PICKER_HELPURL}",
"style": "colour_blocks",
"tooltip": "%{BKY_COLOUR_PICKER_TOOLTIP}",
"extensions": ["parent_tooltip_when_inline"]
},
// Block for random colour.
{
"type": "colour_random",
"message0": "%{BKY_COLOUR_RANDOM_TITLE}",
"output": "Colour",
"helpUrl": "%{BKY_COLOUR_RANDOM_HELPURL}",
"style": "colour_blocks",
"tooltip": "%{BKY_COLOUR_RANDOM_TOOLTIP}"
},
// Block for composing a colour from RGB components.
{
"type": "colour_rgb",
"message0": "%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3",
"args0": [
{
"type": "input_value",
"name": "RED",
"check": "Number",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "GREEN",
"check": "Number",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "BLUE",
"check": "Number",
"align": "RIGHT"
}
],
"output": "Colour",
"helpUrl": "%{BKY_COLOUR_RGB_HELPURL}",
"style": "colour_blocks",
"tooltip": "%{BKY_COLOUR_RGB_TOOLTIP}"
},
// Block for blending two colours together.
{
"type": "colour_blend",
"message0": "%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} " +
"%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3",
"args0": [
{
"type": "input_value",
"name": "COLOUR1",
"check": "Colour",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "COLOUR2",
"check": "Colour",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "RATIO",
"check": "Number",
"align": "RIGHT"
}
],
"output": "Colour",
"helpUrl": "%{BKY_COLOUR_BLEND_HELPURL}",
"style": "colour_blocks",
"tooltip": "%{BKY_COLOUR_BLEND_TOOLTIP}"
}
]); // END JSON EXTRACT (Do not delete this comment.)

861
blockly/blocks/lists.js Normal file
View File

@@ -0,0 +1,861 @@
/**
* @license
* Copyright 2012 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview List 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 fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.Constants.Lists');
goog.require('Blockly');
goog.require('Blockly.Blocks');
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['LISTS_HUE']. (2018 April 5)
*/
Blockly.Constants.Lists.HUE = 260;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for creating an empty list
// The 'list_create_with' block is preferred as it is more flexible.
// <block type="lists_create_with">
// <mutation items="0"></mutation>
// </block>
{
"type": "lists_create_empty",
"message0": "%{BKY_LISTS_CREATE_EMPTY_TITLE}",
"output": "Array",
"style": "list_blocks",
"tooltip": "%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}",
"helpUrl": "%{BKY_LISTS_CREATE_EMPTY_HELPURL}"
},
// Block for creating a list with one element repeated.
{
"type": "lists_repeat",
"message0": "%{BKY_LISTS_REPEAT_TITLE}",
"args0": [
{
"type": "input_value",
"name": "ITEM"
},
{
"type": "input_value",
"name": "NUM",
"check": "Number"
}
],
"output": "Array",
"style": "list_blocks",
"tooltip": "%{BKY_LISTS_REPEAT_TOOLTIP}",
"helpUrl": "%{BKY_LISTS_REPEAT_HELPURL}"
},
// Block for reversing a list.
{
"type": "lists_reverse",
"message0": "%{BKY_LISTS_REVERSE_MESSAGE0}",
"args0": [
{
"type": "input_value",
"name": "LIST",
"check": "Array"
}
],
"output": "Array",
"inputsInline": true,
"style": "list_blocks",
"tooltip": "%{BKY_LISTS_REVERSE_TOOLTIP}",
"helpUrl": "%{BKY_LISTS_REVERSE_HELPURL}"
},
// Block for checking if a list is empty
{
"type": "lists_isEmpty",
"message0": "%{BKY_LISTS_ISEMPTY_TITLE}",
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": ["String", "Array"]
}
],
"output": "Boolean",
"style": "list_blocks",
"tooltip": "%{BKY_LISTS_ISEMPTY_TOOLTIP}",
"helpUrl": "%{BKY_LISTS_ISEMPTY_HELPURL}"
},
// Block for getting the list length
{
"type": "lists_length",
"message0": "%{BKY_LISTS_LENGTH_TITLE}",
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": ["String", "Array"]
}
],
"output": "Number",
"style": "list_blocks",
"tooltip": "%{BKY_LISTS_LENGTH_TOOLTIP}",
"helpUrl": "%{BKY_LISTS_LENGTH_HELPURL}"
}
]); // END JSON EXTRACT (Do not delete this comment.)
Blockly.Blocks['lists_create_with'] = {
/**
* Block for creating a list with any number of elements of any type.
* @this {Blockly.Block}
*/
init: function() {
this.setHelpUrl(Blockly.Msg['LISTS_CREATE_WITH_HELPURL']);
this.setStyle('list_blocks');
this.itemCount_ = 3;
this.updateShape_();
this.setOutput(true, 'Array');
this.setMutator(new Blockly.Mutator(['lists_create_with_item']));
this.setTooltip(Blockly.Msg['LISTS_CREATE_WITH_TOOLTIP']);
},
/**
* Create XML to represent list inputs.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('items', this.itemCount_);
return container;
},
/**
* Parse XML to restore the list inputs.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
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('lists_create_with_container');
containerBlock.initSvg();
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.itemCount_; i++) {
var itemBlock = workspace.newBlock('lists_create_with_item');
itemBlock.initSvg();
connection.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
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 itemBlock = containerBlock.getInputTargetBlock('STACK');
// Count number of inputs.
var connections = [];
while (itemBlock && !itemBlock.isInsertionMarker()) {
connections.push(itemBlock.valueConnection_);
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
// Disconnect any children that don't belong.
for (var i = 0; i < this.itemCount_; i++) {
var connection = this.getInput('ADD' + i).connection.targetConnection;
if (connection && connections.indexOf(connection) == -1) {
connection.disconnect();
}
}
this.itemCount_ = connections.length;
this.updateShape_();
// Reconnect any child blocks.
for (var i = 0; i < this.itemCount_; i++) {
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
}
},
/**
* Store pointers to any connected child blocks.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this {Blockly.Block}
*/
saveConnections: function(containerBlock) {
var itemBlock = containerBlock.getInputTargetBlock('STACK');
var i = 0;
while (itemBlock) {
var input = this.getInput('ADD' + i);
itemBlock.valueConnection_ = input && input.connection.targetConnection;
i++;
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
},
/**
* Modify this block to have the correct number of inputs.
* @private
* @this {Blockly.Block}
*/
updateShape_: function() {
if (this.itemCount_ && this.getInput('EMPTY')) {
this.removeInput('EMPTY');
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
this.appendDummyInput('EMPTY')
.appendField(Blockly.Msg['LISTS_CREATE_EMPTY_TITLE']);
}
// Add new inputs.
for (var i = 0; i < this.itemCount_; i++) {
if (!this.getInput('ADD' + i)) {
var input = this.appendValueInput('ADD' + i)
.setAlign(Blockly.ALIGN_RIGHT);
if (i == 0) {
input.appendField(Blockly.Msg['LISTS_CREATE_WITH_INPUT_WITH']);
}
}
}
// Remove deleted inputs.
while (this.getInput('ADD' + i)) {
this.removeInput('ADD' + i);
i++;
}
}
};
Blockly.Blocks['lists_create_with_container'] = {
/**
* Mutator block for list container.
* @this {Blockly.Block}
*/
init: function() {
this.setStyle('list_blocks');
this.appendDummyInput()
.appendField(Blockly.Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']);
this.appendStatementInput('STACK');
this.setTooltip(Blockly.Msg['LISTS_CREATE_WITH_CONTAINER_TOOLTIP']);
this.contextMenu = false;
}
};
Blockly.Blocks['lists_create_with_item'] = {
/**
* Mutator block for adding items.
* @this {Blockly.Block}
*/
init: function() {
this.setStyle('list_blocks');
this.appendDummyInput()
.appendField(Blockly.Msg['LISTS_CREATE_WITH_ITEM_TITLE']);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg['LISTS_CREATE_WITH_ITEM_TOOLTIP']);
this.contextMenu = false;
}
};
Blockly.Blocks['lists_indexOf'] = {
/**
* Block for finding an item in the list.
* @this {Blockly.Block}
*/
init: function() {
var OPERATORS =
[
[Blockly.Msg['LISTS_INDEX_OF_FIRST'], 'FIRST'],
[Blockly.Msg['LISTS_INDEX_OF_LAST'], 'LAST']
];
this.setHelpUrl(Blockly.Msg['LISTS_INDEX_OF_HELPURL']);
this.setStyle('list_blocks');
this.setOutput(true, 'Number');
this.appendValueInput('VALUE')
.setCheck('Array')
.appendField(Blockly.Msg['LISTS_INDEX_OF_INPUT_IN_LIST']);
this.appendValueInput('FIND')
.appendField(new Blockly.FieldDropdown(OPERATORS), 'END');
this.setInputsInline(true);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return Blockly.Msg['LISTS_INDEX_OF_TOOLTIP'].replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
});
}
};
Blockly.Blocks['lists_getIndex'] = {
/**
* Block for getting element at index.
* @this {Blockly.Block}
*/
init: function() {
var MODE =
[
[Blockly.Msg['LISTS_GET_INDEX_GET'], 'GET'],
[Blockly.Msg['LISTS_GET_INDEX_GET_REMOVE'], 'GET_REMOVE'],
[Blockly.Msg['LISTS_GET_INDEX_REMOVE'], 'REMOVE']
];
this.WHERE_OPTIONS =
[
[Blockly.Msg['LISTS_GET_INDEX_FROM_START'], 'FROM_START'],
[Blockly.Msg['LISTS_GET_INDEX_FROM_END'], 'FROM_END'],
[Blockly.Msg['LISTS_GET_INDEX_FIRST'], 'FIRST'],
[Blockly.Msg['LISTS_GET_INDEX_LAST'], 'LAST'],
[Blockly.Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM']
];
this.setHelpUrl(Blockly.Msg['LISTS_GET_INDEX_HELPURL']);
this.setStyle('list_blocks');
var modeMenu = new Blockly.FieldDropdown(MODE, function(value) {
var isStatement = (value == 'REMOVE');
this.getSourceBlock().updateStatement_(isStatement);
});
this.appendValueInput('VALUE')
.setCheck('Array')
.appendField(Blockly.Msg['LISTS_GET_INDEX_INPUT_IN_LIST']);
this.appendDummyInput()
.appendField(modeMenu, 'MODE')
.appendField('', 'SPACE');
this.appendDummyInput('AT');
if (Blockly.Msg['LISTS_GET_INDEX_TAIL']) {
this.appendDummyInput('TAIL')
.appendField(Blockly.Msg['LISTS_GET_INDEX_TAIL']);
}
this.setInputsInline(true);
this.setOutput(true);
this.updateAt_(true);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('MODE');
var where = thisBlock.getFieldValue('WHERE');
var tooltip = '';
switch (mode + ' ' + where) {
case 'GET FROM_START':
case 'GET FROM_END':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_FROM'];
break;
case 'GET FIRST':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_FIRST'];
break;
case 'GET LAST':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_LAST'];
break;
case 'GET RANDOM':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_RANDOM'];
break;
case 'GET_REMOVE FROM_START':
case 'GET_REMOVE FROM_END':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM'];
break;
case 'GET_REMOVE FIRST':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST'];
break;
case 'GET_REMOVE LAST':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST'];
break;
case 'GET_REMOVE RANDOM':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM'];
break;
case 'REMOVE FROM_START':
case 'REMOVE FROM_END':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM'];
break;
case 'REMOVE FIRST':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST'];
break;
case 'REMOVE LAST':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST'];
break;
case 'REMOVE RANDOM':
tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM'];
break;
}
if (where == 'FROM_START' || where == 'FROM_END') {
var msg = (where == 'FROM_START') ?
Blockly.Msg['LISTS_INDEX_FROM_START_TOOLTIP'] :
Blockly.Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
tooltip += ' ' + msg.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
}
return tooltip;
});
},
/**
* Create XML to represent whether the block is a statement or a value.
* Also represent whether there is an 'AT' input.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
var isStatement = !this.outputConnection;
container.setAttribute('statement', isStatement);
var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
container.setAttribute('at', isAt);
return container;
},
/**
* Parse XML to restore the 'AT' input.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
// Note: Until January 2013 this block did not have mutations,
// so 'statement' defaults to false and 'at' defaults to true.
var isStatement = (xmlElement.getAttribute('statement') == 'true');
this.updateStatement_(isStatement);
var isAt = (xmlElement.getAttribute('at') != 'false');
this.updateAt_(isAt);
},
/**
* Switch between a value block and a statement block.
* @param {boolean} newStatement True if the block should be a statement.
* False if the block should be a value.
* @private
* @this {Blockly.Block}
*/
updateStatement_: function(newStatement) {
var oldStatement = !this.outputConnection;
if (newStatement != oldStatement) {
this.unplug(true, true);
if (newStatement) {
this.setOutput(false);
this.setPreviousStatement(true);
this.setNextStatement(true);
} else {
this.setPreviousStatement(false);
this.setNextStatement(false);
this.setOutput(true);
}
}
},
/**
* Create or delete an input for the numeric index.
* @param {boolean} isAt True if the input should exist.
* @private
* @this {Blockly.Block}
*/
updateAt_: function(isAt) {
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT');
this.removeInput('ORDINAL', true);
// Create either a value 'AT' input or a dummy input.
if (isAt) {
this.appendValueInput('AT').setCheck('Number');
if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL')
.appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']);
}
} else {
this.appendDummyInput('AT');
}
var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
// The 'isAt' variable is available due to this function being a closure.
if (newAt != isAt) {
var block = this.getSourceBlock();
block.updateAt_(newAt);
// This menu has been destroyed and replaced. Update the replacement.
block.setFieldValue(value, 'WHERE');
return null;
}
return undefined;
});
this.getInput('AT').appendField(menu, 'WHERE');
if (Blockly.Msg['LISTS_GET_INDEX_TAIL']) {
this.moveInputBefore('TAIL', null);
}
}
};
Blockly.Blocks['lists_setIndex'] = {
/**
* Block for setting the element at index.
* @this {Blockly.Block}
*/
init: function() {
var MODE =
[
[Blockly.Msg['LISTS_SET_INDEX_SET'], 'SET'],
[Blockly.Msg['LISTS_SET_INDEX_INSERT'], 'INSERT']
];
this.WHERE_OPTIONS =
[
[Blockly.Msg['LISTS_GET_INDEX_FROM_START'], 'FROM_START'],
[Blockly.Msg['LISTS_GET_INDEX_FROM_END'], 'FROM_END'],
[Blockly.Msg['LISTS_GET_INDEX_FIRST'], 'FIRST'],
[Blockly.Msg['LISTS_GET_INDEX_LAST'], 'LAST'],
[Blockly.Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM']
];
this.setHelpUrl(Blockly.Msg['LISTS_SET_INDEX_HELPURL']);
this.setStyle('list_blocks');
this.appendValueInput('LIST')
.setCheck('Array')
.appendField(Blockly.Msg['LISTS_SET_INDEX_INPUT_IN_LIST']);
this.appendDummyInput()
.appendField(new Blockly.FieldDropdown(MODE), 'MODE')
.appendField('', 'SPACE');
this.appendDummyInput('AT');
this.appendValueInput('TO')
.appendField(Blockly.Msg['LISTS_SET_INDEX_INPUT_TO']);
this.setInputsInline(true);
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip(Blockly.Msg['LISTS_SET_INDEX_TOOLTIP']);
this.updateAt_(true);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('MODE');
var where = thisBlock.getFieldValue('WHERE');
var tooltip = '';
switch (mode + ' ' + where) {
case 'SET FROM_START':
case 'SET FROM_END':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_FROM'];
break;
case 'SET FIRST':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_FIRST'];
break;
case 'SET LAST':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_LAST'];
break;
case 'SET RANDOM':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_RANDOM'];
break;
case 'INSERT FROM_START':
case 'INSERT FROM_END':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_FROM'];
break;
case 'INSERT FIRST':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST'];
break;
case 'INSERT LAST':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_LAST'];
break;
case 'INSERT RANDOM':
tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM'];
break;
}
if (where == 'FROM_START' || where == 'FROM_END') {
tooltip += ' ' + Blockly.Msg['LISTS_INDEX_FROM_START_TOOLTIP']
.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
}
return tooltip;
});
},
/**
* Create XML to represent whether there is an 'AT' input.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
container.setAttribute('at', isAt);
return container;
},
/**
* Parse XML to restore the 'AT' input.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
// Note: Until January 2013 this block did not have mutations,
// so 'at' defaults to true.
var isAt = (xmlElement.getAttribute('at') != 'false');
this.updateAt_(isAt);
},
/**
* Create or delete an input for the numeric index.
* @param {boolean} isAt True if the input should exist.
* @private
* @this {Blockly.Block}
*/
updateAt_: function(isAt) {
// Destroy old 'AT' and 'ORDINAL' input.
this.removeInput('AT');
this.removeInput('ORDINAL', true);
// Create either a value 'AT' input or a dummy input.
if (isAt) {
this.appendValueInput('AT').setCheck('Number');
if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL')
.appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']);
}
} else {
this.appendDummyInput('AT');
}
var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
// The 'isAt' variable is available due to this function being a closure.
if (newAt != isAt) {
var block = this.getSourceBlock();
block.updateAt_(newAt);
// This menu has been destroyed and replaced. Update the replacement.
block.setFieldValue(value, 'WHERE');
return null;
}
return undefined;
});
this.moveInputBefore('AT', 'TO');
if (this.getInput('ORDINAL')) {
this.moveInputBefore('ORDINAL', 'TO');
}
this.getInput('AT').appendField(menu, 'WHERE');
}
};
Blockly.Blocks['lists_getSublist'] = {
/**
* Block for getting sublist.
* @this {Blockly.Block}
*/
init: function() {
this['WHERE_OPTIONS_1'] =
[
[Blockly.Msg['LISTS_GET_SUBLIST_START_FROM_START'], 'FROM_START'],
[Blockly.Msg['LISTS_GET_SUBLIST_START_FROM_END'], 'FROM_END'],
[Blockly.Msg['LISTS_GET_SUBLIST_START_FIRST'], 'FIRST']
];
this['WHERE_OPTIONS_2'] =
[
[Blockly.Msg['LISTS_GET_SUBLIST_END_FROM_START'], 'FROM_START'],
[Blockly.Msg['LISTS_GET_SUBLIST_END_FROM_END'], 'FROM_END'],
[Blockly.Msg['LISTS_GET_SUBLIST_END_LAST'], 'LAST']
];
this.setHelpUrl(Blockly.Msg['LISTS_GET_SUBLIST_HELPURL']);
this.setStyle('list_blocks');
this.appendValueInput('LIST')
.setCheck('Array')
.appendField(Blockly.Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']);
this.appendDummyInput('AT1');
this.appendDummyInput('AT2');
if (Blockly.Msg['LISTS_GET_SUBLIST_TAIL']) {
this.appendDummyInput('TAIL')
.appendField(Blockly.Msg['LISTS_GET_SUBLIST_TAIL']);
}
this.setInputsInline(true);
this.setOutput(true, 'Array');
this.updateAt_(1, true);
this.updateAt_(2, true);
this.setTooltip(Blockly.Msg['LISTS_GET_SUBLIST_TOOLTIP']);
},
/**
* Create XML to represent whether there are 'AT' inputs.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE;
container.setAttribute('at1', isAt1);
var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE;
container.setAttribute('at2', isAt2);
return container;
},
/**
* Parse XML to restore the 'AT' inputs.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
var isAt1 = (xmlElement.getAttribute('at1') == 'true');
var isAt2 = (xmlElement.getAttribute('at2') == 'true');
this.updateAt_(1, isAt1);
this.updateAt_(2, isAt2);
},
/**
* Create or delete an input for a numeric index.
* This block has two such inputs, independent of each other.
* @param {number} n Specify first or second input (1 or 2).
* @param {boolean} isAt True if the input should exist.
* @private
* @this {Blockly.Block}
*/
updateAt_: function(n, isAt) {
// Create or delete an input for the numeric index.
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT' + n);
this.removeInput('ORDINAL' + n, true);
// Create either a value 'AT' input or a dummy input.
if (isAt) {
this.appendValueInput('AT' + n).setCheck('Number');
if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL' + n)
.appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']);
}
} else {
this.appendDummyInput('AT' + n);
}
var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n],
function(value) {
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt != isAt) {
var block = this.getSourceBlock();
block.updateAt_(n, newAt);
// This menu has been destroyed and replaced.
// Update the replacement.
block.setFieldValue(value, 'WHERE' + n);
return null;
}
});
this.getInput('AT' + n)
.appendField(menu, 'WHERE' + n);
if (n == 1) {
this.moveInputBefore('AT1', 'AT2');
if (this.getInput('ORDINAL1')) {
this.moveInputBefore('ORDINAL1', 'AT2');
}
}
if (Blockly.Msg['LISTS_GET_SUBLIST_TAIL']) {
this.moveInputBefore('TAIL', null);
}
}
};
Blockly.Blocks['lists_sort'] = {
/**
* Block for sorting a list.
* @this {Blockly.Block}
*/
init: function() {
this.jsonInit({
"message0": Blockly.Msg['LISTS_SORT_TITLE'],
"args0": [
{
"type": "field_dropdown",
"name": "TYPE",
"options": [
[Blockly.Msg['LISTS_SORT_TYPE_NUMERIC'], "NUMERIC"],
[Blockly.Msg['LISTS_SORT_TYPE_TEXT'], "TEXT"],
[Blockly.Msg['LISTS_SORT_TYPE_IGNORECASE'], "IGNORE_CASE"]
]
},
{
"type": "field_dropdown",
"name": "DIRECTION",
"options": [
[Blockly.Msg['LISTS_SORT_ORDER_ASCENDING'], "1"],
[Blockly.Msg['LISTS_SORT_ORDER_DESCENDING'], "-1"]
]
},
{
"type": "input_value",
"name": "LIST",
"check": "Array"
}
],
"output": "Array",
"style": "list_blocks",
"tooltip": Blockly.Msg['LISTS_SORT_TOOLTIP'],
"helpUrl": Blockly.Msg['LISTS_SORT_HELPURL']
});
}
};
Blockly.Blocks['lists_split'] = {
/**
* Block for splitting text into a list, or joining a list into text.
* @this {Blockly.Block}
*/
init: function() {
// Assign 'this' to a variable for use in the closures below.
var thisBlock = this;
var dropdown = new Blockly.FieldDropdown(
[
[Blockly.Msg['LISTS_SPLIT_LIST_FROM_TEXT'], 'SPLIT'],
[Blockly.Msg['LISTS_SPLIT_TEXT_FROM_LIST'], 'JOIN']
],
function(newMode) {
thisBlock.updateType_(newMode);
});
this.setHelpUrl(Blockly.Msg['LISTS_SPLIT_HELPURL']);
this.setStyle('list_blocks');
this.appendValueInput('INPUT')
.setCheck('String')
.appendField(dropdown, 'MODE');
this.appendValueInput('DELIM')
.setCheck('String')
.appendField(Blockly.Msg['LISTS_SPLIT_WITH_DELIMITER']);
this.setInputsInline(true);
this.setOutput(true, 'Array');
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('MODE');
if (mode == 'SPLIT') {
return Blockly.Msg['LISTS_SPLIT_TOOLTIP_SPLIT'];
} else if (mode == 'JOIN') {
return Blockly.Msg['LISTS_SPLIT_TOOLTIP_JOIN'];
}
throw Error('Unknown mode: ' + mode);
});
},
/**
* Modify this block to have the correct input and output types.
* @param {string} newMode Either 'SPLIT' or 'JOIN'.
* @private
* @this {Blockly.Block}
*/
updateType_: function(newMode) {
var mode = this.getFieldValue('MODE');
if (mode != newMode) {
var inputConnection = this.getInput('INPUT').connection;
inputConnection.setShadowDom(null);
var inputBlock = inputConnection.targetBlock();
if (inputBlock) {
inputConnection.disconnect();
if (inputBlock.isShadow()) {
inputBlock.dispose();
} else {
this.bumpNeighbours();
}
}
}
if (newMode == 'SPLIT') {
this.outputConnection.setCheck('Array');
this.getInput('INPUT').setCheck('String');
} else {
this.outputConnection.setCheck('String');
this.getInput('INPUT').setCheck('Array');
}
},
/**
* Create XML to represent the input and output types.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('mode', this.getFieldValue('MODE'));
return container;
},
/**
* Parse XML to restore the input and output types.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.updateType_(xmlElement.getAttribute('mode'));
}
};

635
blockly/blocks/logic.js Normal file
View File

@@ -0,0 +1,635 @@
/**
* @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.Blocks');
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}",
"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}",
"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,
/**
* Don't automatically add STATEMENT_PREFIX and STATEMENT_SUFFIX to generated
* code. These will be handled manually in this block's generators.
*/
suppressPrefixSuffix: true,
/**
* Create XML to represent the number of else-if and else inputs.
* @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.
* @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_();
},
/**
* 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);

356
blockly/blocks/loops.js Normal file
View File

@@ -0,0 +1,356 @@
/**
* @license
* Copyright 2012 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Loop 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 fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.Blocks.loops'); // Deprecated
goog.provide('Blockly.Constants.Loops');
goog.require('Blockly');
goog.require('Blockly.Blocks');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldLabel');
goog.require('Blockly.FieldNumber');
goog.require('Blockly.FieldVariable');
goog.require('Blockly.Warning');
/**
* Unused constant for the common HSV hue for all blocks in this category.
* @deprecated Use Blockly.Msg['LOOPS_HUE']. (2018 April 5)
*/
Blockly.Constants.Loops.HUE = 120;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for repeat n times (external number).
{
"type": "controls_repeat_ext",
"message0": "%{BKY_CONTROLS_REPEAT_TITLE}",
"args0": [{
"type": "input_value",
"name": "TIMES",
"check": "Number"
}],
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
"args1": [{
"type": "input_statement",
"name": "DO"
}],
"previousStatement": null,
"nextStatement": null,
"style": "loop_blocks",
"tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}",
"helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}"
},
// Block for repeat n times (internal number).
// The 'controls_repeat_ext' block is preferred as it is more flexible.
{
"type": "controls_repeat",
"message0": "%{BKY_CONTROLS_REPEAT_TITLE}",
"args0": [{
"type": "field_number",
"name": "TIMES",
"value": 10,
"min": 0,
"precision": 1
}],
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
"args1": [{
"type": "input_statement",
"name": "DO"
}],
"previousStatement": null,
"nextStatement": null,
"style": "loop_blocks",
"tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}",
"helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}"
},
// Block for 'do while/until' loop.
{
"type": "controls_whileUntil",
"message0": "%1 %2",
"args0": [
{
"type": "field_dropdown",
"name": "MODE",
"options": [
["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}", "WHILE"],
["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}", "UNTIL"]
]
},
{
"type": "input_value",
"name": "BOOL",
"check": "Boolean"
}
],
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
"args1": [{
"type": "input_statement",
"name": "DO"
}],
"previousStatement": null,
"nextStatement": null,
"style": "loop_blocks",
"helpUrl": "%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",
"extensions": ["controls_whileUntil_tooltip"]
},
// Block for 'for' loop.
{
"type": "controls_for",
"message0": "%{BKY_CONTROLS_FOR_TITLE}",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": null
},
{
"type": "input_value",
"name": "FROM",
"check": "Number",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "TO",
"check": "Number",
"align": "RIGHT"
},
{
"type": "input_value",
"name": "BY",
"check": "Number",
"align": "RIGHT"
}
],
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
"args1": [{
"type": "input_statement",
"name": "DO"
}],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"style": "loop_blocks",
"helpUrl": "%{BKY_CONTROLS_FOR_HELPURL}",
"extensions": [
"contextMenu_newGetVariableBlock",
"controls_for_tooltip"
]
},
// Block for 'for each' loop.
{
"type": "controls_forEach",
"message0": "%{BKY_CONTROLS_FOREACH_TITLE}",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": null
},
{
"type": "input_value",
"name": "LIST",
"check": "Array"
}
],
"message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
"args1": [{
"type": "input_statement",
"name": "DO"
}],
"previousStatement": null,
"nextStatement": null,
"style": "loop_blocks",
"helpUrl": "%{BKY_CONTROLS_FOREACH_HELPURL}",
"extensions": [
"contextMenu_newGetVariableBlock",
"controls_forEach_tooltip"
]
},
// Block for flow statements: continue, break.
{
"type": "controls_flow_statements",
"message0": "%1",
"args0": [{
"type": "field_dropdown",
"name": "FLOW",
"options": [
["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}", "BREAK"],
["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}", "CONTINUE"]
]
}],
"previousStatement": null,
"style": "loop_blocks",
"helpUrl": "%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}",
"extensions": [
"controls_flow_tooltip",
"controls_flow_in_loop_check"
]
}
]); // END JSON EXTRACT (Do not delete this comment.)
/**
* Tooltips for the 'controls_whileUntil' block, keyed by MODE value.
* @see {Blockly.Extensions#buildTooltipForDropdown}
* @package
* @readonly
*/
Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS = {
'WHILE': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}',
'UNTIL': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}'
};
Blockly.Extensions.register('controls_whileUntil_tooltip',
Blockly.Extensions.buildTooltipForDropdown(
'MODE', Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));
/**
* Tooltips for the 'controls_flow_statements' block, keyed by FLOW value.
* @see {Blockly.Extensions#buildTooltipForDropdown}
* @package
* @readonly
*/
Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS = {
'BREAK': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}',
'CONTINUE': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}'
};
Blockly.Extensions.register('controls_flow_tooltip',
Blockly.Extensions.buildTooltipForDropdown(
'FLOW', Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS));
/**
* Mixin to add a context menu item to create a 'variables_get' block.
* Used by blocks 'controls_for' and 'controls_forEach'.
* @mixin
* @augments Blockly.Block
* @package
* @readonly
*/
Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
/**
* Add context menu option to create getter block for the loop's variable.
* (customContextMenu support limited to web BlockSvg.)
* @param {!Array} options List of menu options to add to.
* @this {Blockly.Block}
*/
customContextMenu: function(options) {
if (this.isInFlyout) {
return;
}
var variable = this.getField('VAR').getVariable();
var varName = variable.name;
if (!this.isCollapsed() && varName != null) {
var option = {enabled: true};
option.text =
Blockly.Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName);
var xmlField = Blockly.Variables.generateVariableFieldDom(variable);
var xmlBlock = Blockly.utils.xml.createElement('block');
xmlBlock.setAttribute('type', 'variables_get');
xmlBlock.appendChild(xmlField);
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
options.push(option);
}
}
};
Blockly.Extensions.registerMixin('contextMenu_newGetVariableBlock',
Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);
Blockly.Extensions.register('controls_for_tooltip',
Blockly.Extensions.buildTooltipWithFieldText(
'%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR'));
Blockly.Extensions.register('controls_forEach_tooltip',
Blockly.Extensions.buildTooltipWithFieldText(
'%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR'));
/**
* This mixin adds a check to make sure the 'controls_flow_statements' block
* is contained in a loop. Otherwise a warning is added to the block.
* @mixin
* @augments Blockly.Block
* @package
* @readonly
*/
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
/**
* List of block types that are loops and thus do not need warnings.
* To add a new loop type add this to your code:
* Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.push('custom_loop');
*/
LOOP_TYPES: [
'controls_repeat',
'controls_repeat_ext',
'controls_forEach',
'controls_for',
'controls_whileUntil'
],
/**
* Don't automatically add STATEMENT_PREFIX and STATEMENT_SUFFIX to generated
* code. These will be handled manually in this block's generators.
*/
suppressPrefixSuffix: true,
/**
* Is the given block enclosed (at any level) by a loop?
* @param {!Blockly.Block} block Current block.
* @return {Blockly.Block} The nearest surrounding loop, or null if none.
*/
getSurroundLoop: function(block) {
// Is the block nested in a loop?
do {
if (Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES
.indexOf(block.type) != -1) {
return block;
}
block = block.getSurroundParent();
} while (block);
return null;
},
/**
* Called whenever anything on the workspace changes.
* Add warning if this flow block is not nested inside a loop.
* @param {!Blockly.Events.Abstract} e Change event.
* @this {Blockly.Block}
*/
onchange: function(e) {
// Don't change state if:
// * It's at the start of a drag.
// * It's not a move event.
if (!this.workspace.isDragging || this.workspace.isDragging() ||
e.type != Blockly.Events.BLOCK_MOVE) {
return;
}
var enabled = Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN
.getSurroundLoop(this);
this.setWarningText(enabled ? null :
Blockly.Msg['CONTROLS_FLOW_STATEMENTS_WARNING']);
if (!this.isInFlyout) {
var group = Blockly.Events.getGroup();
// Makes it so the move and the disable event get undone together.
Blockly.Events.setGroup(e.group);
this.setEnabled(enabled);
Blockly.Events.setGroup(group);
}
}
};
Blockly.Extensions.registerMixin('controls_flow_in_loop_check',
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);

566
blockly/blocks/math.js Normal file
View File

@@ -0,0 +1,566 @@
/**
* @license
* Copyright 2012 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Math 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.math'); // Deprecated
goog.provide('Blockly.Constants.Math');
goog.require('Blockly');
goog.require('Blockly.Blocks');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldLabel');
goog.require('Blockly.FieldNumber');
goog.require('Blockly.FieldVariable');
/**
* Unused constant for the common HSV hue for all blocks in this category.
* @deprecated Use Blockly.Msg['MATH_HUE']. (2018 April 5)
*/
Blockly.Constants.Math.HUE = 230;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for numeric value.
{
"type": "math_number",
"message0": "%1",
"args0": [{
"type": "field_number",
"name": "NUM",
"value": 0
}],
"output": "Number",
"helpUrl": "%{BKY_MATH_NUMBER_HELPURL}",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_NUMBER_TOOLTIP}",
"extensions": ["parent_tooltip_when_inline"]
},
// Block for basic arithmetic operator.
{
"type": "math_arithmetic",
"message0": "%1 %2 %3",
"args0": [
{
"type": "input_value",
"name": "A",
"check": "Number"
},
{
"type": "field_dropdown",
"name": "OP",
"options": [
["%{BKY_MATH_ADDITION_SYMBOL}", "ADD"],
["%{BKY_MATH_SUBTRACTION_SYMBOL}", "MINUS"],
["%{BKY_MATH_MULTIPLICATION_SYMBOL}", "MULTIPLY"],
["%{BKY_MATH_DIVISION_SYMBOL}", "DIVIDE"],
["%{BKY_MATH_POWER_SYMBOL}", "POWER"]
]
},
{
"type": "input_value",
"name": "B",
"check": "Number"
}
],
"inputsInline": true,
"output": "Number",
"style": "math_blocks",
"helpUrl": "%{BKY_MATH_ARITHMETIC_HELPURL}",
"extensions": ["math_op_tooltip"]
},
// Block for advanced math operators with single operand.
{
"type": "math_single",
"message0": "%1 %2",
"args0": [
{
"type": "field_dropdown",
"name": "OP",
"options": [
["%{BKY_MATH_SINGLE_OP_ROOT}", 'ROOT'],
["%{BKY_MATH_SINGLE_OP_ABSOLUTE}", 'ABS'],
['-', 'NEG'],
['ln', 'LN'],
['log10', 'LOG10'],
['e^', 'EXP'],
['10^', 'POW10']
]
},
{
"type": "input_value",
"name": "NUM",
"check": "Number"
}
],
"output": "Number",
"style": "math_blocks",
"helpUrl": "%{BKY_MATH_SINGLE_HELPURL}",
"extensions": ["math_op_tooltip"]
},
// Block for trigonometry operators.
{
"type": "math_trig",
"message0": "%1 %2",
"args0": [
{
"type": "field_dropdown",
"name": "OP",
"options": [
["%{BKY_MATH_TRIG_SIN}", "SIN"],
["%{BKY_MATH_TRIG_COS}", "COS"],
["%{BKY_MATH_TRIG_TAN}", "TAN"],
["%{BKY_MATH_TRIG_ASIN}", "ASIN"],
["%{BKY_MATH_TRIG_ACOS}", "ACOS"],
["%{BKY_MATH_TRIG_ATAN}", "ATAN"]
]
},
{
"type": "input_value",
"name": "NUM",
"check": "Number"
}
],
"output": "Number",
"style": "math_blocks",
"helpUrl": "%{BKY_MATH_TRIG_HELPURL}",
"extensions": ["math_op_tooltip"]
},
// Block for constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
{
"type": "math_constant",
"message0": "%1",
"args0": [
{
"type": "field_dropdown",
"name": "CONSTANT",
"options": [
["\u03c0", "PI"],
["e", "E"],
["\u03c6", "GOLDEN_RATIO"],
["sqrt(2)", "SQRT2"],
["sqrt(\u00bd)", "SQRT1_2"],
["\u221e", "INFINITY"]
]
}
],
"output": "Number",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_CONSTANT_TOOLTIP}",
"helpUrl": "%{BKY_MATH_CONSTANT_HELPURL}"
},
// Block for checking if a number is even, odd, prime, whole, positive,
// negative or if it is divisible by certain number.
{
"type": "math_number_property",
"message0": "%1 %2",
"args0": [
{
"type": "input_value",
"name": "NUMBER_TO_CHECK",
"check": "Number"
},
{
"type": "field_dropdown",
"name": "PROPERTY",
"options": [
["%{BKY_MATH_IS_EVEN}", "EVEN"],
["%{BKY_MATH_IS_ODD}", "ODD"],
["%{BKY_MATH_IS_PRIME}", "PRIME"],
["%{BKY_MATH_IS_WHOLE}", "WHOLE"],
["%{BKY_MATH_IS_POSITIVE}", "POSITIVE"],
["%{BKY_MATH_IS_NEGATIVE}", "NEGATIVE"],
["%{BKY_MATH_IS_DIVISIBLE_BY}", "DIVISIBLE_BY"]
]
}
],
"inputsInline": true,
"output": "Boolean",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_IS_TOOLTIP}",
"mutator": "math_is_divisibleby_mutator"
},
// Block for adding to a variable in place.
{
"type": "math_change",
"message0": "%{BKY_MATH_CHANGE_TITLE}",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "%{BKY_MATH_CHANGE_TITLE_ITEM}"
},
{
"type": "input_value",
"name": "DELTA",
"check": "Number"
}
],
"previousStatement": null,
"nextStatement": null,
"style": "variable_blocks",
"helpUrl": "%{BKY_MATH_CHANGE_HELPURL}",
"extensions": ["math_change_tooltip"]
},
// Block for rounding functions.
{
"type": "math_round",
"message0": "%1 %2",
"args0": [
{
"type": "field_dropdown",
"name": "OP",
"options": [
["%{BKY_MATH_ROUND_OPERATOR_ROUND}", "ROUND"],
["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}", "ROUNDUP"],
["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}", "ROUNDDOWN"]
]
},
{
"type": "input_value",
"name": "NUM",
"check": "Number"
}
],
"output": "Number",
"style": "math_blocks",
"helpUrl": "%{BKY_MATH_ROUND_HELPURL}",
"tooltip": "%{BKY_MATH_ROUND_TOOLTIP}"
},
// Block for evaluating a list of numbers to return sum, average, min, max,
// etc. Some functions also work on text (min, max, mode, median).
{
"type": "math_on_list",
"message0": "%1 %2",
"args0": [
{
"type": "field_dropdown",
"name": "OP",
"options": [
["%{BKY_MATH_ONLIST_OPERATOR_SUM}", "SUM"],
["%{BKY_MATH_ONLIST_OPERATOR_MIN}", "MIN"],
["%{BKY_MATH_ONLIST_OPERATOR_MAX}", "MAX"],
["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}", "AVERAGE"],
["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}", "MEDIAN"],
["%{BKY_MATH_ONLIST_OPERATOR_MODE}", "MODE"],
["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}", "STD_DEV"],
["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}", "RANDOM"]
]
},
{
"type": "input_value",
"name": "LIST",
"check": "Array"
}
],
"output": "Number",
"style": "math_blocks",
"helpUrl": "%{BKY_MATH_ONLIST_HELPURL}",
"mutator": "math_modes_of_list_mutator",
"extensions": ["math_op_tooltip"]
},
// Block for remainder of a division.
{
"type": "math_modulo",
"message0": "%{BKY_MATH_MODULO_TITLE}",
"args0": [
{
"type": "input_value",
"name": "DIVIDEND",
"check": "Number"
},
{
"type": "input_value",
"name": "DIVISOR",
"check": "Number"
}
],
"inputsInline": true,
"output": "Number",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_MODULO_TOOLTIP}",
"helpUrl": "%{BKY_MATH_MODULO_HELPURL}"
},
// Block for constraining a number between two limits.
{
"type": "math_constrain",
"message0": "%{BKY_MATH_CONSTRAIN_TITLE}",
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": "Number"
},
{
"type": "input_value",
"name": "LOW",
"check": "Number"
},
{
"type": "input_value",
"name": "HIGH",
"check": "Number"
}
],
"inputsInline": true,
"output": "Number",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_CONSTRAIN_TOOLTIP}",
"helpUrl": "%{BKY_MATH_CONSTRAIN_HELPURL}"
},
// Block for random integer between [X] and [Y].
{
"type": "math_random_int",
"message0": "%{BKY_MATH_RANDOM_INT_TITLE}",
"args0": [
{
"type": "input_value",
"name": "FROM",
"check": "Number"
},
{
"type": "input_value",
"name": "TO",
"check": "Number"
}
],
"inputsInline": true,
"output": "Number",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_RANDOM_INT_TOOLTIP}",
"helpUrl": "%{BKY_MATH_RANDOM_INT_HELPURL}"
},
// Block for random integer between [X] and [Y].
{
"type": "math_random_float",
"message0": "%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",
"output": "Number",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",
"helpUrl": "%{BKY_MATH_RANDOM_FLOAT_HELPURL}"
},
// Block for calculating atan2 of [X] and [Y].
{
"type": "math_atan2",
"message0": "%{BKY_MATH_ATAN2_TITLE}",
"args0": [
{
"type": "input_value",
"name": "X",
"check": "Number"
},
{
"type": "input_value",
"name": "Y",
"check": "Number"
}
],
"inputsInline": true,
"output": "Number",
"style": "math_blocks",
"tooltip": "%{BKY_MATH_ATAN2_TOOLTIP}",
"helpUrl": "%{BKY_MATH_ATAN2_HELPURL}"
}
]); // END JSON EXTRACT (Do not delete this comment.)
/**
* Mapping of math block OP value to tooltip message for blocks
* math_arithmetic, math_simple, math_trig, and math_on_lists.
* @see {Blockly.Extensions#buildTooltipForDropdown}
* @package
* @readonly
*/
Blockly.Constants.Math.TOOLTIPS_BY_OP = {
// math_arithmetic
'ADD': '%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}',
'MINUS': '%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}',
'MULTIPLY': '%{BKY_MATH_ARITHMETIC_TOOLTIP_MULTIPLY}',
'DIVIDE': '%{BKY_MATH_ARITHMETIC_TOOLTIP_DIVIDE}',
'POWER': '%{BKY_MATH_ARITHMETIC_TOOLTIP_POWER}',
// math_simple
'ROOT': '%{BKY_MATH_SINGLE_TOOLTIP_ROOT}',
'ABS': '%{BKY_MATH_SINGLE_TOOLTIP_ABS}',
'NEG': '%{BKY_MATH_SINGLE_TOOLTIP_NEG}',
'LN': '%{BKY_MATH_SINGLE_TOOLTIP_LN}',
'LOG10': '%{BKY_MATH_SINGLE_TOOLTIP_LOG10}',
'EXP': '%{BKY_MATH_SINGLE_TOOLTIP_EXP}',
'POW10': '%{BKY_MATH_SINGLE_TOOLTIP_POW10}',
// math_trig
'SIN': '%{BKY_MATH_TRIG_TOOLTIP_SIN}',
'COS': '%{BKY_MATH_TRIG_TOOLTIP_COS}',
'TAN': '%{BKY_MATH_TRIG_TOOLTIP_TAN}',
'ASIN': '%{BKY_MATH_TRIG_TOOLTIP_ASIN}',
'ACOS': '%{BKY_MATH_TRIG_TOOLTIP_ACOS}',
'ATAN': '%{BKY_MATH_TRIG_TOOLTIP_ATAN}',
// math_on_lists
'SUM': '%{BKY_MATH_ONLIST_TOOLTIP_SUM}',
'MIN': '%{BKY_MATH_ONLIST_TOOLTIP_MIN}',
'MAX': '%{BKY_MATH_ONLIST_TOOLTIP_MAX}',
'AVERAGE': '%{BKY_MATH_ONLIST_TOOLTIP_AVERAGE}',
'MEDIAN': '%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}',
'MODE': '%{BKY_MATH_ONLIST_TOOLTIP_MODE}',
'STD_DEV': '%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}',
'RANDOM': '%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}'
};
Blockly.Extensions.register('math_op_tooltip',
Blockly.Extensions.buildTooltipForDropdown(
'OP', Blockly.Constants.Math.TOOLTIPS_BY_OP));
/**
* Mixin for mutator functions in the 'math_is_divisibleby_mutator'
* extension.
* @mixin
* @augments Blockly.Block
* @package
*/
Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN = {
/**
* Create XML to represent whether the 'divisorInput' should be present.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
var divisorInput = (this.getFieldValue('PROPERTY') == 'DIVISIBLE_BY');
container.setAttribute('divisor_input', divisorInput);
return container;
},
/**
* Parse XML to restore the 'divisorInput'.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
var divisorInput = (xmlElement.getAttribute('divisor_input') == 'true');
this.updateShape_(divisorInput);
},
/**
* Modify this block to have (or not have) an input for 'is divisible by'.
* @param {boolean} divisorInput True if this block has a divisor input.
* @private
* @this {Blockly.Block}
*/
updateShape_: function(divisorInput) {
// Add or remove a Value Input.
var inputExists = this.getInput('DIVISOR');
if (divisorInput) {
if (!inputExists) {
this.appendValueInput('DIVISOR')
.setCheck('Number');
}
} else if (inputExists) {
this.removeInput('DIVISOR');
}
}
};
/**
* 'math_is_divisibleby_mutator' extension to the 'math_property' block that
* can update the block shape (add/remove divisor input) based on whether
* property is "divisible by".
* @this {Blockly.Block}
* @package
*/
Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION = function() {
this.getField('PROPERTY').setValidator(function(option) {
var divisorInput = (option == 'DIVISIBLE_BY');
this.getSourceBlock().updateShape_(divisorInput);
});
};
Blockly.Extensions.registerMutator('math_is_divisibleby_mutator',
Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN,
Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION);
// Update the tooltip of 'math_change' block to reference the variable.
Blockly.Extensions.register('math_change_tooltip',
Blockly.Extensions.buildTooltipWithFieldText(
'%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR'));
/**
* Mixin with mutator methods to support alternate output based if the
* 'math_on_list' block uses the 'MODE' operation.
* @mixin
* @augments Blockly.Block
* @package
* @readonly
*/
Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN = {
/**
* Modify this block to have the correct output type.
* @param {string} newOp Either 'MODE' or some op than returns a number.
* @private
* @this {Blockly.Block}
*/
updateType_: function(newOp) {
if (newOp == 'MODE') {
this.outputConnection.setCheck('Array');
} else {
this.outputConnection.setCheck('Number');
}
},
/**
* Create XML to represent the output type.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('op', this.getFieldValue('OP'));
return container;
},
/**
* Parse XML to restore the output type.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.updateType_(xmlElement.getAttribute('op'));
}
};
/**
* Extension to 'math_on_list' blocks that allows support of
* modes operation (outputs a list of numbers).
* @this {Blockly.Block}
* @package
*/
Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION = function() {
this.getField('OP').setValidator(function(newOp) {
this.updateType_(newOp);
}.bind(this));
};
Blockly.Extensions.registerMutator('math_modes_of_list_mutator',
Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN,
Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION);

1080
blockly/blocks/procedures.js Normal file

File diff suppressed because it is too large Load Diff

923
blockly/blocks/text.js Normal file
View File

@@ -0,0 +1,923 @@
/**
* @license
* Copyright 2012 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Text blocks for Blockly.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.Blocks.texts'); // Deprecated
goog.provide('Blockly.Constants.Text');
goog.require('Blockly');
goog.require('Blockly.Blocks');
goog.require('Blockly.FieldDropdown');
goog.require('Blockly.FieldImage');
goog.require('Blockly.FieldMultilineInput');
goog.require('Blockly.FieldTextInput');
goog.require('Blockly.FieldVariable');
goog.require('Blockly.Mutator');
/**
* Unused constant for the common HSV hue for all blocks in this category.
* @deprecated Use Blockly.Msg['TEXTS_HUE']. (2018 April 5)
*/
Blockly.Constants.Text.HUE = 160;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for text value
{
"type": "text",
"message0": "%1",
"args0": [{
"type": "field_input",
"name": "TEXT",
"text": ""
}],
"output": "String",
"style": "text_blocks",
"helpUrl": "%{BKY_TEXT_TEXT_HELPURL}",
"tooltip": "%{BKY_TEXT_TEXT_TOOLTIP}",
"extensions": [
"text_quotes",
"parent_tooltip_when_inline"
]
},
{
"type": "text_multiline",
"message0": "%1 %2",
"args0": [{
"type": "field_image",
"src": 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpP' +
'U2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0' +
'U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjm' +
'RIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z88' +
'73zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5luj' +
'nYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiD' +
'MYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYX' +
'fwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4z' +
'wA5X2Z9AYnQrEAAAAASUVORK5CYII=',
"width": 12,
"height": 17,
"alt": '\u00B6'
},{
"type": "field_multilinetext",
"name": "TEXT",
"text": ""
}],
"output": "String",
"style": "text_blocks",
"helpUrl": "%{BKY_TEXT_TEXT_HELPURL}",
"tooltip": "%{BKY_TEXT_TEXT_TOOLTIP}",
"extensions": [
"parent_tooltip_when_inline"
]
},
{
"type": "text_join",
"message0": "",
"output": "String",
"style": "text_blocks",
"helpUrl": "%{BKY_TEXT_JOIN_HELPURL}",
"tooltip": "%{BKY_TEXT_JOIN_TOOLTIP}",
"mutator": "text_join_mutator"
},
{
"type": "text_create_join_container",
"message0": "%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2",
"args0": [{
"type": "input_dummy"
},
{
"type": "input_statement",
"name": "STACK"
}],
"style": "text_blocks",
"tooltip": "%{BKY_TEXT_CREATE_JOIN_TOOLTIP}",
"enableContextMenu": false
},
{
"type": "text_create_join_item",
"message0": "%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}",
"previousStatement": null,
"nextStatement": null,
"style": "text_blocks",
"tooltip": "%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}",
"enableContextMenu": false
},
{
"type": "text_append",
"message0": "%{BKY_TEXT_APPEND_TITLE}",
"args0": [{
"type": "field_variable",
"name": "VAR",
"variable": "%{BKY_TEXT_APPEND_VARIABLE}"
},
{
"type": "input_value",
"name": "TEXT"
}],
"previousStatement": null,
"nextStatement": null,
"style": "text_blocks",
"extensions": [
"text_append_tooltip"
]
},
{
"type": "text_length",
"message0": "%{BKY_TEXT_LENGTH_TITLE}",
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": ['String', 'Array']
}
],
"output": 'Number',
"style": "text_blocks",
"tooltip": "%{BKY_TEXT_LENGTH_TOOLTIP}",
"helpUrl": "%{BKY_TEXT_LENGTH_HELPURL}"
},
{
"type": "text_isEmpty",
"message0": "%{BKY_TEXT_ISEMPTY_TITLE}",
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": ['String', 'Array']
}
],
"output": 'Boolean',
"style": "text_blocks",
"tooltip": "%{BKY_TEXT_ISEMPTY_TOOLTIP}",
"helpUrl": "%{BKY_TEXT_ISEMPTY_HELPURL}"
},
{
"type": "text_indexOf",
"message0": "%{BKY_TEXT_INDEXOF_TITLE}",
"args0": [
{
"type": "input_value",
"name": "VALUE",
"check": "String"
},
{
"type": "field_dropdown",
"name": "END",
"options": [
[
"%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}",
"FIRST"
],
[
"%{BKY_TEXT_INDEXOF_OPERATOR_LAST}",
"LAST"
]
]
},
{
"type": "input_value",
"name": "FIND",
"check": "String"
}
],
"output": "Number",
"style": "text_blocks",
"helpUrl": "%{BKY_TEXT_INDEXOF_HELPURL}",
"inputsInline": true,
"extensions": [
"text_indexOf_tooltip"
]
},
{
"type": "text_charAt",
"message0": "%{BKY_TEXT_CHARAT_TITLE}", // "in text %1 %2"
"args0": [
{
"type":"input_value",
"name": "VALUE",
"check": "String"
},
{
"type": "field_dropdown",
"name": "WHERE",
"options": [
["%{BKY_TEXT_CHARAT_FROM_START}", "FROM_START"],
["%{BKY_TEXT_CHARAT_FROM_END}", "FROM_END"],
["%{BKY_TEXT_CHARAT_FIRST}", "FIRST"],
["%{BKY_TEXT_CHARAT_LAST}", "LAST"],
["%{BKY_TEXT_CHARAT_RANDOM}", "RANDOM"]
]
}
],
"output": "String",
"style": "text_blocks",
"helpUrl": "%{BKY_TEXT_CHARAT_HELPURL}",
"inputsInline": true,
"mutator": "text_charAt_mutator"
}
]); // END JSON EXTRACT (Do not delete this comment.)
Blockly.Blocks['text_getSubstring'] = {
/**
* Block for getting substring.
* @this {Blockly.Block}
*/
init: function() {
this['WHERE_OPTIONS_1'] = [
[Blockly.Msg['TEXT_GET_SUBSTRING_START_FROM_START'], 'FROM_START'],
[Blockly.Msg['TEXT_GET_SUBSTRING_START_FROM_END'], 'FROM_END'],
[Blockly.Msg['TEXT_GET_SUBSTRING_START_FIRST'], 'FIRST']
];
this['WHERE_OPTIONS_2'] = [
[Blockly.Msg['TEXT_GET_SUBSTRING_END_FROM_START'], 'FROM_START'],
[Blockly.Msg['TEXT_GET_SUBSTRING_END_FROM_END'], 'FROM_END'],
[Blockly.Msg['TEXT_GET_SUBSTRING_END_LAST'], 'LAST']
];
this.setHelpUrl(Blockly.Msg['TEXT_GET_SUBSTRING_HELPURL']);
this.setStyle('text_blocks');
this.appendValueInput('STRING')
.setCheck('String')
.appendField(Blockly.Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']);
this.appendDummyInput('AT1');
this.appendDummyInput('AT2');
if (Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']) {
this.appendDummyInput('TAIL')
.appendField(Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']);
}
this.setInputsInline(true);
this.setOutput(true, 'String');
this.updateAt_(1, true);
this.updateAt_(2, true);
this.setTooltip(Blockly.Msg['TEXT_GET_SUBSTRING_TOOLTIP']);
},
/**
* Create XML to represent whether there are 'AT' inputs.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE;
container.setAttribute('at1', isAt1);
var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE;
container.setAttribute('at2', isAt2);
return container;
},
/**
* Parse XML to restore the 'AT' inputs.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
var isAt1 = (xmlElement.getAttribute('at1') == 'true');
var isAt2 = (xmlElement.getAttribute('at2') == 'true');
this.updateAt_(1, isAt1);
this.updateAt_(2, isAt2);
},
/**
* Create or delete an input for a numeric index.
* This block has two such inputs, independent of each other.
* @param {number} n Specify first or second input (1 or 2).
* @param {boolean} isAt True if the input should exist.
* @private
* @this {Blockly.Block}
*/
updateAt_: function(n, isAt) {
// Create or delete an input for the numeric index.
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT' + n);
this.removeInput('ORDINAL' + n, true);
// Create either a value 'AT' input or a dummy input.
if (isAt) {
this.appendValueInput('AT' + n).setCheck('Number');
if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL' + n)
.appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']);
}
} else {
this.appendDummyInput('AT' + n);
}
// Move tail, if present, to end of block.
if (n == 2 && Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']) {
this.removeInput('TAIL', true);
this.appendDummyInput('TAIL')
.appendField(Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']);
}
var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n],
function(value) {
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt != isAt) {
var block = this.getSourceBlock();
block.updateAt_(n, newAt);
// This menu has been destroyed and replaced.
// Update the replacement.
block.setFieldValue(value, 'WHERE' + n);
return null;
}
return undefined;
});
this.getInput('AT' + n)
.appendField(menu, 'WHERE' + n);
if (n == 1) {
this.moveInputBefore('AT1', 'AT2');
if (this.getInput('ORDINAL1')) {
this.moveInputBefore('ORDINAL1', 'AT2');
}
}
}
};
Blockly.Blocks['text_changeCase'] = {
/**
* Block for changing capitalization.
* @this {Blockly.Block}
*/
init: function() {
var OPERATORS = [
[Blockly.Msg['TEXT_CHANGECASE_OPERATOR_UPPERCASE'], 'UPPERCASE'],
[Blockly.Msg['TEXT_CHANGECASE_OPERATOR_LOWERCASE'], 'LOWERCASE'],
[Blockly.Msg['TEXT_CHANGECASE_OPERATOR_TITLECASE'], 'TITLECASE']
];
this.setHelpUrl(Blockly.Msg['TEXT_CHANGECASE_HELPURL']);
this.setStyle('text_blocks');
this.appendValueInput('TEXT')
.setCheck('String')
.appendField(new Blockly.FieldDropdown(OPERATORS), 'CASE');
this.setOutput(true, 'String');
this.setTooltip(Blockly.Msg['TEXT_CHANGECASE_TOOLTIP']);
}
};
Blockly.Blocks['text_trim'] = {
/**
* Block for trimming spaces.
* @this {Blockly.Block}
*/
init: function() {
var OPERATORS = [
[Blockly.Msg['TEXT_TRIM_OPERATOR_BOTH'], 'BOTH'],
[Blockly.Msg['TEXT_TRIM_OPERATOR_LEFT'], 'LEFT'],
[Blockly.Msg['TEXT_TRIM_OPERATOR_RIGHT'], 'RIGHT']
];
this.setHelpUrl(Blockly.Msg['TEXT_TRIM_HELPURL']);
this.setStyle('text_blocks');
this.appendValueInput('TEXT')
.setCheck('String')
.appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
this.setOutput(true, 'String');
this.setTooltip(Blockly.Msg['TEXT_TRIM_TOOLTIP']);
}
};
Blockly.Blocks['text_print'] = {
/**
* Block for print statement.
* @this {Blockly.Block}
*/
init: function() {
this.jsonInit({
"message0": Blockly.Msg['TEXT_PRINT_TITLE'],
"args0": [
{
"type": "input_value",
"name": "TEXT"
}
],
"previousStatement": null,
"nextStatement": null,
"style": "text_blocks",
"tooltip": Blockly.Msg['TEXT_PRINT_TOOLTIP'],
"helpUrl": Blockly.Msg['TEXT_PRINT_HELPURL']
});
}
};
Blockly.Blocks['text_prompt_ext'] = {
/**
* Block for prompt function (external message).
* @this {Blockly.Block}
*/
init: function() {
var TYPES = [
[Blockly.Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'],
[Blockly.Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER']
];
this.setHelpUrl(Blockly.Msg['TEXT_PROMPT_HELPURL']);
this.setStyle('text_blocks');
// Assign 'this' to a variable for use in the closures below.
var thisBlock = this;
var dropdown = new Blockly.FieldDropdown(TYPES, function(newOp) {
thisBlock.updateType_(newOp);
});
this.appendValueInput('TEXT')
.appendField(dropdown, 'TYPE');
this.setOutput(true, 'String');
this.setTooltip(function() {
return (thisBlock.getFieldValue('TYPE') == 'TEXT') ?
Blockly.Msg['TEXT_PROMPT_TOOLTIP_TEXT'] :
Blockly.Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
});
},
/**
* Modify this block to have the correct output type.
* @param {string} newOp Either 'TEXT' or 'NUMBER'.
* @private
* @this {Blockly.Block}
*/
updateType_: function(newOp) {
this.outputConnection.setCheck(newOp == 'NUMBER' ? 'Number' : 'String');
},
/**
* Create XML to represent the output type.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('type', this.getFieldValue('TYPE'));
return container;
},
/**
* Parse XML to restore the output type.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.updateType_(xmlElement.getAttribute('type'));
}
};
Blockly.Blocks['text_prompt'] = {
/**
* Block for prompt function (internal message).
* The 'text_prompt_ext' block is preferred as it is more flexible.
* @this {Blockly.Block}
*/
init: function() {
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
var TYPES = [
[Blockly.Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'],
[Blockly.Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER']
];
// Assign 'this' to a variable for use in the closures below.
var thisBlock = this;
this.setHelpUrl(Blockly.Msg['TEXT_PROMPT_HELPURL']);
this.setStyle('text_blocks');
var dropdown = new Blockly.FieldDropdown(TYPES, function(newOp) {
thisBlock.updateType_(newOp);
});
this.appendDummyInput()
.appendField(dropdown, 'TYPE')
.appendField(this.newQuote_(true))
.appendField(new Blockly.FieldTextInput(''), 'TEXT')
.appendField(this.newQuote_(false));
this.setOutput(true, 'String');
this.setTooltip(function() {
return (thisBlock.getFieldValue('TYPE') == 'TEXT') ?
Blockly.Msg['TEXT_PROMPT_TOOLTIP_TEXT'] :
Blockly.Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
});
},
updateType_: Blockly.Blocks['text_prompt_ext'].updateType_,
mutationToDom: Blockly.Blocks['text_prompt_ext'].mutationToDom,
domToMutation: Blockly.Blocks['text_prompt_ext'].domToMutation
};
Blockly.Blocks['text_count'] = {
/**
* Block for counting how many times one string appears within another string.
* @this {Blockly.Block}
*/
init: function() {
this.jsonInit({
"message0": Blockly.Msg['TEXT_COUNT_MESSAGE0'],
"args0": [
{
"type": "input_value",
"name": "SUB",
"check": "String"
},
{
"type": "input_value",
"name": "TEXT",
"check": "String"
}
],
"output": "Number",
"inputsInline": true,
"style": "text_blocks",
"tooltip": Blockly.Msg['TEXT_COUNT_TOOLTIP'],
"helpUrl": Blockly.Msg['TEXT_COUNT_HELPURL']
});
}
};
Blockly.Blocks['text_replace'] = {
/**
* Block for replacing one string with another in the text.
* @this {Blockly.Block}
*/
init: function() {
this.jsonInit({
"message0": Blockly.Msg['TEXT_REPLACE_MESSAGE0'],
"args0": [
{
"type": "input_value",
"name": "FROM",
"check": "String"
},
{
"type": "input_value",
"name": "TO",
"check": "String"
},
{
"type": "input_value",
"name": "TEXT",
"check": "String"
}
],
"output": "String",
"inputsInline": true,
"style": "text_blocks",
"tooltip": Blockly.Msg['TEXT_REPLACE_TOOLTIP'],
"helpUrl": Blockly.Msg['TEXT_REPLACE_HELPURL']
});
}
};
Blockly.Blocks['text_reverse'] = {
/**
* Block for reversing a string.
* @this {Blockly.Block}
*/
init: function() {
this.jsonInit({
"message0": Blockly.Msg['TEXT_REVERSE_MESSAGE0'],
"args0": [
{
"type": "input_value",
"name": "TEXT",
"check": "String"
}
],
"output": "String",
"inputsInline": true,
"style": "text_blocks",
"tooltip": Blockly.Msg['TEXT_REVERSE_TOOLTIP'],
"helpUrl": Blockly.Msg['TEXT_REVERSE_HELPURL']
});
}
};
/**
*
* @mixin
* @package
* @readonly
*/
Blockly.Constants.Text.QUOTE_IMAGE_MIXIN = {
/**
* Image data URI of an LTR opening double quote (same as RTL closing double quote).
* @readonly
*/
QUOTE_IMAGE_LEFT_DATAURI:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
'n0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY' +
'1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1' +
'HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMf' +
'z9AylsaRRgGzvZAAAAAElFTkSuQmCC',
/**
* Image data URI of an LTR closing double quote (same as RTL opening double quote).
* @readonly
*/
QUOTE_IMAGE_RIGHT_DATAURI:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' +
'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' +
'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' +
'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==',
/**
* Pixel width of QUOTE_IMAGE_LEFT_DATAURI and QUOTE_IMAGE_RIGHT_DATAURI.
* @readonly
*/
QUOTE_IMAGE_WIDTH: 12,
/**
* Pixel height of QUOTE_IMAGE_LEFT_DATAURI and QUOTE_IMAGE_RIGHT_DATAURI.
* @readonly
*/
QUOTE_IMAGE_HEIGHT: 12,
/**
* Inserts appropriate quote images before and after the named field.
* @param {string} fieldName The name of the field to wrap with quotes.
* @this {Blockly.Block}
*/
quoteField_: function(fieldName) {
for (var i = 0, input; (input = this.inputList[i]); i++) {
for (var j = 0, field; (field = input.fieldRow[j]); j++) {
if (fieldName == field.name) {
input.insertFieldAt(j, this.newQuote_(true));
input.insertFieldAt(j + 2, this.newQuote_(false));
return;
}
}
}
console.warn('field named "' + fieldName + '" not found in ' + this.toDevString());
},
/**
* A helper function that generates a FieldImage of an opening or
* closing double quote. The selected quote will be adapted for RTL blocks.
* @param {boolean} open If the image should be open quote (“ in LTR).
* Otherwise, a closing quote is used (” in LTR).
* @return {!Blockly.FieldImage} The new field.
* @this {Blockly.Block}
*/
newQuote_: function(open) {
var isLeft = this.RTL ? !open : open;
var dataUri = isLeft ?
this.QUOTE_IMAGE_LEFT_DATAURI :
this.QUOTE_IMAGE_RIGHT_DATAURI;
return new Blockly.FieldImage(
dataUri,
this.QUOTE_IMAGE_WIDTH,
this.QUOTE_IMAGE_HEIGHT,
isLeft ? '\u201C' : '\u201D');
}
};
/**
* Wraps TEXT field with images of double quote characters.
* @this {Blockly.Block}
*/
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION = function() {
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
this.quoteField_('TEXT');
};
/**
* Mixin for mutator functions in the 'text_join_mutator' extension.
* @mixin
* @augments Blockly.Block
* @package
*/
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN = {
/**
* Create XML to represent number of text inputs.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('items', this.itemCount_);
return container;
},
/**
* Parse XML to restore the text inputs.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
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('text_create_join_container');
containerBlock.initSvg();
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.itemCount_; i++) {
var itemBlock = workspace.newBlock('text_create_join_item');
itemBlock.initSvg();
connection.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
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 itemBlock = containerBlock.getInputTargetBlock('STACK');
// Count number of inputs.
var connections = [];
while (itemBlock && !itemBlock.isInsertionMarker()) {
connections.push(itemBlock.valueConnection_);
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
// Disconnect any children that don't belong.
for (var i = 0; i < this.itemCount_; i++) {
var connection = this.getInput('ADD' + i).connection.targetConnection;
if (connection && connections.indexOf(connection) == -1) {
connection.disconnect();
}
}
this.itemCount_ = connections.length;
this.updateShape_();
// Reconnect any child blocks.
for (var i = 0; i < this.itemCount_; i++) {
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
}
},
/**
* Store pointers to any connected child blocks.
* @param {!Blockly.Block} containerBlock Root block in mutator.
* @this {Blockly.Block}
*/
saveConnections: function(containerBlock) {
var itemBlock = containerBlock.getInputTargetBlock('STACK');
var i = 0;
while (itemBlock) {
var input = this.getInput('ADD' + i);
itemBlock.valueConnection_ = input && input.connection.targetConnection;
i++;
itemBlock = itemBlock.nextConnection &&
itemBlock.nextConnection.targetBlock();
}
},
/**
* Modify this block to have the correct number of inputs.
* @private
* @this {Blockly.Block}
*/
updateShape_: function() {
if (this.itemCount_ && this.getInput('EMPTY')) {
this.removeInput('EMPTY');
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
this.appendDummyInput('EMPTY')
.appendField(this.newQuote_(true))
.appendField(this.newQuote_(false));
}
// Add new inputs.
for (var i = 0; i < this.itemCount_; i++) {
if (!this.getInput('ADD' + i)) {
var input = this.appendValueInput('ADD' + i)
.setAlign(Blockly.ALIGN_RIGHT);
if (i == 0) {
input.appendField(Blockly.Msg['TEXT_JOIN_TITLE_CREATEWITH']);
}
}
}
// Remove deleted inputs.
while (this.getInput('ADD' + i)) {
this.removeInput('ADD' + i);
i++;
}
}
};
/**
* Performs final setup of a text_join block.
* @this {Blockly.Block}
*/
Blockly.Constants.Text.TEXT_JOIN_EXTENSION = function() {
// Add the quote mixin for the itemCount_ = 0 case.
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
// Initialize the mutator values.
this.itemCount_ = 2;
this.updateShape_();
// Configure the mutator UI.
this.setMutator(new Blockly.Mutator(['text_create_join_item']));
};
// Update the tooltip of 'text_append' block to reference the variable.
Blockly.Extensions.register('text_append_tooltip',
Blockly.Extensions.buildTooltipWithFieldText(
'%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR'));
/**
* Update the tooltip of 'text_append' block to reference the variable.
* @this {Blockly.Block}
*/
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION = function() {
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return Blockly.Msg['TEXT_INDEXOF_TOOLTIP'].replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
});
};
/**
* Mixin for mutator functions in the 'text_charAt_mutator' extension.
* @mixin
* @augments Blockly.Block
* @package
*/
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN = {
/**
* Create XML to represent whether there is an 'AT' input.
* @return {!Element} XML storage element.
* @this {Blockly.Block}
*/
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('at', !!this.isAt_);
return container;
},
/**
* Parse XML to restore the 'AT' input.
* @param {!Element} xmlElement XML storage element.
* @this {Blockly.Block}
*/
domToMutation: function(xmlElement) {
// Note: Until January 2013 this block did not have mutations,
// so 'at' defaults to true.
var isAt = (xmlElement.getAttribute('at') != 'false');
this.updateAt_(isAt);
},
/**
* Create or delete an input for the numeric index.
* @param {boolean} isAt True if the input should exist.
* @private
* @this {Blockly.Block}
*/
updateAt_: function(isAt) {
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT', true);
this.removeInput('ORDINAL', true);
// Create either a value 'AT' input or a dummy input.
if (isAt) {
this.appendValueInput('AT').setCheck('Number');
if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL')
.appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']);
}
}
if (Blockly.Msg['TEXT_CHARAT_TAIL']) {
this.removeInput('TAIL', true);
this.appendDummyInput('TAIL')
.appendField(Blockly.Msg['TEXT_CHARAT_TAIL']);
}
this.isAt_ = isAt;
}
};
/**
* Does the initial mutator update of text_charAt and adds the tooltip
* @this {Blockly.Block}
*/
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION = function() {
var dropdown = this.getField('WHERE');
dropdown.setValidator(function(value) {
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
if (newAt != this.isAt_) {
var block = this.getSourceBlock();
block.updateAt_(newAt);
}
});
this.updateAt_(true);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var where = thisBlock.getFieldValue('WHERE');
var tooltip = Blockly.Msg['TEXT_CHARAT_TOOLTIP'];
if (where == 'FROM_START' || where == 'FROM_END') {
var msg = (where == 'FROM_START') ?
Blockly.Msg['LISTS_INDEX_FROM_START_TOOLTIP'] :
Blockly.Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
if (msg) {
tooltip += ' ' + msg.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
}
}
return tooltip;
});
};
Blockly.Extensions.register('text_indexOf_tooltip',
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION);
Blockly.Extensions.register('text_quotes',
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION);
Blockly.Extensions.registerMutator('text_join_mutator',
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN,
Blockly.Constants.Text.TEXT_JOIN_EXTENSION);
Blockly.Extensions.registerMutator('text_charAt_mutator',
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN,
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION);

163
blockly/blocks/variables.js Normal file
View File

@@ -0,0 +1,163 @@
/**
* @license
* Copyright 2012 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Variable 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 fraser@google.com (Neil Fraser)
*/
'use strict';
goog.provide('Blockly.Blocks.variables'); // Deprecated.
goog.provide('Blockly.Constants.Variables');
goog.require('Blockly');
goog.require('Blockly.Blocks');
goog.require('Blockly.FieldLabel');
goog.require('Blockly.FieldVariable');
/**
* Unused constant for the common HSV hue for all blocks in this category.
* @deprecated Use Blockly.Msg['VARIABLES_HUE']. (2018 April 5)
*/
Blockly.Constants.Variables.HUE = 330;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for variable getter.
{
"type": "variables_get",
"message0": "%1",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "%{BKY_VARIABLES_DEFAULT_NAME}"
}
],
"output": null,
"style": "variable_blocks",
"helpUrl": "%{BKY_VARIABLES_GET_HELPURL}",
"tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}",
"extensions": ["contextMenu_variableSetterGetter"]
},
// Block for variable setter.
{
"type": "variables_set",
"message0": "%{BKY_VARIABLES_SET}",
"args0": [
{
"type": "field_variable",
"name": "VAR",
"variable": "%{BKY_VARIABLES_DEFAULT_NAME}"
},
{
"type": "input_value",
"name": "VALUE"
}
],
"previousStatement": null,
"nextStatement": null,
"style": "variable_blocks",
"tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}",
"helpUrl": "%{BKY_VARIABLES_SET_HELPURL}",
"extensions": ["contextMenu_variableSetterGetter"]
}
]); // END JSON EXTRACT (Do not delete this comment.)
/**
* Mixin to add context menu items to create getter/setter blocks for this
* setter/getter.
* Used by blocks 'variables_set' and 'variables_get'.
* @mixin
* @augments Blockly.Block
* @package
* @readonly
*/
Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
/**
* Add menu option to create getter/setter block for this setter/getter.
* @param {!Array} options List of menu options to add to.
* @this {Blockly.Block}
*/
customContextMenu: function(options) {
if (!this.isInFlyout) {
// Getter blocks have the option to create a setter block, and vice versa.
if (this.type == 'variables_get') {
var opposite_type = 'variables_set';
var contextMenuMsg = Blockly.Msg['VARIABLES_GET_CREATE_SET'];
} else {
var opposite_type = 'variables_get';
var contextMenuMsg = Blockly.Msg['VARIABLES_SET_CREATE_GET'];
}
var option = {enabled: this.workspace.remainingCapacity() > 0};
var name = this.getField('VAR').getText();
option.text = contextMenuMsg.replace('%1', name);
var xmlField = Blockly.utils.xml.createElement('field');
xmlField.setAttribute('name', 'VAR');
xmlField.appendChild(Blockly.utils.xml.createTextNode(name));
var xmlBlock = Blockly.utils.xml.createElement('block');
xmlBlock.setAttribute('type', opposite_type);
xmlBlock.appendChild(xmlField);
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
options.push(option);
// Getter blocks have the option to rename or delete that variable.
} else {
if (this.type == 'variables_get' || this.type == 'variables_get_reporter') {
var renameOption = {
text: Blockly.Msg.RENAME_VARIABLE,
enabled: true,
callback: Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)
};
var name = this.getField('VAR').getText();
var deleteOption = {
text: Blockly.Msg.DELETE_VARIABLE.replace('%1', name),
enabled: true,
callback: Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)
};
options.unshift(renameOption);
options.unshift(deleteOption);
}
}
}
};
/**
* Callback for rename variable dropdown menu option associated with a
* variable getter block.
* @param {!Blockly.Block} block The block with the variable to rename.
* @return {!function()} A function that renames the variable.
*/
Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY = function(block) {
return function() {
var workspace = block.workspace;
var variable = block.getField('VAR').getVariable();
Blockly.Variables.renameVariable(workspace, variable);
};
};
/**
* Callback for delete variable dropdown menu option associated with a
* variable getter block.
* @param {!Blockly.Block} block The block with the variable to delete.
* @return {!function()} A function that deletes the variable.
*/
Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY = function(block) {
return function() {
var workspace = block.workspace;
var variable = block.getField('VAR').getVariable();
workspace.deleteVariableById(variable.getId());
workspace.refreshToolboxSelection();
};
};
Blockly.Extensions.registerMixin('contextMenu_variableSetterGetter',
Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);

View File

@@ -0,0 +1,180 @@
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Variable 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 duzc2dtw@gmail.com (Du Tian Wei)
*/
'use strict';
goog.provide('Blockly.Constants.VariablesDynamic');
goog.require('Blockly');
goog.require('Blockly.Blocks');
goog.require('Blockly.FieldLabel');
goog.require('Blockly.FieldVariable');
/**
* Unused constant for the common HSV hue for all blocks in this category.
* @deprecated Use Blockly.Msg['VARIABLES_DYNAMIC_HUE']. (2018 April 5)
*/
Blockly.Constants.VariablesDynamic.HUE = 310;
Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
// Block for variable getter.
{
"type": "variables_get_dynamic",
"message0": "%1",
"args0": [{
"type": "field_variable",
"name": "VAR",
"variable": "%{BKY_VARIABLES_DEFAULT_NAME}"
}],
"output": null,
"style": "variable_dynamic_blocks",
"helpUrl": "%{BKY_VARIABLES_GET_HELPURL}",
"tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}",
"extensions": ["contextMenu_variableDynamicSetterGetter"]
},
// Block for variable setter.
{
"type": "variables_set_dynamic",
"message0": "%{BKY_VARIABLES_SET}",
"args0": [{
"type": "field_variable",
"name": "VAR",
"variable": "%{BKY_VARIABLES_DEFAULT_NAME}"
},
{
"type": "input_value",
"name": "VALUE"
}
],
"previousStatement": null,
"nextStatement": null,
"style": "variable_dynamic_blocks",
"tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}",
"helpUrl": "%{BKY_VARIABLES_SET_HELPURL}",
"extensions": ["contextMenu_variableDynamicSetterGetter"]
}
]); // END JSON EXTRACT (Do not delete this comment.)
/**
* Mixin to add context menu items to create getter/setter blocks for this
* setter/getter.
* Used by blocks 'variables_set_dynamic' and 'variables_get_dynamic'.
* @mixin
* @augments Blockly.Block
* @package
* @readonly
*/
Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
/**
* Add menu option to create getter/setter block for this setter/getter.
* @param {!Array} options List of menu options to add to.
* @this {Blockly.Block}
*/
customContextMenu: function(options) {
// Getter blocks have the option to create a setter block, and vice versa.
if (!this.isInFlyout) {
var opposite_type;
var contextMenuMsg;
var id = this.getFieldValue('VAR');
var variableModel = this.workspace.getVariableById(id);
var varType = variableModel.type;
if (this.type == 'variables_get_dynamic') {
opposite_type = 'variables_set_dynamic';
contextMenuMsg = Blockly.Msg['VARIABLES_GET_CREATE_SET'];
} else {
opposite_type = 'variables_get_dynamic';
contextMenuMsg = Blockly.Msg['VARIABLES_SET_CREATE_GET'];
}
var option = {enabled: this.workspace.remainingCapacity() > 0};
var name = this.getField('VAR').getText();
option.text = contextMenuMsg.replace('%1', name);
var xmlField = Blockly.utils.xml.createElement('field');
xmlField.setAttribute('name', 'VAR');
xmlField.setAttribute('variabletype', varType);
xmlField.appendChild(Blockly.utils.xml.createTextNode(name));
var xmlBlock = Blockly.utils.xml.createElement('block');
xmlBlock.setAttribute('type', opposite_type);
xmlBlock.appendChild(xmlField);
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
options.push(option);
} else {
if (this.type == 'variables_get_dynamic' ||
this.type == 'variables_get_reporter_dynamic') {
var renameOption = {
text: Blockly.Msg.RENAME_VARIABLE,
enabled: true,
callback: Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this)
};
var name = this.getField('VAR').getText();
var deleteOption = {
text: Blockly.Msg.DELETE_VARIABLE.replace('%1', name),
enabled: true,
callback: Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this)
};
options.unshift(renameOption);
options.unshift(deleteOption);
}
}
},
/**
* Called whenever anything on the workspace changes.
* Set the connection type for this block.
* @param {!Blockly.Events.Abstract} _e Change event.
* @this {Blockly.Block}
*/
onchange: function(_e) {
var id = this.getFieldValue('VAR');
var variableModel = Blockly.Variables.getVariable(this.workspace, id);
if (this.type == 'variables_get_dynamic') {
this.outputConnection.setCheck(variableModel.type);
} else {
this.getInput('VALUE').connection.setCheck(variableModel.type);
}
}
};
/**
* Callback for rename variable dropdown menu option associated with a
* variable getter block.
* @param {!Blockly.Block} block The block with the variable to rename.
* @return {!function()} A function that renames the variable.
*/
Blockly.Constants.VariablesDynamic.RENAME_OPTION_CALLBACK_FACTORY = function(block) {
return function() {
var workspace = block.workspace;
var variable = block.getField('VAR').getVariable();
Blockly.Variables.renameVariable(workspace, variable);
};
};
/**
* Callback for delete variable dropdown menu option associated with a
* variable getter block.
* @param {!Blockly.Block} block The block with the variable to delete.
* @return {!function()} A function that deletes the variable.
*/
Blockly.Constants.VariablesDynamic.DELETE_OPTION_CALLBACK_FACTORY = function(block) {
return function() {
var workspace = block.workspace;
var variable = block.getField('VAR').getVariable();
workspace.deleteVariableById(variable.getId());
workspace.refreshToolboxSelection();
};
};
Blockly.Extensions.registerMixin('contextMenu_variableDynamicSetterGetter',
Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);