chore: use prettier instead of clang-format (#7014)

* chore: add and configure prettier

* chore: remove clang-format

* chore: remove clang-format config

* chore: lint additional ts files

* chore: fix lint errors in blocks

* chore: add prettier-ignore where needed

* chore: ignore js blocks when formatting

* chore: fix playground html syntax

* chore: fix yaml spacing from merge

* chore: convert text blocks to use arrow functions

* chore: format everything with prettier

* chore: fix lint unused imports in blocks
This commit is contained in:
Maribeth Bottorff
2023-05-10 16:01:39 -07:00
committed by GitHub
parent af991f5e1b
commit 88ff901a72
425 changed files with 29170 additions and 21169 deletions

View File

@@ -11,11 +11,12 @@
import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.libraryBlocks.colour');
import type {BlockDefinition} from '../core/blocks.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_colour.js';
/**
* A dictionary of the block definitions provided by this module.
*/
@@ -52,7 +53,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'colour_rgb',
'message0':
'%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3',
'%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3',
'args0': [
{
'type': 'input_value',
@@ -82,8 +83,9 @@ export const blocks = createBlockDefinitionsFromJsonArray([
// 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',
'message0':
'%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' +
'%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3',
'args0': [
{
'type': 'input_value',

View File

@@ -18,17 +18,17 @@ import {Align} from '../core/inputs/input.js';
import type {Block} from '../core/block.js';
import type {Connection} from '../core/connection.js';
import type {BlockSvg} from '../core/block_svg.js';
import type {BlockDefinition} from '../core/blocks.js';
import {ConnectionType} from '../core/connection_type.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import {Msg} from '../core/msg.js';
import {Mutator} from '../core/mutator.js';
import type {Workspace} from '../core/workspace.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_dropdown.js';
import {ValueInput} from '../core/inputs/value_input.js';
/**
* A dictionary of the block definitions provided by this module.
*/
@@ -117,9 +117,8 @@ export const blocks = createBlockDefinitionsFromJsonArray([
},
]);
/** Type of a 'lists_create_with' block. */
type CreateWithBlock = Block&ListCreateWithMixin;
type CreateWithBlock = Block & ListCreateWithMixin;
interface ListCreateWithMixin extends ListCreateWithMixinType {
itemCount_: number;
}
@@ -129,22 +128,22 @@ const LISTS_CREATE_WITH = {
/**
* Block for creating a list with any number of elements of any type.
*/
init: function(this: CreateWithBlock) {
init: function (this: CreateWithBlock) {
this.setHelpUrl(Msg['LISTS_CREATE_WITH_HELPURL']);
this.setStyle('list_blocks');
this.itemCount_ = 3;
this.updateShape_();
this.setOutput(true, 'Array');
this.setMutator(new Mutator(
['lists_create_with_item'],
this as unknown as BlockSvg)); // BUG(#6905)
this.setMutator(
new Mutator(['lists_create_with_item'], this as unknown as BlockSvg)
); // BUG(#6905)
this.setTooltip(Msg['LISTS_CREATE_WITH_TOOLTIP']);
},
/**
* Create XML to represent list inputs.
* Backwards compatible serialization implementation.
*/
mutationToDom: function(this: CreateWithBlock): Element {
mutationToDom: function (this: CreateWithBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('items', String(this.itemCount_));
return container;
@@ -155,7 +154,7 @@ const LISTS_CREATE_WITH = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: CreateWithBlock, xmlElement: Element) {
domToMutation: function (this: CreateWithBlock, xmlElement: Element) {
const items = xmlElement.getAttribute('items');
if (!items) throw new TypeError('element did not have items');
this.itemCount_ = parseInt(items, 10);
@@ -166,7 +165,7 @@ const LISTS_CREATE_WITH = {
*
* @return The state of this block, ie the item count.
*/
saveExtraState: function(this: CreateWithBlock): {itemCount: number} {
saveExtraState: function (this: CreateWithBlock): {itemCount: number} {
return {
'itemCount': this.itemCount_,
};
@@ -176,7 +175,7 @@ const LISTS_CREATE_WITH = {
*
* @param state The state to apply to this block, ie the item count.
*/
loadExtraState: function(this: CreateWithBlock, state: AnyDuringMigration) {
loadExtraState: function (this: CreateWithBlock, state: AnyDuringMigration) {
this.itemCount_ = state['itemCount'];
this.updateShape_();
},
@@ -186,32 +185,37 @@ const LISTS_CREATE_WITH = {
* @param workspace Mutator's workspace.
* @return Root block in mutator.
*/
decompose: function(this: CreateWithBlock, workspace: Workspace):
ContainerBlock {
const containerBlock =
workspace.newBlock('lists_create_with_container') as ContainerBlock;
(containerBlock as BlockSvg).initSvg();
let connection = containerBlock.getInput('STACK')!.connection;
for (let i = 0; i < this.itemCount_; i++) {
const itemBlock =
workspace.newBlock('lists_create_with_item') as ItemBlock;
(itemBlock as BlockSvg).initSvg();
if (!itemBlock.previousConnection) {
throw new Error('itemBlock has no previousConnection');
}
connection!.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
return containerBlock;
},
decompose: function (
this: CreateWithBlock,
workspace: Workspace
): ContainerBlock {
const containerBlock = workspace.newBlock(
'lists_create_with_container'
) as ContainerBlock;
(containerBlock as BlockSvg).initSvg();
let connection = containerBlock.getInput('STACK')!.connection;
for (let i = 0; i < this.itemCount_; i++) {
const itemBlock = workspace.newBlock(
'lists_create_with_item'
) as ItemBlock;
(itemBlock as BlockSvg).initSvg();
if (!itemBlock.previousConnection) {
throw new Error('itemBlock has no previousConnection');
}
connection!.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
}
return containerBlock;
},
/**
* Reconfigure this block based on the mutator dialog's components.
*
* @param containerBlock Root block in mutator.
*/
compose: function(this: CreateWithBlock, containerBlock: Block) {
let itemBlock: ItemBlock|null =
containerBlock.getInputTargetBlock('STACK') as ItemBlock;
compose: function (this: CreateWithBlock, containerBlock: Block) {
let itemBlock: ItemBlock | null = containerBlock.getInputTargetBlock(
'STACK'
) as ItemBlock;
// Count number of inputs.
const connections: Connection[] = [];
while (itemBlock) {
@@ -241,9 +245,10 @@ const LISTS_CREATE_WITH = {
*
* @param containerBlock Root block in mutator.
*/
saveConnections: function(this: CreateWithBlock, containerBlock: Block) {
let itemBlock: ItemBlock|null =
containerBlock.getInputTargetBlock('STACK') as ItemBlock;
saveConnections: function (this: CreateWithBlock, containerBlock: Block) {
let itemBlock: ItemBlock | null = containerBlock.getInputTargetBlock(
'STACK'
) as ItemBlock;
let i = 0;
while (itemBlock) {
if (itemBlock.isInsertionMarker()) {
@@ -251,8 +256,8 @@ const LISTS_CREATE_WITH = {
continue;
}
const input = this.getInput('ADD' + i);
itemBlock.valueConnection_ =
input?.connection!.targetConnection as Connection;
itemBlock.valueConnection_ = input?.connection!
.targetConnection as Connection;
itemBlock = itemBlock.getNextBlock() as ItemBlock | null;
i++;
}
@@ -260,12 +265,13 @@ const LISTS_CREATE_WITH = {
/**
* Modify this block to have the correct number of inputs.
*/
updateShape_: function(this: CreateWithBlock) {
updateShape_: function (this: CreateWithBlock) {
if (this.itemCount_ && this.getInput('EMPTY')) {
this.removeInput('EMPTY');
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
this.appendDummyInput('EMPTY').appendField(
Msg['LISTS_CREATE_EMPTY_TITLE']);
Msg['LISTS_CREATE_EMPTY_TITLE']
);
}
// Add new inputs.
for (let i = 0; i < this.itemCount_; i++) {
@@ -285,7 +291,7 @@ const LISTS_CREATE_WITH = {
blocks['lists_create_with'] = LISTS_CREATE_WITH;
/** Type for a 'lists_create_with_container' block. */
type ContainerBlock = Block&ContainerMutator;
type ContainerBlock = Block & ContainerMutator;
interface ContainerMutator extends ContainerMutatorType {}
type ContainerMutatorType = typeof LISTS_CREATE_WITH_CONTAINER;
@@ -293,10 +299,11 @@ const LISTS_CREATE_WITH_CONTAINER = {
/**
* Mutator block for list container.
*/
init: function(this: ContainerBlock) {
init: function (this: ContainerBlock) {
this.setStyle('list_blocks');
this.appendDummyInput().appendField(
Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']);
Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']
);
this.appendStatementInput('STACK');
this.setTooltip(Msg['LISTS_CREATE_WITH_CONTAINER_TOOLTIP']);
this.contextMenu = false;
@@ -305,7 +312,7 @@ const LISTS_CREATE_WITH_CONTAINER = {
blocks['lists_create_with_container'] = LISTS_CREATE_WITH_CONTAINER;
/** Type for a 'lists_create_with_item' block. */
type ItemBlock = Block&ItemMutator;
type ItemBlock = Block & ItemMutator;
interface ItemMutator extends ItemMutatorType {
valueConnection_?: Connection;
}
@@ -315,7 +322,7 @@ const LISTS_CREATE_WITH_ITEM = {
/**
* Mutator block for adding items.
*/
init: function(this: ItemBlock) {
init: function (this: ItemBlock) {
this.setStyle('list_blocks');
this.appendDummyInput().appendField(Msg['LISTS_CREATE_WITH_ITEM_TITLE']);
this.setPreviousStatement(true);
@@ -327,7 +334,7 @@ const LISTS_CREATE_WITH_ITEM = {
blocks['lists_create_with_item'] = LISTS_CREATE_WITH_ITEM;
/** Type for a 'lists_indexOf' block. */
type IndexOfBlock = Block&IndexOfMutator;
type IndexOfBlock = Block & IndexOfMutator;
interface IndexOfMutator extends IndexOfMutatorType {}
type IndexOfMutatorType = typeof LISTS_INDEXOF;
@@ -335,7 +342,7 @@ const LISTS_INDEXOF = {
/**
* Block for finding an item in the list.
*/
init: function(this: IndexOfBlock) {
init: function (this: IndexOfBlock) {
const OPERATORS = [
[Msg['LISTS_INDEX_OF_FIRST'], 'FIRST'],
[Msg['LISTS_INDEX_OF_LAST'], 'LAST'],
@@ -343,8 +350,9 @@ const LISTS_INDEXOF = {
this.setHelpUrl(Msg['LISTS_INDEX_OF_HELPURL']);
this.setStyle('list_blocks');
this.setOutput(true, 'Number');
this.appendValueInput('VALUE').setCheck('Array').appendField(
Msg['LISTS_INDEX_OF_INPUT_IN_LIST']);
this.appendValueInput('VALUE')
.setCheck('Array')
.appendField(Msg['LISTS_INDEX_OF_INPUT_IN_LIST']);
const operatorsDropdown = fieldRegistry.fromJson({
type: 'field_dropdown',
options: OPERATORS,
@@ -352,18 +360,18 @@ const LISTS_INDEXOF = {
if (!operatorsDropdown) throw new Error('field_dropdown not found');
this.appendValueInput('FIND').appendField(operatorsDropdown, 'END');
this.setInputsInline(true);
// Assign 'this' to a variable for use in the tooltip closure below.
const thisBlock = this;
this.setTooltip(function() {
this.setTooltip(() => {
return Msg['LISTS_INDEX_OF_TOOLTIP'].replace(
'%1', thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
'%1',
this.workspace.options.oneBasedIndex ? '0' : '-1'
);
});
},
};
blocks['lists_indexOf'] = LISTS_INDEXOF;
/** Type for a 'lists_getIndex' block. */
type GetIndexBlock = Block&GetIndexMutator;
type GetIndexBlock = Block & GetIndexMutator;
interface GetIndexMutator extends GetIndexMutatorType {
WHERE_OPTIONS: Array<[string, string]>;
}
@@ -373,7 +381,7 @@ const LISTS_GETINDEX = {
/**
* Block for getting element at index.
*/
init: function(this: GetIndexBlock) {
init: function (this: GetIndexBlock) {
const MODE = [
[Msg['LISTS_GET_INDEX_GET'], 'GET'],
[Msg['LISTS_GET_INDEX_GET_REMOVE'], 'GET_REMOVE'],
@@ -393,18 +401,19 @@ const LISTS_GETINDEX = {
options: MODE,
}) as FieldDropdown;
modeMenu.setValidator(
/** @param value The input value. */
function(this: FieldDropdown, value: string) {
const isStatement = (value === 'REMOVE');
(this.getSourceBlock() as GetIndexBlock)
.updateStatement_(isStatement);
return undefined;
});
this.appendValueInput('VALUE').setCheck('Array').appendField(
Msg['LISTS_GET_INDEX_INPUT_IN_LIST']);
/** @param value The input value. */
function (this: FieldDropdown, value: string) {
const isStatement = value === 'REMOVE';
(this.getSourceBlock() as GetIndexBlock).updateStatement_(isStatement);
return undefined;
}
);
this.appendValueInput('VALUE')
.setCheck('Array')
.appendField(Msg['LISTS_GET_INDEX_INPUT_IN_LIST']);
this.appendDummyInput()
.appendField(modeMenu, 'MODE')
.appendField('', 'SPACE');
.appendField(modeMenu, 'MODE')
.appendField('', 'SPACE');
this.appendDummyInput('AT');
if (Msg['LISTS_GET_INDEX_TAIL']) {
this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_INDEX_TAIL']);
@@ -412,11 +421,9 @@ const LISTS_GETINDEX = {
this.setInputsInline(true);
this.setOutput(true);
this.updateAt_(true);
// Assign 'this' to a variable for use in the tooltip closure below.
const thisBlock = this;
this.setTooltip(function() {
const mode = thisBlock.getFieldValue('MODE');
const where = thisBlock.getFieldValue('WHERE');
this.setTooltip(() => {
const mode = this.getFieldValue('MODE');
const where = this.getFieldValue('WHERE');
let tooltip = '';
switch (mode + ' ' + where) {
case 'GET FROM_START':
@@ -460,12 +467,13 @@ const LISTS_GETINDEX = {
break;
}
if (where === 'FROM_START' || where === 'FROM_END') {
const msg = (where === 'FROM_START') ?
Msg['LISTS_INDEX_FROM_START_TOOLTIP'] :
Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
tooltip += ' ' +
msg.replace(
'%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
const msg =
where === 'FROM_START'
? Msg['LISTS_INDEX_FROM_START_TOOLTIP']
: Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
tooltip +=
' ' +
msg.replace('%1', this.workspace.options.oneBasedIndex ? '#1' : '#0');
}
return tooltip;
});
@@ -476,7 +484,7 @@ const LISTS_GETINDEX = {
*
* @return XML storage element.
*/
mutationToDom: function(this: GetIndexBlock): Element {
mutationToDom: function (this: GetIndexBlock): Element {
const container = xmlUtils.createElement('mutation');
const isStatement = !this.outputConnection;
container.setAttribute('statement', String(isStatement));
@@ -489,12 +497,12 @@ const LISTS_GETINDEX = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: GetIndexBlock, xmlElement: Element) {
domToMutation: function (this: GetIndexBlock, xmlElement: Element) {
// Note: Until January 2013 this block did not have mutations,
// so 'statement' defaults to false and 'at' defaults to true.
const isStatement = (xmlElement.getAttribute('statement') === 'true');
const isStatement = xmlElement.getAttribute('statement') === 'true';
this.updateStatement_(isStatement);
const isAt = (xmlElement.getAttribute('at') !== 'false');
const isAt = xmlElement.getAttribute('at') !== 'false';
this.updateAt_(isAt);
},
/**
@@ -503,9 +511,9 @@ const LISTS_GETINDEX = {
*
* @return The state of this block, ie whether it's a statement.
*/
saveExtraState: function(this: GetIndexBlock): ({
isStatement: boolean
} | null) {
saveExtraState: function (this: GetIndexBlock): {
isStatement: boolean;
} | null {
if (!this.outputConnection) {
return {
isStatement: true,
@@ -520,7 +528,7 @@ const LISTS_GETINDEX = {
* @param state The state to apply to this block, ie whether it's a
* statement.
*/
loadExtraState: function(this: GetIndexBlock, state: AnyDuringMigration) {
loadExtraState: function (this: GetIndexBlock, state: AnyDuringMigration) {
if (state['isStatement']) {
this.updateStatement_(true);
} else if (typeof state === 'string') {
@@ -535,7 +543,7 @@ const LISTS_GETINDEX = {
* @param newStatement True if the block should be a statement.
* False if the block should be a value.
*/
updateStatement_: function(this: GetIndexBlock, newStatement: boolean) {
updateStatement_: function (this: GetIndexBlock, newStatement: boolean) {
const oldStatement = !this.outputConnection;
if (newStatement !== oldStatement) {
// TODO(#6920): The .unplug only has one parameter.
@@ -556,7 +564,7 @@ const LISTS_GETINDEX = {
*
* @param isAt True if the input should exist.
*/
updateAt_: function(this: GetIndexBlock, isAt: boolean) {
updateAt_: function (this: GetIndexBlock, isAt: boolean) {
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT');
this.removeInput('ORDINAL', true);
@@ -565,7 +573,8 @@ const LISTS_GETINDEX = {
this.appendValueInput('AT').setCheck('Number');
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL').appendField(
Msg['ORDINAL_NUMBER_SUFFIX']);
Msg['ORDINAL_NUMBER_SUFFIX']
);
}
} else {
this.appendDummyInput('AT');
@@ -575,24 +584,25 @@ const LISTS_GETINDEX = {
options: this.WHERE_OPTIONS,
}) as FieldDropdown;
menu.setValidator(
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function(this: FieldDropdown, value: string) {
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as GetIndexBlock;
block.updateAt_(newAt);
// This menu has been destroyed and replaced. Update the
// replacement.
block.setFieldValue(value, 'WHERE');
return null;
}
return undefined;
});
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function (this: FieldDropdown, value: string) {
const newAt = value === 'FROM_START' || value === 'FROM_END';
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as GetIndexBlock;
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 (Msg['LISTS_GET_INDEX_TAIL']) {
this.moveInputBefore('TAIL', null);
@@ -602,7 +612,7 @@ const LISTS_GETINDEX = {
blocks['lists_getIndex'] = LISTS_GETINDEX;
/** Type for a 'lists_setIndex' block. */
type SetIndexBlock = Block&SetIndexMutator;
type SetIndexBlock = Block & SetIndexMutator;
interface SetIndexMutator extends SetIndexMutatorType {
WHERE_OPTIONS: Array<[string, string]>;
}
@@ -612,7 +622,7 @@ const LISTS_SETINDEX = {
/**
* Block for setting the element at index.
*/
init: function(this: SetIndexBlock) {
init: function (this: SetIndexBlock) {
const MODE = [
[Msg['LISTS_SET_INDEX_SET'], 'SET'],
[Msg['LISTS_SET_INDEX_INSERT'], 'INSERT'],
@@ -626,15 +636,16 @@ const LISTS_SETINDEX = {
];
this.setHelpUrl(Msg['LISTS_SET_INDEX_HELPURL']);
this.setStyle('list_blocks');
this.appendValueInput('LIST').setCheck('Array').appendField(
Msg['LISTS_SET_INDEX_INPUT_IN_LIST']);
this.appendValueInput('LIST')
.setCheck('Array')
.appendField(Msg['LISTS_SET_INDEX_INPUT_IN_LIST']);
const operationDropdown = fieldRegistry.fromJson({
type: 'field_dropdown',
options: MODE,
}) as FieldDropdown;
this.appendDummyInput()
.appendField(operationDropdown, 'MODE')
.appendField('', 'SPACE');
.appendField(operationDropdown, 'MODE')
.appendField('', 'SPACE');
this.appendDummyInput('AT');
this.appendValueInput('TO').appendField(Msg['LISTS_SET_INDEX_INPUT_TO']);
this.setInputsInline(true);
@@ -642,11 +653,9 @@ const LISTS_SETINDEX = {
this.setNextStatement(true);
this.setTooltip(Msg['LISTS_SET_INDEX_TOOLTIP']);
this.updateAt_(true);
// Assign 'this' to a variable for use in the tooltip closure below.
const thisBlock = this;
this.setTooltip(function() {
const mode = thisBlock.getFieldValue('MODE');
const where = thisBlock.getFieldValue('WHERE');
this.setTooltip(() => {
const mode = this.getFieldValue('MODE');
const where = this.getFieldValue('WHERE');
let tooltip = '';
switch (mode + ' ' + where) {
case 'SET FROM_START':
@@ -677,9 +686,12 @@ const LISTS_SETINDEX = {
break;
}
if (where === 'FROM_START' || where === 'FROM_END') {
tooltip += ' ' +
Msg['LISTS_INDEX_FROM_START_TOOLTIP'].replace(
'%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
tooltip +=
' ' +
Msg['LISTS_INDEX_FROM_START_TOOLTIP'].replace(
'%1',
this.workspace.options.oneBasedIndex ? '#1' : '#0'
);
}
return tooltip;
});
@@ -689,7 +701,7 @@ const LISTS_SETINDEX = {
*
* @return XML storage element.
*/
mutationToDom: function(this: SetIndexBlock): Element {
mutationToDom: function (this: SetIndexBlock): Element {
const container = xmlUtils.createElement('mutation');
const isAt = this.getInput('AT') instanceof ValueInput;
container.setAttribute('at', String(isAt));
@@ -700,10 +712,10 @@ const LISTS_SETINDEX = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: SetIndexBlock, xmlElement: Element) {
domToMutation: function (this: SetIndexBlock, xmlElement: Element) {
// Note: Until January 2013 this block did not have mutations,
// so 'at' defaults to true.
const isAt = (xmlElement.getAttribute('at') !== 'false');
const isAt = xmlElement.getAttribute('at') !== 'false';
this.updateAt_(isAt);
},
@@ -715,7 +727,7 @@ const LISTS_SETINDEX = {
*
* @return The state of this block.
*/
saveExtraState: function(this: SetIndexBlock): null {
saveExtraState: function (this: SetIndexBlock): null {
return null;
},
@@ -724,14 +736,14 @@ const LISTS_SETINDEX = {
* No extra state is needed or expected as it is already encoded in the
* dropdown values.
*/
loadExtraState: function(this: SetIndexBlock) {},
loadExtraState: function (this: SetIndexBlock) {},
/**
* Create or delete an input for the numeric index.
*
* @param isAt True if the input should exist.
*/
updateAt_: function(this: SetIndexBlock, isAt: boolean) {
updateAt_: function (this: SetIndexBlock, isAt: boolean) {
// Destroy old 'AT' and 'ORDINAL' input.
this.removeInput('AT');
this.removeInput('ORDINAL', true);
@@ -740,7 +752,8 @@ const LISTS_SETINDEX = {
this.appendValueInput('AT').setCheck('Number');
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL').appendField(
Msg['ORDINAL_NUMBER_SUFFIX']);
Msg['ORDINAL_NUMBER_SUFFIX']
);
}
} else {
this.appendDummyInput('AT');
@@ -750,24 +763,25 @@ const LISTS_SETINDEX = {
options: this.WHERE_OPTIONS,
}) as FieldDropdown;
menu.setValidator(
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function(this: FieldDropdown, value: string) {
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as SetIndexBlock;
block.updateAt_(newAt);
// This menu has been destroyed and replaced. Update the
// replacement.
block.setFieldValue(value, 'WHERE');
return null;
}
return undefined;
});
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function (this: FieldDropdown, value: string) {
const newAt = value === 'FROM_START' || value === 'FROM_END';
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as SetIndexBlock;
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');
@@ -779,7 +793,7 @@ const LISTS_SETINDEX = {
blocks['lists_setIndex'] = LISTS_SETINDEX;
/** Type for a 'lists_getSublist' block. */
type GetSublistBlock = Block&GetSublistMutator;
type GetSublistBlock = Block & GetSublistMutator;
interface GetSublistMutator extends GetSublistMutatorType {
WHERE_OPTIONS_1: Array<[string, string]>;
WHERE_OPTIONS_2: Array<[string, string]>;
@@ -790,7 +804,7 @@ const LISTS_GETSUBLIST = {
/**
* Block for getting sublist.
*/
init: function(this: GetSublistBlock) {
init: function (this: GetSublistBlock) {
this['WHERE_OPTIONS_1'] = [
[Msg['LISTS_GET_SUBLIST_START_FROM_START'], 'FROM_START'],
[Msg['LISTS_GET_SUBLIST_START_FROM_END'], 'FROM_END'],
@@ -803,8 +817,9 @@ const LISTS_GETSUBLIST = {
];
this.setHelpUrl(Msg['LISTS_GET_SUBLIST_HELPURL']);
this.setStyle('list_blocks');
this.appendValueInput('LIST').setCheck('Array').appendField(
Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']);
this.appendValueInput('LIST')
.setCheck('Array')
.appendField(Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']);
this.appendDummyInput('AT1');
this.appendDummyInput('AT2');
if (Msg['LISTS_GET_SUBLIST_TAIL']) {
@@ -821,7 +836,7 @@ const LISTS_GETSUBLIST = {
*
* @return XML storage element.
*/
mutationToDom: function(this: GetSublistBlock): Element {
mutationToDom: function (this: GetSublistBlock): Element {
const container = xmlUtils.createElement('mutation');
const isAt1 = this.getInput('AT1') instanceof ValueInput;
container.setAttribute('at1', String(isAt1));
@@ -834,9 +849,9 @@ const LISTS_GETSUBLIST = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: GetSublistBlock, xmlElement: Element) {
const isAt1 = (xmlElement.getAttribute('at1') === 'true');
const isAt2 = (xmlElement.getAttribute('at2') === 'true');
domToMutation: function (this: GetSublistBlock, xmlElement: Element) {
const isAt1 = xmlElement.getAttribute('at1') === 'true';
const isAt2 = xmlElement.getAttribute('at2') === 'true';
this.updateAt_(1, isAt1);
this.updateAt_(2, isAt2);
},
@@ -849,7 +864,7 @@ const LISTS_GETSUBLIST = {
*
* @return The state of this block.
*/
saveExtraState: function(this: GetSublistBlock): null {
saveExtraState: function (this: GetSublistBlock): null {
return null;
},
@@ -858,7 +873,7 @@ const LISTS_GETSUBLIST = {
* No extra state is needed or expected as it is already encoded in the
* dropdown values.
*/
loadExtraState: function(this: GetSublistBlock) {},
loadExtraState: function (this: GetSublistBlock) {},
/**
* Create or delete an input for a numeric index.
@@ -867,7 +882,7 @@ const LISTS_GETSUBLIST = {
* @param n Specify first or second input (1 or 2).
* @param isAt True if the input should exist.
*/
updateAt_: function(this: GetSublistBlock, n: 1|2, isAt: boolean) {
updateAt_: function (this: GetSublistBlock, n: 1 | 2, isAt: boolean) {
// Create or delete an input for the numeric index.
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT' + n);
@@ -876,37 +891,37 @@ const LISTS_GETSUBLIST = {
if (isAt) {
this.appendValueInput('AT' + n).setCheck('Number');
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL' + n)
.appendField(Msg['ORDINAL_NUMBER_SUFFIX']);
this.appendDummyInput('ORDINAL' + n).appendField(
Msg['ORDINAL_NUMBER_SUFFIX']
);
}
} else {
this.appendDummyInput('AT' + n);
}
const menu = fieldRegistry.fromJson({
type: 'field_dropdown',
// TODO(#6920): Rewrite this so that clang-format doesn't make such an
// awful unreadable mess of it.
options: this
[('WHERE_OPTIONS_' + n) as ('WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2')],
options:
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
}) as FieldDropdown;
menu.setValidator(
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function(this: FieldDropdown, value: string) {
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as GetSublistBlock;
block.updateAt_(n, newAt);
// This menu has been destroyed and replaced.
// Update the replacement.
block.setFieldValue(value, 'WHERE' + n);
return null;
}
});
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function (this: FieldDropdown, value: string) {
const newAt = value === 'FROM_START' || value === 'FROM_END';
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as GetSublistBlock;
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');
@@ -921,13 +936,13 @@ const LISTS_GETSUBLIST = {
};
blocks['lists_getSublist'] = LISTS_GETSUBLIST;
type SortBlock = Block|typeof blocks['lists_sort'];
type SortBlock = Block | (typeof blocks)['lists_sort'];
blocks['lists_sort'] = {
/**
* Block for sorting a list.
*/
init: function(this: SortBlock) {
init: function (this: SortBlock) {
this.jsonInit({
'message0': '%{BKY_LISTS_SORT_TITLE}',
'args0': [
@@ -962,15 +977,13 @@ blocks['lists_sort'] = {
},
};
type SplitBlock = Block|typeof blocks['lists_split'];
type SplitBlock = Block | (typeof blocks)['lists_split'];
blocks['lists_split'] = {
/**
* Block for splitting text into a list, or joining a list into text.
*/
init: function(this: SplitBlock) {
// Assign 'this' to a variable for use in the closures below.
const thisBlock = this;
init: function (this: SplitBlock) {
const dropdown = fieldRegistry.fromJson({
type: 'field_dropdown',
options: [
@@ -979,19 +992,21 @@ blocks['lists_split'] = {
],
});
if (!dropdown) throw new Error('field_dropdown not found');
dropdown.setValidator(function(newMode) {
thisBlock.updateType_(newMode);
dropdown.setValidator((newMode) => {
this.updateType_(newMode);
});
this.setHelpUrl(Msg['LISTS_SPLIT_HELPURL']);
this.setStyle('list_blocks');
this.appendValueInput('INPUT').setCheck('String').appendField(
dropdown, 'MODE');
this.appendValueInput('DELIM').setCheck('String').appendField(
Msg['LISTS_SPLIT_WITH_DELIMITER']);
this.appendValueInput('INPUT')
.setCheck('String')
.appendField(dropdown, 'MODE');
this.appendValueInput('DELIM')
.setCheck('String')
.appendField(Msg['LISTS_SPLIT_WITH_DELIMITER']);
this.setInputsInline(true);
this.setOutput(true, 'Array');
this.setTooltip(function() {
const mode = thisBlock.getFieldValue('MODE');
this.setTooltip(() => {
const mode = this.getFieldValue('MODE');
if (mode === 'SPLIT') {
return Msg['LISTS_SPLIT_TOOLTIP_SPLIT'];
} else if (mode === 'JOIN') {
@@ -1005,7 +1020,7 @@ blocks['lists_split'] = {
*
* @param newMode Either 'SPLIT' or 'JOIN'.
*/
updateType_: function(this: SplitBlock, newMode: string) {
updateType_: function (this: SplitBlock, newMode: string) {
const mode = this.getFieldValue('MODE');
if (mode !== newMode) {
const inputConnection = this.getInput('INPUT')!.connection;
@@ -1034,7 +1049,7 @@ blocks['lists_split'] = {
*
* @return XML storage element.
*/
mutationToDom: function(this: SplitBlock): Element {
mutationToDom: function (this: SplitBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('mode', this.getFieldValue('MODE'));
return container;
@@ -1044,7 +1059,7 @@ blocks['lists_split'] = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: SplitBlock, xmlElement: Element) {
domToMutation: function (this: SplitBlock, xmlElement: Element) {
this.updateType_(xmlElement.getAttribute('mode'));
},
@@ -1056,7 +1071,7 @@ blocks['lists_split'] = {
*
* @return The state of this block.
*/
saveExtraState: function(this: SplitBlock): null {
saveExtraState: function (this: SplitBlock): null {
return null;
},
@@ -1065,7 +1080,7 @@ blocks['lists_split'] = {
* No extra state is needed or expected as it is already encoded in the
* dropdown values.
*/
loadExtraState: function(this: SplitBlock) {},
loadExtraState: function (this: SplitBlock) {},
};
// Register provided blocks.

View File

@@ -14,13 +14,19 @@ goog.declareModuleId('Blockly.libraryBlocks.loops');
import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import type {Block} from '../core/block.js';
import * as ContextMenu from '../core/contextmenu.js';
import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import * as Events from '../core/events/events.js';
import * as Extensions from '../core/extensions.js';
import * as Variables from '../core/variables.js';
import * as xmlUtils from '../core/utils/xml.js';
import {Msg} from '../core/msg.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_dropdown.js';
import '../core/field_label.js';
import '../core/field_number.js';
@@ -29,7 +35,6 @@ import '../core/warning.js';
import {FieldVariable} from '../core/field_variable.js';
import {WorkspaceSvg} from '../core/workspace_svg.js';
/**
* A dictionary of the block definitions provided by this module.
*/
@@ -38,16 +43,20 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'controls_repeat_ext',
'message0': '%{BKY_CONTROLS_REPEAT_TITLE}',
'args0': [{
'type': 'input_value',
'name': 'TIMES',
'check': 'Number',
}],
'args0': [
{
'type': 'input_value',
'name': 'TIMES',
'check': 'Number',
},
],
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
'args1': [{
'type': 'input_statement',
'name': 'DO',
}],
'args1': [
{
'type': 'input_statement',
'name': 'DO',
},
],
'previousStatement': null,
'nextStatement': null,
'style': 'loop_blocks',
@@ -59,18 +68,22 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'controls_repeat',
'message0': '%{BKY_CONTROLS_REPEAT_TITLE}',
'args0': [{
'type': 'field_number',
'name': 'TIMES',
'value': 10,
'min': 0,
'precision': 1,
}],
'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',
}],
'args1': [
{
'type': 'input_statement',
'name': 'DO',
},
],
'previousStatement': null,
'nextStatement': null,
'style': 'loop_blocks',
@@ -97,10 +110,12 @@ export const blocks = createBlockDefinitionsFromJsonArray([
},
],
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
'args1': [{
'type': 'input_statement',
'name': 'DO',
}],
'args1': [
{
'type': 'input_statement',
'name': 'DO',
},
],
'previousStatement': null,
'nextStatement': null,
'style': 'loop_blocks',
@@ -137,19 +152,18 @@ export const blocks = createBlockDefinitionsFromJsonArray([
},
],
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
'args1': [{
'type': 'input_statement',
'name': 'DO',
}],
'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',
],
'extensions': ['contextMenu_newGetVariableBlock', 'controls_for_tooltip'],
},
// Block for 'for each' loop.
{
@@ -168,10 +182,12 @@ export const blocks = createBlockDefinitionsFromJsonArray([
},
],
'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
'args1': [{
'type': 'input_statement',
'name': 'DO',
}],
'args1': [
{
'type': 'input_statement',
'name': 'DO',
},
],
'previousStatement': null,
'nextStatement': null,
'style': 'loop_blocks',
@@ -185,22 +201,21 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'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'],
],
}],
'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}',
'suppressPrefixSuffix': true,
'extensions': [
'controls_flow_tooltip',
'controls_flow_in_loop_check',
],
'extensions': ['controls_flow_tooltip', 'controls_flow_in_loop_check'],
},
]);
@@ -215,8 +230,9 @@ const WHILE_UNTIL_TOOLTIPS = {
};
Extensions.register(
'controls_whileUntil_tooltip',
Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS));
'controls_whileUntil_tooltip',
Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS)
);
/**
* Tooltips for the 'controls_flow_statements' block, keyed by FLOW value.
@@ -229,14 +245,15 @@ const BREAK_CONTINUE_TOOLTIPS = {
};
Extensions.register(
'controls_flow_tooltip',
Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS));
'controls_flow_tooltip',
Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS)
);
/** Type of a block that has CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN */
type CustomContextMenuBlock = Block&CustomContextMenuMixin;
type CustomContextMenuBlock = Block & CustomContextMenuMixin;
interface CustomContextMenuMixin extends CustomContextMenuMixinType {}
type CustomContextMenuMixinType =
typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN;
typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN;
/**
* Mixin to add a context menu item to create a 'variables_get' block.
@@ -249,9 +266,10 @@ const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
*
* @param options List of menu options to add to.
*/
customContextMenu: function(
this: CustomContextMenuBlock,
options: Array<ContextMenuOption|LegacyContextMenuOption>) {
customContextMenu: function (
this: CustomContextMenuBlock,
options: Array<ContextMenuOption | LegacyContextMenuOption>
) {
if (this.isInFlyout) {
return;
}
@@ -267,24 +285,26 @@ const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = {
options.push({
enabled: true,
text: Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName),
callback: ContextMenu.callbackFactory(this, xmlBlock)
callback: ContextMenu.callbackFactory(this, xmlBlock),
});
}
},
};
Extensions.registerMixin(
'contextMenu_newGetVariableBlock',
CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);
'contextMenu_newGetVariableBlock',
CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN
);
Extensions.register(
'controls_for_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR'));
'controls_for_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR')
);
Extensions.register(
'controls_forEach_tooltip',
Extensions.buildTooltipWithFieldText(
'%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR'));
'controls_forEach_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR')
);
/**
* List of block types that are loops and thus do not need warnings.
@@ -310,7 +330,7 @@ export const loopTypes: Set<string> = new Set([
]);
/** Type of a block that has CONTROL_FLOW_IN_LOOP_CHECK_MIXIN */
type ControlFlowInLoopBlock = Block&ControlFlowInLoopMixin;
type ControlFlowInLoopBlock = Block & ControlFlowInLoopMixin;
interface ControlFlowInLoopMixin extends ControlFlowInLoopMixinType {}
type ControlFlowInLoopMixinType = typeof CONTROL_FLOW_IN_LOOP_CHECK_MIXIN;
@@ -324,23 +344,23 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
*
* @returns The nearest surrounding loop, or null if none.
*/
getSurroundLoop: function(this: ControlFlowInLoopBlock): Block |
null {
let block: Block|null = this;
do {
if (loopTypes.has(block.type)) {
return block;
}
block = block.getSurroundParent();
} while (block);
return null;
},
getSurroundLoop: function (this: ControlFlowInLoopBlock): Block | null {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let block: Block | null = this;
do {
if (loopTypes.has(block.type)) {
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.
*/
onchange: function(this: ControlFlowInLoopBlock, e: AbstractEvent) {
onchange: function (this: ControlFlowInLoopBlock, e: AbstractEvent) {
const ws = this.workspace as WorkspaceSvg;
// Don't change state if:
// * It's at the start of a drag.
@@ -350,7 +370,8 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
}
const enabled = !!this.getSurroundLoop();
this.setWarningText(
enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']);
enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']
);
if (!this.isInFlyout) {
const group = Events.getGroup();
// Makes it so the move and the disable event get undone together.
@@ -362,7 +383,9 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = {
};
Extensions.registerMixin(
'controls_flow_in_loop_check', CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);
'controls_flow_in_loop_check',
CONTROL_FLOW_IN_LOOP_CHECK_MIXIN
);
// Register provided blocks.
defineBlocks(blocks);

View File

@@ -12,18 +12,18 @@ import * as goog from '../closure/goog/goog.js';
goog.declareModuleId('Blockly.libraryBlocks.math');
import * as Extensions from '../core/extensions.js';
import type {Field} from '../core/field.js';
import type {FieldDropdown} from '../core/field_dropdown.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Block} from '../core/block.js';
import type {BlockDefinition} from '../core/blocks.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_dropdown.js';
import '../core/field_label.js';
import '../core/field_number.js';
import '../core/field_variable.js';
/**
* A dictionary of the block definitions provided by this module.
*/
@@ -32,11 +32,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'math_number',
'message0': '%1',
'args0': [{
'type': 'field_number',
'name': 'NUM',
'value': 0,
}],
'args0': [
{
'type': 'field_number',
'name': 'NUM',
'value': 0,
},
],
'output': 'Number',
'helpUrl': '%{BKY_MATH_NUMBER_HELPURL}',
'style': 'math_blocks',
@@ -428,13 +430,13 @@ const TOOLTIPS_BY_OP = {
};
Extensions.register(
'math_op_tooltip',
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP));
'math_op_tooltip',
Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP)
);
/** Type of a block that has IS_DIVISBLEBY_MUTATOR_MIXIN */
type DivisiblebyBlock = Block&DivisiblebyMixin;
type DivisiblebyBlock = Block & DivisiblebyMixin;
interface DivisiblebyMixin extends DivisiblebyMixinType {}
;
type DivisiblebyMixinType = typeof IS_DIVISIBLEBY_MUTATOR_MIXIN;
/**
@@ -452,9 +454,9 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
*
* @returns XML storage element.
*/
mutationToDom: function(this: DivisiblebyBlock): Element {
mutationToDom: function (this: DivisiblebyBlock): Element {
const container = xmlUtils.createElement('mutation');
const divisorInput = (this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY');
const divisorInput = this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY';
container.setAttribute('divisor_input', String(divisorInput));
return container;
},
@@ -464,8 +466,8 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: DivisiblebyBlock, xmlElement: Element) {
const divisorInput = (xmlElement.getAttribute('divisor_input') === 'true');
domToMutation: function (this: DivisiblebyBlock, xmlElement: Element) {
const divisorInput = xmlElement.getAttribute('divisor_input') === 'true';
this.updateShape_(divisorInput);
},
@@ -479,7 +481,7 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
*
* @param divisorInput True if this block has a divisor input.
*/
updateShape_: function(this: DivisiblebyBlock, divisorInput: boolean) {
updateShape_: function (this: DivisiblebyBlock, divisorInput: boolean) {
// Add or remove a Value Input.
const inputExists = this.getInput('DIVISOR');
if (divisorInput) {
@@ -497,29 +499,32 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = {
* can update the block shape (add/remove divisor input) based on whether
* property is "divisible by".
*/
const IS_DIVISIBLE_MUTATOR_EXTENSION = function(this: DivisiblebyBlock) {
const IS_DIVISIBLE_MUTATOR_EXTENSION = function (this: DivisiblebyBlock) {
this.getField('PROPERTY')!.setValidator(
/** @param option The selected dropdown option. */
function(this: FieldDropdown, option: string) {
const divisorInput = (option === 'DIVISIBLE_BY');
(this.getSourceBlock() as DivisiblebyBlock).updateShape_(divisorInput);
return undefined; // FieldValidators can't be void. Use option as-is.
});
/** @param option The selected dropdown option. */
function (this: FieldDropdown, option: string) {
const divisorInput = option === 'DIVISIBLE_BY';
(this.getSourceBlock() as DivisiblebyBlock).updateShape_(divisorInput);
return undefined; // FieldValidators can't be void. Use option as-is.
}
);
};
Extensions.registerMutator(
'math_is_divisibleby_mutator', IS_DIVISIBLEBY_MUTATOR_MIXIN,
IS_DIVISIBLE_MUTATOR_EXTENSION);
'math_is_divisibleby_mutator',
IS_DIVISIBLEBY_MUTATOR_MIXIN,
IS_DIVISIBLE_MUTATOR_EXTENSION
);
// Update the tooltip of 'math_change' block to reference the variable.
Extensions.register(
'math_change_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR'));
'math_change_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR')
);
/** Type of a block that has LIST_MODES_MUTATOR_MIXIN */
type ListModesBlock = Block&ListModesMixin;
type ListModesBlock = Block & ListModesMixin;
interface ListModesMixin extends ListModesMixinType {}
;
type ListModesMixinType = typeof LIST_MODES_MUTATOR_MIXIN;
/**
@@ -532,7 +537,7 @@ const LIST_MODES_MUTATOR_MIXIN = {
*
* @param newOp Either 'MODE' or some op than returns a number.
*/
updateType_: function(this: ListModesBlock, newOp: string) {
updateType_: function (this: ListModesBlock, newOp: string) {
if (newOp === 'MODE') {
this.outputConnection!.setCheck('Array');
} else {
@@ -545,7 +550,7 @@ const LIST_MODES_MUTATOR_MIXIN = {
*
* @returns XML storage element.
*/
mutationToDom: function(this: ListModesBlock): Element {
mutationToDom: function (this: ListModesBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('op', this.getFieldValue('OP'));
return container;
@@ -556,7 +561,7 @@ const LIST_MODES_MUTATOR_MIXIN = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: ListModesBlock, xmlElement: Element) {
domToMutation: function (this: ListModesBlock, xmlElement: Element) {
const op = xmlElement.getAttribute('op');
if (op === null) throw new TypeError('xmlElement had no op attribute');
this.updateType_(op);
@@ -572,17 +577,20 @@ const LIST_MODES_MUTATOR_MIXIN = {
* Extension to 'math_on_list' blocks that allows support of
* modes operation (outputs a list of numbers).
*/
const LIST_MODES_MUTATOR_EXTENSION = function(this: ListModesBlock) {
this.getField(
'OP')!.setValidator(function(this: ListModesBlock, newOp: string) {
this.updateType_(newOp);
return undefined;
}.bind(this));
const LIST_MODES_MUTATOR_EXTENSION = function (this: ListModesBlock) {
this.getField('OP')!.setValidator(
function (this: ListModesBlock, newOp: string) {
this.updateType_(newOp);
return undefined;
}.bind(this)
);
};
Extensions.registerMutator(
'math_modes_of_list_mutator', LIST_MODES_MUTATOR_MIXIN,
LIST_MODES_MUTATOR_EXTENSION);
'math_modes_of_list_mutator',
LIST_MODES_MUTATOR_MIXIN,
LIST_MODES_MUTATOR_EXTENSION
);
// Register provided blocks.
defineBlocks(blocks);

View File

@@ -18,18 +18,19 @@ import {Align} from '../core/inputs/input.js';
import type {Block} from '../core/block.js';
import type {BlockSvg} from '../core/block_svg.js';
import {Connection} from '../core/connection.js';
import {ConnectionType} from '../core/connection_type.js';
import {FieldImage} from '../core/field_image.js';
import {FieldDropdown} from '../core/field_dropdown.js';
import {FieldTextInput} from '../core/field_textinput.js';
import {Msg} from '../core/msg.js';
import {Mutator} from '../core/mutator.js';
import type {Workspace} from '../core/workspace.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_multilineinput.js';
import '../core/field_variable.js';
import { ValueInput } from '../core/inputs/value_input.js';
import {ValueInput} from '../core/inputs/value_input.js';
/**
* A dictionary of the block definitions provided by this module.
@@ -39,19 +40,18 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'text',
'message0': '%1',
'args0': [{
'type': 'field_input',
'name': 'TEXT',
'text': '',
}],
'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',
],
'extensions': ['text_quotes', 'parent_tooltip_when_inline'],
},
{
'type': 'text_multiline',
@@ -60,15 +60,15 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'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=',
'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',
@@ -83,9 +83,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'style': 'text_blocks',
'helpUrl': '%{BKY_TEXT_TEXT_HELPURL}',
'tooltip': '%{BKY_TEXT_TEXT_TOOLTIP}',
'extensions': [
'parent_tooltip_when_inline',
],
'extensions': ['parent_tooltip_when_inline'],
},
{
'type': 'text_join',
@@ -95,7 +93,6 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'helpUrl': '%{BKY_TEXT_JOIN_HELPURL}',
'tooltip': '%{BKY_TEXT_JOIN_TOOLTIP}',
'mutator': 'text_join_mutator',
},
{
'type': 'text_create_join_container',
@@ -139,9 +136,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'previousStatement': null,
'nextStatement': null,
'style': 'text_blocks',
'extensions': [
'text_append_tooltip',
],
'extensions': ['text_append_tooltip'],
},
{
'type': 'text_length',
@@ -186,14 +181,8 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'type': 'field_dropdown',
'name': 'END',
'options': [
[
'%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}',
'FIRST',
],
[
'%{BKY_TEXT_INDEXOF_OPERATOR_LAST}',
'LAST',
],
['%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}', 'FIRST'],
['%{BKY_TEXT_INDEXOF_OPERATOR_LAST}', 'LAST'],
],
},
{
@@ -206,13 +195,11 @@ export const blocks = createBlockDefinitionsFromJsonArray([
'style': 'text_blocks',
'helpUrl': '%{BKY_TEXT_INDEXOF_HELPURL}',
'inputsInline': true,
'extensions': [
'text_indexOf_tooltip',
],
'extensions': ['text_indexOf_tooltip'],
},
{
'type': 'text_charAt',
'message0': '%{BKY_TEXT_CHARAT_TITLE}', // "in text %1 %2"
'message0': '%{BKY_TEXT_CHARAT_TITLE}', // "in text %1 %2"
'args0': [
{
'type': 'input_value',
@@ -240,7 +227,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([
]);
/** Type of a 'text_get_substring' block. */
type GetSubstringBlock = Block&GetSubstringMixin;
type GetSubstringBlock = Block & GetSubstringMixin;
interface GetSubstringMixin extends GetSubstringType {
WHERE_OPTIONS_1: Array<[string, string]>;
WHERE_OPTIONS_2: Array<[string, string]>;
@@ -251,7 +238,7 @@ const GET_SUBSTRING_BLOCK = {
/**
* Block for getting substring.
*/
init: function(this: GetSubstringBlock) {
init: function (this: GetSubstringBlock) {
this['WHERE_OPTIONS_1'] = [
[Msg['TEXT_GET_SUBSTRING_START_FROM_START'], 'FROM_START'],
[Msg['TEXT_GET_SUBSTRING_START_FROM_END'], 'FROM_END'],
@@ -264,8 +251,9 @@ const GET_SUBSTRING_BLOCK = {
];
this.setHelpUrl(Msg['TEXT_GET_SUBSTRING_HELPURL']);
this.setStyle('text_blocks');
this.appendValueInput('STRING').setCheck('String').appendField(
Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']);
this.appendValueInput('STRING')
.setCheck('String')
.appendField(Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']);
this.appendDummyInput('AT1');
this.appendDummyInput('AT2');
if (Msg['TEXT_GET_SUBSTRING_TAIL']) {
@@ -283,7 +271,7 @@ const GET_SUBSTRING_BLOCK = {
*
* @returns XML storage element.
*/
mutationToDom: function(this: GetSubstringBlock): Element {
mutationToDom: function (this: GetSubstringBlock): Element {
const container = xmlUtils.createElement('mutation');
const isAt1 = this.getInput('AT1') instanceof ValueInput;
container.setAttribute('at1', `${isAt1}`);
@@ -297,9 +285,9 @@ const GET_SUBSTRING_BLOCK = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: GetSubstringBlock, xmlElement: Element) {
const isAt1 = (xmlElement.getAttribute('at1') === 'true');
const isAt2 = (xmlElement.getAttribute('at2') === 'true');
domToMutation: function (this: GetSubstringBlock, xmlElement: Element) {
const isAt1 = xmlElement.getAttribute('at1') === 'true';
const isAt2 = xmlElement.getAttribute('at2') === 'true';
this.updateAt_(1, isAt1);
this.updateAt_(2, isAt2);
},
@@ -316,7 +304,7 @@ const GET_SUBSTRING_BLOCK = {
* @param n Which input to modify (either 1 or 2).
* @param isAt True if the input includes a value connection, false otherwise.
*/
updateAt_: function(this: GetSubstringBlock, n: 1|2, isAt: boolean) {
updateAt_: function (this: GetSubstringBlock, n: 1 | 2, isAt: boolean) {
// Create or delete an input for the numeric index.
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT' + n);
@@ -325,8 +313,9 @@ const GET_SUBSTRING_BLOCK = {
if (isAt) {
this.appendValueInput('AT' + n).setCheck('Number');
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL' + n)
.appendField(Msg['ORDINAL_NUMBER_SUFFIX']);
this.appendDummyInput('ORDINAL' + n).appendField(
Msg['ORDINAL_NUMBER_SUFFIX']
);
}
} else {
this.appendDummyInput('AT' + n);
@@ -339,27 +328,28 @@ const GET_SUBSTRING_BLOCK = {
const menu = fieldRegistry.fromJson({
type: 'field_dropdown',
options:
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'],
}) as FieldDropdown;
menu.setValidator(
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function(this: FieldDropdown, value: any): null|undefined {
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as GetSubstringBlock;
block.updateAt_(n, newAt);
// This menu has been destroyed and replaced.
// Update the replacement.
block.setFieldValue(value, 'WHERE' + n);
return null;
}
return undefined;
});
/**
* @param value The input value.
* @return Null if the field has been replaced; otherwise undefined.
*/
function (this: FieldDropdown, value: any): null | undefined {
const newAt = value === 'FROM_START' || value === 'FROM_END';
// The 'isAt' variable is available due to this function being a
// closure.
if (newAt !== isAt) {
const block = this.getSourceBlock() as GetSubstringBlock;
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) {
@@ -377,7 +367,7 @@ blocks['text_changeCase'] = {
/**
* Block for changing capitalization.
*/
init: function(this: Block) {
init: function (this: Block) {
const OPERATORS = [
[Msg['TEXT_CHANGECASE_OPERATOR_UPPERCASE'], 'UPPERCASE'],
[Msg['TEXT_CHANGECASE_OPERATOR_LOWERCASE'], 'LOWERCASE'],
@@ -385,12 +375,15 @@ blocks['text_changeCase'] = {
];
this.setHelpUrl(Msg['TEXT_CHANGECASE_HELPURL']);
this.setStyle('text_blocks');
this.appendValueInput('TEXT').setCheck('String').appendField(
this.appendValueInput('TEXT')
.setCheck('String')
.appendField(
fieldRegistry.fromJson({
type: 'field_dropdown',
options: OPERATORS,
}) as FieldDropdown,
'CASE');
'CASE'
);
this.setOutput(true, 'String');
this.setTooltip(Msg['TEXT_CHANGECASE_TOOLTIP']);
},
@@ -400,7 +393,7 @@ blocks['text_trim'] = {
/**
* Block for trimming spaces.
*/
init: function(this: Block) {
init: function (this: Block) {
const OPERATORS = [
[Msg['TEXT_TRIM_OPERATOR_BOTH'], 'BOTH'],
[Msg['TEXT_TRIM_OPERATOR_LEFT'], 'LEFT'],
@@ -408,12 +401,15 @@ blocks['text_trim'] = {
];
this.setHelpUrl(Msg['TEXT_TRIM_HELPURL']);
this.setStyle('text_blocks');
this.appendValueInput('TEXT').setCheck('String').appendField(
this.appendValueInput('TEXT')
.setCheck('String')
.appendField(
fieldRegistry.fromJson({
type: 'field_dropdown',
options: OPERATORS,
}) as FieldDropdown,
'MODE');
'MODE'
);
this.setOutput(true, 'String');
this.setTooltip(Msg['TEXT_TRIM_TOOLTIP']);
},
@@ -423,7 +419,7 @@ blocks['text_print'] = {
/**
* Block for print statement.
*/
init: function(this: Block) {
init: function (this: Block) {
this.jsonInit({
'message0': Msg['TEXT_PRINT_TITLE'],
'args0': [
@@ -441,7 +437,7 @@ blocks['text_print'] = {
},
};
type PromptCommonBlock = Block&PromptCommonMixin;
type PromptCommonBlock = Block & PromptCommonMixin;
interface PromptCommonMixin extends PromptCommonType {}
type PromptCommonType = typeof PROMPT_COMMON;
@@ -455,7 +451,7 @@ const PROMPT_COMMON = {
*
* @param newOp The new output type. Should be either 'TEXT' or 'NUMBER'.
*/
updateType_: function(this: PromptCommonBlock, newOp: string) {
updateType_: function (this: PromptCommonBlock, newOp: string) {
this.outputConnection!.setCheck(newOp === 'NUMBER' ? 'Number' : 'String');
},
/**
@@ -464,7 +460,7 @@ const PROMPT_COMMON = {
*
* @returns XML storage element.
*/
mutationToDom: function(this: PromptCommonBlock): Element {
mutationToDom: function (this: PromptCommonBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('type', this.getFieldValue('TYPE'));
return container;
@@ -475,7 +471,7 @@ const PROMPT_COMMON = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: PromptCommonBlock, xmlElement: Element) {
domToMutation: function (this: PromptCommonBlock, xmlElement: Element) {
this.updateType_(xmlElement.getAttribute('type')!);
},
};
@@ -485,29 +481,27 @@ blocks['text_prompt_ext'] = {
/**
* Block for prompt function (external message).
*/
init: function(this: PromptCommonBlock) {
init: function (this: PromptCommonBlock) {
const TYPES = [
[Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'],
[Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'],
];
this.setHelpUrl(Msg['TEXT_PROMPT_HELPURL']);
this.setStyle('text_blocks');
// Assign 'this' to a variable for use in the closures below.
const thisBlock = this;
const dropdown = fieldRegistry.fromJson({
type: 'field_dropdown',
options: TYPES,
}) as FieldDropdown;
dropdown.setValidator(function(this: FieldDropdown, newOp: string) {
thisBlock.updateType_(newOp);
return undefined; // FieldValidators can't be void. Use option as-is.
dropdown.setValidator((newOp: string) => {
this.updateType_(newOp);
return undefined; // FieldValidators can't be void. Use option as-is.
});
this.appendValueInput('TEXT').appendField(dropdown, 'TYPE');
this.setOutput(true, 'String');
this.setTooltip(function() {
return (thisBlock.getFieldValue('TYPE') === 'TEXT') ?
Msg['TEXT_PROMPT_TOOLTIP_TEXT'] :
Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
this.setTooltip(() => {
return this.getFieldValue('TYPE') === 'TEXT'
? Msg['TEXT_PROMPT_TOOLTIP_TEXT']
: Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
});
},
@@ -517,7 +511,7 @@ blocks['text_prompt_ext'] = {
// XML hooks are kept for backwards compatibility.
};
type PromptBlock = Block&PromptCommonMixin&QuoteImageMixin;
type PromptBlock = Block & PromptCommonMixin & QuoteImageMixin;
const TEXT_PROMPT_BLOCK = {
...PROMPT_COMMON,
@@ -525,40 +519,39 @@ const TEXT_PROMPT_BLOCK = {
* Block for prompt function (internal message).
* The 'text_prompt_ext' block is preferred as it is more flexible.
*/
init: function(this: PromptBlock) {
init: function (this: PromptBlock) {
this.mixin(QUOTE_IMAGE_MIXIN);
const TYPES = [
[Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'],
[Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'],
];
// Assign 'this' to a variable for use in the closures below.
const thisBlock = this;
this.setHelpUrl(Msg['TEXT_PROMPT_HELPURL']);
this.setStyle('text_blocks');
const dropdown = fieldRegistry.fromJson({
type: 'field_dropdown',
options: TYPES,
}) as FieldDropdown;
dropdown.setValidator(function(this: FieldDropdown, newOp: string) {
thisBlock.updateType_(newOp);
return undefined; // FieldValidators can't be void. Use option as-is.
dropdown.setValidator((newOp: string) => {
this.updateType_(newOp);
return undefined; // FieldValidators can't be void. Use option as-is.
});
this.appendDummyInput()
.appendField(dropdown, 'TYPE')
.appendField(this.newQuote_(true))
.appendField(
fieldRegistry.fromJson({
type: 'field_input',
text: '',
}) as FieldTextInput,
'TEXT')
.appendField(this.newQuote_(false));
.appendField(dropdown, 'TYPE')
.appendField(this.newQuote_(true))
.appendField(
fieldRegistry.fromJson({
type: 'field_input',
text: '',
}) as FieldTextInput,
'TEXT'
)
.appendField(this.newQuote_(false));
this.setOutput(true, 'String');
this.setTooltip(function() {
return (thisBlock.getFieldValue('TYPE') === 'TEXT') ?
Msg['TEXT_PROMPT_TOOLTIP_TEXT'] :
Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
this.setTooltip(() => {
return this.getFieldValue('TYPE') === 'TEXT'
? Msg['TEXT_PROMPT_TOOLTIP_TEXT']
: Msg['TEXT_PROMPT_TOOLTIP_NUMBER'];
});
},
};
@@ -569,7 +562,7 @@ blocks['text_count'] = {
/**
* Block for counting how many times one string appears within another string.
*/
init: function(this: Block) {
init: function (this: Block) {
this.jsonInit({
'message0': Msg['TEXT_COUNT_MESSAGE0'],
'args0': [
@@ -597,7 +590,7 @@ blocks['text_replace'] = {
/**
* Block for replacing one string with another in the text.
*/
init: function(this: Block) {
init: function (this: Block) {
this.jsonInit({
'message0': Msg['TEXT_REPLACE_MESSAGE0'],
'args0': [
@@ -630,7 +623,7 @@ blocks['text_reverse'] = {
/**
* Block for reversing a string.
*/
init: function(this: Block) {
init: function (this: Block) {
this.jsonInit({
'message0': Msg['TEXT_REVERSE_MESSAGE0'],
'args0': [
@@ -650,7 +643,7 @@ blocks['text_reverse'] = {
};
/** Type of a block that has QUOTE_IMAGE_MIXIN */
type QuoteImageBlock = Block&QuoteImageMixin;
type QuoteImageBlock = Block & QuoteImageMixin;
interface QuoteImageMixin extends QuoteImageMixinType {}
type QuoteImageMixinType = typeof QUOTE_IMAGE_MIXIN;
@@ -660,21 +653,21 @@ const QUOTE_IMAGE_MIXIN = {
* quote).
*/
QUOTE_IMAGE_LEFT_DATAURI:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
'n0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY' +
'1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1' +
'HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMf' +
'z9AylsaRRgGzvZAAAAAElFTkSuQmCC',
'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).
*/
QUOTE_IMAGE_RIGHT_DATAURI:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' +
'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' +
'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' +
'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==',
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' +
'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' +
'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' +
'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' +
'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==',
/**
* Pixel width of QUOTE_IMAGE_LEFT_DATAURI and QUOTE_IMAGE_RIGHT_DATAURI.
*/
@@ -689,7 +682,7 @@ const QUOTE_IMAGE_MIXIN = {
*
* @param fieldName The name of the field to wrap with quotes.
*/
quoteField_: function(this: QuoteImageBlock, fieldName: string) {
quoteField_: function (this: QuoteImageBlock, fieldName: string) {
for (let i = 0, input; (input = this.inputList[i]); i++) {
for (let j = 0, field; (field = input.fieldRow[j]); j++) {
if (fieldName === field.name) {
@@ -700,7 +693,8 @@ const QUOTE_IMAGE_MIXIN = {
}
}
console.warn(
'field named "' + fieldName + '" not found in ' + this.toDevString());
'field named "' + fieldName + '" not found in ' + this.toDevString()
);
},
/**
@@ -711,10 +705,11 @@ const QUOTE_IMAGE_MIXIN = {
* Otherwise, a closing quote is used (” in LTR).
* @returns The new field.
*/
newQuote_: function(this: QuoteImageBlock, open: boolean): FieldImage {
newQuote_: function (this: QuoteImageBlock, open: boolean): FieldImage {
const isLeft = this.RTL ? !open : open;
const dataUri =
isLeft ? this.QUOTE_IMAGE_LEFT_DATAURI : this.QUOTE_IMAGE_RIGHT_DATAURI;
const dataUri = isLeft
? this.QUOTE_IMAGE_LEFT_DATAURI
: this.QUOTE_IMAGE_RIGHT_DATAURI;
return fieldRegistry.fromJson({
type: 'field_image',
src: dataUri,
@@ -728,20 +723,20 @@ const QUOTE_IMAGE_MIXIN = {
/**
* Wraps TEXT field with images of double quote characters.
*/
const QUOTES_EXTENSION = function(this: QuoteImageBlock) {
const QUOTES_EXTENSION = function (this: QuoteImageBlock) {
this.mixin(QUOTE_IMAGE_MIXIN);
this.quoteField_('TEXT');
};
/** Type of a block that has TEXT_JOIN_MUTATOR_MIXIN */
type JoinMutatorBlock = Block&JoinMutatorMixin&QuoteImageMixin;
type JoinMutatorBlock = Block & JoinMutatorMixin & QuoteImageMixin;
interface JoinMutatorMixin extends JoinMutatorMixinType {}
type JoinMutatorMixinType = typeof JOIN_MUTATOR_MIXIN;
/** Type of a item block in the text_join_mutator bubble. */
type JoinItemBlock = BlockSvg&JoinItemMixin;
type JoinItemBlock = BlockSvg & JoinItemMixin;
interface JoinItemMixin {
valueConnection_: Connection|null
valueConnection_: Connection | null;
}
/**
@@ -755,7 +750,7 @@ const JOIN_MUTATOR_MIXIN = {
*
* @returns XML storage element.
*/
mutationToDom: function(this: JoinMutatorBlock): Element {
mutationToDom: function (this: JoinMutatorBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('items', `${this.itemCount_}`);
return container;
@@ -766,7 +761,7 @@ const JOIN_MUTATOR_MIXIN = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: JoinMutatorBlock, xmlElement: Element) {
domToMutation: function (this: JoinMutatorBlock, xmlElement: Element) {
this.itemCount_ = parseInt(xmlElement.getAttribute('items')!, 10);
this.updateShape_();
},
@@ -775,7 +770,7 @@ const JOIN_MUTATOR_MIXIN = {
*
* @returns The state of this block, ie the item count.
*/
saveExtraState: function(this: JoinMutatorBlock): {itemCount: number;} {
saveExtraState: function (this: JoinMutatorBlock): {itemCount: number} {
return {
'itemCount': this.itemCount_,
};
@@ -785,7 +780,7 @@ const JOIN_MUTATOR_MIXIN = {
*
* @param state The state to apply to this block, ie the item count.
*/
loadExtraState: function(this: JoinMutatorBlock, state: {[x: string]: any;}) {
loadExtraState: function (this: JoinMutatorBlock, state: {[x: string]: any}) {
this.itemCount_ = state['itemCount'];
this.updateShape_();
},
@@ -795,14 +790,16 @@ const JOIN_MUTATOR_MIXIN = {
* @param workspace Mutator's workspace.
* @returns Root block in mutator.
*/
decompose: function(this: JoinMutatorBlock, workspace: Workspace): Block {
const containerBlock =
workspace.newBlock('text_create_join_container') as BlockSvg;
decompose: function (this: JoinMutatorBlock, workspace: Workspace): Block {
const containerBlock = workspace.newBlock(
'text_create_join_container'
) as BlockSvg;
containerBlock.initSvg();
let connection = containerBlock.getInput('STACK')!.connection!;
for (let i = 0; i < this.itemCount_; i++) {
const itemBlock =
workspace.newBlock('text_create_join_item') as JoinItemBlock;
const itemBlock = workspace.newBlock(
'text_create_join_item'
) as JoinItemBlock;
itemBlock.initSvg();
connection.connect(itemBlock.previousConnection);
connection = itemBlock.nextConnection;
@@ -814,9 +811,10 @@ const JOIN_MUTATOR_MIXIN = {
*
* @param containerBlock Root block in mutator.
*/
compose: function(this: JoinMutatorBlock, containerBlock: Block) {
let itemBlock =
containerBlock.getInputTargetBlock('STACK') as JoinItemBlock;
compose: function (this: JoinMutatorBlock, containerBlock: Block) {
let itemBlock = containerBlock.getInputTargetBlock(
'STACK'
) as JoinItemBlock;
// Count number of inputs.
const connections = [];
while (itemBlock) {
@@ -846,7 +844,7 @@ const JOIN_MUTATOR_MIXIN = {
*
* @param containerBlock Root block in mutator.
*/
saveConnections: function(this: JoinMutatorBlock, containerBlock: Block) {
saveConnections: function (this: JoinMutatorBlock, containerBlock: Block) {
let itemBlock = containerBlock.getInputTargetBlock('STACK');
let i = 0;
while (itemBlock) {
@@ -856,7 +854,7 @@ const JOIN_MUTATOR_MIXIN = {
}
const input = this.getInput('ADD' + i);
(itemBlock as JoinItemBlock).valueConnection_ =
input && input.connection!.targetConnection;
input && input.connection!.targetConnection;
itemBlock = itemBlock.getNextBlock();
i++;
}
@@ -864,13 +862,13 @@ const JOIN_MUTATOR_MIXIN = {
/**
* Modify this block to have the correct number of inputs.
*/
updateShape_: function(this: JoinMutatorBlock) {
updateShape_: function (this: JoinMutatorBlock) {
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));
.appendField(this.newQuote_(true))
.appendField(this.newQuote_(false));
}
// Add new inputs.
for (let i = 0; i < this.itemCount_; i++) {
@@ -891,7 +889,7 @@ const JOIN_MUTATOR_MIXIN = {
/**
* Performs final setup of a text_join block.
*/
const JOIN_EXTENSION = function(this: JoinMutatorBlock) {
const JOIN_EXTENSION = function (this: JoinMutatorBlock) {
// Add the quote mixin for the itemCount_ = 0 case.
this.mixin(QUOTE_IMAGE_MIXIN);
// Initialize the mutator values.
@@ -903,22 +901,24 @@ const JOIN_EXTENSION = function(this: JoinMutatorBlock) {
// Update the tooltip of 'text_append' block to reference the variable.
Extensions.register(
'text_append_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR'));
'text_append_tooltip',
Extensions.buildTooltipWithFieldText('%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR')
);
/**
* Update the tooltip of 'text_append' block to reference the variable.
*/
const INDEXOF_TOOLTIP_EXTENSION = function(this: Block) {
const INDEXOF_TOOLTIP_EXTENSION = function (this: Block) {
this.setTooltip(() => {
return Msg['TEXT_INDEXOF_TOOLTIP'].replace(
'%1', this.workspace.options.oneBasedIndex ? '0' : '-1');
'%1',
this.workspace.options.oneBasedIndex ? '0' : '-1'
);
});
};
/** Type of a block that has TEXT_CHARAT_MUTATOR_MIXIN */
type CharAtBlock = Block&CharAtMixin;
type CharAtBlock = Block & CharAtMixin;
interface CharAtMixin extends CharAtMixinType {}
type CharAtMixinType = typeof CHARAT_MUTATOR_MIXIN;
@@ -933,7 +933,7 @@ const CHARAT_MUTATOR_MIXIN = {
*
* @returns XML storage element.
*/
mutationToDom: function(this: CharAtBlock): Element {
mutationToDom: function (this: CharAtBlock): Element {
const container = xmlUtils.createElement('mutation');
container.setAttribute('at', `${this.isAt_}`);
return container;
@@ -944,10 +944,10 @@ const CHARAT_MUTATOR_MIXIN = {
*
* @param xmlElement XML storage element.
*/
domToMutation: function(this: CharAtBlock, xmlElement: Element) {
domToMutation: function (this: CharAtBlock, xmlElement: Element) {
// Note: Until January 2013 this block did not have mutations,
// so 'at' defaults to true.
const isAt = (xmlElement.getAttribute('at') !== 'false');
const isAt = xmlElement.getAttribute('at') !== 'false';
this.updateAt_(isAt);
},
@@ -961,7 +961,7 @@ const CHARAT_MUTATOR_MIXIN = {
*
* @param isAt True if the input should exist.
*/
updateAt_: function(this: CharAtBlock, isAt: boolean) {
updateAt_: function (this: CharAtBlock, isAt: boolean) {
// Destroy old 'AT' and 'ORDINAL' inputs.
this.removeInput('AT', true);
this.removeInput('ORDINAL', true);
@@ -970,7 +970,8 @@ const CHARAT_MUTATOR_MIXIN = {
this.appendValueInput('AT').setCheck('Number');
if (Msg['ORDINAL_NUMBER_SUFFIX']) {
this.appendDummyInput('ORDINAL').appendField(
Msg['ORDINAL_NUMBER_SUFFIX']);
Msg['ORDINAL_NUMBER_SUFFIX']
);
}
}
if (Msg['TEXT_CHARAT_TAIL']) {
@@ -985,30 +986,29 @@ const CHARAT_MUTATOR_MIXIN = {
/**
* Does the initial mutator update of text_charAt and adds the tooltip
*/
const CHARAT_EXTENSION = function(this: CharAtBlock) {
const CHARAT_EXTENSION = function (this: CharAtBlock) {
const dropdown = this.getField('WHERE') as FieldDropdown;
dropdown.setValidator(function(this: FieldDropdown, value: any) {
const newAt = (value === 'FROM_START') || (value === 'FROM_END');
dropdown.setValidator(function (this: FieldDropdown, value: any) {
const newAt = value === 'FROM_START' || value === 'FROM_END';
const block = this.getSourceBlock() as CharAtBlock;
if (newAt !== block.isAt_) {
block.updateAt_(newAt);
}
return undefined; // FieldValidators can't be void. Use option as-is.
return undefined; // FieldValidators can't be void. Use option as-is.
});
this.updateAt_(true);
// Assign 'this' to a variable for use in the tooltip closure below.
const thisBlock = this;
this.setTooltip(function() {
const where = thisBlock.getFieldValue('WHERE');
this.setTooltip(() => {
const where = this.getFieldValue('WHERE');
let tooltip = Msg['TEXT_CHARAT_TOOLTIP'];
if (where === 'FROM_START' || where === 'FROM_END') {
const msg = (where === 'FROM_START') ?
Msg['LISTS_INDEX_FROM_START_TOOLTIP'] :
Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
const msg =
where === 'FROM_START'
? Msg['LISTS_INDEX_FROM_START_TOOLTIP']
: Msg['LISTS_INDEX_FROM_END_TOOLTIP'];
if (msg) {
tooltip += ' ' +
msg.replace(
'%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
tooltip +=
' ' +
msg.replace('%1', this.workspace.options.oneBasedIndex ? '#1' : '#0');
}
}
return tooltip;
@@ -1020,10 +1020,16 @@ Extensions.register('text_indexOf_tooltip', INDEXOF_TOOLTIP_EXTENSION);
Extensions.register('text_quotes', QUOTES_EXTENSION);
Extensions.registerMutator(
'text_join_mutator', JOIN_MUTATOR_MIXIN, JOIN_EXTENSION);
'text_join_mutator',
JOIN_MUTATOR_MIXIN,
JOIN_EXTENSION
);
Extensions.registerMutator(
'text_charAt_mutator', CHARAT_MUTATOR_MIXIN, CHARAT_EXTENSION);
'text_charAt_mutator',
CHARAT_MUTATOR_MIXIN,
CHARAT_EXTENSION
);
// Register provided blocks.
defineBlocks(blocks);

View File

@@ -17,14 +17,19 @@ import * as Extensions from '../core/extensions.js';
import * as Variables from '../core/variables.js';
import * as xmlUtils from '../core/utils/xml.js';
import type {Block} from '../core/block.js';
import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import {FieldVariable} from '../core/field_variable.js';
import {Msg} from '../core/msg.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_label.js';
/**
* A dictionary of the block definitions provided by this module.
*/
@@ -71,10 +76,10 @@ export const blocks = createBlockDefinitionsFromJsonArray([
]);
/** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */
type VariableBlock = Block&VariableMixin;
type VariableBlock = Block & VariableMixin;
interface VariableMixin extends VariableMixinType {}
type VariableMixinType =
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
/**
* Mixin to add context menu items to create getter/setter blocks for this
@@ -87,9 +92,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
*
* @param options List of menu options to add to.
*/
customContextMenu: function(
this: VariableBlock,
options: Array<ContextMenuOption|LegacyContextMenuOption>) {
customContextMenu: function (
this: VariableBlock,
options: Array<ContextMenuOption | LegacyContextMenuOption>
) {
if (!this.isInFlyout) {
let oppositeType;
let contextMenuMsg;
@@ -113,12 +119,14 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
options.push({
enabled: this.workspace.remainingCapacity() > 0,
text: contextMenuMsg.replace('%1', name),
callback: ContextMenu.callbackFactory(this, xmlBlock)
callback: ContextMenu.callbackFactory(this, xmlBlock),
});
// Getter blocks have the option to rename or delete that variable.
} else {
if (this.type === 'variables_get' ||
this.type === 'variables_get_reporter') {
if (
this.type === 'variables_get' ||
this.type === 'variables_get_reporter'
) {
const renameOption = {
text: Msg['RENAME_VARIABLE'],
enabled: true,
@@ -144,8 +152,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
* @param block The block with the variable to rename.
* @returns A function that renames the variable.
*/
const renameOptionCallbackFactory = function(block: VariableBlock): () => void {
return function() {
const renameOptionCallbackFactory = function (
block: VariableBlock
): () => void {
return function () {
const workspace = block.workspace;
const variableField = block.getField('VAR') as FieldVariable;
const variable = variableField.getVariable()!;
@@ -160,8 +170,10 @@ const renameOptionCallbackFactory = function(block: VariableBlock): () => void {
* @param block The block with the variable to delete.
* @returns A function that deletes the variable.
*/
const deleteOptionCallbackFactory = function(block: VariableBlock): () => void {
return function() {
const deleteOptionCallbackFactory = function (
block: VariableBlock
): () => void {
return function () {
const workspace = block.workspace;
const variableField = block.getField('VAR') as FieldVariable;
const variable = variableField.getVariable()!;
@@ -171,8 +183,9 @@ const deleteOptionCallbackFactory = function(block: VariableBlock): () => void {
};
Extensions.registerMixin(
'contextMenu_variableSetterGetter',
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);
'contextMenu_variableSetterGetter',
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN
);
// Register provided blocks.
defineBlocks(blocks);

View File

@@ -18,14 +18,19 @@ import * as Variables from '../core/variables.js';
import * as xml from '../core/utils/xml.js';
import {Abstract as AbstractEvent} from '../core/events/events_abstract.js';
import type {Block} from '../core/block.js';
import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js';
import type {
ContextMenuOption,
LegacyContextMenuOption,
} from '../core/contextmenu_registry.js';
import {FieldVariable} from '../core/field_variable.js';
import {Msg} from '../core/msg.js';
import type {WorkspaceSvg} from '../core/workspace_svg.js';
import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js';
import {
createBlockDefinitionsFromJsonArray,
defineBlocks,
} from '../core/common.js';
import '../core/field_label.js';
/**
* A dictionary of the block definitions provided by this module.
*/
@@ -34,11 +39,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([
{
'type': 'variables_get_dynamic',
'message0': '%1',
'args0': [{
'type': 'field_variable',
'name': 'VAR',
'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
}],
'args0': [
{
'type': 'field_variable',
'name': 'VAR',
'variable': '%{BKY_VARIABLES_DEFAULT_NAME}',
},
],
'output': null,
'style': 'variable_dynamic_blocks',
'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}',
@@ -70,10 +77,10 @@ export const blocks = createBlockDefinitionsFromJsonArray([
]);
/** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */
type VariableBlock = Block&VariableMixin;
type VariableBlock = Block & VariableMixin;
interface VariableMixin extends VariableMixinType {}
type VariableMixinType =
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN;
/**
* Mixin to add context menu items to create getter/setter blocks for this
@@ -86,9 +93,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
*
* @param options List of menu options to add to.
*/
customContextMenu: function(
this: VariableBlock,
options: Array<ContextMenuOption|LegacyContextMenuOption>) {
customContextMenu: function (
this: VariableBlock,
options: Array<ContextMenuOption | LegacyContextMenuOption>
) {
// Getter blocks have the option to create a setter block, and vice versa.
if (!this.isInFlyout) {
let oppositeType;
@@ -116,11 +124,13 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
options.push({
enabled: this.workspace.remainingCapacity() > 0,
text: contextMenuMsg.replace('%1', name),
callback: ContextMenu.callbackFactory(this, xmlBlock)
callback: ContextMenu.callbackFactory(this, xmlBlock),
});
} else {
if (this.type === 'variables_get_dynamic' ||
this.type === 'variables_get_reporter_dynamic') {
if (
this.type === 'variables_get_dynamic' ||
this.type === 'variables_get_reporter_dynamic'
) {
const renameOption = {
text: Msg['RENAME_VARIABLE'],
enabled: true,
@@ -143,7 +153,7 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
*
* @param _e Change event.
*/
onchange: function(this: VariableBlock, _e: AbstractEvent) {
onchange: function (this: VariableBlock, _e: AbstractEvent) {
const id = this.getFieldValue('VAR');
const variableModel = Variables.getVariable(this.workspace, id)!;
if (this.type === 'variables_get_dynamic') {
@@ -161,8 +171,8 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = {
* @param block The block with the variable to rename.
* @returns A function that renames the variable.
*/
const renameOptionCallbackFactory = function(block: VariableBlock) {
return function() {
const renameOptionCallbackFactory = function (block: VariableBlock) {
return function () {
const workspace = block.workspace;
const variableField = block.getField('VAR') as FieldVariable;
const variable = variableField.getVariable()!;
@@ -177,8 +187,8 @@ const renameOptionCallbackFactory = function(block: VariableBlock) {
* @param block The block with the variable to delete.
* @returns A function that deletes the variable.
*/
const deleteOptionCallbackFactory = function(block: VariableBlock) {
return function() {
const deleteOptionCallbackFactory = function (block: VariableBlock) {
return function () {
const workspace = block.workspace;
const variableField = block.getField('VAR') as FieldVariable;
const variable = variableField.getVariable()!;
@@ -188,8 +198,9 @@ const deleteOptionCallbackFactory = function(block: VariableBlock) {
};
Extensions.registerMixin(
'contextMenu_variableDynamicSetterGetter',
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN);
'contextMenu_variableDynamicSetterGetter',
CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN
);
// Register provided blocks.
defineBlocks(blocks);