Blockly.Blocks['factory_base'] = {
// Base of new block.
init: function() {
this.setColour(120);
this.appendDummyInput()
.appendField('name')
.appendField(new Blockly.FieldTextInput('block_type'), 'NAME');
this.appendStatementInput('INPUTS')
.setCheck('Input')
.appendField('inputs');
var dropdown = new Blockly.FieldDropdown([
['automatic inputs', 'AUTO'],
['external inputs', 'EXT'],
['inline inputs', 'INT']]);
this.appendDummyInput()
.appendField(dropdown, 'INLINE');
dropdown = new Blockly.FieldDropdown([
['no connections', 'NONE'],
['← left output', 'LEFT'],
['↕ top+bottom connections', 'BOTH'],
['↑ top connection', 'TOP'],
['↓ bottom connection', 'BOTTOM']],
function(option) {
this.sourceBlock_.updateShape_(option);
// Connect a shadow block to this new input.
this.sourceBlock_.spawnOutputShadow_(option);
});
this.appendDummyInput()
.appendField(dropdown, 'CONNECTIONS');
this.appendValueInput('COLOUR')
.setCheck('Colour')
.appendField('colour');
this.setTooltip('Build a custom block by plugging\n' +
'fields, inputs and other blocks here.');
this.setHelpUrl(
'
https://developers.google.com/blockly/guides/create-custom-blocks/block-factory');
},
mutationToDom: function() {
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('connections', this.getFieldValue('CONNECTIONS'));
return container;
},
domToMutation: function(xmlElement) {
var connections = xmlElement.getAttribute('connections');
this.updateShape_(connections);
},
spawnOutputShadow_: function(option) {
// Helper method for deciding which type of outputs this block needs
// to attach shadow blocks to.
switch (option) {
case 'LEFT':
this.connectOutputShadow_('OUTPUTTYPE');
break;
case 'TOP':
this.connectOutputShadow_('TOPTYPE');
break;
case 'BOTTOM':
this.connectOutputShadow_('BOTTOMTYPE');
break;
case 'BOTH':
this.connectOutputShadow_('TOPTYPE');
this.connectOutputShadow_('BOTTOMTYPE');
break;
}
},
connectOutputShadow_: function(outputType) {
// Helper method to create & connect shadow block.
var type = this.workspace.newBlock('type_null');
type.setShadow(true);
type.outputConnection.connect(this.getInput(outputType).connection);
type.initSvg();
type.render();
},
updateShape_: function(option) {
var outputExists = this.getInput('OUTPUTTYPE');
var topExists = this.getInput('TOPTYPE');
var bottomExists = this.getInput('BOTTOMTYPE');
if (option == 'LEFT') {
if (!outputExists) {
this.addTypeInput_('OUTPUTTYPE', 'output type');
}
} else if (outputExists) {
this.removeInput('OUTPUTTYPE');
}
if (option == 'TOP' || option == 'BOTH') {
if (!topExists) {
this.addTypeInput_('TOPTYPE', 'top type');
}
} else if (topExists) {
this.removeInput('TOPTYPE');
}
if (option == 'BOTTOM' || option == 'BOTH') {
if (!bottomExists) {
this.addTypeInput_('BOTTOMTYPE', 'bottom type');
}
} else if (bottomExists) {
this.removeInput('BOTTOMTYPE');
}
},
addTypeInput_: function(name, label) {
this.appendValueInput(name)
.setCheck('Type')
.appendField(label);
this.moveInputBefore(name, 'COLOUR');
}
};
var FIELD_MESSAGE = 'fields %1 %2';
var FIELD_ARGS = [
{
"type": "field_dropdown",
"name": "ALIGN",
"options": [['left', 'LEFT'], ['right', 'RIGHT'], ['centre', 'CENTRE']],
},
{
"type": "input_statement",
"name": "FIELDS",
"check": "Field"
}
];
var TYPE_MESSAGE = 'type %1';
var TYPE_ARGS = [
{
"type": "input_value",
"name": "TYPE",
"check": "Type",
"align": "RIGHT"
}
];
Blockly.Blocks['input_value'] = {
// Value input.
init: function() {
this.jsonInit({
"message0": "value input %1 by %2",
"args0": [
{
"type": "field_input",
"name": "INPUTNAME",
"text": "NAME"
},
{
"type": "input_dummy"
}
],
"message1": FIELD_MESSAGE,
"args1": FIELD_ARGS,
"message2": TYPE_MESSAGE,
"args2": TYPE_ARGS,
"previousStatement": "Input",
"nextStatement": "Input",
"colour": 210,
"tooltip": "A value socket for horizontal connections.",
"helpUrl": "
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=71"
});
},
onchange: function() {
inputNameCheck(this);
}
};
Blockly.Blocks['input_statement'] = {
// Statement input.
init: function() {
this.jsonInit({
"message0": "statement input %1 %2",
"args0": [
{
"type": "field_input",
"name": "INPUTNAME",
"text": "NAME"
},
{
"type": "input_dummy"
},
],
"message1": FIELD_MESSAGE,
"args1": FIELD_ARGS,
"message2": TYPE_MESSAGE,
"args2": TYPE_ARGS,
"previousStatement": "Input",
"nextStatement": "Input",
"colour": 210,
"tooltip": "A statement socket for enclosed vertical stacks.",
"helpUrl": "
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=246"
});
},
onchange: function() {
inputNameCheck(this);
}
};
Blockly.Blocks['input_dummy'] = {
// Dummy input.
init: function() {
this.jsonInit({
"message0": "dummy input",
"message1": FIELD_MESSAGE,
"args1": FIELD_ARGS,
"previousStatement": "Input",
"nextStatement": "Input",
"colour": 210,
"tooltip": "For adding fields on a separate row with no " +
"connections. Alignment options (left, right, centre) " +
"apply only to multi-line fields.",
"helpUrl": "
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293"
});
}
};
Blockly.Blocks['field_static'] = {
// Text value.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('text')
.appendField(new Blockly.FieldTextInput(''), 'TEXT');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Static text that serves as a label.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=88');
}
};
Blockly.Blocks['field_input'] = {
// Text input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('text input')
.appendField(new Blockly.FieldTextInput('default'), 'TEXT')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('An input field for the user to enter text.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_number'] = {
// Numeric input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('numeric input')
.appendField(new Blockly.FieldNumber(0), 'VALUE')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.appendDummyInput()
.appendField('min')
.appendField(new Blockly.FieldNumber(-Infinity), 'MIN')
.appendField('max')
.appendField(new Blockly.FieldNumber(Infinity), 'MAX')
.appendField('precision')
.appendField(new Blockly.FieldNumber(0, 0), 'PRECISION');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('An input field for the user to enter a number.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_angle'] = {
// Angle input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('angle input')
.appendField(new Blockly.FieldAngle('90'), 'ANGLE')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('An input field for the user to enter an angle.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=372');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_dropdown'] = {
// Dropdown menu.
init: function() {
this.appendDummyInput()
.appendField('dropdown')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.optionCount_ = 3;
this.updateShape_();
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setMutator(new Blockly.Mutator(['field_dropdown_option']));
this.setColour(160);
this.setTooltip('Dropdown menu with a list of options.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
},
mutationToDom: function(workspace) {
// Create XML to represent menu options.
var container = Blockly.utils.xml.createElement('mutation');
container.setAttribute('options', this.optionCount_);
return container;
},
domToMutation: function(container) {
// Parse XML to restore the menu options.
this.optionCount_ = parseInt(container.getAttribute('options'), 10);
this.updateShape_();
},
decompose: function(workspace) {
// Populate the mutator's dialog with this block's components.
var containerBlock = workspace.newBlock('field_dropdown_container');
containerBlock.initSvg();
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.optionCount_; i++) {
var optionBlock = workspace.newBlock('field_dropdown_option');
optionBlock.initSvg();
connection.connect(optionBlock.previousConnection);
connection = optionBlock.nextConnection;
}
return containerBlock;
},
compose: function(containerBlock) {
// Reconfigure this block based on the mutator dialog's components.
var optionBlock = containerBlock.getInputTargetBlock('STACK');
// Count number of inputs.
var data = [];
while (optionBlock) {
data.push([optionBlock.userData_, optionBlock.cpuData_]);
optionBlock = optionBlock.nextConnection &&
optionBlock.nextConnection.targetBlock();
}
this.optionCount_ = data.length;
this.updateShape_();
// Restore any data.
for (var i = 0; i < this.optionCount_; i++) {
this.setFieldValue(data[i][0] || 'option', 'USER' + i);
this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
}
},
saveConnections: function(containerBlock) {
// Store names and values for each option.
var optionBlock = containerBlock.getInputTargetBlock('STACK');
var i = 0;
while (optionBlock) {
optionBlock.userData_ = this.getFieldValue('USER' + i);
optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
i++;
optionBlock = optionBlock.nextConnection &&
optionBlock.nextConnection.targetBlock();
}
},
updateShape_: function() {
// Modify this block to have the correct number of options.
// Add new options.
for (var i = 0; i < this.optionCount_; i++) {
if (!this.getInput('OPTION' + i)) {
this.appendDummyInput('OPTION' + i)
.appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
.appendField(',')
.appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
}
}
// Remove deleted options.
while (this.getInput('OPTION' + i)) {
this.removeInput('OPTION' + i);
i++;
}
},
onchange: function() {
if (this.workspace && this.optionCount_ < 1) {
this.setWarningText('Drop down menu must\nhave at least one option.');
} else {
fieldNameCheck(this);
}
}
};
Blockly.Blocks['field_dropdown_container'] = {
// Container.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('add options');
this.appendStatementInput('STACK');
this.setTooltip('Add, remove, or reorder options\n' +
'to reconfigure this dropdown menu.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
this.contextMenu = false;
}
};
Blockly.Blocks['field_dropdown_option'] = {
// Add option.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('option');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip('Add a new option to the dropdown menu.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
this.contextMenu = false;
}
};
Blockly.Blocks['field_checkbox'] = {
// Checkbox.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('checkbox')
.appendField(new Blockly.FieldCheckbox('TRUE'), 'CHECKED')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Checkbox field.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=485');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_colour'] = {
// Colour input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('colour')
.appendField(new Blockly.FieldColour('#ff0000'), 'COLOUR')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatem0ent(true, 'Field');
this.setTooltip('Colour input field.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=495');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_variable'] = {
// Dropdown for variables.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('variable')
.appendField(new Blockly.FieldTextInput('item'), 'TEXT')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Dropdown menu for variable names.');
this.setHelpUrl('
https://www.youtube.com/watch?v=s2_xaEvcVI0#t=510');
},
onchange: function() {
fieldNameCheck(this);
}
};