forked from Open-CT/openct-tasks
2600 lines
110 KiB
JavaScript
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);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|