mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
283 lines
8.3 KiB
JavaScript
283 lines
8.3 KiB
JavaScript
/**
|
|
* @license
|
|
* Visual Blocks Editor
|
|
*
|
|
* Copyright 2012 Google Inc.
|
|
* https://developers.google.com/blockly/
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Loop blocks for Blockly.
|
|
* @author fraser@google.com (Neil Fraser)
|
|
*/
|
|
'use strict';
|
|
|
|
goog.provide('Blockly.Blocks.loops');
|
|
|
|
goog.require('Blockly.Blocks');
|
|
|
|
|
|
/**
|
|
* Common HSV hue for all blocks in this category.
|
|
*/
|
|
Blockly.Blocks.loops.HUE = 120;
|
|
|
|
Blockly.Blocks['controls_repeat_ext'] = {
|
|
/**
|
|
* Block for repeat n times (external number).
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
this.jsonInit({
|
|
"message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
|
|
"args0": [
|
|
{
|
|
"type": "input_value",
|
|
"name": "TIMES",
|
|
"check": "Number"
|
|
}
|
|
],
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"colour": Blockly.Blocks.loops.HUE,
|
|
"tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
|
|
"helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
|
|
});
|
|
this.appendStatementInput('DO')
|
|
.appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['controls_repeat'] = {
|
|
/**
|
|
* Block for repeat n times (internal number).
|
|
* The 'controls_repeat_ext' block is preferred as it is more flexible.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
this.jsonInit({
|
|
"message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
|
|
"args0": [
|
|
{
|
|
"type": "field_number",
|
|
"name": "TIMES",
|
|
"value": 10,
|
|
"min": 0,
|
|
"precision": 1
|
|
}
|
|
],
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"colour": Blockly.Blocks.loops.HUE,
|
|
"tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
|
|
"helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
|
|
});
|
|
this.appendStatementInput('DO')
|
|
.appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['controls_whileUntil'] = {
|
|
/**
|
|
* Block for 'do while/until' loop.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE, 'WHILE'],
|
|
[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL, 'UNTIL']];
|
|
this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);
|
|
this.setColour(Blockly.Blocks.loops.HUE);
|
|
this.appendValueInput('BOOL')
|
|
.setCheck('Boolean')
|
|
.appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
|
|
this.appendStatementInput('DO')
|
|
.appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);
|
|
this.setPreviousStatement(true);
|
|
this.setNextStatement(true);
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var op = thisBlock.getFieldValue('MODE');
|
|
var TOOLTIPS = {
|
|
'WHILE': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE,
|
|
'UNTIL': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL
|
|
};
|
|
return TOOLTIPS[op];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['controls_for'] = {
|
|
/**
|
|
* Block for 'for' loop.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
this.jsonInit({
|
|
"message0": Blockly.Msg.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"
|
|
}
|
|
],
|
|
"inputsInline": true,
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"colour": Blockly.Blocks.loops.HUE,
|
|
"helpUrl": Blockly.Msg.CONTROLS_FOR_HELPURL
|
|
});
|
|
this.appendStatementInput('DO')
|
|
.appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO);
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
return Blockly.Msg.CONTROLS_FOR_TOOLTIP.replace('%1',
|
|
thisBlock.getFieldValue('VAR'));
|
|
});
|
|
},
|
|
/**
|
|
* Add menu option to create getter block for loop variable.
|
|
* @param {!Array} options List of menu options to add to.
|
|
* @this Blockly.Block
|
|
*/
|
|
customContextMenu: function(options) {
|
|
if (!this.isCollapsed()) {
|
|
var option = {enabled: true};
|
|
var name = this.getFieldValue('VAR');
|
|
option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name);
|
|
var xmlField = goog.dom.createDom('field', null, name);
|
|
xmlField.setAttribute('name', 'VAR');
|
|
var xmlBlock = goog.dom.createDom('block', null, xmlField);
|
|
xmlBlock.setAttribute('type', 'variables_get');
|
|
option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
|
|
options.push(option);
|
|
}
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['controls_forEach'] = {
|
|
/**
|
|
* Block for 'for each' loop.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
this.jsonInit({
|
|
"message0": Blockly.Msg.CONTROLS_FOREACH_TITLE,
|
|
"args0": [
|
|
{
|
|
"type": "field_variable",
|
|
"name": "VAR",
|
|
"variable": null
|
|
},
|
|
{
|
|
"type": "input_value",
|
|
"name": "LIST",
|
|
"check": "Array"
|
|
}
|
|
],
|
|
"previousStatement": null,
|
|
"nextStatement": null,
|
|
"colour": Blockly.Blocks.loops.HUE,
|
|
"helpUrl": Blockly.Msg.CONTROLS_FOREACH_HELPURL
|
|
});
|
|
this.appendStatementInput('DO')
|
|
.appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_DO);
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
return Blockly.Msg.CONTROLS_FOREACH_TOOLTIP.replace('%1',
|
|
thisBlock.getFieldValue('VAR'));
|
|
});
|
|
},
|
|
customContextMenu: Blockly.Blocks['controls_for'].customContextMenu
|
|
};
|
|
|
|
Blockly.Blocks['controls_flow_statements'] = {
|
|
/**
|
|
* Block for flow statements: continue, break.
|
|
* @this Blockly.Block
|
|
*/
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK, 'BREAK'],
|
|
[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE, 'CONTINUE']];
|
|
this.setHelpUrl(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL);
|
|
this.setColour(Blockly.Blocks.loops.HUE);
|
|
this.appendDummyInput()
|
|
.appendField(new Blockly.FieldDropdown(OPERATORS), 'FLOW');
|
|
this.setPreviousStatement(true);
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var op = thisBlock.getFieldValue('FLOW');
|
|
var TOOLTIPS = {
|
|
'BREAK': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK,
|
|
'CONTINUE': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE
|
|
};
|
|
return TOOLTIPS[op];
|
|
});
|
|
},
|
|
/**
|
|
* 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) {
|
|
var legal = false;
|
|
// Is the block nested in a loop?
|
|
var block = this;
|
|
do {
|
|
if (this.LOOP_TYPES.indexOf(block.type) != -1) {
|
|
legal = true;
|
|
break;
|
|
}
|
|
block = block.getSurroundParent();
|
|
} while (block);
|
|
if (legal) {
|
|
this.setWarningText(null);
|
|
} else {
|
|
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
|
|
}
|
|
},
|
|
/**
|
|
* 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.Blocks['controls_flow_statements'].LOOP_TYPES.push('custom_loop');
|
|
*/
|
|
LOOP_TYPES: ['controls_repeat', 'controls_repeat_ext', 'controls_forEach',
|
|
'controls_for', 'controls_whileUntil']
|
|
};
|