openct-tasks/_common/modules/pemFioi/quickAlgo/blockly_blocks.js

2600 lines
110 KiB
JavaScript

/*
blockly_blocks:
Block generation and configuration logic for the Blockly mode
*/
// Sets of blocks
var blocklySets = {
allDefault: {
wholeCategories: ["input", "logic", "loops", "math", "texts", "lists", "dicts", "tables", "variables", "functions"]
},
allJls: {
wholeCategories: ["input", "logic", "loops", "math", "texts", "lists", "dicts", "tables", "variables", "functions"],
excludedBlocks: ['text_eval', 'text_print', 'text_print_noend']
}
};
// Blockly to Scratch translations
var blocklyToScratch = {
singleBlocks: {
'controls_if': ['control_if'],
'controls_if_else': ['control_if_else'],
'controls_infiniteloop': ['control_forever'],
'controls_repeat': ['control_repeat'],
'controls_repeat_ext': ['control_repeat'],
'controls_whileUntil': ['control_repeat_until'],
'controls_untilWhile': ['control_repeat_until'],
'lists_repeat': ['data_listrepeat'],
'lists_create_with_empty': [], // Scratch logic is not to initialize
'lists_getIndex': ['data_itemoflist'],
'lists_setIndex': ['data_replaceitemoflist'],
'logic_negate': ['operator_not'],
'logic_boolean': [],
'logic_compare': ['operator_equals', 'operator_gt', 'operator_gte', 'operator_lt', 'operator_lte', 'operator_not'],
'logic_operation': ['operator_and', 'operator_or'],
'text': [],
'text_append': [],
'text_join': ['operator_join'],
'math_arithmetic': ['operator_add', 'operator_subtract', 'operator_multiply', 'operator_divide', 'operator_dividefloor'],
'math_change': ['data_changevariableby'],
'math_number': ['math_number'],
'variables_get': ['data_variable'],
'variables_set': ['data_setvariableto']
},
wholeCategories: {
'loops': 'control',
'logic': 'operator',
'math': 'operator'
}
};
// Allowed blocks that make another block allowed as well
var blocklyAllowedSiblings = {
'controls_if_else': ['controls_if'],
'lists_create_with_empty': ['lists_create_with']
}
function getBlocklyBlockFunctions(maxBlocks, nbTestCases) {
// TODO :: completely split the logic so it can be a separate object
return {
allBlocksAllowed: [],
addBlocksAllowed: function(blocks) {
for(var i=0; i < blocks.length; i++) {
var name = blocks[i];
if(arrayContains(this.allBlocksAllowed, name)) { continue; }
this.allBlocksAllowed.push(name);
if(blocklyAllowedSiblings[name]) {
this.addBlocksAllowed(blocklyAllowedSiblings[name]);
}
}
},
getBlocksAllowed: function() {
return this.scratchMode ? this.blocksToScratch(this.allBlocksAllowed) : this.allBlocksAllowed;
},
getBlockLabel: function(type) {
// Fetch user-friendly name for the block
var msg = this.mainContext.strings.label[type];
// TODO :: Names for Blockly/Scratch blocks
return msg ? msg : type;
},
checkConstraints: function(workspace) {
// Check we satisfy constraints
return this.getRemainingCapacity(workspace) >= 0 && !this.findLimited(workspace);
},
makeLimitedUsesPointers: function() {
// Make the list of pointers for each block to the limitedUses it
// appears in
if(this.limitedPointers && this.limitedPointers.limitedUses === this.mainContext.infos.limitedUses) { return; }
this.limitedPointers = {
// Keep in memory the limitedUses these limitedPointers were made for
limitedUses: this.mainContext.infos.limitedUses
};
for(var i=0; i < this.mainContext.infos.limitedUses.length; i++) {
var curLimit = this.mainContext.infos.limitedUses[i];
if(this.scratchMode) {
// Convert block list to Scratch
var blocks = [];
for(var j=0; j < curLimit.blocks.length; j++) {
var curBlock = curLimit.blocks[j];
var convBlockList = blocklyToScratch.singleBlocks[curBlock];
if(convBlockList) {
for(var k=0; k < convBlockList.length; k++) {
addInSet(blocks, convBlockList[k]);
}
} else {
addInSet(blocks, curBlock);
}
}
} else {
var blocks = curLimit.blocks;
}
for(var j=0; j < blocks.length; j++) {
var block = blocks[j];
if(!this.limitedPointers[block]) {
this.limitedPointers[block] = [];
}
this.limitedPointers[block].push(i);
}
}
},
findLimited: function(workspace) {
// Check we don't use blocks with limited uses too much
// Returns false if there's none, else the name of the first block
// found which is over the limit
if(!this.mainContext.infos || !this.mainContext.infos.limitedUses) { return false; }
this.makeLimitedUsesPointers();
var workspaceBlocks = workspace.getAllBlocks();
var usesCount = {};
for(var i = 0; i < workspaceBlocks.length; i++) {
var blockType = workspaceBlocks[i].type;
if(!this.limitedPointers[blockType]) { continue; }
for(var j = 0; j < this.limitedPointers[blockType].length; j++) {
// Each pointer is a position in the limitedUses array that
// this block appears in
var pointer = this.limitedPointers[blockType][j];
if(!usesCount[pointer]) { usesCount[pointer] = 0; }
usesCount[pointer]++;
// Exceeded the number of uses
if(usesCount[pointer] > this.mainContext.infos.limitedUses[pointer].nbUses) {
return blockType;
}
}
}
// All blocks are under the use limit
return false;
},
getRemainingCapacity: function(workspace) {
// Get the number of blocks allowed
if(!this.maxBlocks) { return Infinity; }
var remaining = workspace.remainingCapacity(this.maxBlocks+1);
if(this.maxBlocks && remaining == Infinity) {
// Blockly won't return anything as we didn't set a limit
remaining = this.maxBlocks+1 - workspace.getAllBlocks().length;
}
return remaining;
},
isEmpty: function(workspace) {
// Check if workspace is empty
if(!workspace) { workspace = this.workspace; }
var blocks = workspace.getAllBlocks();
if(blocks.length == 1) {
return blocks[0].type == 'robot_start';
} else {
return blocks.length == 0;
}
},
getAllCodes: function(answer) {
// Generate codes for each node
var codes = [];
for (var iNode = 0; iNode < this.mainContext.nbNodes; iNode++) {
if(this.mainContext.codeIdForNode) {
var iCode = this.mainContext.codeIdForNode(iNode);
} else {
var iCode = Math.min(iNode, this.mainContext.nbCodes-1);
}
var language = this.languages[iCode];
if (language == "blockly") {
language = "blocklyJS";
}
if(answer) {
// Generate codes for specified answer
var code = this.getCodeFromXml(answer[iCode].blockly, "javascript");
codes[iNode] = this.getFullCode(code);
} else {
// Generate codes for current program
codes[iNode] = this.getFullCode(this.programs[iCode][language]);
}
}
return codes;
},
getCodeFromXml: function(xmlText, language) {
try {
var xml = Blockly.Xml.textToDom(xmlText)
} catch (e) {
alert(e);
return;
}
// Remove statement prefix (highlightBlock)
var statementPrefix = Blockly.JavaScript.STATEMENT_PREFIX;
Blockly.JavaScript.STATEMENT_PREFIX = '';
// New workspaces need options, else they can give unpredictable results
var tmpOptions = new Blockly.Options({});
var tmpWorkspace = new Blockly.Workspace(tmpOptions);
if(this.scratchMode) {
// Make sure it has the right information from this blocklyHelper
tmpWorkspace.maxBlocks = function () { return maxBlocks; };
}
Blockly.Xml.domToWorkspace(xml, tmpWorkspace);
var code = this.getCode(language, tmpWorkspace);
Blockly.JavaScript.STATEMENT_PREFIX = statementPrefix;
return code;
},
getCode: function(language, codeWorkspace, noReportValue) {
if (codeWorkspace == undefined) {
codeWorkspace = this.workspace;
}
if(!this.checkConstraints(codeWorkspace)) {
// Safeguard: avoid generating code when we use too many blocks
return 'throw "'+this.strings.tooManyBlocks+'";';
}
var blocks = codeWorkspace.getTopBlocks(true);
var languageObj = null;
if (language == "javascript") {
languageObj = Blockly.JavaScript;
}
if (language == "python") {
languageObj = Blockly.Python;
}
languageObj.init(codeWorkspace);
var oldReportValues = this.reportValues;
if(noReportValue) {
this.reportValues = false;
}
var code = [];
var comments = [];
for (var b = 0; b < blocks.length; b++) {
var block = blocks[b];
var blockCode = languageObj.blockToCode(block);
if (arrayContains(["procedures_defnoreturn", "procedures_defreturn"], block.type)) {
// For function blocks, the code is stored in languageObj.definitions_
} else {
if (block.type == "robot_start" || !this.startingBlock) {
comments.push(blockCode);
}
}
}
for (var def in languageObj.definitions_) {
code.push(languageObj.definitions_[def]);
}
var code = code.join("\n");
code += "\n";
code += comments.join("\n");
this.reportValues = oldReportValues;
return code;
},
getPyfeCode: function() {
var that = this;
return Blockly.Python.blocksToCommentedCode(function() {
return that.getCode('python');
});
},
completeBlockHandler: function(block, objectName, context) {
if (typeof block.handler == "undefined") {
block.handler = context[objectName][block.name];
}
if (typeof block.handler == "undefined") {
block.handler = (function(oName, bName) {
return function() { console.error("Error: No handler given. No function context." + oName + "." + bName + "() found!" ); }
})(objectName, block.name);
}
},
completeBlockJson: function(block, objectName, categoryName, context) {
// Needs context object solely for the language strings. Maybe change that …
if (typeof block.blocklyJson == "undefined") {
block.blocklyJson = {};
}
// Set block name
if (typeof block.blocklyJson.type == "undefined") {
block.blocklyJson.type = block.name;
}
// Add connectors (top-bottom or left)
if (typeof block.blocklyJson.output == "undefined" &&
typeof block.blocklyJson.previousStatement == "undefined" &&
typeof block.blocklyJson.nextStatement == "undefined" &&
!(block.noConnectors)) {
if (block.yieldsValue) {
block.blocklyJson.output = null;
if(this.scratchMode) {
if(block.yieldsValue == 'int') {
block.blocklyJson.outputShape = Blockly.OUTPUT_SHAPE_ROUND;
} else {
block.blocklyJson.outputShape = Blockly.OUTPUT_SHAPE_HEXAGONAL;
}
if(typeof block.blocklyJson.colour == "undefined") {
block.blocklyJson.colour = Blockly.Colours.sensing.primary;
block.blocklyJson.colourSecondary = Blockly.Colours.sensing.secondary;
block.blocklyJson.colourTertiary = Blockly.Colours.sensing.tertiary;
}
}
}
else {
block.blocklyJson.previousStatement = null;
block.blocklyJson.nextStatement = null;
if(this.scratchMode) {
if(typeof block.blocklyJson.colour == "undefined") {
block.blocklyJson.colour = Blockly.Colours.motion.primary;
block.blocklyJson.colourSecondary = Blockly.Colours.motion.secondary;
block.blocklyJson.colourTertiary = Blockly.Colours.motion.tertiary;
}
}
}
}
// Add parameters
if (typeof block.blocklyJson.args0 == "undefined" &&
typeof block.params != "undefined" &&
block.params.length > 0) {
block.blocklyJson.args0 = [];
for (var iParam = 0; iParam < block.params.length; iParam++) {
var param = {
type: "input_value",
name: "PARAM_" + iParam
}
if (block.params[iParam] != null) {
param.check = block.params[iParam]; // Should be a string!
}
block.blocklyJson.args0.push(param);
}
}
// Add message string
if (typeof block.blocklyJson.message0 == "undefined") {
block.blocklyJson.message0 = context.strings.label[block.name];
// TODO: Load default colours + custom styles
if (typeof block.blocklyJson.message0 == "undefined") {
block.blocklyJson.message0 = "<translation missing: " + block.name + ">";
}
// append all missing params to the message string
if (typeof block.blocklyJson.args0 != "undefined") {
var alreadyInserted = (block.blocklyJson.message0.match(/%/g) || []).length;
for (var iArgs0 = alreadyInserted; iArgs0 < block.blocklyJson.args0.length; iArgs0++) {
if (block.blocklyJson.args0[iArgs0].type == "input_value"
|| block.blocklyJson.args0[iArgs0].type == "field_number"
|| block.blocklyJson.args0[iArgs0].type == "field_angle"
|| block.blocklyJson.args0[iArgs0].type == "field_colour"
|| block.blocklyJson.args0[iArgs0].type == "field_dropdown"
|| block.blocklyJson.args0[iArgs0].type == "field_input") {
block.blocklyJson.message0 += " %" + (iArgs0 + 1);
}
}
}
}
// Tooltip & HelpUrl should always exist, so lets just add empty ones in case they don't exist
if (typeof block.blocklyJson.tooltip == "undefined") { block.blocklyJson.tooltip = ""; }
if (typeof block.blocklyJson.helpUrl == "undefined") { block.blocklyJson.helpUrl = ""; } // TODO: Or maybe not?
// TODO: Load default colours + custom styles
if (typeof block.blocklyJson.colour == "undefined") {
if(this.scratchMode) {
block.blocklyJson.colour = Blockly.Colours.motion.primary;
block.blocklyJson.colourSecondary = Blockly.Colours.motion.secondary;
block.blocklyJson.colourTertiary = Blockly.Colours.motion.tertiary;
} else {
var colours = this.getDefaultColours();
block.blocklyJson.colour = 210; // default: blue
if ("blocks" in colours && block.name in colours.blocks) {
block.blocklyJson.colour = colours.blocks[block.name];
}
else if ("categories" in colours) {
if (categoryName in colours.categories) {
block.blocklyJson.colour = colours.categories[categoryName];
}
else if ("_default" in colours.categories) {
block.blocklyJson.colour = colours.categories["_default"];
}
}
}
}
},
completeBlockXml: function(block) {
if (typeof block.blocklyXml == "undefined" || block.blocklyXml == "") {
block.blocklyXml = "<block type='" + block.name + "'></block>";
}
},
completeCodeGenerators: function(blockInfo, objectName) {
if (typeof blockInfo.codeGenerators == "undefined") {
blockInfo.codeGenerators = {};
}
var that = this;
// for closure:
var args0 = blockInfo.blocklyJson.args0;
var code = this.mainContext.strings.code[blockInfo.name];
var output = blockInfo.blocklyJson.output;
var blockParams = blockInfo.params;
for (var language in {JavaScript: null, Python: null}) {
if (typeof blockInfo.codeGenerators[language] == "undefined") {
// Prevent the function name to be used as a variable
Blockly[language].addReservedWords(code);
function setCodeGeneratorForLanguage(language) {
blockInfo.codeGenerators[language] = function(block) {
var params = "";
/* There are three kinds of input: value_input, statement_input and dummy_input,
We should definitely consider value_input here and not consider dummy_input here.
I don't know how statement_input is handled best, so I'll ignore it first -- Robert
*/
var iParam = 0;
for (var iArgs0 in args0) {
if (args0[iArgs0].type == "input_value") {
if (iParam) {
params += ", ";
}
params += Blockly[language].valueToCode(block, 'PARAM_' + iParam, Blockly[language].ORDER_ATOMIC);
iParam += 1;
}
if (args0[iArgs0].type == "field_number"
|| args0[iArgs0].type == "field_angle"
|| args0[iArgs0].type == "field_dropdown"
|| args0[iArgs0].type == "field_input") {
if (iParam) {
params += ", ";
}
var fieldValue = block.getFieldValue('PARAM_' + iParam);
if(blockParams && blockParams[iArgs0] == 'Number') {
params += parseInt(fieldValue);
} else {
params += JSON.stringify(fieldValue);
}
iParam += 1;
}
if (args0[iArgs0].type == "field_colour") {
if (iParam) {
params += ", ";
}
params += '"' + block.getFieldValue('PARAM_' + iParam) + '"';
iParam += 1;
}
}
var callCode = code + '(' + params + ')';
// Add reportValue to show the value in step-by-step mode
if(that.reportValues) {
var reportedCode = "reportBlockValue('" + block.id + "', " + callCode + ")";
} else {
var reportedCode = callCode;
}
if (typeof output == "undefined") {
return callCode + ";\n";
}
else {
return [reportedCode, Blockly[language].ORDER_NONE];
}
}
};
setCodeGeneratorForLanguage(language);
}
}
},
applyCodeGenerators: function(block) {
for (var language in block.codeGenerators) {
Blockly[language][block.name] = block.codeGenerators[language];
}
},
createBlock: function(block) {
if (typeof block.blocklyInit == "undefined") {
var blocklyjson = block.blocklyJson;
Blockly.Blocks[block.name] = {
init: function() {
this.jsonInit(blocklyjson);
}
};
}
else if (typeof block.blocklyInit == "function") {
Blockly.Blocks[block.name] = {
init: block.blocklyInit()
};
}
else {
console.err(block.name + ".blocklyInit is defined but not a function");
}
},
createSimpleGenerator: function(label, code, type, nbParams) {
var jsDefinitions = this.definitions['javascript'] ? this.definitions['javascript'] : [];
var pyDefinitions = this.definitions['python'] ? this.definitions['python'] : [];
// Prevent the function name to be used as a variable
Blockly.JavaScript.addReservedWords(code);
Blockly.Python.addReservedWords(code);
Blockly.JavaScript[label] = function(block) {
for (var iDef=0; iDef < jsDefinitions.length; iDef++) {
var def = jsDefinitions[iDef];
Blockly.Javascript.definitions_[def.label] = def.code;
}
var params = "";
for (var iParam = 0; iParam < nbParams; iParam++) {
if (iParam != 0) {
params += ", ";
}
params += Blockly.JavaScript.valueToCode(block, 'NAME_' + (iParam + 1), Blockly.JavaScript.ORDER_ATOMIC);
}
if (type == 0) {
return code + "(" + params + ");\n";
} else if (type == 1){
return [code + "(" + params + ")", Blockly.JavaScript.ORDER_NONE];
}
};
Blockly.Python[label] = function(block) {
for (var iDef=0; iDef < pyDefinitions.length; iDef++) {
var def = pyDefinitions[iDef];
Blockly.Python.definitions_[def.label] = def.code;
}
var params = "";
for (var iParam = 0; iParam < nbParams; iParam++) {
if (iParam != 0) {
params += ", ";
}
params += Blockly.Python.valueToCode(block, 'NAME_' + (iParam + 1), Blockly.Python.ORDER_ATOMIC);
}
if (type == 0) {
return code + "(" + params + ")\n";
} else if (type == 1) {
return [code + "(" + params + ")", Blockly.Python.ORDER_NONE];
}
};
},
createSimpleBlock: function(label, code, type, nbParams) {
Blockly.Blocks[label] = {
init: function() {
this.appendDummyInput()
.appendField(code);
if (type == 0) {
this.setPreviousStatement(true);
this.setNextStatement(true);
}
if (type == 1) {
this.setOutput(true);
}
this.setInputsInline(true);
for (var iParam = 0; iParam < nbParams; iParam++) {
this.appendValueInput("NAME_" + (iParam + 1)).setCheck(null);
}
this.setColour(210);
this.setTooltip('');
this.setHelpUrl('');
}
};
},
createSimpleGeneratorsAndBlocks: function() {
for (var genName in this.simpleGenerators) {
for (var iGen = 0; iGen < this.simpleGenerators[genName].length; iGen++) {
var generator = this.simpleGenerators[genName][iGen];
if(genName == '.') {
var label = generator.label + "__";
var code = generator.code;
} else {
var label = genName + "_" + generator.label + "__";
var code = genName + "." + generator.code;
}
this.createSimpleGenerator(label, code, generator.type, generator.nbParams);
// TODO :: merge createSimpleBlock with completeBlock*
this.createSimpleBlock(label, generator.label, generator.type, generator.nbParams);
}
}
},
createGeneratorsAndBlocks: function() {
var customGenerators = this.mainContext.customBlocks;
for (var objectName in customGenerators) {
for (var categoryName in customGenerators[objectName]) {
var category = customGenerators[objectName][categoryName];
for (var iBlock = 0; iBlock < category.length; iBlock++) {
var block = category[iBlock];
/* TODO: Allow library writers to provide their own JS/Python code instead of just a handler */
this.completeBlockHandler(block, objectName, this.mainContext);
this.completeBlockJson(block, objectName, categoryName, this.mainContext); /* category.category is category name */
this.completeBlockXml(block);
this.completeCodeGenerators(block, objectName);
this.applyCodeGenerators(block);
this.createBlock(block);
}
// TODO: Anything of this still needs to be done?
//this.createGenerator(label, objectName + "." + code, generator.type, generator.nbParams);
//this.createBlock(label, generator.labelFr, generator.type, generator.nbParams);
}
}
},
getBlocklyLibCode: function(generators) {
var strCode = "";
for (var objectName in generators) {
strCode += "var " + objectName + " = {\n";
for (var iGen = 0; iGen < generators[objectName].length; iGen++) {
var generator = generators[objectName][iGen];
if (generator.nbParams == 0) {
strCode += generator.codeFr + ": function() { ";
} else {
strCode += generator.codeFr + ": function(param1) { ";
}
if (generator.type == 1) {
strCode += "return ";
}
if (generator.nbParams == 0) {
strCode += objectName + "_" + generator.labelEn + "(); }";
} else {
strCode += objectName + "_" + generator.labelEn + "(param1); }";
}
if (iGen < generators[objectName].length - 1) {
strCode += ",";
}
strCode += "\n";
}
strCode += "};\n\n";
}
strCode += "Math['max'] = function(a, b) { if (a > b) return a; return b; };\n";
strCode += "Math['min'] = function(a, b) { if (a > b) return b; return a; };\n";
return strCode;
},
getDefaultColours: function() {
var colours = {
categories: {
logic: 210,
loops: 120,
control: 120,
math: 230,
operator: 230,
texts: 160,
lists: 260,
colour: 20,
variables: 330,
functions: 290,
_default: 65
},
blocks: {}
};
if (typeof this.mainContext.provideBlocklyColours == "function") {
var providedColours = this.mainContext.provideBlocklyColours();
for (var group in providedColours) {
if (!(group in colours)) {
colours[group] = {};
}
for (name in providedColours[group]) {
colours[group][name] = providedColours[group][name];
}
}
}
if (typeof provideBlocklyColours == "function") {
var providedColours = provideBlocklyColours();
for (var group in providedColours) {
if (!(group in colours)) {
colours[group] = {};
}
for (name in providedColours[group]) {
colours[group][name] = providedColours[group][name];
}
}
}
return colours;
},
getStdBlocks: function() {
return this.scratchMode ? this.getStdScratchBlocks() : this.getStdBlocklyBlocks();
},
getStdBlocklyBlocks: function() {
return {
input: [
{
name: "input_num",
blocklyXml: "<block type='input_num'></block>"
},
{
name: "input_num_list",
blocklyXml: "<block type='input_num_list'></block>"
},
{
name: "input_line",
blocklyXml: "<block type='input_line'></block>"
},
{
name: "input_num_next",
blocklyXml: "<block type='input_num_next'></block>"
},
{
name: "input_char",
blocklyXml: "<block type='input_char'></block>"
},
{
name: "input_word",
blocklyXml: "<block type='input_word'></block>"
}
],
logic: [
{
name: "controls_if",
blocklyXml: "<block type='controls_if'></block>"
},
{
name: "controls_if_else",
blocklyXml: "<block type='controls_if'><mutation else='1'></mutation></block>",
excludedByDefault: this.mainContext ? this.mainContext.showIfMutator : false
},
{
name: "logic_compare",
blocklyXml: "<block type='logic_compare'></block>"
},
{
name: "logic_operation",
blocklyXml: "<block type='logic_operation' inline='false'></block>"
},
{
name: "logic_negate",
blocklyXml: "<block type='logic_negate'></block>"
},
{
name: "logic_boolean",
blocklyXml: "<block type='logic_boolean'></block>"
},
{
name: "logic_null",
blocklyXml: "<block type='logic_null'></block>",
excludedByDefault: true
},
{
name: "logic_ternary",
blocklyXml: "<block type='logic_ternary'></block>",
excludedByDefault: true
}
],
loops: [
{
name: "controls_loop",
blocklyXml: "<block type='controls_loop'></block>",
excludedByDefault: true
},
{
name: "controls_repeat",
blocklyXml: "<block type='controls_repeat'></block>",
excludedByDefault: true
},
{
name: "controls_repeat_ext",
blocklyXml: "<block type='controls_repeat_ext'>" +
" <value name='TIMES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>10</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "controls_repeat_ext_noShadow",
blocklyXml: "<block type='controls_repeat_ext'></block>",
excludedByDefault: true
},
{
name: "controls_whileUntil",
blocklyXml: "<block type='controls_whileUntil'></block>"
},
{
name: "controls_untilWhile",
blocklyXml: "<block type='controls_whileUntil'><field name='MODE'>UNTIL</field></block>",
excludedByDefault: true
},
{
name: "controls_for",
blocklyXml: "<block type='controls_for'>" +
" <value name='FROM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='TO'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>10</field>" +
" </shadow>" +
" </value>" +
" <value name='BY'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "controls_for_noShadow",
blocklyXml: "<block type='controls_for'></block>",
excludedByDefault: true
},
{
name: "controls_for_fillShadow",
blocklyXml: "<block type='controls_for'>" +
" <value name='FROM'>" +
" <block type='math_number'>" +
" <field name='NUM'>1</field>" +
" </block>" +
" </value>" +
" <value name='TO'>" +
" <block type='math_number'>" +
" <field name='NUM'>10</field>" +
" </block>" +
" </value>" +
" <value name='BY'>" +
" <block type='math_number'>" +
" <field name='NUM'>1</field>" +
" </block>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "controls_forEach",
blocklyXml: "<block type='controls_forEach'></block>",
excludedByDefault: true
},
{
name: "controls_flow_statements",
blocklyXml: "<block type='controls_flow_statements'></block>"
},
{
name: "controls_infiniteloop",
blocklyXml: "<block type='controls_infiniteloop'></block>",
excludedByDefault: true
},
],
math: [
{
name: "math_number",
blocklyXml: "<block type='math_number' gap='32'></block>"
},
{
name: "math_arithmetic",
blocklyXml: "<block type='math_arithmetic'>" +
" <value name='A'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='B'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "math_arithmetic_noShadow",
blocklyXml: "<block type='math_arithmetic'></block>",
excludedByDefault: true
},
{
name: "math_single",
blocklyXml: "<block type='math_single'>" +
" <value name='NUM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>9</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "math_single_noShadow",
blocklyXml: "<block type='math_single'></block>",
excludedByDefault: true
},
{
name: "math_extra_single",
blocklyXml: "<block type='math_extra_single'>" +
" <value name='NUM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>9</field>" +
" </shadow>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "math_extra_single_noShadow",
blocklyXml: "<block type='math_extra_single'></block>",
excludedByDefault: true
},
{
name: "math_extra_double",
blocklyXml: "<block type='math_extra_double'>" +
" <value name='A'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='B'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "math_extra_double",
blocklyXml: "<block type='math_extra_double'></block>",
excludedByDefault: true
},
{
name: "math_trig",
blocklyXml: "<block type='math_trig'>" +
" <value name='NUM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>45</field>" +
" </shadow>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "math_trig_noShadow",
blocklyXml: "<block type='math_trig'></block>",
excludedByDefault: true
},
{
name: "math_constant",
blocklyXml: "<block type='math_constant'></block>",
excludedByDefault: true
},
{
name: "math_number_property",
blocklyXml: "<block type='math_number_property'>" +
" <value name='NUMBER_TO_CHECK'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "math_number_property_noShadow",
blocklyXml: "<block type='math_number_property'></block>",
excludedByDefault: true
},
{
name: "math_round",
blocklyXml: "<block type='math_round'>" +
" <value name='NUM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>3.1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "math_round_noShadow",
blocklyXml: "<block type='math_round'></block>",
excludedByDefault: true
},
{
name: "math_on_list",
blocklyXml: "<block type='math_on_list'></block>",
excludedByDefault: true
},
{
name: "math_modulo",
blocklyXml: "<block type='math_modulo'>" +
" <value name='DIVIDEND'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>64</field>" +
" </shadow>" +
" </value>" +
" <value name='DIVISOR'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>10</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "math_modulo_noShadow",
blocklyXml: "<block type='math_modulo'></block>",
excludedByDefault: true
},
{
name: "math_constrain",
blocklyXml: "<block type='math_constrain'>" +
" <value name='VALUE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>50</field>" +
" </shadow>" +
" </value>" +
" <value name='LOW'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='HIGH'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>100</field>" +
" </shadow>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "math_constrain_noShadow",
blocklyXml: "<block type='math_constrain'></block>",
excludedByDefault: true
},
{
name: "math_random_int",
blocklyXml: "<block type='math_random_int'>" +
" <value name='FROM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='TO'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>100</field>" +
" </shadow>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "math_random_int_noShadow",
blocklyXml: "<block type='math_random_int'></block>",
excludedByDefault: true
},
{
name: "math_random_float",
blocklyXml: "<block type='math_random_float'></block>",
excludedByDefault: true
}
],
texts: [
{
name: "text",
blocklyXml: "<block type='text'></block>"
},
{
name: "text_eval",
blocklyXml: "<block type='text_eval'></block>"
},
{
name: "text_print",
blocklyXml: "<block type='text_print'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_print_noend",
blocklyXml: "<block type='text_print_noend'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_join",
blocklyXml: "<block type='text_join'></block>"
},
{
name: "text_append",
blocklyXml: "<block type='text_append'></block>"
},
{
name: "text_length",
blocklyXml: "<block type='text_length'>" +
" <value name='VALUE'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_length_noShadow",
blocklyXml: "<block type='text_length'></block>",
excludedByDefault: true
},
{
name: "text_isEmpty",
blocklyXml: "<block type='text_isEmpty'>" +
" <value name='VALUE'>" +
" <shadow type='text'>" +
" <field name='TEXT'></field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_isEmpty_noShadow",
blocklyXml: "<block type='text_isEmpty'></block>",
excludedByDefault: true
},
{
name: "text_indexOf",
blocklyXml: "<block type='text_indexOf'>" +
" <value name='VALUE'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{textVariable}</field>" +
" </block>" +
" </value>" +
" <value name='FIND'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_indexOf_noShadow",
blocklyXml: "<block type='text_indexOf'></block>",
excludedByDefault: true
},
{
name: "text_charAt",
blocklyXml: "<block type='text_charAt'>" +
" <value name='VALUE'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{textVariable}</field>" +
" </block>" +
" </value>" +
"</block>"
},
{
name: "text_charAt_noShado",
blocklyXml: "<block type='text_charAt'></block>",
excludedByDefault: true
},
{
name: "text_getSubstring",
blocklyXml: "<block type='text_getSubstring'>" +
" <value name='STRING'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{textVariable}</field>" +
" </block>" +
" </value>" +
"</block>"
},
{
name: "text_getSubstring_noShadow",
blocklyXml: "<block type='text_getSubstring'></block>",
excludedByDefault: true
},
{
name: "text_changeCase",
blocklyXml: "<block type='text_changeCase'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_changeCase_noShadow",
blocklyXml: "<block type='text_changeCase'></block>",
excludedByDefault: true
},
{
name: "text_trim",
blocklyXml: "<block type='text_trim'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_trim_noShadow",
blocklyXml: "<block type='text_trim'></block>",
excludedByDefault: true
},
{
name: "text_print_noShadow",
blocklyXml: "<block type='text_print'></block>",
excludedByDefault: true
},
{
name: "text_prompt_ext",
blocklyXml: "<block type='text_prompt_ext'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>",
excludedByDefault: true
},
{
name: "text_prompt_ext_noShadow",
blocklyXml: "<block type='text_prompt_ext'></block>",
excludedByDefault: true
}
],
lists: [
{
name: "lists_create_with_empty",
blocklyXml: "<block type='lists_create_with'>" +
" <mutation items='0'></mutation>" +
"</block>"
},
{
name: "lists_create_with",
blocklyXml: "<block type='lists_create_with'></block>"
},
{
name: "lists_repeat",
blocklyXml: "<block type='lists_repeat'>" +
" <value name='NUM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>5</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "lists_length",
blocklyXml: "<block type='lists_length'></block>"
},
{
name: "lists_isEmpty",
blocklyXml: "<block type='lists_isEmpty'></block>"
},
{
name: "lists_indexOf",
blocklyXml: "<block type='lists_indexOf'>" +
" <value name='VALUE'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{listVariable}</field>" +
" </block>" +
" </value>" +
"</block>"
},
{
name: "lists_getIndex",
blocklyXml: "<block type='lists_getIndex'>" +
" <value name='VALUE'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{listVariable}</field>" +
" </block>" +
" </value>" +
"</block>"
},
{
name: "lists_setIndex",
blocklyXml: "<block type='lists_setIndex'>" +
" <value name='LIST'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{listVariable}</field>" +
" </block>" +
" </value>" +
"</block>"
},
{
name: "lists_getSublist",
blocklyXml: "<block type='lists_getSublist'>" +
" <value name='LIST'>" +
" <block type='variables_get'>" +
" <field name='VAR'>{listVariable}</field>" +
" </block>" +
" </value>" +
"</block>"
},
{
name: "lists_sort_place",
blocklyXml: "<block type='lists_sort_place'></block>"
},
{
name: "lists_sort",
blocklyXml: "<block type='lists_sort'></block>"
},
{
name: "lists_split",
blocklyXml: "<block type='lists_split'>" +
" <value name='DELIM'>" +
" <shadow type='text'>" +
" <field name='TEXT'>,</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "lists_append",
blocklyXml: "<block type='lists_append'></block>"
}
],
tables: [
{
name: "tables_2d_init",
blocklyXml: "<block type='tables_2d_init'>" +
" <value name='LINES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='COLS'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_2d_set",
blocklyXml: "<block type='tables_2d_set'>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_2d_get",
blocklyXml: "<block type='tables_2d_get'>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_3d_init",
blocklyXml: "<block type='tables_3d_init'>" +
" <value name='LAYERS'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='LINES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='COLS'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_3d_set",
blocklyXml: "<block type='tables_3d_set'>" +
" <value name='LAYER'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_3d_get",
blocklyXml: "<block type='tables_3d_get'>" +
" <value name='LAYER'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
}
],
// Note :: this category is not enabled unless explicitly specified
colour: [
{
name: "colour_picker",
blocklyXml: "<block type='colour_picker'></block>"
},
{
name: "colour_random",
blocklyXml: "<block type='colour_random'></block>"
},
{
name: "colour_rgb",
blocklyXml: "<block type='colour_rgb'>" +
" <value name='RED'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>100</field>" +
" </shadow>" +
" </value>" +
" <value name='GREEN'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>50</field>" +
" </shadow>" +
" </value>" +
" <value name='BLUE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "colour_rgb_noShadow",
blocklyXml: "<block type='colour_rgb'></block>",
excludedByDefault: true
},
{
name: "colour_blend",
blocklyXml: "<block type='colour_blend'>" +
" <value name='COLOUR1'>" +
" <shadow type='colour_picker'>" +
" <field name='COLOUR'>#ff0000</field>" +
" </shadow>" +
" </value>" +
" <value name='COLOUR2'>" +
" <shadow type='colour_picker'>" +
" <field name='COLOUR'>#3333ff</field>" +
" </shadow>" +
" </value>" +
" <value name='RATIO'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0.5</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "colour_blend_noShadow",
blocklyXml: "<block type='colour_blend'></block>",
excludedByDefault: true
}
],
dicts: [
{
name: "dicts_create_with",
blocklyXml: "<block type='dicts_create_with'></block>"
},
{
name: "dict_get_literal",
blocklyXml: "<block type='dict_get_literal'></block>"
},
{
name: "dict_set_literal",
blocklyXml: "<block type='dict_set_literal'></block>"
},
{
name: "dict_keys",
blocklyXml: "<block type='dict_keys'></block>"
}
],
variables: [],
functions: []
};
},
getStdScratchBlocks: function() {
// TODO :: make the list of standard scratch blocks
return {
control: [
{
name: "control_if",
blocklyXml: "<block type='control_if'></block>"
},
{
name: "control_if_else",
blocklyXml: "<block type='control_if_else'></block>"
},
{
name: "control_repeat",
blocklyXml: "<block type='control_repeat'>" +
" <value name='TIMES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>10</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "control_repeat_until",
blocklyXml: "<block type='control_repeat_until'></block>"
},
{
name: "control_forever",
blocklyXml: "<block type='control_forever'></block>",
excludedByDefault: true
}
],
input: [
{
name: "input_num",
blocklyXml: "<block type='input_num'></block>"
},
{
name: "input_num_list",
blocklyXml: "<block type='input_num_list'></block>"
},
{
name: "input_line",
blocklyXml: "<block type='input_line'></block>"
},
{
name: "input_num_next",
blocklyXml: "<block type='input_num_next'></block>"
},
{
name: "input_char",
blocklyXml: "<block type='input_char'></block>"
},
{
name: "input_word",
blocklyXml: "<block type='input_word'></block>"
}
],
lists: [
{
name: "data_listrepeat",
blocklyXml: "<block type='data_listrepeat'>" +
" <field name='LIST'>" + (this.strings ? this.strings.listVariable : 'list') + "</field>" +
" <value name='ITEM'>" +
" <shadow type='text'>" +
" <field name='TEXT'></field>" +
" </shadow>" +
" </value>" +
" <value name='TIMES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "data_itemoflist",
blocklyXml: "<block type='data_itemoflist'>" +
" <field name='LIST'>" + (this.strings ? this.strings.listVariable : 'list') + "</field>" +
" <value name='INDEX'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "data_replaceitemoflist",
blocklyXml: "<block type='data_replaceitemoflist'>" +
" <field name='LIST'>" + (this.strings ? this.strings.listVariable : 'list') + "</field>" +
" <value name='INDEX'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='text'>" +
" <field name='TEXT'></field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "lists_sort_place",
blocklyXml: "<block type='lists_sort_place'></block>"
}
],
math: [
{
name: "math_number",
blocklyXml: "<block type='math_number' gap='32'></block>"
}
],
operator: [
{
name: "operator_add",
blocklyXml: "<block type='operator_add'>" +
" <value name='NUM1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='NUM2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_subtract",
blocklyXml: "<block type='operator_subtract'>" +
" <value name='NUM1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='NUM2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_multiply",
blocklyXml: "<block type='operator_multiply'>" +
" <value name='NUM1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='NUM2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_divide",
blocklyXml: "<block type='operator_divide'>" +
" <value name='NUM1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='NUM2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_dividefloor",
blocklyXml: "<block type='operator_dividefloor'>" +
" <value name='NUM1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='NUM2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_equals",
blocklyXml: "<block type='operator_equals'>" +
" <value name='OPERAND1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='OPERAND2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_gt",
blocklyXml: "<block type='operator_gt'>" +
" <value name='OPERAND1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='OPERAND2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_gte",
blocklyXml: "<block type='operator_gte'>" +
" <value name='OPERAND1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='OPERAND2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_lt",
blocklyXml: "<block type='operator_lt'>" +
" <value name='OPERAND1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='OPERAND2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_lte",
blocklyXml: "<block type='operator_lte'>" +
" <value name='OPERAND1'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
" <value name='OPERAND2'><shadow type='math_number'><field name='NUM'></field></shadow></value>" +
"</block>"
},
{
name: "operator_and",
blocklyXml: "<block type='operator_and'></block>"
},
{
name: "operator_or",
blocklyXml: "<block type='operator_or'></block>"
},
{
name: "operator_not",
blocklyXml: "<block type='operator_not'></block>"
},
{
name: "operator_join",
blocklyXml: "<block type='operator_join'>" +
" <value name='STRING1'><shadow type='text'><field name='TEXT'></field></shadow></value>" +
" <value name='STRING2'><shadow type='text'><field name='TEXT'></field></shadow></value>" +
"</block>"
}
],
tables: [
{
name: "tables_2d_init",
blocklyXml: "<block type='tables_2d_init'>" +
" <value name='LINES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='COLS'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_2d_set",
blocklyXml: "<block type='tables_2d_set'>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_2d_get",
blocklyXml: "<block type='tables_2d_get'>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_3d_init",
blocklyXml: "<block type='tables_3d_init'>" +
" <value name='LAYERS'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='LINES'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='COLS'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_3d_set",
blocklyXml: "<block type='tables_3d_set'>" +
" <value name='LAYER'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='ITEM'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>0</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "tables_3d_get",
blocklyXml: "<block type='tables_3d_get'>" +
" <value name='LAYER'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>2</field>" +
" </shadow>" +
" </value>" +
" <value name='LINE'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
" <value name='COL'>" +
" <shadow type='math_number'>" +
" <field name='NUM'>1</field>" +
" </shadow>" +
" </value>" +
"</block>"
}
],
texts: [
{
name: "text_print",
blocklyXml: "<block type='text_print'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_print_noend",
blocklyXml: "<block type='text_print_noend'>" +
" <value name='TEXT'>" +
" <shadow type='text'>" +
" <field name='TEXT'>abc</field>" +
" </shadow>" +
" </value>" +
"</block>"
},
{
name: "text_eval",
blocklyXml: "<block type='text_eval'></block>"
}
],
variables: [],
functions: []
};
},
getBlockXmlInfo: function(generatorStruct, blockName) {
for (var categoryName in generatorStruct) {
var blocks = generatorStruct[categoryName];
for (var iBlock = 0; iBlock < blocks.length; iBlock++) {
var block = blocks[iBlock];
if (block.name == blockName) {
return {
category: categoryName,
xml: block.blocklyXml
};
}
}
}
console.error("Block not found: " + blockName);
return null;
},
addBlocksAndCategories: function(blockNames, blocksDefinition, categoriesInfos) {
var colours = this.getDefaultColours();
for (var iBlock = 0; iBlock < blockNames.length; iBlock++) {
var blockName = blockNames[iBlock];
var blockXmlInfo = this.getBlockXmlInfo(blocksDefinition, blockName);
var categoryName = blockXmlInfo.category;
if (!(categoryName in categoriesInfos)) {
categoriesInfos[categoryName] = {
blocksXml: [],
colour: colours.blocks[blockName]
};
}
var blockXml = blockXmlInfo.xml;
if(categoriesInfos[categoryName].blocksXml.indexOf(blockXml) == -1) {
categoriesInfos[categoryName].blocksXml.push(blockXml);
}
this.addBlocksAllowed([blockName]);
}
// by the way, just change the defaul colours of the blockly blocks:
if(!this.scratchMode) {
var defCat = ["logic", "loops", "math", "texts", "lists", "colour"];
for (var iCat in defCat) {
Blockly.Blocks[defCat[iCat]].HUE = colours.categories[defCat[iCat]];
}
}
},
getToolboxXml: function() {
var categoriesInfos = {};
var colours = this.getDefaultColours();
// Reset the flyoutOptions for the variables and the procedures
Blockly.Variables.flyoutOptions = {
any: false,
anyButton: !!this.includeBlocks.groupByCategory,
fixed: [],
includedBlocks: {get: true, set: true, incr: true},
shortList: true
};
Blockly.Procedures.flyoutOptions = {
includedBlocks: {noret: false, ret: false, ifret: false}
};
// Initialize allBlocksAllowed
this.allBlocksAllowed = [];
this.addBlocksAllowed(['robot_start']);
if(this.scratchMode) {
this.addBlocksAllowed(['math_number', 'text']);
}
// *** Blocks from the lib
if(this.includeBlocks.generatedBlocks && 'wholeCategories' in this.includeBlocks.generatedBlocks) {
for(var blockType in this.includeBlocks.generatedBlocks.wholeCategories) {
var categories = this.includeBlocks.generatedBlocks.wholeCategories[blockType];
for(var i=0; i<categories.length; i++) {
var category = categories[i];
if(blockType in this.mainContext.customBlocks && category in this.mainContext.customBlocks[blockType]) {
var contextBlocks = this.mainContext.customBlocks[blockType][category];
var blockNames = [];
for(var i=0; i<contextBlocks.length; i++) {
blockNames.push(contextBlocks[i].name);
}
this.addBlocksAndCategories(
blockNames,
this.mainContext.customBlocks[blockType],
categoriesInfos
);
}
}
}
}
if(this.includeBlocks.generatedBlocks && 'singleBlocks' in this.includeBlocks.generatedBlocks) {
for(var blockType in this.includeBlocks.generatedBlocks.singleBlocks) {
this.addBlocksAndCategories(
this.includeBlocks.generatedBlocks.singleBlocks[blockType],
this.mainContext.customBlocks[blockType],
categoriesInfos
);
}
}
for (var blockType in this.includeBlocks.generatedBlocks) {
if(blockType == 'wholeCategories' || blockType == 'singleBlocks') continue;
this.addBlocksAndCategories(
this.includeBlocks.generatedBlocks[blockType],
this.mainContext.customBlocks[blockType],
categoriesInfos
);
}
for (var genName in this.simpleGenerators) {
for (var iGen = 0; iGen < this.simpleGenerators[genName].length; iGen++) {
var generator = this.simpleGenerators[genName][iGen];
if (categoriesInfos[generator.category] == undefined) {
categoriesInfos[generator.category] = {
blocksXml: [],
colour: 210
};
}
var blockName = (genName == '.') ? generator.label + "__" : genName + "_" + generator.label + "__";
categoriesInfos[generator.category].blocksXml.push("<block type='"+blockName+"'></block>");
}
}
// *** Standard blocks
var stdBlocks = this.getStdBlocks();
var taskStdInclude = (this.includeBlocks && this.includeBlocks.standardBlocks) || {};
var stdInclude = {
wholeCategories: [],
singleBlocks: [],
excludedBlocks: []
};
// Merge all lists into stdInclude
if (taskStdInclude.includeAll) {
if(this.scratchMode) {
stdInclude.wholeCategories = ["control", "input", "lists", "operator", "tables", "texts", "variables", "functions"];
} else {
stdInclude.wholeCategories = ["input", "logic", "loops", "math", "texts", "lists", "dicts", "tables", "variables", "functions"];
}
}
mergeIntoArray(stdInclude.wholeCategories, taskStdInclude.wholeCategories || []);
mergeIntoArray(stdInclude.singleBlocks, taskStdInclude.singleBlocks || []);
mergeIntoArray(stdInclude.excludedBlocks, taskStdInclude.excludedBlocks || []);
// Add block sets
if(taskStdInclude.blockSets) {
for(var iSet in taskStdInclude.blockSets) {
mergeIntoObject(stdInclude, blocklySets[taskStdInclude.blockSets[iSet]]);
}
}
// Prevent from using excludedBlocks if includeAll is set
if(taskStdInclude.includeAll) { stdInclude.excludedBlocks = []; }
// Remove excludedBlocks from singleBlocks
for(var iBlock=0; iBlock < stdInclude.singleBlocks; iBlock++) {
if(arrayContains(stdInclude.excludedBlocks, stdInclude.singleBlocks[iBlock])) {
stdInclude.singleBlocks.splice(iBlock, 1);
iBlock--;
}
}
var handledCategories = [];
for (var iCategory = 0; iCategory < stdInclude.wholeCategories.length; iCategory++) {
var categoryName = stdInclude.wholeCategories[iCategory];
if(this.scratchMode && !taskStdInclude.includeAll && blocklyToScratch.wholeCategories[categoryName]) {
categoryName = blocklyToScratch.wholeCategories[categoryName];
}
if(arrayContains(handledCategories, categoryName)) { continue; }
handledCategories.push(categoryName);
if (!(categoryName in categoriesInfos)) {
categoriesInfos[categoryName] = {
blocksXml: []
};
}
if (categoryName == 'variables') {
Blockly.Variables.flyoutOptions.any = true;
continue;
} else if (categoryName == 'functions') {
Blockly.Procedures.flyoutOptions.includedBlocks = {noret: true, ret: true, ifret: true};
continue;
}
var blocks = stdBlocks[categoryName];
if(blocks) {
if (!(blocks instanceof Array)) { // just for now, maintain backwards compatibility
blocks = blocks.blocks;
}
var blockNames = [];
for (var iBlock = 0; iBlock < blocks.length; iBlock++) {
if (!(blocks[iBlock].excludedByDefault) && !arrayContains(stdInclude.excludedBlocks, blocks[iBlock].name)) {
blockNames.push(blocks[iBlock].name);
categoriesInfos[categoryName].blocksXml.push(blocks[iBlock].blocklyXml);
}
}
this.addBlocksAllowed(blockNames);
}
}
var singleBlocks = stdInclude.singleBlocks;
for(var iBlock = 0; iBlock < singleBlocks.length; iBlock++) {
var blockName = singleBlocks[iBlock];
if(blockName == 'procedures_defnoreturn') {
Blockly.Procedures.flyoutOptions.includedBlocks['noret'] = true;
} else if(blockName == 'procedures_defreturn') {
Blockly.Procedures.flyoutOptions.includedBlocks['ret'] = true;
} else if(blockName == 'procedures_ifreturn') {
Blockly.Procedures.flyoutOptions.includedBlocks['ifret'] = true;
} else {
continue;
}
// If we're here, a block has been found
this.addBlocksAllowed([blockName, 'procedures_callnoreturn', 'procedures_callreturn']);
singleBlocks.splice(iBlock, 1);
iBlock--;
}
if(Blockly.Procedures.flyoutOptions.includedBlocks['noret']
|| Blockly.Procedures.flyoutOptions.includedBlocks['ret']
|| Blockly.Procedures.flyoutOptions.includedBlocks['ifret']) {
if(Blockly.Procedures.flyoutOptions.includedBlocks['noret']) {
this.addBlocksAllowed(['procedures_defnoreturn', 'procedures_callnoreturn']);
} else if(Blockly.Procedures.flyoutOptions.includedBlocks['ret']) {
this.addBlocksAllowed(['procedures_defreturn', 'procedures_callnoreturn']);
} else if(Blockly.Procedures.flyoutOptions.includedBlocks['ifret']) {
this.addBlocksAllowed(['procedures_ifreturn']);
}
categoriesInfos['functions'] = {
blocksXml: []
};
if(this.scratchMode && !arrayContains(singleBlocks, 'math_number')) {
singleBlocks.push('math_number'); // TODO :: temporary
}
if(!this.includeBlocks.groupByCategory) {
console.error('Task configuration error: groupByCategory must be activated for functions.');
}
}
this.addBlocksAndCategories(singleBlocks, stdBlocks, categoriesInfos);
// Handle variable blocks, which are normally automatically added with
// the VARIABLES category but can be customized here
if (typeof this.includeBlocks.variables !== 'undefined') {
Blockly.Variables.flyoutOptions.fixed = (this.includeBlocks.variables.length > 0) ? this.includeBlocks.variables : [];
if (typeof this.includeBlocks.variablesOnlyBlocks !== 'undefined') {
Blockly.Variables.flyoutOptions.includedBlocks = {get: false, set: false, incr: false};
for (var iBlock=0; iBlock < this.includeBlocks.variablesOnlyBlocks.length; iBlock++) {
Blockly.Variables.flyoutOptions.includedBlocks[this.includeBlocks.variablesOnlyBlocks[iBlock]] = true;
}
}
var varAnyIdx = Blockly.Variables.flyoutOptions.fixed.indexOf('*');
if(varAnyIdx > -1) {
Blockly.Variables.flyoutOptions.fixed.splice(varAnyIdx, 1);
Blockly.Variables.flyoutOptions.any = true;
}
var blocksXml = Blockly.Variables.flyoutCategory();
var xmlSer = new XMLSerializer();
for(var i=0; i<blocksXml.length; i++) {
blocksXml[i] = xmlSer.serializeToString(blocksXml[i]);
}
categoriesInfos["variables"] = {
blocksXml: blocksXml,
colour: 330
}
}
if(Blockly.Variables.flyoutOptions.includedBlocks['get']) {
this.addBlocksAllowed(['variables_get']);
}
if(Blockly.Variables.flyoutOptions.includedBlocks['set']) {
this.addBlocksAllowed(['variables_set']);
}
if(Blockly.Variables.flyoutOptions.includedBlocks['incr']) {
this.addBlocksAllowed(['math_change']);
}
var xmlString = "";
for (var categoryName in categoriesInfos) {
var categoryInfo = categoriesInfos[categoryName];
if (this.includeBlocks.groupByCategory) {
var colour = categoryInfo.colour;
if (typeof(colour) == "undefined") {
colour = colours.categories[categoryName]
if (typeof(colour) == "undefined") {
colour = colours.categories._default;
}
}
xmlString += "<category "
+ " name='" + this.strings.categories[categoryName] + "'"
+ " colour='" + colour + "'"
+ (this.scratchMode ? " secondaryColour='" + colour + "'" : '')
+ (categoryName == 'variables' ? ' custom="VARIABLE"' : '')
+ (categoryName == 'functions' ? ' custom="PROCEDURE"' : '')
+ ">";
}
var blocks = categoryInfo.blocksXml;
for (var iBlock = 0; iBlock < blocks.length; iBlock++) {
xmlString += blocks[iBlock];
}
if (this.includeBlocks.groupByCategory) {
xmlString += "</category>";
}
}
(function (strings) {
xmlString = xmlString.replace(/{(\w+)}/g, function(m, p1) {return strings[p1]}); // taken from blockly/demo/code
})(this.strings);
return xmlString;
},
addExtraBlocks: function() {
var that = this;
Blockly.Blocks['controls_untilWhile'] = Blockly.Blocks['controls_whileUntil'];
Blockly.JavaScript['controls_untilWhile'] = Blockly.JavaScript['controls_whileUntil'];
Blockly.Python['controls_untilWhile'] = Blockly.Python['controls_whileUntil'];
Blockly.Blocks['math_angle'] = {
init: function() {
this.setOutput(true, 'Number');
this.appendDummyInput()
.appendField(new Blockly.FieldAngle(90), "ANGLE");
this.setColour(Blockly.Blocks.math.HUE);
}
};
Blockly.JavaScript['math_angle'] = function(block) {
return ['' + block.getFieldValue('ANGLE'), Blockly.JavaScript.ORDER_FUNCTION_CALL];
};
Blockly.Python['math_angle'] = function(block) {
return ['' + block.getFieldValue('ANGLE'), Blockly.Python.ORDER_FUNCTION_CALL];
};
Blockly.Blocks['math_extra_single'] = {
/**
* Block for advanced math operators with single operand.
* @this Blockly.Block
*/
init: function() {
var OPERATORS =
[
[Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE, 'ABS'],
['-', 'NEG']
];
this.setHelpUrl(Blockly.Msg.MATH_SINGLE_HELPURL);
this.setColour(Blockly.Blocks.math.HUE);
this.setOutput(true, 'Number');
this.appendValueInput('NUM')
.setCheck('Number')
.appendField(new Blockly.FieldDropdown(OPERATORS), 'OP');
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('OP');
var TOOLTIPS = {
'ABS': Blockly.Msg.MATH_SINGLE_TOOLTIP_ABS,
'NEG': Blockly.Msg.MATH_SINGLE_TOOLTIP_NEG
};
return TOOLTIPS[mode];
});
}
};
Blockly.JavaScript['math_extra_single'] = Blockly.JavaScript['math_single'];
Blockly.Python['math_extra_single'] = Blockly.Python['math_single'];
Blockly.Blocks['math_extra_double'] = {
/**
* Block for advanced math operators with double operand.
* @this Blockly.Block
*/
init: function() {
var OPERATORS =
[
['min', 'MIN'],
['max', 'MAX']
];
this.setColour(Blockly.Blocks.math.HUE);
this.setInputsInline(true);
this.setOutput(true, 'Number');
this.appendDummyInput('OP').appendField(new Blockly.FieldDropdown([["min", "MIN"], ["max", "MAX"], ["", ""]]), "OP");
this.appendDummyInput().appendField(" entre ");
this.appendValueInput('A').setCheck('Number');
this.appendDummyInput().appendField(" et ");
this.appendValueInput('B').setCheck('Number');
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
var mode = thisBlock.getFieldValue('OP');
var TOOLTIPS = {
'MIN': that.strings.smallestOfTwoNumbers,
'MAX': that.strings.greatestOfTwoNumbers
};
return TOOLTIPS[mode];
});
}
};
Blockly.JavaScript['math_extra_double'] = function(block) {
// Math operators with double operand.
var operator = block.getFieldValue('OP');
var arg1 = Blockly.JavaScript.valueToCode(block, 'A', Blockly.JavaScript.ORDER_NONE) || '0';
var arg2 = Blockly.JavaScript.valueToCode(block, 'B', Blockly.JavaScript.ORDER_NONE) || '0';
if (operator == 'MIN') {
var code = "Math.min(" + arg1 + ", " + arg2 + ")";
}
if (operator == 'MAX') {
var code = "Math.max(" + arg1 + ", " + arg2 + ")";
}
return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
};
Blockly.Python['math_extra_double'] = function(block) {
// Math operators with double operand.
var operator = block.getFieldValue('OP');
var arg1 = Blockly.Python.valueToCode(block, 'A', Blockly.Python.ORDER_NONE) || '0';
var arg2 = Blockly.Python.valueToCode(block, 'B', Blockly.Python.ORDER_NONE) || '0';
if (operator == 'MIN') {
var code = "Math.min(" + arg1 + ", " + arg2 + ")";
}
if (operator == 'MAX') {
var code = "Math.max(" + arg1 + ", " + arg2 + ")";
}
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
};
Blockly.Blocks['controls_loop'] = {
init: function() {
this.appendDummyInput()
.appendField(that.strings.loopRepeat);
this.appendStatementInput("inner_blocks")
.setCheck(null)
.appendField(that.strings.loopDo);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setColour(that.getDefaultColours().categories["loops"])
this.setTooltip("");
this.setHelpUrl("");
}
}
Blockly.JavaScript['controls_loop'] = function(block) {
var statements = Blockly.JavaScript.statementToCode(block, 'inner_blocks');
var code = 'while(true){\n' + statements + '}\n';
return code;
};
Blockly.Blocks['controls_infiniteloop'] = {
init: function() {
this.appendStatementInput("inner_blocks")
.setCheck(null)
.appendField(that.strings.infiniteLoop);
this.setPreviousStatement(true, null);
this.setNextStatement(false, null);
this.setColour(that.getDefaultColours().categories["loops"])
this.setTooltip("");
this.setHelpUrl("");
}
}
Blockly.JavaScript['controls_infiniteloop'] = function(block) {
var statements = Blockly.JavaScript.statementToCode(block, 'inner_blocks');
var code = 'while(true){\n' + statements + '}\n';
return code;
};
Blockly.Python['controls_infiniteloop'] = function(block) {
// Do while/until loop.
var branch = Blockly.Python.statementToCode(block, 'inner_blocks');
branch = Blockly.Python.addLoopTrap(branch, block.id) ||
Blockly.Python.PASS;
return 'while True:\n' + branch;
};
if(this.scratchMode) {
Blockly.Blocks['robot_start'] = {
init: function() {
this.jsonInit({
"id": "event_whenflagclicked",
"message0": that.strings.startingBlockName,
// former Scratch-like display
/*"message0": that.strings.flagClicked,
"args0": [
{
"type": "field_image",
"src": Blockly.mainWorkspace.options.pathToMedia + "icons/event_whenflagclicked.svg",
"width": 24,
"height": 24,
"alt": "flag",
"flip_rtl": true
}
],*/
"inputsInline": true,
"nextStatement": null,
"category": Blockly.Categories.event,
"colour": Blockly.Colours.event.primary,
"colourSecondary": Blockly.Colours.event.secondary,
"colourTertiary": Blockly.Colours.event.tertiary
});
}
};
Blockly.JavaScript['control_forever'] = function(block) {
var statements = Blockly.JavaScript.statementToCode(block, 'SUBSTACK');
var code = 'while(true){\n' + statements + '}\n';
return code;
};
Blockly.Python['control_forever'] = function(block) {
// Do while/until loop.
var branch = Blockly.Python.statementToCode(block, 'SUBSTACK');
branch = Blockly.Python.addLoopTrap(branch, block.id) ||
Blockly.Python.PASS;
return 'while True:\n' + branch;
};
} else {
if (!this.mainContext.infos || !this.mainContext.infos.showIfMutator) {
var old = Blockly.Blocks.controls_if.init;
Blockly.Blocks.controls_if.init = function() {
old.call(this);
this.setMutator(undefined)
};
}
Blockly.Blocks['robot_start'] = {
init: function() {
this.appendDummyInput()
.appendField(that.strings.startingBlockName);
this.setNextStatement(true);
this.setColour(210);
this.setTooltip('');
this.deletable_ = false;
this.editable_ = false;
this.movable_ = false;
// this.setHelpUrl('http://www.example.com/');
}
};
}
Blockly.JavaScript['robot_start'] = function(block) {
return "";
};
Blockly.Python['robot_start'] = function(block) {
return "";
};
},
blocksToScratch: function(blockList) {
var scratchBlocks = [];
for (var iBlock = 0; iBlock < blockList.length; iBlock++) {
var blockName = blockList[iBlock];
if(blocklyToScratch.singleBlocks[blockName]) {
for(var b=0; b<blocklyToScratch.singleBlocks[blockName].length; b++) {
scratchBlocks.push(blocklyToScratch.singleBlocks[blockName][b]);
}
} else {
scratchBlocks.push(blockName);
}
}
return scratchBlocks;
},
fixScratch: function() {
// Store the maxBlocks information somewhere, as Scratch ignores it
Blockly.Workspace.prototype.maxBlocks = function () { return maxBlocks; };
// Translate requested Blocks from Blockly to Scratch blocks
// TODO :: full translation
this.includeBlocks.standardBlocks.singleBlocks = this.blocksToScratch(this.includeBlocks.standardBlocks.singleBlocks || []);
},
getFullCode: function(code) {
return this.getBlocklyLibCode(this.generators) + code + "program_end()";
},
checkCode: function(code, display) {
// TODO :: check a code is okay for validation; for now it's checked
// by getCode so this function is not useful in the Blockly/Scratch
// version
return true;
},
checkCodes: function(codes, display) {
// Check multiple codes
for(var i = 0; i < codes.length; i++) {
if(!this.checkCode(codes[i], display)) {
return false;
}
}
return true;
},
checkBlocksAreAllowed: function(xml, silent) {
if(this.includeBlocks && this.includeBlocks.standardBlocks && this.includeBlocks.standardBlocks.includeAll) { return true; }
var allowed = this.getBlocksAllowed();
var blockList = xml.getElementsByTagName('block');
var notAllowed = [];
function checkBlock(block) {
var blockName = block.getAttribute('type');
if(!arrayContains(allowed, blockName)
&& (blockName.substr(blockName.length - 9) != '_noShadow' || !arrayContains(allowed, blockName.substr(0, blockName.length - 9)))) {
notAllowed.push(blockName);
}
}
for(var i=0; i<blockList.length; i++) {
checkBlock(blockList[i]);
}
if(xml.localName == 'block') {
// Also check the top element
checkBlock(xml);
}
if(!silent && notAllowed.length > 0) {
console.error('Error: tried to load programs with unallowed blocks '+notAllowed.join(', '));
}
return !(notAllowed.length);
},
cleanBlockAttributes: function(xml, origin) {
// Clean up block attributes
if(!origin) {
origin = {x: 0, y: 0};
}
var blockList = xml.getElementsByTagName('block');
var minX = Infinity, minY = Infinity;
for(var i=0; i<blockList.length; i++) {
var block = blockList[i];
// Clean up IDs which contain now forbidden characters
var blockId = block.getAttribute('id');
if(blockId && (blockId.indexOf('%') != -1 || blockId.indexOf('$') != -1 || blockId.indexOf('^') != -1)) {
block.setAttribute('id', Blockly.genUid());
}
// Clean up read-only attributes
if(block.getAttribute('type') != 'robot_start') {
block.removeAttribute('deletable');
block.removeAttribute('movable');
block.removeAttribute('editable');
}
// Get minimum x and y
var x = block.getAttribute('x');
if(x !== null) { minX = Math.min(minX, parseInt(x)); }
var y = block.getAttribute('y');
if(y !== null) { minY = Math.min(minY, parseInt(y)); }
}
// Move blocks to start at x=0, y=0
for(var i=0; i<blockList.length; i++) {
var block = blockList[i];
var x = block.getAttribute('x');
if(x !== null) {
block.setAttribute('x', parseInt(x) - minX + origin.x);
}
var y = block.getAttribute('y');
if(y !== null) {
block.setAttribute('y', parseInt(y) - minY + origin.y);
}
}
}
};
}